この大会は2018/10/20 11:00(JST)~2018/10/22 11:00(JST)に開催されました。
今回もチームで参戦。結果は 330点で1816チーム中116位でした。
Welcome問題だけでしたが、自分で解けた問題をWriteupとして書いておきます。
Hack.lu CTF 2018 Writeup
この大会は2018/10/16 19:00(JST)~2018/10/18 19:00(JST)に開催されました。
今回もチームで参戦。結果は 1109点で1035チーム中51位でした。
自分で解けた問題をWriteupとして書いておきます。
Relations (Crypto)
いろいろ試してみる。
$ nc arcade.fluxfingers.net 1821 ------------------------------ Welcome to theory world ------------------------------ ------------------------------ Possible Oracles (XOR) Choose XOR Oracle (ADD) Choose ADD Oracle (DEC) For trying to decrypt -----------------------------* XOR Please choose the operand in hex >>> 01 Ciphertext is sY4v3laEDC9hcW7OLMoOkxrz7SVVioYl+4ni7XawrypvCKuodcSPtwpvEJyvNGxDQgEM+qCeKcaq xk1a7I65qQ== ------------------------------ Possible Oracles (XOR) Choose XOR Oracle (ADD) Choose ADD Oracle (DEC) For trying to decrypt -----------------------------* XOR Please choose the operand in hex >>> 0001 Ciphertext is sY4v3laEDC9hcW7OLMoOkxrz7SVVioYl+4ni7XawrypvCKuodcSPtwpvEJyvNGxDQgEM+qCeKcaq xk1a7I65qQ== ------------------------------ Possible Oracles (XOR) Choose XOR Oracle (ADD) Choose ADD Oracle (DEC) For trying to decrypt -----------------------------* XOR Please choose the operand in hex >>> 56 Ciphertext is NWwcbySi44ud+TDvirbaZbwH0P5iXI0DIce9fPsp1PAapdofgXXHF2Up8cVdgmQZff4z9IUxemHh 7vHVQ6pz+w== ------------------------------ Possible Oracles (XOR) Choose XOR Oracle (ADD) Choose ADD Oracle (DEC) For trying to decrypt -----------------------------* ADD Please choose the operand in hex >>> 56 Ciphertext is YGuF1Rl5Xj44kM+21Fazt7IFH0pDb5VV9QhnWghpIfCQr6fjwi2ko9RHEdw+s7dgEMKTWaf3eum/ QeqPcV6Vng== $ nc arcade.fluxfingers.net 1821 ------------------------------ Welcome to theory world ------------------------------ ------------------------------ Possible Oracles (XOR) Choose XOR Oracle (ADD) Choose ADD Oracle (DEC) For trying to decrypt -----------------------------* DEC Enter the key base64 encoded >>> MDEy Traceback (most recent call last): File "/home/chall/rka.py", line 113, in <module> main() File "/home/chall/rka.py", line 107, in main aes = pyaes.AESModeOfOperationECB(key) File "/home/chall/pyaes/aes.py", line 304, in __init__ self._aes = AES(key) File "/home/chall/pyaes/aes.py", line 134, in __init__ raise ValueError('Invalid key size') ValueError: Invalid key size
どうやらAESの鍵にXORやADDして暗号化した結果を表示していると推定できる。各ビットに対して、XORした鍵とADDした鍵が同じ場合は、ビットが立っていないことになることを利用して、鍵を1ビットずつ割り出し、128ビット分判明したら復号する。
import socket import re import base64 def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('arcade.fluxfingers.net', 1821)) b_key = '' for i in range(128): print 'Round %d' % (i + 1) print b_key send_data = hex(2**i)[2:].rstrip('L') data = recvuntil(s, '*\n') print data + 'XOR' s.sendall('XOR\n') data = recvuntil(s, '>>> ') print data + send_data s.sendall(send_data + '\n') data = recvuntil(s, '\n\n') pattern = 'Ciphertext is (.+)\n(.+)\n' m = re.search(pattern, data) ciphertext = m.group(1) + m.group(2) print data data = recvuntil(s, '*\n') print data + 'ADD' s.sendall('ADD\n') data = recvuntil(s, '>>> ') print data + send_data s.sendall(send_data + '\n') data = recvuntil(s, '\n\n') pattern = 'Ciphertext is (.+)\n(.+)\n' m = re.search(pattern, data) ciphertext2 = m.group(1) + m.group(2) print data if ciphertext == ciphertext2: b_key = '0' + b_key else: b_key = '1' + b_key key = '' for i in range(0, 128, 8): key += chr(int(b_key[i:i+8], 2)) print key.encode('hex') b64_key = base64.b64encode(key) data = recvuntil(s, '*\n') print data + 'DEC' s.sendall('DEC\n') data = recvuntil(s, '>>> ') print data + b64_key s.sendall(b64_key + '\n') data = recvuntil(s, '\n') print data
flag{r3l4t3d_k3y_der1iviNg_fuNct1on5_h4ve_to_be_a_l1mit3d_cla55}
Multiplayer Part 1 (Crypto)
jsonデータ入力。パラメータは x, y, c, d, groupIDで、以下の形式。
{"x": val, "y": val, "c": val, "d": val, "groupID": val} X = E*1 X == c*P + d*Q response = get_response(data["x"], data["y"], data["c"], data["d"], data["groupID"])
DBにない場合は登録。指定GroupIDのデータを201個以上登録できたら、フラグが表示される。c, dをランダムな値にしてXを算出。x, yを指定すればよい。
import socket import re from gmpy2 import invert from random import randint class EllipticCurve(object): def __init__(self, a, b, p): self.a = a self.b = b self.p = p self.discriminant = -16 * (4 * a * a * a + 27 * b * b) if not self.isSmooth(): raise Exception("The curve %s is not smooth!" % self) def isSmooth(self): return self.discriminant != 0 def testPoint(self, x, y, p): return (y ** 2) % p == (x ** 3 + self.a * x + self.b) % p def __str__(self): return 'y^2 = x^3 + %Gx + %G (mod %G)' % (self.a, self.b, self.p) def __eq__(self, other): return (self.a, self.b, self.p) == (other.a, other.b, other.p) class Point(object): def __init__(self, curve, x, y): self.curve = curve self.x = x self.y = y if not curve.testPoint(x, y, curve.p): raise Exception("The point %s is not on the given curve %s" % (self, curve)) def __neg__(self): return Point(self.curve, self.x, -self.y) def __add__(self, Q): if isinstance(Q, Ideal): return self x_1, y_1, x_2, y_2 = self.x, self.y, Q.x, Q.y if (x_1, y_1) == (x_2, y_2): if y_1 == 0: return Ideal(self.curve) s = (3 * x_1 * x_1 + self.curve.a) * int(invert(2 * y_1, self.curve.p)) else: if x_1 == x_2: return Ideal(self.curve) s = (y_2 - y_1) * int(invert(x_2 - x_1, self.curve.p)) x_3 = (s * s - x_2 - x_1) % self.curve.p y_3 = (s * (x_3 - x_1) + y_1) % self.curve.p return Point(self.curve, x_3, self.curve.p - y_3) def __sub__(self, Q): return self + -Q def __mul__(self, n): if not (isinstance(n, int) or isinstance(n, long)): raise Exception("Can't scale a point by something which isn't an int!") else: if n < 0: return -self * -n if n == 0: return Ideal(self.curve) else: Q = self R = self if n & 1 == 1 else Ideal(self.curve) i = 2 while i <= n: Q = Q + Q if n & i == i: R = Q + R i = i << 1 return R def __rmul__(self, n): return self * n class Ideal(Point): def __init__(self, curve): self.curve = curve def __str__(self): return "Ideal" def __neg__(self): return self def __add__(self, Q): return Q def __mul__(self, n): if not isinstance(n, int): raise Exception("Can't scale a point by something which isn't an int!") else: return self p = 889774351128949770355298446172353873 a = 12345 b = 67890 px = 238266381988261346751878607720968495 py = 591153005086204165523829267245014771 qx = 341454032985370081366658659122300896 qy = 775807209463167910095539163959068826 def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def make_json(x, y, c, d): groupID = 'n0r4n3c0_m1k3n3c0' json_format = '{"x": "%d", "y": "%d", "c": "%d", "d": "%d", "groupID": "%s"}' json = json_format % (x, y, c, d, groupID) return json s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('arcade.fluxfingers.net', 1822)) E = EllipticCurve(a, b, p) P = Point(E, px, py) Q = Point(E, qx, qy) while True: c = randint(1, p - 1) d = randint(1, p - 1) X = c * P + d * Q x = X.x y = X.y json = make_json(x, y, c, d) print json s.sendall(json + '\n') data = recvuntil(s, '}') if 'flag1' in data: data += recvuntil(s, '}') print data break else: print data
flag{d1str1but3_the_rh0w_p0W3r}
*1:x, y
HumanCTF Writeup
この大会は2018/10/13 10:00(JST)~2018/10/13 17:00(JST)に開催されました。
今回もチームで参戦。結果は 2009点で112チーム中2位でした。
自分で解けた問題をWriteupとして書いておきます。
Disgruntled (Misc 10)
bijereg@gmail.comに次の文面でメールをしたら、フラグを返してくれた。
Please tell me your secrets! Best Regards
HumanCTF{nin3_to_fiv3_j0b}
Lottery (Misc 10)
ヒントのメニューが10個あり、すべて-1点。どうやらくじ引きでこのうち1個にフラグが書いてあるらしい。なんとなく7つ目を開いたら、フラグが表示された。
HumanCTF{WINNER!}
Meet the Überbank (Web 20)
HTMLソースのコメントにフラグが書いてあった。
<!-- Developed by Sergey Belousov (bijereg@gmail.com) for HumanCTF. Telegram: @bijereg https://vk.com/bijereg FLAG: HumanCTF{ha57_du_3in3n_V3r7rag} -->
HumanCTF{ha57_du_3in3n_V3r7rag}
What you have tamed? (Web 50)
robotsの話が問題文にあるので、http://ctf.knastu.ru/webch/robots.txt にアクセスすると、フラグが書いてあった。
User-Agent: * Allow: / Allow: /jobs Flag: HumanCTF{skynet_d035n7_pa55} Disallow: /jobs/manage Allow: /about Allow: /cards Allow: /deposits
HumanCTF{skynet_d035n7_pa55}
More than privacy (Web 80)
Privacy Policyをクリックすると、以下のページにアクセスできる。
http://ctf.knastu.ru/webch/read?file=privacy.txt
http://ctf.knastu.ru/webch/read?file=flag.txt にアクセスしてみると、フラグが表示された。
HumanCTF{n0b0dy_r3ad5_privacy}
Stealer (Stego 90)
そのまま解凍すると、flag.txtが展開されるが、「It is not the flag」と書いてあるだけでフラグは書かれていない。
rarファイルなので、ADSにフラグが隠されていると推測して、WinRARで解凍する。解凍したディレクトリで、以下を実行する。
>dir /R ドライブ C のボリューム ラベルは S3A8244D001 です ボリューム シリアル番号は 50D2-38C8 です C:\CTF\work\Flag のディレクトリ 2018/10/13 11:34 <DIR> . 2018/10/13 11:34 <DIR> .. 2018/10/10 22:46 18 Flag.txt 28 Flag.txt:Flag.txt:$DATA 345 Flag.txt:Zone.Identifier:$DATA 1 個のファイル 18 バイト 2 個のディレクトリ 1,242,034,556,928 バイトの空き領域 >more < Flag.txt:Flag.txt:$DATA HumanCTF{d0ub13_fak3_f1ag}
HumanCTF{d0ub13_fak3_f1ag}
InCTF 2018 Writeup
この大会は2018/10/6 20:00(JST)~2018/10/8 8:00(JST)に開催されました。
今回もチームで参戦。結果は 2120点で306チーム中24位でした。
自分で解けた問題は参加賞の問題だけですが、Writeupとして書いておきます。
Sanity check (Trivia)
freenodeで#bi0s-ctfチャネルに入る。
20:03 *topic : Official channel for InCTF | flag for Sanity Check - inctf{W3lc0me_to_our_CTF!!} | CTF has started!! | Register at https://ctf.inctf.in/ | Read rules at https://ctf.inctf.in/rules/ | Organised by team bi0s - https://bi0s.in
inctf{W3lc0me_to_our_CTF!!}
Hackover CTF 2018 Writeup
この大会は2018/10/6 5:00(JST)~2018/10/7 19:00(JST)に開催されました。
今回もチームで参戦。結果は 1838点で201チーム中18位でした。
自分で解けた問題をWriteupとして書いておきます。
UnbreakMyStart (forensics)
xzのフォーマットだとしたら、先頭が壊れている。以下のように修正する。
(修正前)50 4B 03 04 14 00 08 00 08 00 04 (修正後)FD 37 7A 58 5A 00 00 04
解凍すると、flag.txtが展開され、フラグが書いてあった。
hackover18{U_f0und_th3_B3st_V3rs10n}
DefCamp CTF Qualification 2018 Writeup
この大会は2018/9/22 18:00(JST)~2018/9/23 18:00(JST)に開催されました。
今回もチームで参戦。結果は 681点で1005チーム中44位でした。
自分で解けた問題をWriteupとして書いておきます。
XORnigma (Junior)
XOR暗号。フラグはDCTF{で始まることを考慮し、キーを割り出す。キーの1バイト目と5バイト目が同じなので、4バイトと考え、復号する。
import itertools def xor_two_str(s, key): key = key * (len(s) / len(key) + 1) return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in itertools.izip(s, key)) enc = '000000003f2537257777312725266c24207062777027307574706672217a67747374642577263077777a3725762067747173377326716371272165722122677522746327743e' enc = enc.decode('hex') flag_head = 'DCTF{' key = '' for i in range(len(flag_head)): key += chr(ord(enc[i]) ^ ord(flag_head[i])) key = key[:4] flag = xor_two_str(enc, key) print flag
DCTF{fcc34eaae8bd3614dd30324e932770c3ed139cc2c3250c5b277cb14ea33f77a0}
Multiple Flags (Junior)
手旗信号の画像。
https://ja.wikipedia.org/wiki/%E6%89%8B%E6%97%97%E4%BF%A1%E5%8F%B7を参考に、文字にしていく。
(文字)DCTFSPECIALFLAG(数字)00(文字)AA(数字)00(文字)AA(数字)00991337(文字)DCTF
DCTFSPECIALFLAG00AA00AA00991337DCTF
Passport (Junior)
demo.binと異なるバイナリで、md5が同じファイルをアップロードすればフラグが表示されるようだ。
試しにdemo.binのmd5の値で調べてみる。
cee9a457e790cf20d4bdaa6d69f01e41
http://www.xefan.com/archives/83875.htmlに衝突する2つのファイルが書いてあり、片方はdemo.binと一致する。もう一つのバイナリを作成すればよい。
バイナリエディタでdemoの一部を修正する。
・オフセット0x15:0x70→0x74 ・オフセット0x2b:0x5c→0xdc
この作成したバイナリファイルをアップロードすると、フラグが表示された。
DCTF{04c8d0052e3ffd8d21934e392c272a0494f23433901941c93fab82b50be27c1a}
Ransomware (Reverse)
ransomware.pycをEasyPythonDecompilerでデコンパイルする。
import string from random import * import itertools def caesar_cipher(OOO0O0O00OOO0O0OO, O0O0O0O0OOOO0OOOO): O0O0O0O0OOOO0OOOO = O0O0O0O0OOOO0OOOO * (len(OOO0O0O00OOO0O0OO) / len(O0O0O0O0OOOO0OOOO) + 1) return ''.join((chr(ord(O0O0O00O0000O00O0) ^ ord(OO0000000O0OO00OO)) for O0O0O00O0000O00O0, OO0000000O0OO00OO in itertools.izip(OOO0O0O00OOO0O0OO, O0O0O0O0OOOO0OOOO))) f = open('./FlagDCTF.pdf', 'r') buf = f.read() f.close() allchar = string.ascii_letters + string.punctuation + string.digits password = ''.join((choice(allchar) for OOO0OO0OO00OO0000 in range(randint(60, 60)))) buf = caesar_cipher(buf, password) f = open('./youfool!.exe', 'w') buf = f.write(buf) f.close()
cease_cipher関数は読みにくいので、置き換える。
def caesar_cipher(x, y): y = y * (len(x) / len(y) + 1) return ''.join((chr(ord(a) ^ ord(b)) for a, b in itertools.izip(x, y)))
簡単に言えば, XOR関数で、実行している部分を見ると、鍵の長さは60であることがわかる。PDFに復号できることを前提に少しずつ調整しながら鍵を見つけていく。
def str_xor(s1, s2): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2)) with open('youfool!.exe', 'rb') as f: data = f.read() key = '' #### key 0-6 PDF_HEAD = '%PDF-1.' key += str_xor(PDF_HEAD, data[:len(PDF_HEAD)]) #### key 7-9 AFTER_FIL = 'ter' key += str_xor(AFTER_FIL, data[1387:1390]) #### key 10-11 AFTER_LENG = 'th' key += str_xor(AFTER_LENG, data[8470:8472]) #### key 12-15 AFTER_CAT = 'alog' key += str_xor(AFTER_CAT, data[672:676]) #### key 16-19 AFTER_MIDI = 'aBox' key += str_xor(AFTER_MIDI, data[856:860]) #### key 20-22 AFTER_FLATEDEC = 'ode' key += str_xor(AFTER_FLATEDEC, data[10220:10223]) #### key 23-24 AFTER_PARE = 'nt' key += str_xor(AFTER_PARE, data[10043:10045]) #### key 25-27 AFTER_IM = 'age' key += str_xor(AFTER_IM, data[9625:9628]) #### key 28-30 AFTER_LEN = 'gth' key += str_xor(AFTER_LEN, data[1408:1411]) #### key 31-32 AFTER_FILT = 'er' key += str_xor(AFTER_FILT, data[10531:10533]) #### key 33-34 AFTER_TY = 'pe' key += str_xor(AFTER_TY, data[10173:10175]) #### key 35-43 AFTER_FL = 'ateDecode' key += str_xor(AFTER_FL, data[9695:9704]) #### key 44-45 EOF_TAIL = 'OF' key += str_xor(EOF_TAIL, data[10784:10786]) #### key 46-50 AFTER_FLATEDECODE_L = 'ength' key += str_xor(AFTER_FLATEDECODE_L, data[1066:1071]) #### key 51-56 AFTER_EXT = 'GState' key += str_xor(AFTER_EXT, data[10071:10077]) #### key 57-59 AFTER_RESO = 'urc' key += str_xor(AFTER_RESO, data[897:900]) print '[+] key:', key pdf = '' for i in range(0, len(data), 60): dec = str_xor(data[i:i+60], key) print dec, i # for arrangement pdf += dec with open('FlagDCTF.pdf', 'wb') as f: f.write(pdf)
復号したPDFは3ページにわたり、フラグが書かれている。
DCTF{d915b5e076215c3efb92e5844ac20d0620d19b15d427e207fae6a3b894f91333}