もふもふ

くんかくんか

ICHSA CTF 2021

zlib問題初めて触ったのでメモ

crime_cloud

次のようなサービスが動いてる。

import base64
import os
import zlib


def rand(n):
    return os.urandom(n)

def xor(a,b):
    return bytes(x^y for x,y in zip(a,b))

def enc_otp(pt):
    return xor(pt, rand(len(pt)))

def process(req):
    pt = zlib.compress(b"ICHSA_CTF{fake_flag_to_annoy_you_pay_us_ten_thousand_BTC}" + rand(32) + req)
    return enc_otp(pt)


def main():
    print("Wellcome!\nInput an empty line to exit.")
    while True:
        req = input("\nYour input: ")
        if len(req) == 0:
            print("Bye!")
            break
        req_bytes = req.encode()
        if len(req_bytes) > 128:
            print(f'Bad input length {len(req_bytes)} > 128')
            continue
        print(base64.b64encode(process(req_bytes)).decode())

if __name__ == "__main__":
   try:
      main()
   except:
      print("Some error occured")

最初はos.urandomが細工されてるのじゃないかという明後日な発想で周期を探したりしたがそんなことはなかった。

zlibについて調べるとDeflateというアルゴリズムで圧縮されているらしく、LZ77とハフマン符号を組み合わせているらしい。 LZ77は同じ文字列が複数回出現する場合、ポインタに置き換えて圧縮するらしい。 ”ICHSA_CTF{”というflagに一致する文字列と”ABDOIJGEA"みたいな適当な文字列を送ってレスポンスを見たところ、適当な文字列を送った場合のほうがレスポンスの長さが大きいことがわかった。

乱数が交じるので若干ブレがあったが大体長さが96〜97のとき正しいフラッグが得られているっぽかったので、ソースコードにちまちま追記しながらフラッグを読んだ。

import pwn
import base64
import string

io = pwn.remote("crime.ichsa.ctf.today", 8008)

rand_initial = None
previous = None
first_value = None
cycle_list = []
i=0
flag = "ICHSA_CTF{compressing_secret_with_input_is_a_crime"
#flag = "ICHSA_CTF{" # 最初はここから初めて↑のように継ぎ足して行った。

def bt(io, flag):
    for i in string.ascii_lowercase + "_}":
        print(io.readuntil('Your input: '))
        payload = flag + i
        io.sendline(payload)
        res = io.readline()[:-1]
        print(res)
        print("length: ", len(res))
        zlibdata = base64.b64decode(res)
        print("b64decode len: ", len(zlibdata))
        if len(zlibdata) in [95, 96, 97]:
            print(flag+i)
            bt(io, flag + i)
        else:
            return None

bt(io, flag)

flag: ICHSA_CTF{compressing_secret_with_input_is_a_crime}

super super mario

スーパーマリオのゲームのコピー?っぽいバイナリが渡される。 各ステージの一番最後にコインでflagが書かれている。 制限時間が少なく普通に遊ぶと1面クリアが精一杯である。

バイナリを読んでいき、時間を減少させている場所をnopで埋めて、死亡処理関数をすぐreturnするように書き換えた。

< uMario:     file format elf64-x86-64
---
> custom_uMario:     file format elf64-x86-64
22123c22123,22125
<    53a85:  8d 50 ff                lea    -0x1(%rax),%edx
---
>    53a85:  90                      nop
>    53a86:  90                      nop
>    53a87:  90                      nop
106547c106549
<    ab846:  55                      push   %rbp
---
>    ab846:  c3                      retq   
106769c106771,106773
<    abc2f:  83 e8 01                sub    $0x1,%eax
---
>    abc2f:  90                      nop
>    abc30:  90                      nop
>    abc31:  90                      nop

あとは普通に遊んでゴールまでたどり着いて読んでいった。 死なないのにめっちゃ苦労したとか言えない

f:id:b_tya_nya:20210603175300p:plain

flag: ICHSA_CTF{R3VERS1NG_IS_NO7_SCA!}