この大会は2017/9/24 0:30(JST)~2017/9/25 0:30(JST)に開催されました。
今回もチームで参戦。結果は全問制覇の3600点で、212チーム中6位でした。
自分で解けた問題をWriteupとして書いておきます。
IMAGEREV (200)
RGBの値をASCIIコードとして結合(3バイト)。それに対して、暗号化してその結果、64バイトになる。暗号化処理の内容は復号するコードにすることがかなり難しい。
そこでencrypted.txtを見ると、64バイトごとに同じようなデータが多数あるので、RとGとBの値はすべて同じであると推定して、256パターンの総当たりで復元する。
またピクセルの数はデータの長さから7371。素因数分解し、その組み合わせの積を考慮して、画像の幅と高さをいろいろと試してみる。
7371 = 3^4 * 7 * 13
結果は幅が351の時にフラグが書かれた画像に復元することができた。そのときのコードは次の通り。
from PIL import Image def bin_return(dec): return(str(format(dec,'b'))) def bin_8bit(dec): return(str(format(dec,'08b'))) def convert_32bit(dec): return(str(format(dec,'032b'))) def convert_64bit(dec): return(str(format(dec,'064b'))) def hex_return(dec): return expand(hex(dec).replace('0x','').replace('L','')) def dec_return_bin(bin_string): return(int(bin_string,2)) def dec_return_hex(hex_string): return(int(hex_string,16)) def some_LP(l,n): l1=[] j=0 k=n while k<len(l)+1: l1.append(l[j:k]) j=k k+=n return(l1) def rotate_right(bit_string,n): bit_list = list(bit_string) count=0 while count <= n-1: list_main=list(bit_list) var_0=list_main.pop(-1) list_main=list([var_0]+list_main) bit_list=list(list_main) count+=1 return(''.join(list_main)) def shift_right(bit_string,n): bit_list=list(bit_string) count=0 while count <= n-1: bit_list.pop(-1) count+=1 front_append=['0']*n return(''.join(front_append+bit_list)) def addition(input_set): value=0 for i in range(len(input_set)): value+=input_set[i] mod_32 = 4294967296 return(value%mod_32) def str_xor(s1,s2): return ''.join([str(int(i)^int(j)) for i,j in zip(s1,s2)]) def str_and(s1,s2): return ''.join([str(int(i)&int(j)) for i,j in zip(s1,s2)]) def str_not(s): return ''.join([str(int(i)^1) for i in s]) def not_and_and_xor(x,y,z): return(str_xor(str_and(x,y),str_and(str_not(x),z))) def and_and_and_xor_xor(x,y,z): return(str_xor(str_xor(str_and(x,y),str_and(x,z)),str_and(y,z))) def some_e0(x): return(str_xor(str_xor(rotate_right(x,2),rotate_right(x,13)),rotate_right(x,22))) def some_e1(x): return(str_xor(str_xor(rotate_right(x,6),rotate_right(x,11)),rotate_right(x,25))) def some_s0(x): return(str_xor(str_xor(rotate_right(x,7),rotate_right(x,18)),shift_right(x,3))) def some_s1(x): return(str_xor(str_xor(rotate_right(x,17),rotate_right(x,19)),shift_right(x,10))) def expand(s): return '0'*(8-len(s))+s def get_pixels_list(filename): im = Image.open(filename) return list(im.getdata()) def data_encrypted(list_of_pixels): data = '' for i in list_of_pixels: d = ''.join([chr(j) for j in i]) d = encryption(d) data += ''.join(d) print len(data) return data def message_pad(bit_list): pad_one = bit_list + '1' pad_len = len(pad_one) k=0 while ((pad_len+k)-448)%512 != 0: k+=1 back_append_0 = '0'*k back_append_1 = convert_64bit(len(bit_list)) return(pad_one+back_append_0+back_append_1) def message_bit_return(string_input): bit_list=[] for i in range(len(string_input)): bit_list.append(bin_8bit(ord(string_input[i]))) return(''.join(bit_list)) def message_pre_pro(input_string): bit_main = message_bit_return(input_string) return(message_pad(bit_main)) def message_parsing(input_string): return(some_LP(message_pre_pro(input_string),32)) def message_schedule(index,w_t): new_word = convert_32bit(addition([int(some_s1(w_t[index-2]),2),int(w_t[index-7],2),int(some_s0(w_t[index-15]),2),int(w_t[index-16],2)])) return(new_word) initial=['6a09e667','bb67ae85','3c6ef372','a54ff53a','510e527f','9b05688c','1f83d9ab','5be0cd19'] values=['428a2f98','71374491','b5c0fbcf','e9b5dba5','3956c25b','59f111f1','923f82a4','ab1c5ed5','d807aa98','12835b01','243185be','550c7dc3','72be5d74','80deb1fe','9bdc06a7','c19bf174','e49b69c1','efbe4786','0fc19dc6','240ca1cc','2de92c6f','4a7484aa','5cb0a9dc','76f988da','983e5152','a831c66d','b00327c8','bf597fc7','c6e00bf3','d5a79147','06ca6351','14292967','27b70a85','2e1b2138','4d2c6dfc','53380d13','650a7354','766a0abb','81c2c92e','92722c85','a2bfe8a1','a81a664b','c24b8b70','c76c51a3','d192e819','d6990624','f40e3585','106aa070','19a4c116','1e376c08','2748774c','34b0bcb5','391c0cb3','4ed8aa4a','5b9cca4f','682e6ff3','748f82ee','78a5636f','84c87814','8cc70208','90befffa','a4506ceb','bef9a3f7','c67178f2'] def encryption(input_string): w_t=message_parsing(input_string) a=convert_32bit(dec_return_hex(initial[0])) b=convert_32bit(dec_return_hex(initial[1])) c=convert_32bit(dec_return_hex(initial[2])) d=convert_32bit(dec_return_hex(initial[3])) e=convert_32bit(dec_return_hex(initial[4])) f=convert_32bit(dec_return_hex(initial[5])) g=convert_32bit(dec_return_hex(initial[6])) h=convert_32bit(dec_return_hex(initial[7])) for i in range(0,64): if i <= 15: t_1=addition([int(h,2),int(some_e1(e),2),int(not_and_and_xor(e,f,g),2),int(values[i],16),int(w_t[i],2)]) t_2=addition([int(some_e0(a),2),int(and_and_and_xor_xor(a,b,c),2)]) h=g g=f f=e e=addition([int(d,2),t_1]) d=c c=b b=a a=addition([t_1,t_2]) a=convert_32bit(a) e=convert_32bit(e) if i > 15: w_t.append(message_schedule(i,w_t)) t_1=addition([int(h,2),int(some_e1(e),2),int(not_and_and_xor(e,f,g),2),int(values[i],16),int(w_t[i],2)]) t_2=addition([int(some_e0(a),2),int(and_and_and_xor_xor(a,b,c),2)]) h=g g=f f=e e=addition([int(d,2),t_1]) d=c c=b b=a a=addition([t_1,t_2]) a=convert_32bit(a) e=convert_32bit(e) value_0 = addition([dec_return_hex(initial[0]),int(a,2)]) value_1 = addition([dec_return_hex(initial[1]),int(b,2)]) value_2 = addition([dec_return_hex(initial[2]),int(c,2)]) value_3 = addition([dec_return_hex(initial[3]),int(d,2)]) value_4 = addition([dec_return_hex(initial[4]),int(e,2)]) value_5 = addition([dec_return_hex(initial[5]),int(f,2)]) value_6 = addition([dec_return_hex(initial[6]),int(g,2)]) value_7 = addition([dec_return_hex(initial[7]),int(h,2)]) value = (hex_return(value_0),hex_return(value_1),hex_return(value_2),hex_return(value_3),hex_return(value_4),hex_return(value_5),hex_return(value_6),hex_return(value_7)) return(value) def get_string(d): remain = d s = '' for i in range(3): code = remain / (256 ** (2 - i)) s += chr(code) remain = remain % (256 ** (2 - i)) return s with open('encrypted.txt', 'r') as f: data = f.read() e = [] for i in range(256): val = i + i*256 + i*256*256 s = get_string(val) e.append(''.join(encryption(s))) width = 351 height = 7371 / width img = Image.new('RGB', (width, height), (255, 255, 255)) for i in range(0, len(data), 64): pixel_data = data[i:i+64] color_code = e.index(pixel_data) x = (i / 64) % width y = (i / 64) / width img.putpixel((x, y), (color_code, color_code, color_code)) img.save('flag.png')
$ echo -n 'CTF{d4mm17_y0u_f0und_my_h1dd3n_5h4_h45h}' | sha256sum d66f0a384abd48764b5caecb3e204c88746661c3d7f25471d79ac265c59c9085 -
d66f0a384abd48764b5caecb3e204c88746661c3d7f25471d79ac265c59c9085
COMPLEX-RSA (200)
公開鍵が2つと暗号ファイルが渡される。2回別の鍵で暗号化しているので、それを復号する問題。
$ openssl rsa -pubin -text < pubkey1 Public-Key: (1026 bit) Modulus: 03:15:4c:a2:cd:44:39:ad:97:e0:38:7a:51:4c:97: bf:21:0c:f7:5f:3b:cf:26:25:94:68:3b:b5:3c:e6: 99:48:12:df:6a:19:55:c4:4f:9f:12:e9:9c:2a:69: 8c:b9:7f:0e:47:e8:e8:d1:48:a7:f3:47:2d:2b:4d: e2:54:10:ad:f2:1e:49:8b:b9:b4:66:5a:0c:66:d8: d2:66:ff:20:63:7e:ac:f2:71:8d:9a:59:c1:c4:a3: cf:02:2f:0b:f2:8d:53:89:d5:86:41:f3:dd:99:4e: f2:ff:fa:4b:70:d3:d3:98:e9:48:42:00:f8:77:93: 06:10:13:a8:ed:07:2c:d7:7f Exponent: 37507589401 (0x8bba06519) writing RSA key -----BEGIN PUBLIC KEY----- MIGhMA0GCSqGSIb3DQEBAQUAA4GPADCBiwKBgQMVTKLNRDmtl+A4elFMl78hDPdf O88mJZRoO7U85plIEt9qGVXET58S6ZwqaYy5fw5H6OjRSKfzRy0rTeJUEK3yHkmL ubRmWgxm2NJm/yBjfqzycY2aWcHEo88CLwvyjVOJ1YZB892ZTvL/+ktw09OY6UhC APh3kwYQE6jtByzXfwIFCLugZRk= -----END PUBLIC KEY----- yoshinori@yoshinori-virtual-machine:/mnt/hgfs/Shared$ openssl rsa -pubin -text < pubkey2 Public-Key: (1026 bit) Modulus: 03:15:4c:a2:cd:44:39:ad:97:e0:38:7a:51:4c:97: bf:21:0c:f7:5f:3b:cf:26:25:94:68:3b:b5:3c:e6: 99:48:12:df:6a:19:55:c4:4f:9f:12:e9:9c:2a:69: 8c:b9:7f:0e:47:e8:e8:d1:48:a7:f3:47:2d:2b:4d: e2:54:10:ad:f2:1e:49:8b:b9:b4:66:5a:0c:66:d8: d2:66:ff:20:63:7e:ac:f2:71:8d:9a:59:c1:c4:a3: cf:02:2f:0b:f2:8d:53:89:d5:86:41:f3:dd:99:4e: f2:ff:fa:4b:70:d3:d3:98:e9:48:42:00:f8:77:93: 06:10:13:a8:ed:07:2c:d7:7f Exponent: 1a:1b:48:c3:a6:ce:4a:6a:f6:e3:cb:78:c5:5f:ac: 7f:59:29:6f:41:bb:11:e5:8a:30:1f:b8:d2:f3:c4: 7e:bb:15:fa:3e:68:e9:f7:7f:8c:52:39:3b:e2:22: 17:5d:e4:a7:bc:55:d9:9d:51:80:1b:5a:1d:be:72: dd:6e:ff:ae:52:39:f3:a4:48:d3:e9:ef:5a:a1:c9: 54:5e:1a:38:49:18:9b:e6:3c:49:fe:3b:94:92:54: d4:20:d9:81:76:77:a7:db:4a:d1:f3:f5:df:99:b3: 25:7d:d6:04:c5:72:ce:e2:5e:43:c2:69:43:cf:d1: 64:91:80:3b writing RSA key -----BEGIN PUBLIC KEY----- MIIBGjANBgkqhkiG9w0BAQEFAAOCAQcAMIIBAgKBgQMVTKLNRDmtl+A4elFMl78h DPdfO88mJZRoO7U85plIEt9qGVXET58S6ZwqaYy5fw5H6OjRSKfzRy0rTeJUEK3y HkmLubRmWgxm2NJm/yBjfqzycY2aWcHEo88CLwvyjVOJ1YZB892ZTvL/+ktw09OY 6UhCAPh3kwYQE6jtByzXfwJ8GhtIw6bOSmr248t4xV+sf1kpb0G7EeWKMB+40vPE frsV+j5o6fd/jFI5O+IiF13kp7xV2Z1RgBtaHb5y3W7/rlI586RI0+nvWqHJVF4a OEkYm+Y8Sf47lJJU1CDZgXZ3p9tK0fP135mzJX3WBMVyzuJeQ8JpQ8/RZJGAOw== -----END PUBLIC KEY-----
nの値が同じで、eの値はそれぞれ次の通り。
n = 0x03154ca2cd4439ad97e0387a514c97bf210cf75f3bcf262594683bb53ce6994812df6a1955c44f9f12e99c2a698cb97f0e47e8e8d148a7f3472d2b4de25410adf21e498bb9b4665a0c66d8d266ff20637eacf2718d9a59c1c4a3cf022f0bf28d5389d58641f3dd994ef2fffa4b70d3d398e9484200f87793061013a8ed072cd77f e1 = 0x8bba06519 e2 = 0x1a1b48c3a6ce4a6af6e3cb78c55fac7f59296f41bb11e58a301fb8d2f3c47ebb15fa3e68e9f77f8c52393be222175de4a7bc55d99d51801b5a1dbe72dd6effae5239f3a448d3e9ef5aa1c9545e1a3849189be63c49fe3b949254d420d9817677a7db4ad1f3f5df99b3257dd604c572cee25e43c26943cfd16491803b
c1 = pow(m, e1, n) c = pow(c1, e2, n) → c = pow(m, e1*e2, n)
Wiener's attackで復号する。
from fractions import Fraction def egcd(a, b): x,y, u,v = 0,1, 1,0 while a != 0: q, r = b//a, b%a m, n = x-u*q, y-v*q b,a, x,y, u,v = a,r, u,v, m,n gcd = b return gcd, x, y def decrypt(p, q, e, c): n = p * q phi = (p - 1) * (q - 1) gcd, a, b = egcd(e, phi) d = a pt = pow(c, d, n) return hex(pt)[2:-1].decode('hex') def continued_fractions(n,e): cf = [0] while e != 0: cf.append(int(n/e)) N = n n = e e = N%e return cf def calcKD(cf): kd = list() for i in range(1,len(cf)+1): tmp = Fraction(0) for j in cf[1:i][::-1]: tmp = 1/(tmp+j) kd.append((tmp.numerator,tmp.denominator)) return kd def int_sqrt(n): def f(prev): while True: m = (prev + n/prev)/2 if m >= prev: return prev prev = m return f(n) def calcPQ(a,b): if a*a < 4*b or a < 0: return None c = int_sqrt(a*a-4*b) p = (a + c) /2 q = (a - c) /2 if p + q == a and p * q == b: return (p,q) else: return None def wiener(n,e): kd = calcKD(continued_fractions(n,e)) for (k,d) in kd: if k == 0: continue if (e*d-1) % k != 0: continue phin = (e*d-1) / k if phin >= n: continue ans = calcPQ(n-phin+1,n) if ans is None: continue return (ans[0],ans[1]) n = 0x03154ca2cd4439ad97e0387a514c97bf210cf75f3bcf262594683bb53ce6994812df6a1955c44f9f12e99c2a698cb97f0e47e8e8d148a7f3472d2b4de25410adf21e498bb9b4665a0c66d8d266ff20637eacf2718d9a59c1c4a3cf022f0bf28d5389d58641f3dd994ef2fffa4b70d3d398e9484200f87793061013a8ed072cd77f e1 = 0x8bba06519 e2 = 0x1a1b48c3a6ce4a6af6e3cb78c55fac7f59296f41bb11e58a301fb8d2f3c47ebb15fa3e68e9f77f8c52393be222175de4a7bc55d99d51801b5a1dbe72dd6effae5239f3a448d3e9ef5aa1c9545e1a3849189be63c49fe3b949254d420d9817677a7db4ad1f3f5df99b3257dd604c572cee25e43c26943cfd16491803b e = e1 * e2 p, q = wiener(n, e) with open('flag.enc', 'rb') as f: c = int(f.read().encode('hex'), 16) flag = decrypt(p, q, e, c) print flag
実行結果は CTF{c0n6r47zzz_y0u_f0und_0ur_h1dd3n_w13n3r!!}
$ echo -n 'CTF{c0n6r47zzz_y0u_f0und_0ur_h1dd3n_w13n3r!!}' | sha256sum c0081b21d2c1d3f581914be8bc4ce98158b9eafbcf68dda83f6eddae99d4b1e2 -
c0081b21d2c1d3f581914be8bc4ce98158b9eafbcf68dda83f6eddae99d4b1e2
STEREOTYPES (300)
$ openssl rsa -pubin -text < pubkey Public-Key: (2048 bit) Modulus: 00:87:ae:91:10:c6:f2:df:9a:d5:a7:47:c9:c2:5b: 60:57:6a:c2:bf:17:76:00:2d:4e:5d:ba:17:72:ae: a0:b8:70:34:94:29:9c:f8:51:38:be:84:bf:2d:9f: 4f:88:79:5e:9c:cd:63:45:4f:df:55:32:57:da:09: 5a:0d:ea:cd:44:6f:d9:a9:4d:90:e4:06:d3:86:1c: c7:4e:cb:5c:a1:1c:6e:ce:b3:fd:0b:13:bc:23:26: 7f:9f:14:ef:20:19:3e:61:02:70:3c:c3:b3:fd:f7: a7:f3:e7:20:3b:9d:43:4e:c9:66:d9:04:ab:d8:0b: 68:37:3f:fb:87:f3:b4:06:ff:53:af:8b:9f:85:48: dd:3a:da:63:4e:06:f0:87:ed:4a:d5:98:cd:a1:75: 20:02:45:e0:b5:ec:d4:5e:83:11:60:ef:7e:c0:b9: 32:7a:28:18:70:97:1e:c7:0d:63:2e:ed:79:a2:1d: a6:35:18:77:b9:46:dd:54:b2:eb:a1:6d:ad:7b:f5: d2:11:73:d1:5b:22:3a:4c:58:12:c6:8c:e9:41:4f: 3f:ba:bf:03:ee:d2:8f:fc:65:64:6d:4f:32:81:cc: 6a:fe:8d:21:c1:45:27:ef:50:dd:0d:50:d6:58:99: 69:5b:53:c6:b6:37:4c:ec:05:e3:81:53:ca:65:e8: 73:f1 Exponent: 7 (0x7) writing RSA key -----BEGIN PUBLIC KEY----- MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAh66REMby35rVp0fJwltg V2rCvxd2AC1OXboXcq6guHA0lCmc+FE4voS/LZ9PiHlenM1jRU/fVTJX2glaDerN RG/ZqU2Q5AbThhzHTstcoRxuzrP9CxO8IyZ/nxTvIBk+YQJwPMOz/fen8+cgO51D Tslm2QSr2AtoNz/7h/O0Bv9Tr4ufhUjdOtpjTgbwh+1K1ZjNoXUgAkXgtezUXoMR YO9+wLkyeigYcJcexw1jLu15oh2mNRh3uUbdVLLroW2te/XSEXPRWyI6TFgSxozp QU8/ur8D7tKP/GVkbU8ygcxq/o0hwUUn71DdDVDWWJlpW1PGtjdM7AXjgVPKZehz 8QIBBw== -----END PUBLIC KEY-----
n = 0x0087ae9110c6f2df9ad5a747c9c25b60576ac2bf1776002d4e5dba1772aea0b8703494299cf85138be84bf2d9f4f88795e9ccd63454fdf553257da095a0deacd446fd9a94d90e406d3861cc74ecb5ca11c6eceb3fd0b13bc23267f9f14ef20193e6102703cc3b3fdf7a7f3e7203b9d434ec966d904abd80b68373ffb87f3b406ff53af8b9f8548dd3ada634e06f087ed4ad598cda175200245e0b5ecd45e831160ef7ec0b9327a281870971ec70d632eed79a21da6351877b946dd54b2eba16dad7bf5d21173d15b223a4c5812c68ce9414f3fbabf03eed28ffc65646d4f3281cc6afe8d21c14527ef50dd0d50d65899695b53c6b6374cec05e38153ca65e873f1 e = 7
平文は末尾以外わかっているので、Coppersmith's Attackを実行。
# coppersmith_attack.sage n = 0x0087ae9110c6f2df9ad5a747c9c25b60576ac2bf1776002d4e5dba1772aea0b8703494299cf85138be84bf2d9f4f88795e9ccd63454fdf553257da095a0deacd446fd9a94d90e406d3861cc74ecb5ca11c6eceb3fd0b13bc23267f9f14ef20193e6102703cc3b3fdf7a7f3e7203b9d434ec966d904abd80b68373ffb87f3b406ff53af8b9f8548dd3ada634e06f087ed4ad598cda175200245e0b5ecd45e831160ef7ec0b9327a281870971ec70d632eed79a21da6351877b946dd54b2eba16dad7bf5d21173d15b223a4c5812c68ce9414f3fbabf03eed28ffc65646d4f3281cc6afe8d21c14527ef50dd0d50d65899695b53c6b6374cec05e38153ca65e873f1 e = 7 with open('ciphertext', 'rb') as f: c = int(f.read().encode('hex'), 16) with open('plaintext', 'r') as f: m0 = f.read() m0 = int(m0.replace('X', '\x00').encode('hex'), 16) kbits = 15 * 8 PR.<x> = PolynomialRing(Zmod(n)) f = (m0 + x)^e - c x0 = f.small_roots(X=2^kbits, beta=1)[0] plaintext = ('%x' % (m0 + x0)).decode('hex') print plaintext
復号結果は次の通り。
Welcome to BackdoorCTF17. Just one advice Never ignore the challenge names, they provides the maximum hint. Okay now let's leave the boring stuff apart i know you are eagerly waiting to gain some points So here it is : CTF{y0u_607_17}
$ echo -n 'CTF{y0u_607_17}' | sha256sum 229eab315f374015ad0e8a0374b6df89a2510538a305cbd2b88df81eb05349a6 -
229eab315f374015ad0e8a0374b6df89a2510538a305cbd2b88df81eb05349a6