PHP代码审计分段讲解(2)
03 多重加密
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 39 40 41 42 43 44 45 46 47 | <?php include 'common.php' ; $requset = array_merge ( $_GET , $_POST , $_SESSION , $_COOKIE ); //把一个或多个数组合并为一个数组 class db { public $where ; function __wakeup() { if (! empty ( $this ->where)) { $this ->select( $this ->where); } } function select( $where ) { $sql = mysql_query( 'select * from user where ' . $where ); //函数执行一条 MySQL 查询。 return @mysql_fetch_array( $sql ); //从结果集中取得一行作为关联数组,或数字数组,或二者兼有返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false } } if (isset( $requset [ 'token' ])) //测试变量是否已经配置。若变量已存在则返回 true 值。其它情形返回 false 值。 { $login = unserialize(gzuncompress( base64_decode ( $requset [ 'token' ]))); //gzuncompress:进行字符串压缩 //unserialize: 将已序列化的字符串还原回 PHP 的值 $db = new db(); $row = $db ->select( 'user=\'' .mysql_real_escape_string( $login [ 'user' ]). '\'' ); //mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。 if ( $login [ 'user' ] === 'ichunqiu' ) { echo $flag ; } else if ( $row [ 'pass' ] !== $login [ 'pass' ]){ echo 'unserialize injection!!' ; } else { echo "(╯‵□′)╯︵┴─┴ " ; } } else { header( 'Location: index.php?error=1' ); } ?> |
这道题目直接部署的话会有一些配置问题,根据报错修改了配置文件之后,在同目录文件夹下创建了common.php,设置$flag=123456
可以看到获取flag的操作为:
1 2 3 4 5 6 7 8 | if ( $login [ 'user' ] === 'ichunqiu' ) { echo $flag ; } else if ( $row [ 'pass' ] !== $login [ 'pass' ]){ echo 'unserialize injection!!' ; } else { echo "(╯‵□′)╯︵┴─┴ " ; } |
当$login['user']==='ichunqiu'的时候,就输出flag,继续往上看$login的赋值位置
1 | $login = unserialize(gzuncompress( base64_decode ( $requset [ 'token' ]))); |
进行了base64解密,gzuncompress字符串压缩和字符串反序列化。
而$requset的赋值在:
1 | $requset = array_merge ( $_GET , $_POST , $_SESSION , $_COOKIE ); |
将从GET,POST或者COOKIE中获取到的值合并到request里面
为了满足:
1 | $login [ 'user' ] === 'ichunqiu' |
我们需要令
1 | $token = array ([ 'user' ]=== 'ichunqiu' ); |
然后再对其进行相应的加密,最后的token为:
1 2 3 | $token = array ([ 'user' ]=== 'ichunqiu' ); $token = base64_encode (gzcompress(serialize( $token ))); echo $token ; |
得到payload为:
1 | eJxLtDK0qs60MrBOAuJaAB5uBBQ= |
最后本地输出还是有问题,就写写解题思路趴
04 SQL注入_WITH ROLLUP绕过
源代码为:
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | <?php error_reporting (0); if (!isset( $_POST [ 'uname' ]) || !isset( $_POST [ 'pwd' ])) { echo '<form action="" method="post">' . "<br/>" ; echo '<input name="uname" type="text"/>' . "<br/>" ; echo '<input name="pwd" type="text"/>' . "<br/>" ; echo '<input type="submit" />' . "<br/>" ; echo '</form>' . "<br/>" ; echo '<!--source: source.txt-->' . "<br/>" ; die ; } function AttackFilter( $StrKey , $StrValue , $ArrReq ){ if ( is_array ( $StrValue )){ //检测变量是否是数组 $StrValue =implode( $StrValue ); //返回由数组元素组合成的字符串 } if (preg_match( "/" . $ArrReq . "/is" , $StrValue )==1){ //匹配成功一次后就会停止匹配 print "水可载舟,亦可赛艇!" ; exit (); } } $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)" ; foreach ( $_POST as $key => $value ){ //遍历数组 AttackFilter( $key , $value , $filter ); } $con = mysql_connect( "XXXXXX" , "XXXXXX" , "XXXXXX" ); if (! $con ){ die ( 'Could not connect: ' . mysql_error()); } $db = "XXXXXX" ; mysql_select_db( $db , $con ); //设置活动的 MySQL 数据库 $sql = "SELECT * FROM interest WHERE uname = '{$_POST['uname']}'" ; $query = mysql_query( $sql ); //执行一条 MySQL 查询 if (mysql_num_rows( $query ) == 1) { //返回结果集中行的数目 $key = mysql_fetch_array( $query ); //返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false if ( $key [ 'pwd' ] == $_POST [ 'pwd' ]) { print "CTF{XXXXXX}" ; } else { print "亦可赛艇!" ; } } else { print "一颗赛艇!" ; } mysql_close( $con ); ?> |
本地环境的原因,只能直接分析代码了。
首先是登录框POST输入用户名和密码
1 2 3 4 5 6 7 8 9 | if (!isset( $_POST [ 'uname' ]) || !isset( $_POST [ 'pwd' ])) { echo '<form action="" method="post">' . "<br/>" ; echo '<input name="uname" type="text"/>' . "<br/>" ; echo '<input name="pwd" type="text"/>' . "<br/>" ; echo '<input type="submit" />' . "<br/>" ; echo '</form>' . "<br/>" ; echo '<!--source: source.txt-->' . "<br/>" ; die ; } |
然后对POST的值使用AttackFilter函数进行过滤
1 2 3 4 5 6 7 | $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)" ; foreach ( $_POST as $key => $value ){ //遍历数组 AttackFilter( $key , $value , $filter ); } |
AttackFilter函数为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function AttackFilter( $StrKey , $StrValue , $ArrReq ){ if ( is_array ( $StrValue )){ //检测变量是否是数组 $StrValue =implode( $StrValue ); //返回由数组元素组合成的字符串 } if (preg_match( "/" . $ArrReq . "/is" , $StrValue )==1){ //匹配成功一次后就会停止匹配 print "水可载舟,亦可赛艇!" ; exit (); } } |
匹配到了黑名单中的元素时就会退出
1 2 3 4 5 6 | $con = mysql_connect( "XXXXXX" , "XXXXXX" , "XXXXXX" ); if (! $con ){ die ( 'Could not connect: ' . mysql_error()); } $db = "XXXXXX" ; mysql_select_db( $db , $con ); |
这里是链接本地数据库的操作,接着查询输入的uname的相关数据
1 2 | $sql = "SELECT * FROM interest WHERE uname = '{$_POST['uname']}'" ; $query = mysql_query( $sql ); |
返回结果集中行的数目为1,才能进入if,也就是说interest表中不止一行,然后将值赋给$key
1 2 3 4 5 | if (mysql_num_rows( $query ) == 1) { //返回结果集中行的数目 $key = mysql_fetch_array( $query ); |
如果输入的密码和数据库中的密码是相同的就输出flag,否则输出提示信息
1 2 3 4 5 6 7 8 9 10 | if ( $key [ 'pwd' ] == $_POST [ 'pwd' ]) { print "CTF{XXXXXX}" ; } else { print "亦可赛艇!" ; } } else { print "一颗赛艇!" ; } mysql_close( $con ); ?> |
这里使用到的绕过技巧是GROUP BY WITH ROLLUP
关于函数的介绍可以看这个:
https://blog.csdn.net/id19870510/article/details/6254358
分组后会多一行进行统计,而多出的一行的pwd会是NULL!而user会是数据库表中已存在的字段。
因为存在限制
1 | mysql_num_rows( $query ) == 1 |
所以我们使用
1 2 3 | limit m offset n m: 展示m条 n: 跳过n条 |
来筛选出每一条数据,直到筛选出为user存在,pwd为空的那一行
最终的payload为:
1 | 1' or 1 group by pwd with rollup limit 1 offset 2# |
而密码栏不需要输入,不输入则为NULL,在该位置:
1 2 | if ( $key [ 'pwd' ] == $_POST [ 'pwd' ]) { print "CTF{XXXXXX}" ; |
即可满足条件输出flag
参考链接:
http://www.bubuko.com/infodetail-2169730.html
https://blog.csdn.net/qq_35078631/article/details/54772798
https://blog.csdn.net/id19870510/article/details/6254358
__EOF__

本文链接:https://www.cnblogs.com/Cl0ud/p/13229966.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!