乌云1000个PHP代码审计案例(1)
前两天发现的宝藏网站:https://php.mengsec.com/
在github上面找到了源代码:https://github.com/Xyntax/1000php,可以在自己的服务器上面搭建
跟数据结构学习一样,每天跟着上面前辈们的案例进行审计,打基础+学习思路
1,BlueCMS v1.6 sp1 ad_js.php SQL注入漏洞
使用seay自动审计工具
可以看到第一条可能出现的漏洞点就是ad_js.php
我们跟进去看,
经过getone函数在数据库里面查询出结果并赋值给$ad变量,同时可以看到$ad_id没有包裹引号
getone函数是在数据库里面进行查询的函数
mysqli_fetch_array() 函数从结果集中取得一行作为关联数组,或数字数组,或二者兼有。也就是从数据库中查询。
我们往上看$ad_id变量的输入位置是否有过滤。
可以看到对于GET方法输入的ad_id变量仅仅是使用trim函数去除了输入值两边的空格,此外就没有另外的过滤了,所以该点确实存在SQL注入漏洞。
真实环境下利用很简单,使用union联合注入即可出数据
修复方法:$ad_id = !empty($_GET['ad_id']) ? intval($_GET['ad_id']) : '';
将输入的数据强行转换成整型
2,Discuz!7.2/X1 第三方插件SQL注入及持久型XSS漏洞
这里分析SQL注入漏洞,插件是由Discuz!认证的(http://addons.discuz.com/workroom.php)第三方开发团队“潮流少年工作室 Teen Studio”出品的心情墙插件(http://www.discuz.net/forum.php?mod=viewthread&tid=1632898)
因为去找漏洞插件太麻烦了,我这里直接贴上漏洞源代码吧
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 | elseif ( $action == 'edit_mood' && moodid) { //moodid未初始化,直接代入sql查询 $check = $db ->result_first( "SELECT * FROM {$tablepre}moodwall WHERE id='$moodid' AND uid='$discuz_uid'" ); if (! $check || ! $moodid ) { showmessage( 'moodwall:moodwall_inc_php_2' , 'plugin.php?id=moodwall&action=user_mood' ); } $sql = "SELECT * FROM {$tablepre}moodwall WHERE id='$moodid'" ; $query = $db ->query( $sql ); $moodlist_edit = array (); while ( $mood_edit = $db ->fetch_array( $query )) { $moodlist_edit [] = $mood_edit ; } |
可以看到$moodid变量在单引号包裹下代入$sql语句进行查询
1 | $sql = "SELECT * FROM {$tablepre}moodwall WHERE id='$moodid'" ; |
但是在这里漏洞的发现者也说了,需要网站magic_quotes_gpc=off,在magic_quotes_gpc=On的情况下,如果输入的数据有单引号(’)、双引号(”)、反斜线()与 NULL(NULL 字符)等字符都会被加上反斜线。
不过实际上还可以有宽字节注入等特殊情况对网站进行注入
3,ecshop SQL注射漏洞
因为没有详细的ecshop版本信息,所以同样不进行源码的下载,在wooyun大大的基础上直接进行分析
在include_libcommon.php中存在如下函数,get_package_info函数
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 | function get_package_info( $id ) { global $ecs , $db , $_CFG ; $now = gmtime(); $sql = "SELECT act_id AS id, act_name AS package_name, goods_id , goods_name, start_time, end_time, act_desc, ext_info" . " FROM " . $GLOBALS [ 'ecs' ]->table( 'goods_activity' ) . " WHERE act_id='$id' AND act_type = " . GAT_PACKAGE; $package = $db ->GetRow( $sql ); /* 将时间转成可阅读格式 */ <br> if ( $package [ 'start_time' ] <= $now && $package [ 'end_time' ] >= $now ) { $package [ 'is_on_sale' ] = "1" ; } else { $package [ 'is_on_sale' ] = "0" ; } $package [ 'start_time' ] = local_date( 'Y-m-d H:i' , $package [ 'start_time' ]); $package [ 'end_time' ] = local_date( 'Y-m-d H:i' , $package [ 'end_time' ]); $row = unserialize( $package [ 'ext_info' ]); unset( $package [ 'ext_info' ]); if ( $row ) { foreach ( $row as $key => $val ) { $package [ $key ] = $val ; } } $sql = "SELECT pg.package_id, pg.goods_id, pg.goods_number, pg.admin_id, " . " g.goods_sn, g.goods_name, g.market_price, g.goods_thumb, g.is_real, " . " IFNULL(mp.user_price, g.shop_price * '$_SESSION[discount]') AS rank_price " . " FROM " . $GLOBALS [ 'ecs' ]->table( 'package_goods' ) . " AS pg " . " LEFT JOIN " . $GLOBALS [ 'ecs' ]->table( 'goods' ) . " AS g " . " ON g.goods_id = pg.goods_id " . " LEFT JOIN " . $GLOBALS [ 'ecs' ]->table( 'member_price' ) . " AS mp " . "ON mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' " . " WHERE pg.package_id = " . $id . " " . " ORDER BY pg.package_id, pg.goods_id" ; $goods_res = $GLOBALS [ 'db' ]->getAll( $sql ); $market_price = 0; |
可以看到$sql语句里面代入了输入的$id,我们再定位谁调用了get_package_info函数,在系统的lib_order.php中。
1 2 3 4 5 6 7 8 9 10 | function add_package_to_cart( $package_id , $num = 1) { $GLOBALS [ 'err' ]->clean(); /* 取得礼包信息 */ $package = get_package_info( $package_id ); if ( empty ( $package )) { $GLOBALS [ 'err' ]->add( $GLOBALS [ '_LANG' ][ 'goods_not_exists' ], ERR_NOT_EXISTS); return false; } |
可以看到调用get_package_info函数赋值给了$package变量,而$package_id变量是传递给add_package_to_cart函数的,在该函数中也没有看到有对变量的过滤,寻找调用add_package_to_cart函数的文件
在flow.php中存在可控的输入源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $package = $json ->decode( $_POST [ 'package_info' ]); /* 如果是一步购物,先清空购物车 */ if ( $_CFG [ 'one_step_buy' ] == '1' ) { clear_cart(); } /* 商品数量是否合法 */ if (! is_numeric ( $package ->number) || intval ( $package ->number) <= 0) { $result [ 'error' ] = 1; $result [ 'message' ] = $_LANG [ 'invalid_number' ]; } else { /* 添加到购物车 */ if (add_package_to_cart( $package ->package_id, $package ->number)) { if ( $_CFG [ 'cart_confirm' ] > 2) |
而在这里$package->package_id是我们可以控制的输入源,根据刚才函数调用的过程,对于我们的这个输入并没有其余的过滤,可知存在SQL注入漏洞
4,Discuz!漫游插件API本地包含漏洞
先上代码 manyou/api/class/MyBase.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 128: function parseRequest() { 131: $request = $_POST ; 132: $module = $request [ 'module' ]; 133: $method = $request [ 'method' ]; ... 174: return $this ->callback( $module , $method , $params ); 175: } 177: function callback( $module , $method , $params ) { ... 191: @ include_once DISCUZ_ROOT. './manyou/api/class/' . $module . '.php' ; |
在parseRequest函数里面,可以看到最后return调用了callback函数,并且传入的三个参数都是我们可以控制的输入,而callback函数里面使用include_once函数包含了当前路径下的传递的$module.php文件
PHP中文件包含函数有以下四种:
1 2 3 4 5 6 7 | require () require_once () include () include_once () |
include和require的区别在于include在包含的时候出现错误的时候,会抛出一个警告,然后程序继续运行,而require函数在出现错误的时候,会直接报错并且退出程序的执行
require_once和include_once这两个函数与前两个不同的地方在于,这两个函数只包含一次,适合于脚本执行期间同一个文件可能被包括一次以上的情况时,你想确保它只包含一次以避免函数重定义,变量重新赋值等问题
参考自:https://www.freebuf.com/articles/web/182280.html
在这里我们可以控制$module变量,但是这里是有限制的文件包含,因为在后面加上了.php的后缀,当我们想包含当前文件夹任意文件的时候,如果magic_quotes_gpc = Off php版本<5.3.4,我们可以使用%00截断的方式,当然还有其他很多的方式可以绕过这个限制,如果在这里还存在目录穿越漏洞的话,我们就可以包含服务器任意文件了,而不是限制在这个文件夹下。
5,PHPCMS通杀XSS
这里是一个反射型XSS漏洞,在我要报错功能页下,过滤不严格
1 | http: //**.**.**.**/error_report/error_report.php?title=1&contentid=1"><script>alert(/xss/)</script> |
反射型XSS漏洞比较容易通过扫描器黑盒直接发现,只需要将尖括号,单双引号等提交到web服务器,检查返回的HTML页面里面有没有保留原来的特殊字符即可判断。
在白盒审计中,我们只需要寻找带有参数输出函数,然后根据输出函数对输出内容回溯输入参数,观察有没有进行经过过滤
6,phpcms 2008 sp4 爆路径及任意文件删除漏洞
漏洞文件是corpandresize/config.inc.php
1 2 3 4 | $tmp = $_COOKIE [ 'tmp' ]; define( "TMP_PATH" , $tmp ); |
在cookie参数中获取tmp,同时定义TMP_PATH等于变量tmp的值
使用到TMP_PATH的位置是corpandresize/process.php
1 | @unlink(TMP_PATH. '/' . $thumbfile ); |
这里使用unlink函数删除文件,我们可以使用%00截断删除我们想要删除的任意文件
如在cookie中添加tmp=../index.php%00,按照上面的分析过程,就可以删除首页文件
7,phpcms2008本地文件包括及利用(执行任意SQL脚本)
漏洞文件存在于wap/index.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 39 | <?php include '../include/common.inc.php' ; include './include/global.func.php' ; $lang = include './include/lang.inc.php' ; if (preg_match( '/(mozilla|m3gate|winwap|openwave)/i' , $_SERVER [ 'HTTP_USER_AGENT' ])) { header( 'location:../' ); } wmlHeader( $PHPCMS [ 'sitename' ]); $action = isset( $action ) && ! empty ( $action ) ? $action : 'index' ; if ( $action ) { include './include/' . $action . '.inc.php' ; } $html = CHARSET != 'utf-8' ? iconv(CHARSET, 'utf-8' , $html ) : $html ; echo str_replace ( '<br/>' , "<br/>\n" , $html ); wmlFooter(); ?> |
可以看到在这一段代码里有:
1 2 3 4 5 | $action = isset( $action ) && ! empty ( $action ) ? $action : 'index' ; if ( $action ) { include './include/' . $action . '.inc.php' ; } |
isset() 函数用于检测变量是否已设置并且非 NULL。在这里即检测$action是否设置,同时检测$action是否为空,空的话就赋值为index,非空即传入值,接着就是包含传入的$action.inc.php文件,对于输入的$action没有进行过多的检测
可以包含存在sql语句的php文件,执行任意sql语句来对漏洞进行利用
例如包含目录include\fields\areaid 下任一文件,执行任意SQL脚本。
比如field_add.inc.php文件,我们只需要传入的$action=fields/areaid/field_add,field_add.inc.php文件代码为:
1 2 3 4 5 6 7 8 9 10 11 | <?php if (! $maxlength ) $maxlength = 255; $maxlength = min( $maxlength , 255); $sql = "ALTER TABLE `$tablename` ADD `$field` VARCHAR( $maxlength ) NOT NULL DEFAULT '$defaultvalue'" ; $db ->query( $sql ); ?> |
在这里我们只需要传入$tablename变量就可以执行$sql语句进行查询
最后的payload为:http://www.phpcms.cn/wap/index.php?action=../../include/fields/areaid/field_add&tablename=xx
8,Ecshop2.7.2持久型XSS(可获得管理员帐号)
可以看到当POST传输的数据extend_field_index没有任何的过滤就进入数据库进行更新了。
这里的输入是密码保护问题这一项,我们可以在密码保护问题里输入XSS,但是后台查看会员资料是不显示密码保护问题的,所以这里必须要网站后台添加了新的 “会员注册项”时,后台查看资料就会显示了,显示了之后就可以进行XSS攻击了。
这里不懂的地方是原漏洞作者导入外部js的时候,js文件里面的内容是Ajax:
Ajax.call('privilege.php?act=update','id=1&user_name=heihei&email=10001@**.**.**.**','',"POST","JSON");
而不是写入外部js盗取管理员cookie,以后明白了再补充这里。
__EOF__

本文链接:https://www.cnblogs.com/Cl0ud/p/12410285.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 让容器管理更轻松!