izumo’s diary

主に競プロの精進記録

WaniCTF'21-spring writeup

WaniCTF'21-springに参加しました。

3023ptで353人中63位でした。

CTFに絶賛入門中なので、知らないツールや概念が多く勉強になりました。
こういうCTFが毎月あればかなり強くなれそうです。

あと何より多くの問題を解けたので楽しかったです(小並感)。

https://cdn-ak.f.st-hatena.com/images/fotolife/i/izumo27/20210502/20210502204443_original.png

https://cdn-ak.f.st-hatena.com/images/fotolife/i/izumo27/20210502/20210502204316_original.png

目次

Crypto

Simple conversion [Beginner]

問題
戻し方を忘れました…

与えられるファイル

  • cry-simple-conversion.zip

解法
整数をバイト列に変換する。

def integer_to_bytes(x):
  x= x.to_bytes((x.bit_length() + 7) // 8, byteorder='big')
  return x

input=int(input())
print(integer_to_bytes(input))
FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}

Easy [Easy]

問題
手始めに

与えられるファイル

  • cry-easy.zip

解法
アフィン暗号なので
http://www.quipqiup.com/
を使った。
最初の4文字はFLAGであることが分かっているので指定して復号。

FLAG{WELCOMETOCRYPTOCHALLENGE}

Extra [Normal]

問題
いつものRSA

与えられるファイル

  • cry-extra.zip

解法
N, Mがわかっているので、まず2次方程式をSympyで解いてp,qを求める。
あとはRSAを復号する。解が2つあるが片方はbyteからstringへdecodeできない。
使っているpythonのバージョンが3.7だったので逆元は自分で求めた。

from Crypto.Util.number import getPrime, bytes_to_long, long_to_bytes
import sympy
import math

N=int(input())
M=int(input())
e=int(input())
c=int(input())

p=sympy.Symbol("p")
sol = sympy.solve (-2*p**2 + M*p -N, p)
p=int(sol[1])

q=M-2*p

def xgcd(a, b):
    x0, y0, x1, y1 = 1, 0, 0, 1
    while b != 0:
        q, a, b = a // b, b, a % b
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return a, x0, y0

def modinv(a, m):
    g, x, y = xgcd(a, m)
    return x % m

def lcm(a, b):
  return a // math.gcd(a, b) * b

def decrypt(c):
  d=modinv(e, lcm(p-1, q-1))
  plaintext=pow(c, d, N)
  plaintext=long_to_bytes(plaintext)
  return plaintext


if __name__ == "__main__":
  _plaintext = decrypt(c)
  print(_plaintext.decode())
FLAG{@n_ex7ra_param3ter_ru1n5_3very7h1n9}

Can't restore the flag? [Hard]

問題
ちりつもですよ

nc crt.cry.wanictf.org 50000

与えられるファイル

  • cry-cant-restore-the-flag.zip

解法
サーバはflagをある数で割った余りを無限に返してくれるので、中国剰余定理を使う。
flagは10^{103}以下なので300以下の素数を使えば十分。
入力は手作業でやった()
あとは出てきた数をbyteに変換する。

from functools import reduce
from Crypto.Util.number import long_to_bytes

def xgcd(a, b):
    x0, y0, x1, y1 = 1, 0, 0, 1
    while b != 0:
        q, a, b = a // b, b, a % b
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return a, x0, y0

def modinv(a, m):
    g, x, y = xgcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

def chinese_remainder(a, n):
    # a := [a1, a2, ..., ak]
    # n := [n1, n2, ..., nk]
    total = 0
    prod = reduce(lambda x, y: x*y, n)
    for n_i, a_i in zip(n, a):
        b_i = prod // n_i
        total += a_i * b_i * modinv(b_i, n_i)
    return total % prod


if __name__ == "__main__":
  n=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293]
  a=[0, 2, 0, 2, 10, 7, 8, 10, 17, 17, 24, 7, 31, 30, 18, 43, 2, 31, 44, 14, 1, 50, 67, 48, 21, 54, 47, 105, 80, 43, 103, 74, 104, 83, 56, 140, 148, 13, 154, 96, 76, 162, 49, 43, 182, 168, 73, 49, 158, 39, 95, 40, 189, 206, 245, 242, 205, 67, 89, 0, 282, 222]
  print(long_to_bytes(chinese_remainder(a, n)).decode())
FLAG{Ch1n3s3_r3m@1nd3r_7h30r3m__so_u5eful}

Forensics

presentation [Beginner]

問題
このままじゃFLAGをプレゼンできない...

与えられるファイル

  • for-presentation.zip

解法
まずpresentation.ppsxの拡張子をzipに変えて解凍してみた。
するといっぱいファイルが出てくる。
それっぽい画像ファイルがあったので、strings試したりexifとか調べたが何もない。

Beginnerから難しい問題だ……と思ったが、ppsxを開くのではなく、powerpointから開くと編集できるらしい。
青い図形を除くとフラグが現れる。

FLAG{you_know_how_to_edit_ppsx}

MixedUSB [Very hard]

問題
USBにパーティションを設定したら、どこにFLAGを保存しているのかわからなくなってしまいました...

与えられるファイル

  • MixedUSB.img

解法
stringsコマンドを使うだけ。
これがVery hardな訳がないのでおそらく出題ミス。

FLAG{mixed_file_allocation_table}

Misc

binary [Beginner]

問題
文字も所詮1と0の集合です。
sample.pyを参考に復号器を作ってみてください。

与えられるファイル

  • misc-binary.zip

解法
sampleの後半を少し変えるだけで復号できる。

s = ""
c = 0
for i in range(336):
    val = int(input())
    c = (c << 1) | val
    if i % 8 == 7:
        s = s + chr(c)
        c = 0

print(s)
FLAG{the_basic_knowledge_of_communication}

Pwn

01 netcat [Beginner]

問題
nc netcat.pwn.wanictf.org 9001

netcat (nc)と呼ばれるコマンドを使うだけです。
つないだら何も出力されなくても知っているコマンドを打ってみましょう。

使用ツール例

  • netcat (nc)

解法
問題文の通りncコマンドを使って接続してcatコマンドを使う。

$ cat flag.txt
FLAG{the_basic_knowledge_of_communication}

02 free hook [Easy]

問題
nc free.pwn.wanictf.org 9002

ヒント
free_hookの仕組みを理解する必要があります。

使用ツール例

  • netcat (nc)

与えられるファイル

  • pwn-02-free-hook.zip

解法
(pwn) kitten - HackMD
を参考にした。
親切にも__free_hookにはsystemがセットされてる。
さらに、メモを削除する際にfreeするコードになっている。
ということは/bin/shのメモを削除することで、free("/bin/sh")させればいい。
f:id:izumo27:20210502224958p:plain

FLAG{malloc_hook_is_a_tech_for_heap_exploitation}

03 rop machine easy [Easy]

問題
nc rop-easy.pwn.wanictf.org 9003

ヒント
ropでsystem("/bin/sh")を実行して下さい。
"/bin/sh"のアドレスは提供されています
rop machineの使い方->wani-hackase/rop-machine

使用ツール例

  • netcat (nc)

与えられるファイル

  • pwn-03-rop-machine-easy.zip

解法
ROPを調べたら去年のWaniCTFが出てきた。
WaniCTF 2020 Writeup - m412uのメモ

  1. "pop rdi; ret" addr
  2. "/bin/sh"のアドレス
  3. "system" addr

の順にすればいいらしい。
f:id:izumo27:20210502224626p:plain

FLAG{this-is-simple-return-oriented-programming}

04 rop machine normal [Easy]

問題
nc rop-normal.pwn.wanictf.org 9004

ヒント
ropでexecve("/bin/sh", 0, 0)を実行して下さい。
"/bin/sh"のアドレスは提供されています
execveのsyscall番号は0x3bです。
rop machineの使い方->wani-hackase/rop-machine

使用ツール例

  • netcat (nc)

与えられるファイル

  • pwn-04-rop-machine-normal.zip

解法
ヒントが意味するのは、引数を3つセットし、execveをsyscallで呼び出せということらしい。
アセンブラは忘れてしまっていたので色々調べた。
引数はrdi, rsi, rdxの順に入れるといいことが分かった。
syscallで呼び出すのはraxにsyscall番号をセットすればできる。
f:id:izumo27:20210502224847p:plain

FLAG{now-you-can-call-any-system-calls-with-syscall}

05 rop machine hard [Normal]

問題
nc rop-hard.pwn.wanictf.org 9005

ヒント
ROPgadgetコマンドの使い方を覚えましょう。
rop machineの使い方->wani-hackase/rop-machine

使用ツール例

  • netcat (nc)
  • ROPgadget

与えられるファイル

  • pwn-05-rop-machine-hard.zip

解法
前問同様、引数を3つセットし、execveをsyscallで呼び出すことを目指す。
残念ながらpop rdi ; retなどは与えられていないので自分で探さなくてはならない。

ROPgadgetというのはpop rdi ; retなどの場所を見つけてくれるツールらしい(参考:めちゃくちゃ遅れてきたSECCON2018 Writeup · 八階ないブログ)。
色々探してみるとそれらしいものが見つかる。
0x40128f pop rdi ; ret
0x401611 pop rsi ; pop r15 ; ret
0x40129c pop rdx ; ret
0x4012a9 pop rax ; ret

Ghidra(後述)でsyscallと"bin/sh"を探した。
0x4012b6 syscall
0x404078 "bin/sh"

pop rsi ; retはないので適当な値(今回は0xaaaaaaaa)をpopする必要がある(参考:x64 ELF Binary リモートエクスプロイト(Blind ROP) | by Ryota yaoi (nop) | Medium)。
f:id:izumo27:20210502225055p:plain

FLAG{y0ur-next-step-is-to-use-pwntools}

Reversing

secret [Beginner]

問題
この問題では Linux の ELF 実行ファイル(バイナリ)である「secret」が配布されています。

このバイナリを実行すると secret key を入力するように表示されます。

試しに「abcde」と入力してみると「Incorrect」と言われました。

$ file secret
secret: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1daf4ab43cfa357911806c3ccae34a1b6e027913, for GNU/Linux 3.2.0, not stripped

$ sudo chmod +x secret

$ ./secret 
...
Input secret key : abcde
Incorrect

$ ./secret 
...
Input secret key : ??????
Correct! Flag is ??????

このバイナリが正しいと判断する secret key を見つけて読み込んでみましょう!

(secret key とフラグは別の文字列です)

(このファイルを実行するためには Linux 環境が必要となりますので WSL や VirtualBox で用意してください)

ヒント
「表層解析」や「静的解析」を行うことで secret key が見つかるかも...?

使用ツール例

  • 表層解析ツール strings
  • 静的解析ツール Ghidra

与えられるファイル

  • rev-secret.zip

解法
言われたとおりGhidraで解析してみる。
デコンパイルしたmainを見たところ、以下の文字列と比較していることがわかった。
wani_is_the_coolest_animals_in_the_world!

FLAG{ana1yze_4nd_strin6s_and_execu7e_6in}

execute [Easy]

問題
コマンドを間違えて、ソースコードも消しちゃった!

今残っているファイルだけで実行して頂けますか?

(reverse engineeringすれば、実行しなくても中身は分かるみたいです。)

与えられるファイル

  • rev-execute.zip

解法

$ as -o main.o main.s

でオブジェクトファイルを生成。

$ gcc -shared -o main main.o libprint.so

で実行ファイルを生成。

stringsでFLAGを探すとそれっぽいのが出てくる。

FLAG{c4nH
_y0u_exeH
cu4e_th1H
s_fi1e}

何となく最後のHを消したら通った。

FLAG{c4n_y0u_execu4e_th1s_fi1e}

timer [Hard]

問題
フラグが出てくるまで待てますか?

super_complex_flag_print_function 関数でフラグを表示しているようですが、難読化されているため静的解析でフラグを特定するのは難しそうです...

GDBを使って動的解析してみるのはいかがでしょうか?

与えられるファイル
rev-timer.zip

解法
言われた通りGDBを使った。

  1. timerにbreakpointを設定。
  2. retでtimerを抜ける。
  3. continueでsuper_complex_flag_print_functionを実行。

f:id:izumo27:20210502224751p:plain

FLAG{S0rry_Hav3_you_b3en_wai7ing_lon6?_No_I_ju5t_g0t_her3}

Web

fake [Beginner]

問題文
偽物を見破れますか?

https://fake.web.wanictf.org

解法
開発者ツールでリンクを見つける。

https://fake.web.wanictf.org/144c9defac04969c7bfad8efaa8ea194.html
f:id:izumo27:20210502224500p:plain

FLAG{wow_y0u_h4ve_3po4ted_th3_7ake}

Wani Request 1 [Easy]

問題文
RequestBinを使ってみよう!!

https://request1.web.wanictf.org/

この問題ではあどみんちゃんから自分のサーバにアクセスしてもらう必要があります。

自前でサーバを用意するのが難しい方はRequestBinなどのサービスを利用してみましょう。

サーバが用意出来たらいよいよ本番です。

問題ページにアクセスし、あなたが用意したサーバのURLを送信してください。

送信するとあどみんちゃんの秘密のページにあなたの送信したURLのリンクが表示されます。

あどみんちゃんは表示されたリンクをクリックしてあなたのサーバにアクセスしてくれます。

あどみんちゃんからのアクセスを分析して秘密のページを探してみてください。

ヒント
HINT1 : HTTP ヘッダー
HINT2 : xss問題ではありません。

解法
問題文に出てくるRequestBinを使った。
RequestBinで作ったURLを送信するとGETリクエストが来た。
refererにURLがあったので開いてみるとフラグがあった。

FLAG{h77p_r3f3r3r_15_54f3_42a2cc2f275}