SECCON 2017 Online CTF に参加した
テスト期間中ですがチームBiPhoneからSECCON 2017 Online CTFに参加しました。
チームでは900点、個人では600点でした(Thanks入れて)。
pwn1問も解けなかったのが残念です。Vigenere3d, Baby Stack, Election, z80, printf_machineはどういう解法なのかあとで見てみます。
以下解いた3問のwrite-up。
Powerful_Shell
powerful_shell.ps1
というPowerShellのスクリプトが渡される。
LinuxにはPowerShellが入っていないので、インストールする。
https://github.com/PowerShell/PowerShell/releases/
ここからAppImageを落とすだけなので簡単。debを落としてもうまく動かなかった...
実行すると「SECCON」のロゴと鍵盤が出てくる。そしてNo Admin rights!
などと言って落ちる。
中身はこんな感じ。
$ECCON+=[char](137-127); $ECCON+=[char](-905+918); $ECCON+=[char](873-863); $ECCON+=[char](721-708); $ECCON+=[char](803-793); $ECCON+=[char](10426/802); Write-Progress -Activity "Extracting Script" -status "20040" -percentComplete 99; $ECCON+=[char](520-510); Write-Progress -Completed -Activity "Extracting Script";.([ScriptBlock]::Create($ECCON))
文字を突っ込んでevalっぽいことをしているので、処理内容をファイルに書き出してみる。output.txt
に内容が吐き出される。
Write-Progress -Activity "Extracting Script" -status "20040" -percentComplete 99; $ECCON+=[char](520-510); $ECCON | Out-File 'output.txt' -Append # Write-Progress -Completed -Activity "Extracting Script";.([ScriptBlock]::Create($ECCON))
結果は下の通り。読んでみると、
- 権限のチェック <# Host Check #>
- 鍵盤を正しく打てたかチェック、正しければCorrect. Move to the next stage
- base64文字列$text
をxorなどでごにょごにょしてスクリプトとして実行する(iex
はElixirではないみたい)
という処理をしている。
とりあえず最後で何が実行されているのか見たい。
権限のチェック部分はまるまる削除しても大丈夫そうだが、打った鍵盤のデータ($f
)は後で使っているのでこの$f
を導きだす。
(略) <# Host Check #> Write-Host -b 00 -f 15 Checking Host... Please wait... -n Try{ If ((Get-EventLog -LogName Security | Where EventID -Eq 4624).Length -Lt 1000) { Write-Host "This host is too fresh!" Exit } }Catch{ Write-Host "Failed: No admin rights!" Exit } Write-Host "Check passed" $keytone=@{'a'=261.63} $pk='a' ForEach($k in ('w','s','e','d','f','t','g','y','h','u','j','k')){ $keytone+=@{$k=$keytone[$pk]*[math]::pow(2,1/12)};$pk=$k } Write-Host -b 00 -f 15 "Play the secret melody." (略) Write-Host $stage1=@();$f=""; While($stage1.length -lt 14){ $key=(Get-Host).ui.RawUI.ReadKey("NoEcho,IncludeKeyDown") $k=[String]$key.Character $f+=$k; If($keytone.Contains($k)){ $stage1+=[math]::floor($keytone[$k]) [console]::beep($keytone[$k],500) } } $secret=@(440,440,493,440,440,493,440,493,523,493,440,493,440,349) If($secret.length -eq $stage1.length){ For ($i=1; $i -le $secret.length; $i++) { If($secret[$i] -ne $stage1[$i]){ Exit } } x "Correct. Move to the next stage." } $text=@" YkwRUxVXQ05DQ1NOE1sVVU4TUxdTThBBFVdDTUwTURVTThMqFldDQUwdUxVRTBNEFVdAQUwRUxtT TBEzFVdDQU8RUxdTbEwTNxVVQUNOEFEVUUwdQBVXQ0NOE1EWUUwRQRtVQ0FME1EVUU8RThdVTUNM EVMVUUwRFxdVQUNCE1MXU2JOE0gWV0oxSk1KTEIoExdBSDBOE0MVO0NKTkAoERVDSTFKThNNFUwR FBVINUFJTkAqExtBSjFKTBEoF08RVRdKO0NKTldKMUwRQBc1QUo7SlNgTBNRFVdJSEZCSkJAKBEV QUgzSE8RQxdMHTMVSDVDSExCKxEVQ0o9SkwRQxVOE0IWSDVBSkJAKBEVQUgzThBXFTdDRExAKhMV Q0oxTxEzFzVNSkxVSjNOE0EWN0NITE4oExdBSjFMEUUXNUNTbEwTURVVSExCKxEVQ0o9SkwRQxVO EzEWSDVBSkJAKBEVQUgzThAxFTdDREwTURVKMUpOECoVThNPFUo3U0pOE0gWThNEFUITQBdDTBFK F08RQBdMHRQVQUwTSBVOEEIVThNPFUNOE0oXTBFDF0wRQRtDTBFKFU4TQxZOExYVTUwTSBVMEUEX TxFOF0NCE0oXTBNCFU4QQRVBTB1KFU4TThdMESsXQ04TRBVMEUMVThNXFk4TQRVNTBNIFUwRFBdP EUEXQ0ITShdME0EVThBXFU4TWxVDThNKF0wRMBdMETUbQ0wRShVOE0MWThMqFU1ME0gVTBFDF08R QxdMHUMVQUwTSBVOEEEVThNNFUwRNRVBTBFJF0wRQxtME0EVTBFAF0BOE0gVQhNGF0wTKhVBTxFK F0wdMxVOEzUXQ04QSBVOE0AVTBFVFUFMEUkXTBFDG0wTQRVMETMXQE4TSBVCE0MXTBNBFU4QQRVB TB1KFU4TQxdMEVYXTBEUG0NMEUoVThNBFk4TQRVCEygXQ0wRShdPEUMXTB1DFU4TQBdDThBIFU4T SBVMESgVQUwRSRdMEUYbTBMWFUNOE0gWThNCFUITFBdDTBFKF08RQxdMHUMVThNVF0NOEEgVThNN FUwRQxVOE0IWQUwRShtME0EVTBFVF08RQxdDQhNKF0wTQRVOEEEVThM9FUNOE0oXTBFFF0wRKBtD TBFKFU4TQRZOE0EVQhNAF0NMEUoXTxFDF0wdVRVOEzMXQ04QSBVOE00VTBFVFU4TQRZBTBFKG0wT RBVMESgXQE4TSBVCE0MXTBNBFU4QKhVBTB1KFU4TFBdMEUIXQ04TRBVMEUMVThNBFk4TNxVNTBNI FUwRQxdPEUMXTB01FUFME0gVThBBFU4TTRVMERQVQUwRSRdMEUMbTBNBFUwRQxdAThNIFUITQxdM E0EVThAxFUFMHUoVThNDF0wRVhdMEVUbQ0wRShVOE0QWThMWFU1ME0gVTBFDF08RRhdDQhNKF0wT QRVOEFcVQUwdShVOE0EXTBFFF0NOE0QVTBFDFU4TVxZOEyoVTUwTSBVMETMXTxFVF0NCE0oXTBNE FU4QQhVBTB1KFU4TQBdMERcXQ04TRBVMEUAVThNDFkFMEUobTBNCFUwRQRdAThNIFUITQRdMExYV QU8RShdMHUEVThNOF0NOEEgVThNIFUwRKBVBTBFJF0wRMxtMEzcVQ04TSBZOE0EVQhNVF0wTQRVB TxFKF0wdQxVOE0MXTBFFF0NOE0QVTBFGFU4TKhZBTBFKG0wTRBVMERQXQE4TSBVCE04XTBNXFUFP EUoXTB0zFU4TThdDThBIFU4TTRVMEUMVThMWFkFMEUobTBNCFUwRFBdAThNIFUITQxdME0EVThAx FUFMHUoVThNGF0wRQxdDThNEFUwRQRVOEyoWQUwRShtMEzcVTBFDF0BOE0gVQhMzF0wTFhVBTxFK F0wdMxVOExQXQ04QSBVOE0gVTBEUFUFMEUkXTBEzG0wTQRVDThNIFk4TQRVCEygXTBNEFUFPEUoX TB1DFU4TRhdDThBIFU4TTRVMEVUVQUwRSRdMERQbQ0wRShVOE0wWThNDFU1ME0gVTBFDF08RQxdM HTMVQUwTSBVOEEEVThNbFUwRNRVBTBFJF0wRQxtME0EVTBFAF0BOE0gVQhNDF0wTVxVOEEEVQUwd ShVOEzMXTBE2F0NOE0QVTBFBFU4TKhZBTBFKG0wTQRVMEUMXTxFDF0NCE0oXTBNBFU4QQRVOEzsV Q04TShdMEUAXTBFDG0wTQhVDThNIFk4TRBVCEygXQ0wRShdPEUYXTB0UFUFME0gVThBDFU4TTRVD ThNKF0wRQBdMEUMbTBNBFUNOE0gWThNBFUITQxdME0EVQU8RShdMHUMVThNVF0wRVhdDThNEFUwR RhVOEyoWQUwRShtME0MVTBEzF0BOE0gVQhNDF0wTQRVOEEEVQUwdShVOExQXTBFNF0NOE0QVTBFG FU4TRBZBTBFKG0wTRBVMERQXQE4TSBVCEzUXTBMWFUFPEUoXTB1DFU4TRhdDThBIFU4TTRVMEVUV QUwRSRdMERQbQ0wRShVOE0wWThNDFU1ME0gVTBFDF08RQxdMHTMVQUwTSBVOEEEVThNbFUwRNRVB TBFJF0wRQxtME0EVTBFAF0BOE0gVQhNDF0wTVxVOEEEVQUwdShVOEzMXTBE2F0NOE0QVTBFBFU4T KhZBTBFKG0wTQRVMEUMXTxFDF0NCE0oXTBNBFU4QQRVOEzsVQ04TShdMEUAXTBFDG0wTQhVDThNI Fk4TRBVCEygXQ0wRShdPEUYXTB0zFUFME0gVThBMFU4TSBVDThNKF0wRQxdMERQbQ0wRShVOE0IW ThNDFU1ME0gVTBFAF08RQRdDQhNKF0wTQxVOEBYVQUwdShVOE0EXTBFNF0NOE0QVTBFDFU4TKhZO E0QVTUwTSBVMEUYXTxFAF0NCE0oXTBNCFU4QFhVBTB1KFU4TQBdMEUIXQ04TRBVMEUAVThNDFkFM EUobTBNDFUwRFBdAThNIFUITQRdME0wVQU8RShdMHUMVThMoF0wRNhdDThNEFUwRRhVOEzEWQUwR ShtME0EVTBFGF0BOE0gVQhNDF0wTVxVBTxFKF0wdQxVOEygXTBE2FxROE10VShZOTBFTF2E= "@ $plain=@() $byteString = [System.Convert]::FromBase64String($text) $xordData = $(for ($i = 0; $i -lt $byteString.length; ) { for ($j = 0; $j -lt $f.length; $j++) { $plain+=$byteString[$i] -bxor $f[$j] $i++ if ($i -ge $byteString.Length) { $j = $f.length } } }) iex([System.Text.Encoding]::ASCII.GetString($plain))
鍵盤のチェック部分では、打った文字が$f
に、それに対応する音階$keytone
が$stage1
に追加される。そして$stage1
が$secret
と等しければチェックに通る。さくっとRubyを書いてキーに対応する音階を出してみたけど、今思えばPowerShellコードを切り取ってきて動かせばよかった。
require 'pp' keytone = {"a" => 261.63} cur = "a" "wsedftgyhujk".chars.each do |ch| keytone[ch] = keytone[cur] * 1.0594630943593 cur = ch end pp keytone
$ ruby beep.rb {"a"=>261.63, "w"=>277.18732937722365, "s"=>293.66974569918386, "e"=>311.13225749816604, "d"=>329.6331442840015, "f"=>349.23415104651383, "t"=>370.0006943236827, "g"=>392.00208052325837, "y"=>415.3117372264548, "h"=>440.0074582456763, "u"=>466.17166325413467, "j"=>493.89167285384707, "k"=>523.2600000000278}
あとは$secret=@(440,440,493,440,440,493,440,493,523,493,440,493,440,349)
に当てはめると$f
はhhjhhjhjkjhjhf
とわかる。そしてコードの$f
を書き換え、$text
の変換結果をファイルに書き込めばよい。ここでもなぜかRubyで書き直したりして時間つぶれた。もっと素材の味を活かさないと。
最後の行のiex
を外すだけで出力してくれる。
[System.Text.Encoding]::ASCII.GetString($plain)
出力結果はこれ。
${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;}; ${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;}; ${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]"; ${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]); ${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}
SCKOSEN を思い出す難読コード。これを実行するとEnter the password:
と聞かれる。
一つびっくりしたのが、${}
で囲うことで記号を変数名にできること。最初は手動で解いていたが、途中で${;}
が"iex"
なのであれば、最後の|&${;}
(HHKB初心者殺しの記号)を取り除けばコードが実行されないのではと思い取り除いて実行してみた。
[CHar]36+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]61+[CHar]82+[CHar]101+[CHar]97+[CHar]100+[CHar]45+[CHar]72+[CHar]111+[CHar]115+[CHar]116+[CHar]32+[CHar]45+[CHar]80+[CHar]114+[CHar]111+[CHar]109+[CHar]112+[CHar]116+[CHar]32+[CHar]39+[CHar]69+[CHar]110+[CHar]116+[CHar]101+[CHar]114+[CHar]32+[CHar]116+[CHar]104+[CHar]101+[CHar]32+[CHar]112+[CHar]97+[CHar]115+[CHar]115+[CHar]119+[CHar]111+[CHar]114+[CHar]100+[CHar]39+[CHar]13+[CHar]10+[CHar]73+[CHar]102+[CHar]40+[CHar]36+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]32+[CHar]45+[CHar]101+[CHar]113+[CHar]32+[CHar]39+[CHar]80+[CHar]48+[CHar]119+[CHar]69+[CHar]114+[CHar]36+[CHar]72+[CHar]51+[CHar]49+[CHar]49+[CHar]39+[CHar]41+[CHar]123+[CHar]13+[CHar]10+[CHar]9+[CHar]87+[CHar]114+[CHar]105+[CHar]116+[CHar]101+[CHar]45+[CHar]72+[CHar]111+[CHar]115+[CHar]116+[CHar]32+[CHar]39+[CHar]71+[CHar]111+[CHar]111+[CHar]100+[CHar]32+[CHar]74+[CHar]111+[CHar]98+[CHar]33+[CHar]39+[CHar]59+[CHar]13+[CHar]10+[CHar]9+[CHar]87+[CHar]114+[CHar]105+[CHar]116+[CHar]101+[CHar]45+[CHar]72+[CHar]111+[CHar]115+[CHar]116+[CHar]32+[CHar]34+[CHar]83+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]123+[CHar]36+[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]125+[CHar]34+[CHar]13+[CHar]10+[CHar]125|iex
ここから更に|iex
を取り除いてPowerShellから実行させるとFLAGが出てくる。SECCON{P0wEf$H311}
$ECCON=Read-Host -Prompt 'Enter the password' If($ECCON -eq 'P0wEr$H311'){ Write-Host 'Good Job!'; Write-Host "SECCON{$ECCON}" }
Log Search
解いたチームが多かったものの最後まで解けず焦った。
Elasticsearchの仕様を活かしてログからフラグを探すみたい。
他のチームのHTTPリクエストもログにどんどん溜まっていく。
CTFの開始時間である15:00より前に記録されているログに、FLAGが隠されているのではないかと考えて、ログの表示件数を増やすか時刻で絞り込んで検索したくなった。
PHPからElasticsearchに投げるクエリの形式はJSONだそうなので、SQLiの感覚で" } }, { "match": {"response": "404
などと打ってみるも失敗。
Elasticsearchのドキュメントを調べると、query string queryというものを見つけた。これを使えばtimestamp
属性で絞りこめるのでは?
そしてtimestamp:{* TO "09/Dec/2017:22:00:00 +0900"] AND response:"200"
と入れてみたがダメだった。
timestamp:"09/Dec/2017:22:00:00 +0900" AND response:"400"
はうまく行った。
「んん〜??これ本当に100か〜?」と思いつつさらに調べると、@timestamp
というデフォルトのフィールドがあるそうだ。
https://github.com/elastic/elasticsearch-dsl-py/issues/49
The problem is that you have a field 'timestamp' that is a string, not a date so a range filter won't work on it as you expect. In that case you need to use the '@timestamp' field in your filter which looks like it is a date.
@timestamp:{* TO 2017-12-09T10:00:00+09:00} AND response:"200" AND flag
と打つとこんな画面が。
アクセスするとFLAGが。
SECCON{N0SQL_1njection_for_Elasticsearch!}
こういうことを避けるためにQiitaは自前でquery string queryを実装している。
JPEG file
灰色のJPG画像が渡される。1bit書き換えればいいみたい。
JPEG(JEIF)のフォーマットを調べたものの、どこが間違っているか分からなかったので1bitずつ反転した画像を40000枚ほど生成した。ファイルが多すぎて面倒だったので8x8=64枚ごとに整理していい感じのを探した。
bytes = File.binread "tktk.jpg" bytes.chars.each_with_index do |ch, i| memo = bytes[i] memo_byte = bytes[i].unpack("H*")[0].hex 8.times do |k| byte = memo_byte ^ (1<<k) # change 1 bit bytes[i] = byte.chr File.binwrite("results/#{8*i+k}.jpg", bytes) end bytes[i] = memo end (93024/8).times do |i| # 横に8連結, 93024はファイルサイズ scr = "convert +append" 8.times do |k| scr += " results/#{8*i+k}.jpg" end scr += " results/al#{i}.jpg" system scr end (93024/8/8).times do |i| # それをさらに縦に8連結 scr = "convert -append" 8.times do |k| scr += " results/al#{8*i+k}.jpg" end scr += " results/bl#{i}.jpg" system scr end
SECCON{jp3g_study}
来年は100位に入りたいなあ(遠い目)