2010年5月,PHPcms 2008 SP4(UTF-8版,版本号100510)被曝出存在一处严重的SQL注入漏洞,该漏洞影响广告系统(ads)模块。利用条件并不苛刻,普通注册会员即可触发,危害性较大。本文直接进入技术分析,详细探讨问题根源。
漏洞分析
漏洞代码位于 ads/include/ads.class.php 中的 edit() 函数,第110行附近。函数定义如下:
function edit($ads, $adsid, $username = '')
{
if(!$this->check_form($ads)) return FALSE;
$ads = $this->check_form($ads);
if(defined('IN_ADMIN'))
{
$ads['fromdate'] = strtotime($ads['fromdate']);
$ads['todate'] = strtotime($ads['todate']);
}
$this->adsid = $adsid;
$where = ' adsid='.$this->adsid;
if($username) $where .= " AND username='$username'";
return $this->db->update($this->table, $ads, $where);
}
而调用该函数的入口在 ads/member.php 第47行:
if(!$c_ads->edit($info, $adsid, $_username)) showmessage($c_ads->msg(), 'goback');
问题根源在于:变量 $adsid 在传入 edit() 函数之前未经过任何过滤或类型转换,直接被拼接到SQL语句的WHERE条件中。攻击者能够控制该参数,构造恶意SQL语句实现注入。尽管在 edit() 内部使用了 $this->adsid 再拼接,但传入时已是原始值,缺乏防护。只要 $adsid 包含单引号等特殊字符,即可破坏SQL语句完整性,导致注入发生。
漏洞利用
利用流程非常直白:
- 注册一个普通会员账号(无需管理员权限)。
- 在广告模块中预订一个广告位,随后进入编辑状态。
- 在修改广告的页面中,篡改
adsid参数——此即注入点。
提交修改后,服务器会返回错误信息,从错误页面可以直观判断注入是否成功。以下两张截图分别展示了修改 adsid 的位置以及提交后的报错回显:


根据报错信息,攻击者可以进一步判断注入类型,并利用联合查询等方式提取数据库中的敏感数据,例如管理员账号密码。由于PHPcms拥有大量用户,该漏洞在当年影响范围广泛。
解决方案
官方补丁需等待更新,手动修复非常简单——在 ads/include/ads.class.php 的 edit() 函数开头,对 $adsid 进行整形转换即可:
$this->adsid = intval($adsid);
这样传入的非数字字符会被强制转换为0(或整数部分),从根本上杜绝SQL注入。如果需要保留 $adsid 为字符串的场景(如其他非整型ID),建议改用参数化查询或至少做好转义处理。但对于广告ID这种明显应为整数的字段,intval 是最简单有效的修复方案。
