この大会は2020/7/12 2:00(JST)~2020/7/14 2:00(JST)に開催されました。
今回もチームで参戦。結果は4399点で1047チーム中72位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (Beginner)
問題にフラグが書いてあった。
rgbCTF{g0_g3t_th4t_j4m!}
Joke Check! (Beginner)
シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 11: rgbCTF{a_chicken_caesar_salad}
rgbCTF{a_chicken_caesar_salad}
A Fine Day (Beginner)
quipqiupで復号する。
This is encrypted with an affine cipher. Affine ciphers are a form of substitution cipher. Its key consists of two numbers, a and b. To encrypt a letter, multiply its place in the alphabet (so a=0, b=1, etc.) by a, and then add b. Finally take that mod 26 and convert it back into a character. The affine cipher isn't really that strong. Since it's mod 26, there are only a few hundred different keys, which can be easily brute forced. Anyway, here's your flag: rgbCTF{a_fine_affine_cipher}
実際はAffine暗号になっていたようだ。
rgbCTF{a_fine_affine_cipher}
r/ciphers (Beginner)
quipqiupで復号する。
This is a monoalphabetic substitution cipher, which can be attacked with frequency analysis. Although this attack can be done by hand, it's usually much easier to use a program to do it for you. Two good websites that will decrypt substitution ciphers for you are at guballa.de/substitution-solver and quipqiup.com. If you haven't tried them before, you should check them out. Here's your flag: rgbCTF{just_4sk_th3_int3rn3t_t0_d3crypt_it} Also, Alec believes it's very important that you see this: https://i.redd.it/1p7w8k0272851.jpg
rgbCTF{just_4sk_th3_int3rn3t_t0_d3crypt_it}
Simple RSA (Beginner)
factordbでnを素因数分解する。
n = 255097177 * 22034393943473183756163118460342519430053
あとはリトルエンディアンになることに注意して、そのまま復号する。
#!/usr/bin/env python3 from sympy import mod_inverse from math import gcd def lcm(a: int, b: int) -> int: return a * b // gcd(a, b) n = 5620911691885906751399467870749963159674169260381 e = 65537 c = 1415060907955076984980255543080831671725408472748 p = 255097177 q = 22034393943473183756163118460342519430053 lmbd = lcm(p - 1, q - 1) d = mod_inverse(e, lmbd) m = pow(c, d, n) flag = m.to_bytes(m.bit_length()//8 + 1, byteorder='little') print(flag)
rgbCTF{brut3_f0rc3}
Pieces (Beginner)
divideは以下の処理で1文字を2文字であらわす。
・ASCIIコードで2で割った値の文字 ・2で割ったあまりが0の場合は|、1の場合は/
この処理を逆算して、フラグを割り出す。
def rev_divide(s): c1 = s[0] c2 = s[1] code = ord(c1) * 2 if c2 == '/': code += 1 return chr(code) ct = '9|2/9/:|4/7|8|4/2/1/2/9/' input = '' for i in range(0, len(ct), 2): input += rev_divide(ct[i:i+2]) flag = 'rgbCTF{%s}' % input print flag
rgbCTF{restinpieces}
Shoob (Beginner)
Stegsolveで開き、Green plane 0を見ると、フラグが書いてあった。
rgbCTF{3zier_4haN_s4n1ty}
Quirky Resolution (Beginner)
Red plane 0を見ると、QRコードが出てきた。
コードリーダでデコードする。
VfuflVAouacXoIOHsrNP xgSDHLQSDBIO7fZ1w8zV SUdwzyRJLKEHkqP2i0NK h5fYSD1b3rymDWKkx9uw fZELgZV2l0xllyyMADkw voRu9GvHFy3qI7BK3yAL EIUOvFKPbxcci6ExsXFv G5YlE7Id9NELQ3NL5Kex gaf6UQ77Q8aQkmjY6vzq IF45ubpqdAZertRvfnER XIj9b7ZGaep7Aspqvzhu P6IhEFnLdlx0w5BSjo0A L9ifDGyICNqaxNywIeAg mxWg0h8XpHRN3Ai6P3aU rgbCTF{th3_qu1rk!er_th3_b3tt3r} VXPphIDrM0WagtZJYecY PjXSHuJTAcLK9uUnGrCB PW2MjAPE6KGe1iB8Hmqg 3WfKjbXOXXNVMj79F2UI gzFHSU1HN9KAKhwPElOf x9lL2wLHtee0KG90DFW9 OoO0YnJDU8Kfm7sFADjQ XJ7FnxIARmTZ0WgGKeyS AQEgDg8Sx1XBicDHfZ2i OxvFwnTl9FKQTm1UikjK aifFg3hc1gKLJWAjC5b4 q2SoVWnm95W2DNVGFtzf cxaLH9lpesGwhehpv8YU zO9W4DZM6uIlhCAyM1Hj
この文字列の中にフラグが入っていた。
rgbCTF{th3_qu1rk!er_th3_b3tt3r}
Ralphie! ([ZTC])
Stegsolveで開き、Red plane 3でQRコードが見えた。
コードリーダでデコードする。
rgbCTF{BESURETODRINKYOUROVALTINE}
vaporwave1 ([ZTC])
Sonic Visualiserで開き、スペクトログラムを見るとフラグが現れた。
rgbCTF{s331ng_s0undz}
icanhaz ([ZTC])
icanhazを解凍すると、hexdumpが1行8バイト単位で書いてある。バイナリにしてみる、
with open('icanhaz', 'r') as f: lines = f.readlines() data = '' for line in lines: data += ''.join(line.split(' ')[1:5]).decode('hex') with open('icanhaz.7z', 'wb') as f: f.write(data)
7zのファイルフォーマットのファイルになった。そして7zipを解凍するとsvgファイルが展開された。そのままブラウザで見ても、色が分かりにくいので、#fffffdを#000000に変更して開く。
QRコードになっているので、コードリーダでデコードする。
/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AbxAN1dAA2XxNFhRNBaOJSxhV08AXoOcZxtalpXU+c+q/ppfZc1/t0z3BU/P16F9jAlXbjrzh5cXk/9vLbc+8NQJ8PNawtALEPD17f25zdggODx3xzNLY3SjGTIlX0fbqo6HFkHYkIzOjjUgJcN1KbzGRouW+G8TakjrJ4y5Pk7jv/stqRiV0ICPYxKpnZSEn0aLzQSl46j6H3BBUBhRuGgxue3TXIzw5HGMlchgNBs6SCfHU0SkX4zlSKqOWSyKrJ5JMgwC47en2kI68/tRNQYaYzvGGcWcR/iEgNYO/jHVDVLAAAAADjqmgxrEIjCAAH5AfINAADD+B/oscRn+wIAAAAABFla
base64文字列なので、デコードしてファイル保存すると、また7zファイルになる。展開すると、UTF-8テキストでQRコードが書かれている。
これをコードリーダでデコードする。
rgbCTF{iCanHaz4N6DEVJOB}
Too Slow (Pwn/Rev)
Ghidraでデコンパイルする。
undefined8 main(void) { uint uVar1; puts("Flag Decryptor v1.0"); puts("Generating key..."); uVar1 = getKey(); win((ulong)uVar1); return 0; } ulong getKey(void) { uint local_10; uint local_c; local_10 = 0; while (local_10 < 0x265d1d23) { local_c = local_10; while (local_c != 1) { if ((local_c & 1) == 0) { local_c = (int)local_c / 2; } else { local_c = local_c * 3 + 1; } } local_10 = local_10 + 1; } return (ulong)local_10; } void win(uint param_1) { long in_FS_OFFSET; uint local_3c; undefined8 local_38; undefined8 local_30; undefined8 local_28; undefined8 local_20; undefined4 local_18; undefined local_14; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); local_38 = 0x12297e12426e6f53; local_30 = 0x79242e48796e7141; local_28 = 0x49334216426e2e4d; local_20 = 0x473e425717696a7c; local_18 = 0x42642a41; local_14 = 0; local_3c = 0; while (local_3c < 9) { *(uint *)((long)&local_38 + (ulong)local_3c * 4) = *(uint *)((long)&local_38 + (ulong)local_3c * 4) ^ param_1; local_3c = local_3c + 1; } printf("Your flag: rgbCTF{%36s}\n",&local_38); if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }
getKey()は終わらないが、取得できたとしたら、0x265d1d23であると考える。32bit単位でXORを取ればフラグになる。
from Crypto.Util.strxor import strxor key = '265d1d23'.decode('hex')[::-1] ct0 = '12297e12426e6f53'.decode('hex')[::-1] ct1 = '79242e48796e7141'.decode('hex')[::-1] ct2 = '49334216426e2e4d'.decode('hex')[::-1] ct3 = '473e425717696a7c'.decode('hex')[::-1] ct4 = '42642a41'.decode('hex')[::-1] ct = ct0 + ct1 + ct2 + ct3 + ct4 flag = '' for i in range(0, len(ct), 4): flag += strxor(key, ct[i:i+4]) flag = 'rgbCTF{%s}' % flag print flag
rgbCTF{pr3d1ct4bl3_k3y_n33d5_no_w41t_cab79d}
Advanced Reversing Mechanics 1 (Pwn/Rev)
Ghidraでデコンパイルする。
undefined4 main(undefined4 param_1,int param_2) { char *pcVar1; byte *pbVar2; byte *pbVar3; byte local_110 [256]; pbVar2 = local_110; pcVar1 = stpcpy((char *)local_110,*(char **)(param_2 + 4)); while (local_110[0] != 0) { *pbVar2 = local_110[0] - 1; pbVar2 = pbVar2 + 1; local_110[0] = *pbVar2; } if (pcVar1 + -(int)local_110 != (char *)0x0) { pbVar2 = local_110; do { pbVar3 = pbVar2 + 1; printf("%02X, ",(uint)*pbVar2); pbVar2 = pbVar3; } while (local_110 + (int)(pcVar1 + -(int)local_110) != pbVar3); } putchar(10); return 0; }
暗号処理は1つコードがシフトされるだけなので、簡単に戻せる。
enc = ['71', '66', '61', '42', '53', '45', '7A', '40', '51', '4C', '5E', '30', '79', '5E', '31', '5E', '64', '59', '5E', '38', '61', '36', '65', '37', '63', '7C'] flag = '' for e in enc: code = int(e, 16) + 1 flag += chr(code) print flag
rgbCTF{ARM_1z_2_eZ_9b7f8d}
Alien Transmission 1 (Forensics)
wavの画像受信データがあるかもしれないので、sstvのツールでデコードしてみる。
$ sstv -d squeakymusic.wav -o flag.png [sstv] Searching for calibration header... Found! [sstv] Detected SSTV mode Robot 36 [sstv] Decoding image... [##############################################] 99% [sstv] Reached end of audio whilst decoding. [sstv] Drawing image data... [sstv] ...Done!
rgbctf{s10w_2c4n_1s_7h3_W4V3}
I Love Rainbows (Cryptography)
md5またはsha256の値が並んでいる。2文字以下のハッシュのブルートフォースで戻す。
from hashlib import * from string import * import itertools with open('rainbows.txt', 'r') as f: hashes = [line.rstrip() for line in f.readlines()] flag = '' for h in hashes: found = False for r in range(1, 3): for c in itertools.product(printable, repeat=r): text = ''.join(c) if len(h) == 32: try_h = md5(text).hexdigest() else: try_h = sha256(text).hexdigest() if try_h == h: found = True flag += text break if found: break print flag
rgbCTF{4lw4ys_us3_s4lt_wh3n_h4shing}
e (Cryptography)
eの値が小さいので、e乗根を取る。
#!/usr/bin/env python3 import gmpy e = 0b1101 c = 0x6003a15ff3f9bc74fcc48dc0f5fc59c31cb84df2424c9311d94cb40570eeaa78e0f8fc2917addd1afc8e5810b2e80a95019c88c4ee74849777eb9d0ee27ab80d3528c6f3f95a37d1581f9b3cd8976904c42f8613ee79cf8c94074ede9f034b61433f1fef835f2a0a45663ec4a0facedc068f6fa2b534c9c7a2f4789c699c2dcd952ed82180a6de00a51904c2df74eb73996845842276d5523c66800034351204b921d4780180ca646421c61033017e4986d9f6a892ed649c4fd40d4cf5b4faf0befb1e2098ee33b8bea461a8626dd8cd2eed05ccd471700e2a1b99ed347660cbd0f202212f6c0d7ad8ef6f878d887af0cd0429c417c9f7dd64890146b91152ea0c30637ce503635018fd2caf436a12378e5892992b8ec563f0988fc0cebd2926662d4604b8393fb2000 m = int(gmpy.root(c, e)[0]) flag = m.to_bytes(m.bit_length()//8 + 1, byteorder='little') print(flag)
rgbCTF{lucky_number_13}
Occasionally Tested Protocol (Cryptography)
UNIXTIMEをseedにしていて、10回乱数を出力してから、次以降の乱数とXORしている。UNIXTIMEの多少のずれを出力して試して特定してから、XORキーを割り出して、復号する。
#!/usr/bin/env python3 import socket from random import seed, randint as w from time import time def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('167.172.123.213', 12345)) data = recvuntil(s, b'\n').rstrip() print(data) nums = [] for _ in range(10): data = recvuntil(s, b'\n').rstrip() print(data) nums.append(int(data)) ut = int(time()) - 1 while True: seed(ut) try_nums = [] for _ in range(10): try_nums.append(w(5, 10000)) if nums == try_nums: break ut += 1 data = recvuntil(s, b'\n').rstrip() print(data) c = int(data.split(' ')[-1]) ct = c.to_bytes(c.bit_length()//8 + 1, byteorder='little') b = bytearray([w(0, 255) for _ in range(40)]) flag = bytes([l ^ p for l, p in zip(ct, b)]) print(flag)
実行結果は以下の通り。
Here's 10 numbers for you: 6530 3291 6449 1757 2206 6694 8303 4500 8037 8608 Here's another number I found: 11924462008570498664266304436057399923000110040161498324004106845812 b'rgbCTF{random_is_not_secure}'
rgbCTF{random_is_not_secure}
Shakespeare Play, Lost (and found!) (Cryptography)
行と文字の位置を書いていると推測して、抜き出す。
with open('play', 'r') as f: lines = [line.rstrip() for line in f.readlines()] with open('some_numbers.txt', 'r') as f: nums = [line.rstrip() for line in f.readlines()] flag = '' for num in nums: n0 = int(num.split(', ')[0]) n1 = int(num.split(', ')[1]) flag += lines[n0][n1] flag = 'rgbCTF{%s}' % flag print flag
rgbCTF{itsanrgbtreeeeeee!}
N-AES (Cryptography)
サーバの処理概要は以下の通り。
・challenge: ランダム64バイトのbase64エンコード ・challengeにpaddingして、AES-ECB暗号化 ・ランダム1バイトseed ・16バイトrandintで鍵生成 ■1. Encrypt ・base64で平文を指定 ・base64でkeyのseedを指定 ・seedの各バイトの分暗号化 ■2. Decrypt ・base64で暗号文を指定 ・base64でkeyのseedを指定 ・seedの各バイトの分暗号化 ■3. Solve challenge ・challengeのbase64文字列を当てたら、フラグが表示される。
フラグの暗号化処理のコードが悪く、ランダム1バイト文字だけで、128回暗号化している。総当たりでchallengeとしてbase64文字列に復号できるものを探す。
#!/usr/bin/env python3 import socket from base64 import b64encode, b64decode from Crypto.Cipher import AES from Crypto.Util.Padding import unpad from os import urandom from random import seed, randint from string import * BLOCK_SIZE = 16 def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def rand_block(key_seed=urandom(1)): seed(key_seed) return bytes([randint(0, 255) for _ in range(BLOCK_SIZE)]) def decrypt_chall(key, text): for i in range(128): text = AES.new(key, AES.MODE_ECB).decrypt(text) return text def is_b64str(s): if len(s) == 0: return False chars = ascii_letters + digits + '+/' for c in s.rstrip('='): if c not in chars: return False return True s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('challenge.rgbsec.xyz', 34567)) data = recvuntil(s, b'\n').rstrip() print(data) ct = b64decode(data) data = recvuntil(s, b'> ') print(data + '3') s.sendall(b'3\n') for code in range(256): key = rand_block(bytes([code])) try: challenge = unpad(decrypt_chall(key, ct), BLOCK_SIZE) if is_b64str(challenge): break except: continue ans = b64encode(challenge) data = recvuntil(s, b': ') print(data + ans.decode()) s.sendall(ans + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
UOVh5i8DP7uB2MbWOPevMmsOiddn5K8se5EXHEm4ON3/wMgHdzO2yJ+NrxFCt8CWwqS/kvpo7/N0Tmfh/DF17xE7FyyFL9nP7EOWnaMgswKzY4hwnFCmHOvrkUK2WX/u [1] Encrypt [2] Decrypt [3] Solve challenge [4] Give up > 3 Enter the decrypted challenge, in base64: QXBuYWcyTVY5ai9zRUR1SVRQek84VUtqKzg4ejB4bWdrbVBjbDFjUUVaY1lUeVVHM0NZNS83SzNDU01saFBza01QUC9XOUtpQnBIR0NlNDRJSnVtcmc9PQ== Correct! Here's your flag: rgbCTF{i_d0nt_7hink_7his_d03s_wh47_y0u_7hink_i7_d03s}
rgbCTF{i_d0nt_7hink_7his_d03s_wh47_y0u_7hink_i7_d03s}