해커스쿨 FTZ - 13단계
cc언어 코드를 해석해보면, setreuid를 통해 권한을 얻는 내용을 실행하고, 프로그램을 실행할때 받았던 인자의 개수를 의미하는 argc가 1보다 클 경우 argv[1]의 내용을 buf에 복사하고, i가 0x1234567이 아니라면, 그러니까 long i의 값이 수정되면 출력 후 kill 시그널을 특정 프로세스에 보내주는 함수인 kill을 실행하는 코드이다.
kill 함수에 대해 잘 알지못해 kill 함수에 대해 찾아보았는데, 그 내용은 다음과 같다.
일단 kill 함수의 모양은 kill(pid_t pid, int sig) 이다.
pid 는 시그널을 받을 프로세스 id이다.
그러나 양의 정수일 경우에는 지정한 프로세스 id에만 시그널을 전송,
0일 경우에는 함수를 호출하는 프로세스와 같은 그룹에 있는 모든 프로세스에 시그널을 전송,
-1일 경우에는 함수를 호출하는 프로세스가 전송 할 수 있는 권한을 가진 모든 프로세스에 시그널을 전송,
-1이외의 음수는 절대값 프로세스 그룹에 속하는 모든 프로세스에 시그널을 전송하는 기능을 갖고 있다고한다.
sig는 시그널 번호를 말하는데, 보통은 signal.h 헤더에 포함되어있는 상수를 통해 값을 넣는다. 그래서 검색해보니 11은 프로세스가 다른 메모리 영역을 침범했다는 내용을 담는다.
그러니까, 정상적인 작동 방법이라면 우리는 buf의 범위 안에서만 값 수정을 해야하는데 그렇지 않았다면 오류를 발생시키는 코드라고 할 수 있다. 다음과 같이 말이다:
아무튼 그래서 우리가 노려야 할 부분은 저 strcpy() 부분이다. 복사할 배열의 크기를 검사하는 기능이 없기 때문이다. 그래서 오버플로우를 내서 long i를 변조해야할것같다는 생각이 들었다.
어셈블리 코드를 보면서 어떤식으로 페이로드를 짜야 할 지 생각해보자.
<main+3> 의 sub 0x418, esp 의 내용은 스택에 0x418(10진수로 1048) 만큼의 공간을 만든다는 것을 의미한다.(즉 1048바이트 크기의 변수를 만든다는 뜻이다.)
크기상으로 봤을 때, buf[1024], 그리고 그 이후 스택에서 12만큼 위에 movl 을 통해 0x1234567을 넣는것을 보아 1048바이트중 1024는 buf, 4는 i인것같다. 그러니까 현재 스택구조를 보자면,
[4byte] <- ret
[4byte] <- sfp
[8byte] <- dummy
[4byte] <- i
[12byte] <- dummy
[1024byte] <- buf
-------- <- ebp
아무튼, i의 내용을 변조시키지 말아야한다는 제한 사항이 걸렸는데 그렇다면 어떻게 해야할까?
답은 i의 내용을 그대로 넣은 뒤에 RET의 위치에 값을 넣으면 된다.
즉 buf 와 dummy에 아무값이나 넣고, i에 리틀엔디안으로 \x76\x54\x32\x01 을 넣고, dummy와 sfp까지 아무 값이나 넣고 ret에 실행하고자 하는 코드의 위치를 적어주면 될것이다.
따라서 페이로드는
(1024+12바이트의 아무 값) + (\x76\x54\x32\x01) + (8+4바이트의 아무 값) + RET
가 될것이다.
일단 환경변수에 셸코드를 넣고 그 주소부터 구하자.
이렇게 이쁘게 실행해주면 주소가 나온다!
그래서 다음의 페이로드로 풀이 할 수 있었다.
./attackme `python -c 'print "A"*1036+"\x67\x45\x23\x01"+"A"*12+"\x06\xfd\xff\xbf"'`
Comments
Post a Comment