b00t2root '18 Writeup

この大会は2018/3/17 13:30(JST)~2018/3/18 1:30(JST)に開催されました。
今回もチームで参戦。結果は1616点で134チーム中3位でした。
自分で解けた問題をWriteupとして書いておきます。

RSA-2 (Crypto 200)

2組の平文の差と暗号文がわかっているので、Franklin-Reiter Related Message Attackで復号する。

# solve.sage
from hashlib import sha256

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+diff)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]

n = 114725527397185618184017233206819193913174443780510744606142335459665478168081417742295326326458510125306461590118257162988125409459000413629137879229803717947627133370343339582895822944017711093729671794212087753322731071609302218014807365556283824229308384059742494244873283137838666434755861643308137132991

e = 12289

c1 = 87410813732157727701928184577314318681587457726095432638836338681211650253979034474596959990411435773763619929643745595561018045828590610328140736165000754846648327232298646701600080979346670157972588491030528441191645554122003288005262778737855011793921980764813901447954145380508985385929190951189001811183

c2 = 13405530225102142120310029551994876557837309021095393214835868463875586021351445304878372433515568058695315045246405214821866800311993984428311760617739453096525618753380012567374424357402936368910846206438489444245873338816383688500972351617863313954864557810634214028546221991063291427532826592675208605764

pad1 = int('qwerty'.encode('hex'), 16)
pad2 = int('asdfgh'.encode('hex'), 16)
diff = pad1 - pad2
m = (related_message_attack(c2, c1, diff, e, n) - pad2) >> (8 * 6)
flag = ('%x' % m).decode('hex')
print flag
b00t2root{M4ny_pa7hs_up_th3_m0unt4in_4ll_l3ading_t0_th3_s4me_plac3}

hlea (Crypto 200)

Hash Length Extention Attackの問題。

hash(SALT + 'a') = 既知ハッシュ
hash(SALT + 'a' + '???' + 'administrator') 

HashPumpを使って、'???'とハッシュ値を取得、提示することができれば、フラグが得られる。

import socket
import re
import subprocess

def encHex(s):
    enc = ''
    xcode = ''
    for i in range(len(s)):
        if s[i] == '\\' and len(xcode) == 0:
            xcode += s[i]
        elif s[i] == 'x' and len(xcode) == 1:
            xcode += s[i]
        elif len(xcode) == 2:
            xcode += s[i]
        elif len(xcode) == 3:
            xcode += s[i]
            enc += xcode[2:]
            xcode = ''
        else:
            enc += '%02x' % ord(s[i])
    return enc

cmd_format = 'hashpump -s %s -d a -k %d -a administrator'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('18.188.130.86', 1223))

for j in range(2):
    data = s.recv(1024)
    print data

choice = '1'
print choice
s.sendall(choice + '\n')
data = s.recv(1024)
print data

username = 'a'
print username
s.sendall(username + '\n')
data = s.recv(1024)
print data
pattern = 'Here\'s your token\:(.+)'
m = re.search(pattern, data, re.DOTALL)
h = m.group(1).replace('\n', '').split(':')[1]
data = s.recv(1024)
print data

for i in range(1, 32):
    print 'SALT Length: %d' % i
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('18.188.130.86', 1223))

    for j in range(2):
        data = s.recv(1024)
        print data

    choice = '2'
    print choice
    s.sendall(choice + '\n')
    data = s.recv(1024)
    print data

    cmd = cmd_format % (h, i)
    print cmd
    ret = subprocess.check_output(cmd.split(' '))
    print ret
    try_ans = ret.split('\n')

    token = encHex(try_ans[1])
    token += ':' + try_ans[0]
    print token
    s.sendall(token + '\n')
    data = s.recv(1024)
    print data

    if 'Invalid Token' not in data:
        data = s.recv(1024)
        print data
        break

実行結果は以下の通り。

     :
SALT Length: 15
What do you want to do?

[1] Register
[2] Login
Enter your choice: 
2
Enter your token: 
hashpump -s d346d2179993563cfc0cc81ff7d31f627ca9cce5645ea5b8d0b6588d975f0396 -d a -k 15 -a administrator
30dea6e09040845ef0474fe8b15490f2afc5e36a4e14a808c065e9b054b996d2
a\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80administrator

6180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008061646d696e6973747261746f72:30dea6e09040845ef0474fe8b15490f2afc5e36a4e14a808c065e9b054b996d2
Welcome a��administrator!

Here's the flag: b00t2root{51mpl3_h45h_l3ng7h_3x73n510n_4774ck}
Closing connection.
b00t2root{51mpl3_h45h_l3ng7h_3x73n510n_4774ck}

unZipitCat (Forensics 150)

ZIPの既知平文攻撃。

$ zipinfo ZIPIT.zip 
Archive:  ZIPIT.zip   1201088 bytes   5 files
-rw-r--r--  3.0 unx       47 TX stor 16-Mar-18 17:00 gg
-rw-rw-r--  3.0 unx  1023704 BX defN 13-Mar-18 23:04 catgif.gif
-rw-rw-r--  3.0 unx   186442 BX defN 13-Mar-18 23:05 minion.gif
-rw-rw-r--  3.0 unx    10342 BX defN 13-Mar-18 23:06 TaPz.gif
-rw-r--r--  3.0 unx        5 TX stor 16-Mar-18 17:01 flag
5 files, 1220540 bytes uncompressed, 1200218 bytes compressed:  1.7%
$ ls -l am.gif
-rwxrwxrwx 1 root root 1023704  317 18:26 am.gif
$ zip am.zip ./am.gif
  adding: am.gif (deflated 1%)
$ ./pkcrack-1.2.2/src/pkcrack -C ZIPIT.zip -c catgif.gif -p am.gif -P am.zip -d decrypted.zip
          :
Stage 2 completed. Starting zipdecrypt on Sat Mar 17 18:59:11 2018
Decrypting gg (373132b4e5a73cb6227ad66b)... OK!
Decrypting catgif.gif (f9fac3f69bc4ecbde55f609c)... OK!
Decrypting minion.gif (275d6236c096e1f8116f749c)... OK!
Decrypting TaPz.gif (a7c27d9a7f06f41ad7e5849c)... OK!
Decrypting flag (e65781eb8c4ddbbbbc1ce26b)... OK!
Finished on Sat Mar 17 18:59:11 2018
$ unzip decrypted.zip 
Archive:  decrypted.zip
 extracting: gg                      
  inflating: catgif.gif              
  inflating: minion.gif              
  inflating: TaPz.gif                
replace flag? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
 extracting: flag
$ cat gg
b00t2root{1t_only_requ1r3s_a_k3y_noth1ng_els3}
b00t2root{1t_only_requ1r3s_a_k3y_noth1ng_els3}

Thousand Milliard (Misc 120)

SEEDを元に最初のランダムな値を求め、そこから四則演算や今の値を答えていく。

import socket
import re
import random

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(('18.188.130.86', 1000))

for i in range(8):
    data = recvuntil(s, '\n')
    print data
    if i == 2:
        pattern = 'SEED: (.+)\n'
        m = re.search(pattern, data)
        SEED = int(m.group(1))

random.seed(SEED)
now = random.randint(0, 1000000000000)

for i in range(600):
    print 'Round %d' % (i+1)

    data = recvuntil(s, '\n')
    print data

    first = data.split(' ')[0]
    op = data.split(' ')[2]
    num = data.split(' ')[4]
    num_sub = data.split(' ')[1]

    ans = ''
    if first == 'What':
        ans = str(now)
    elif first == 'Subtract':
        ans = str(now - int(num_sub))
    elif op == 'added':
        ans = str(now + int(num))
    elif op == 'multiplied':
        ans = str(now * int(num))
    elif op == 'divided':
        ans = str(now / int(num))

    print ans
    s.sendall(ans + '\n')
    now = int(ans)

data = s.recv(1024)
print data
data = s.recv(1024)
print data
data = s.recv(1024)
print data

実行結果は以下の通り。

   :
Round 600
Subtract 4960 from your number

739060326684744025972495958689
You did it
Here is your reward


b00t2root{h0pe_y0u_g0t_this_3asily}
b00t2root{h0pe_y0u_g0t_this_3asily}