Overflow To Fall Writeup

この大会は2021/10/2 7:00(JST)~2021/10/4 7:00(JST)に開催されました。
今回もチームで参戦。結果は4154点で18チーム中4位でした。
自分で解けた問題をWriteupとして書いておきます。

Scrambled JPEGs (Algorithms)

以下の処理が指示されている。

5バイトを1ブロックとして処理をする。
・0 1 2 3 4 -> 4 2 3 1 0という順序に変える。
・偶数ブロックと奇数ブロックを逆にする。
・偶数ブロックは右に2バイトシフト、奇数ブロックは左に1バイトシフト

上記のロジックから逆の処理を行い、復号する。

with open('scrambled.jpg', 'rb') as f:
    enc = f.read()

blocks = [enc[i:i+5] for i in range(0, len(enc), 5)]

for i in range(len(blocks)):
    b = blocks[i]
    if i % 2 == 0:
        blocks[i] = b[2:] + b[:2]
    else:
        blocks[i] = b[-1:] + b[:-1]

for i in range(0, len(blocks), 2):
    blocks[i], blocks[i+1] = blocks[i+1], blocks[i]

for i in range(len(blocks)):
    b = blocks[i]
    blocks[i] = b[4] + b[3] + b[1] + b[2] + b[0]

dec = ''.join(blocks)

with open('flag.jpg', 'wb') as f:
    f.write(dec)

f:id:satou-y:20211006075027j:plain

O2F{0v3r_345y_3gg5}

Fibbo Frenzy (Algorithms)

1000番目までのフィボナッチ数列素数判定し、素数の合計を求める。

from Crypto.Util.number import *

fibs = [0, 1]
for i in range(2, 1001):
    v = fibs[i-1] + fibs[i-2]
    fibs.append(v)

sum = 0
for f in fibs:
    if isPrime(f):
        sum += f

print sum

実行結果は以下の通り。

132725674935014129884681801561570257444232919795125324970917377000488365086871380952521159138505357911319846367559249240
O2F{132725674935014129884681801561570257444232919795125324970917377000488365086871380952521159138505357911319846367559249240}

Barred for life! (OSINT)

バーコードの画像から対応する製品に関する質問に答えていく。バーコードのナンバーは885909918188。885909918188を調べると、以下のページなどで情報を確認できる。

https://www.upcitemdb.com/upc/885909918188
https://www.buycott.com/upc/885909918188

上記のサイトなどでこのような情報が得られる。

UPC 885909918188 is associated with Apple MacBook Pro 13" Retina Display MF840LLA Intel Core i5 2.7GHz 8GB 256GB SSD

Model #:MF840LL/A

Apple MacBook Pro A1502 13.3" Laptop - MF840LL/A (March, 2015)

モデルナンバーは?

O2F{MF840LL/A}

バイスは?(Manufacturer + Modelという形式)

O2F{Apple MacBook Pro}

バイスのドライブ容量は?

O2F{256 GB}

製造された年は?

O2F{2015}

Password Cracking Easy (Password)

md5の値が与えられているので、CrackStationでクラックする。

#dare2stealmapassword#

School is cool! (Rev)

各文字のASCIIコードで199とXORしたものと比較しているので、そのことを前提に復号する。

vals = [159, 168, 181, 152, 178, 152, 245, 152, 164, 247, 247, 171, 152, 243,
    152, 189, 164, 175, 247, 168, 246]

flag = ''
for val in vals:
    flag += chr(val ^ 199)
print flag

復号結果は以下の通り。

Xor_u_2_c00l_4_zch0o1
O2F{Xor_u_2_c00l_4_zch0o1}

Bashing My Head In (Rev)

.bash_historyとパスワード付きzip、パスワード生成スクリプトが添付されている。スクリプトを見ると、passwordは時刻から導き出したseedを元に生成している。.bash_historyからpasswordgen.pyを実行した時刻がわかる。seedの可能性があるのは10**5個あるので、そこからパスワードのリストを作成する。

#!/usr/bin/python3
from datetime import datetime

class Random:
    def __init__(self, seed=None):
        if seed is None:
            self.seed = round(int(time.time() * (10 ** 5)))
        else:
            try:
                self.seed = int(seed)
            except ValueError:
                raise ValueError("Please use a valid integer for the seed.")
        self.next_seed = 0

    def __generatePercentage(self):
        a = 1664525
        c = 1013904223
        m = 2 ** 32

        if self.next_seed == 0:
            next_seed = (self.seed * a + c) % m
        else:
            next_seed = (self.next_seed * a + c) % m
        self.next_seed = next_seed
        randomPercentage = next_seed / m

        return randomPercentage

    def choice(self, index):
        randomIndex = round((self.__generatePercentage() * (len(index) - 1)))
        randomChoice = index[randomIndex]

        return randomChoice

tstr = '2021-09-24 18:35:20 -0400'
t = datetime.strptime(tstr, '%Y-%m-%d %H:%M:%S %z')
min_seed = int(t.timestamp() * (10 ** 5))
max_seed = min_seed + 10 ** 5

alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890?!@#$%^&*()"
for seed in range(min_seed, max_seed):
    rand = Random(seed)
    print(''.join([rand.choice(alphabet) for i in range(16)]))
$ python3 solve.py > password.lst

作成したパスワードのリストを使って、zipパスワードのクラックをする。

$ fcrackzip -u -D -p password.lst data.zip 


PASSWORD FOUND!!!!: pw == zPd9GoG?ECOho71&
$ unzip -P "zPd9GoG?ECOho71&" data.zip
Archive:  data.zip
 extracting: flag.txt
$ cat flag.txt
O2F{t1m3_w0und5_4ll_h33l5}
O2F{t1m3_w0und5_4ll_h33l5}

Shake that AES for me! (Rev)

スクリプトの処理概要は以下の通り。

・AES鍵:'H@ck3rMan__kN0ws'
・flag1:入力
・AES_flag1_cipher:AES-ECBでflag1を暗号化
 →'\x19\xe3\xc9\xa8\xf2\x05\xdaX\x90aP~\xe2L\xbeY'と一致していたら次のパート
・msg_input: 入力(hex)
・msg: msg_inputの0x付きの16進数文字列
・msg: 10bit左シフト(一番左のビットは一番右へ移動)
・msg: 3bit右シフト(一番右のビットは一番左へ移動)
 →"b92faa3419b6afa0363619"と一致していたら、入力文字からフラグを構成される。

最初のパートは、AES鍵がわかっているので、そのまま復号する。次のパートは7bit左シフトしているのと同じため、7bit右シフトして復号する。

#!/usr/bin/python3
from Crypto.Cipher import AES
import bitstring

def Mathers(i, msg):
    msg.ror(1)
    return msg

flag1_enc = b'\x19\xe3\xc9\xa8\xf2\x05\xdaX\x90aP~\xe2L\xbeY'
AES_key = b'H@ck3rMan__kN0ws'
flag1_cipher = AES.new(AES_key, AES.MODE_ECB)
flag1 = flag1_cipher.decrypt(flag1_enc).decode()
print('[+] flag1: ' + flag1)

msg_enc = 'b92faa3419b6afa0363619'
msg = bitstring.BitArray(hex='0x' + msg_enc)
for i in range(7):
    msg = Mathers(i, msg)
flag2 = bytes.fromhex(str(msg).lstrip('0x')).decode()
print('[+] flag2: ' + flag2)

flag = 'O2F{%s%s}' % (flag1, flag2)
print('[*] flag : ' + flag)

実行結果は以下の通り。

[+] flag1: DiViD3_@nD_C0nQu
[+] flag2: 3r_Th3m_@ll
[*] flag : O2F{DiViD3_@nD_C0nQu3r_Th3m_@ll}
O2F{DiViD3_@nD_C0nQu3r_Th3m_@ll}

Dare To Be Stupid (Forensics-Analytics)

$ file diputsebote.rad.1
diputsebote.rad.1: dar archive, label "44024461 00000000 8302"

darファイルが16個入っている。ファイル名をすべて以下のように修正する。

diputsebote.rad.1 -> diputsebote.1.dar
$ sudo dar -x diputsebote
The format version of the archive is too high for that software version, try reading anyway? [return = YES | Esc = NO]
Continuing...


 --------------------------------------------
 5 inode(s) restored
    including 0 hard link(s)
 0 inode(s) not restored (not saved in archive)
 0 inode(s) not restored (overwriting policy decision)
 0 inode(s) ignored (excluded by filters)
 0 inode(s) failed to restore (filesystem error)
 0 inode(s) deleted
 --------------------------------------------
 Total number of inode(s) considered: 5
 --------------------------------------------
 EA restored for 0 inode(s)
 FSA restored for 0 inode(s)
 --------------------------------------------

5個のjpgを復旧でき、そのうち4.jpgにフラグが書いてあった。
f:id:satou-y:20211006080820j:plain

O2F{h4rdw4re_5t0r3}

Reserved Words (Forensics-Analytics)

以前にも予約語を使った似たような問題があったので、そのときと同じようにperlで実行してみる。

$ mv "reserved words.txt" reserved_words.pl
$ perl reserved_words.pl 
The flag is: O2F{perl_is_fun!}
O2F{perl_is_fun!}

Forgotten Password (Crypt-Stego)

$ zipinfo memearchive.enc.zip 
Archive:  memearchive.enc.zip
Zip file size: 2634651 bytes, number of entries: 7
-rwxrwxrwx  3.0 unx    27356 BX defN 21-Sep-26 07:39 meme 3.jpg
-rwxrwxrwx  3.0 unx  1769703 BX defN 21-Sep-26 07:39 meme 6.gif
-rwxrwxrwx  3.0 unx    11517 BX defN 21-Sep-26 07:43 manananan 9.jpg
-rwxrwxrwx  3.0 unx    87495 BX defN 21-Sep-26 07:43 10.jpg
-rwxrwxrwx  3.0 unx   243780 BX defN 21-Sep-26 07:43 pain 11.png
-rwxrwxrwx  3.0 unx    32239 BX defN 21-Sep-26 07:41 mem8.jpg
-rwxrwxrwx  3.0 unx   497607 BX defN 21-Sep-26 07:46 license.jpg
7 files, 2669697 bytes uncompressed, 2633395 bytes compressed:  1.4%

$ unzip memearchive.zip
Archive:  memearchive.zip
  inflating: meme 1.png              
  inflating: meme 2.jpg              
  inflating: meme 4.jpg              
  inflating: mem 5.jpg               
  inflating: memq 7.png              
  inflating: mem8.jpg
$ ls -l mem8.jpg 
-rwxrwxrwx 1 root root 32239  926 07:41 mem8.jpg

memearchive.zipの方にもmem8.jpgがあり、ファイルサイズ的にも同じものと考えられる。pkcrackで既知平文攻撃をして、memearchive.enc.zipを復号する。

$ zip mem8.zip mem8.jpg
  adding: mem8.jpg (deflated 1%)
$ ./pkcrack-1.2.2/src/pkcrack -C memearchive.enc.zip -c mem8.jpg -p mem8.jpg -P mem8.zip -d decrypted.zip
Files read. Starting stage 1 on Sat Oct  2 10:59:09 2021
Generating 1st generation of possible key2_31846 values...done.
Found 4194304 possible key2-values.
Now we're trying to reduce these...
Lowest number: 943 values at offset 28081
Lowest number: 917 values at offset 28078
Done. Left with 917 possible Values. bestOffset is 28078.
Stage 1 completed. Starting stage 2 on Sat Oct  2 10:59:23 2021
Ta-daaaaa! key0=bdb07b3f, key1=1e0989cd, key2=b817b2f9
Probabilistic test succeeded for 3773 bytes.
Strange... had a false hit.
Strange... had a false hit.
Strange... had a false hit.
Strange... had a false hit.
Strange... had a false hit.
Strange... had a false hit.
Strange... had a false hit.
Strange... had a false hit.
Stage 2 completed. Starting zipdecrypt on Sat Oct  2 10:59:43 2021
Decrypting meme 3.jpg (df171296401a566b8aaeef94)... OK!
Decrypting meme 6.gif (ef4260c8d77febf550acfc94)... OK!
Decrypting manananan 9.jpg (bdb4659561cf0e6df9f66a95)... OK!
Decrypting 10.jpg (5f0fbabed5857871422e6d95)... OK!
Decrypting pain 11.png (80d170db68a6de6fdaf47a95)... OK!
Decrypting mem8.jpg (18bc5cccc97dc033b0903195)... OK!
Decrypting license.jpg (7573f47a29d6bd74c450c995)... OK!
Finished on Sat Oct  2 10:59:43 2021
$ unzip decrypted.zip 
Archive:  decrypted.zip
  inflating: meme 3.jpg              
  inflating: meme 6.gif              
  inflating: manananan 9.jpg         
  inflating: 10.jpg                  
  inflating: pain 11.png             
replace mem8.jpg? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
  inflating: mem8.jpg                
  inflating: license.jpg

license.jpgにフラグが書いてあった。
f:id:satou-y:20211006081216j:plain

O2F{p141nt3xt_15_p41n}

Based (Crypt-Stego)

base64エンコードする。

from base64 import *

enc = '3b 61 7f fc 10 12 13 ae 04 34 23 83 10 3f ff'.split(' ')
enc = ''.join([chr(int(c, 16)) for c in enc])
flag = b64encode(enc)
print flag
O2F//BASE64ENCODED//

San Francisco (Crypt-Stego)

ルータのコンフィグファイルが添付されている。Ciscoのtype 7のパスワードと思われる記述がある。このパスワードをhttps://www.firewall.cx/cisco-technical-knowledgebase/cisco-routers/358-cisco-type7-password-crack.htmlでクラックする。

w34k_v1gn3re
O2F{w34k_v1gn3re}