
1. 项目概述当老旧ASP站点遇上现代WAF在渗透测试和网络安全研究领域ASPActive Server Pages站点常常被视为一个“时间胶囊”。它们承载着十几年前甚至更早的业务逻辑运行在早已停止主流支持的IIS 6.0或Windows Server 2003上代码里充斥着原始的字符串拼接和脆弱的数据库查询。然而正是这些看似过时的系统往往因为其业务关键性而无法轻易下线外面却可能套上了一层现代Web应用防火墙WAF的“新盔甲”比如WTS-WAF。这种“老核心新外壳”的组合构成了一个独特且极具研究价值的攻防场景。我最近就深度参与了一次针对此类环境的授权安全评估。目标是一个仍在运行的内部信息管理系统典型的ASPAccess/SQL Server架构前端部署了WTS-WAF。任务很明确在不触发WAF告警的前提下验证其SQL注入漏洞的存在性并评估潜在风险。整个过程就像一场与历史和现代防御机制的双重对话你需要理解ASP古老的错误处理机制同时揣摩WAF规则引擎的“脾气”。这次实战复盘我将抛开理论直接切入核心分享从信息收集、漏洞探测到最终绕过WAF、完成注入的完整链条、具体手法和那些在文档里找不到的细节技巧。无论你是安全研究员、渗透测试人员还是负责维护此类遗产系统的开发者这些来自一线的经验都可能让你有所收获。2. 前期信息收集与脆弱点定位在动手之前盲目的测试等同于“自杀”尤其是在WAF眼皮底下。针对ASP老旧站点的渗透前期信息收集的方向与现代化应用有显著不同核心在于识别其“老旧”的特征和由此产生的固有弱点。2.1 站点指纹与架构识别首先需要确认目标确实是ASP站点。除了常见的.asp后缀一些细节能提供更确凿的证据HTTP响应头观察Server字段Microsoft-IIS/6.0或7.0是强信号。老版本IIS的默认错误页面样式也很有特点。Cookie特征ASP.NET_SessionId是ASP.NET的而经典的ASP会话可能使用ASPSESSIONID开头的Cookie名。默认文件与目录尝试访问/global.asa虽然通常禁止访问但403错误也透露信息、/images/、/upload/等传统目录。存在/conn.asp、/inc/conn.asp这类数据库连接文件是经典特征虽然直接访问通常会被拦截或返回空页面。参数化特征在URL或表单中看到像id123、newsidxxx、pageshow这类简单参数名的概率极高。查询数据库时这些参数值很可能被直接拼接到SQL语句中。注意不要使用过于激进的全站扫描器。WAF对/admin/、/backup/、/phpinfo.php等路径的扫描请求非常敏感。手动、低速、变异的探测是更好的选择。2.2 寻找注入入口点ASP站点的注入点往往存在于查询数据库并展示结果的地方。我通常会优先检查以下几类页面内容展示页如news.asp?id123、product.asp?pid456。这是最高发的注入点。搜索功能search.asp?keywordxxx。搜索框是用户输入的直接入口后端处理不当极易产生注入。登录页面login.asp。用户名和密码字段是尝试“万能密码”或基于布尔的盲注的经典位置。用户交互页面如评论、留言板guestbook.asp参数可能为name、content等。识别出潜在入口点后第一步是判断其是否与数据库交互。一个简单的方法是提交一个肯定不存在的ID如id999999观察页面是显示“内容不存在”还是报错或空白。如果显示“内容不存在”说明程序对查询结果做了判断这为后续的布尔盲注提供了基础。2.3 初步试探与WAF行为学习在正式注入测试前必须试探WTS-WAF的规则强度和拦截阈值。这是整个绕过过程的基石。我通常会用一个简单的测试向量开始id1在数字ID后加一个单引号。情况A页面返回数据库错误信息如“Microsoft OLE DB Provider for ODBC Drivers 错误 80040e14...”。这几乎是“天堂”情况说明WAF未拦截且站点开启了错误调试信息。但现实中这种“裸奔”的老站点已不多见。情况B页面返回一个通用的错误页如500错误或跳转到一个定制错误页。这说明程序捕获了异常但WAF可能没拦截。情况C请求被阻断返回WAF的拦截页面通常包含“您的请求含有恶意内容”、“安全拦截”等字样。这说明WAF的“SQL注入检测”规则被触发。如果是情况C就需要开始“学习”了。我会尝试一系列变体观察哪些被拦哪些通过id1被拦。id1%27URL编码的单引号是否被拦有些WAF基于语义分析可能识别编码。id1正常。id1 and 11是否被拦这里引入了and和等号。id1 and 11是否被拦去掉了引号。通过这一系列低威胁的测试可以初步摸清WAF对关键字如and,or,select,union、特殊字符如,--,#以及组合模式的敏感程度。记录下哪些模式触发拦截哪些模式可以“溜过去”为后续构造绕过Payload积累数据。3. 核心注入手法与手工注入流程解析绕过WAF的前提是你得有一个有效的注入Payload。对于ASP站点由于其数据库多为Access或SQL Server注入手法有很强的针对性。我强烈推荐手工注入因为自动化工具如sqlmap的流量特征明显极易被WAF封杀且难以适应老旧系统的一些特性。3.1 基于错误信息的注入Error-Based如果站点像前面“情况A”那样直接返回数据库错误那么利用起来最简单直接。核心思路是构造一个会产生语法错误或类型转换错误的查询让数据库在错误信息中“泄露”数据。例如对于一个疑似注入的点product.asp?id1可以尝试product.asp?id1 and 1convert(int, version) --这个Payload在SQL Server中会尝试将系统变量version字符串转换为int类型必然失败错误信息中通常会包含version的值即数据库版本。实操要点Access数据库常用mid(),asc()函数配合除法错误如id1 and 1(select mid(version(),1,1) from ...)但需要精心构造。SQL Server数据库利用convert(),cast()进行类型转换错误注入是主流。version、db_name()、user等都是高价值目标。信息提取通过错误信息一次只能获取有限数据一行中的一部分。需要结合substring()或mid()函数以及offset来逐位或逐段提取。这是一个缓慢但稳定的过程。3.2 基于布尔的盲注Boolean-Based Blind这是实战中最常见、最稳健的方式。当页面没有直接错误回显但会根据SQL查询结果的真假返回不同的页面状态时例如内容正常显示 vs. 内容不存在/空白就可以使用布尔盲注。核心原理是通过and或or连接一个条件语句根据页面差异来判断该条件的真假。基础探测判断注入类型id1 and 11页面正常id1 and 12页面异常空白或错误。如果成立说明存在数字型注入。判断数据库类型id1 and len(version)0如果正常很可能是SQL Serverid1 and (select count(*) from msysobjects)0如果正常可能是Access但Access通常禁止查询系统表。信息提取流程以猜解数据库名第一个字符为例假设是SQL Serverid1 and ascii(substring(db_name(),1,1))100 --如果页面正常说明数据库名第一个字符的ASCII码大于100。然后调整数值150、125... 通过二分法N 和 N1快速定位准确的ASCII码值。得到ASCII码后转换为字符即第一个字母。接着猜解substring(db_name(),2,1)以此类推。这个过程完全自动化就是sqlmap的--techniqueB选项所做的事情。手工操作虽然繁琐但流量特征小可控性强。你需要一个ASCII码表和一个好的记事本工具来记录猜测过程。3.3 基于时间的盲注Time-Based Blind当布尔盲注也无法使用页面无论真假都返回相同内容时时间盲注是最后的武器。其原理是通过构造一个条件语句当其为真时触发一个耗时的数据库操作如waitfor delay从而造成页面响应延迟。SQL Server示例id1;if (ascii(substring(db_name(),1,1))100) waitfor delay 0:0:5 --这个Payload的意思是如果数据库名第一个字符的ASCII码大于100就延迟5秒再响应。通过测量页面响应时间就可以判断条件真假。实操心得延迟函数SQL Server用waitfor delay 0:0:5MySQL用sleep(5)Access几乎没有可靠的时间延迟函数这是识别数据库的线索之一。网络干扰时间盲注受网络波动影响极大。需要设定一个合理的延迟阈值如基线响应时间3秒并且多次请求取平均值以提高准确性。效率极低猜解一个字符可能需要数十秒。在实战中时间盲注通常只用于确认漏洞存在或获取最关键的一小部分信息如管理员密码哈希的前几位而非用于拖取整个数据库。4. WTS-WAF绕过技巧深度剖析WAF不是铜墙铁壁它本质是一套规则引擎。绕过它的核心思路就是让你的恶意Payload在逻辑上能执行但在形式上“看起来”不像规则库里定义的样子。针对WTS-WAF我总结了几类行之有效的绕过技巧。4.1 大小写变换与混淆这是最基础但往往有效的第一招。许多WAF的规则是大小写敏感的。原始可能被拦union select尝试绕过UnIoN SeLeCt、UNION SELECT、uNiOn sElEcT对于简单的关键字匹配规则这招有时就能过关。可以配合使用例如id1 UnIoN/**/SeLeCt 1,2,3 from admin --4.2 内联注释与空白符滥用利用数据库注释和空白符的特殊性打断WAF的字符串匹配。SQL内联注释在SQL Server中/**/是注释但WAF可能不会深入解析它。union/**/select- 可能绕过union select的规则。sel/**/ect- 拆解关键字本身。空白符变体除了空格%20尝试Tab键%09换行符%0a回车符%0d多个空格%20%20%20union%0aselect或union%09select可能被WAF视为两个单词而绕过union select的整体匹配规则。4.3 等价函数与操作符替换寻找执行相同功能但字符序列不同的替代品。and-(在某些环境下)or-||-like,in,between ... and ...id1 and substring(db_name(),1,1)a可能被拦。id1 and substring(db_name(),1,1) like a可能通过。id1 and ascii(substring(db_name(),1,1)) between 97 and 97猜解字母‘a’。select- 在某些极其特殊的情况下可以考虑使用(select)或(select top 1)等变体但效果有限。4.4 编码与双重编码WAF的解码层和应用程序的解码层可能不一致。URL编码-%27select-%73%65%6c%65%63%74。但现代WAF通常能解码一次。双重URL编码-%27-%25%32%37。如果WAF只解码一次看到的是%27可能不匹配单引号规则而应用服务器解码两次后得到原始的。十六进制编码将字符串转换为十六进制。在SQL Server中0x开头表示十六进制。select-0x73656c656374Payload示例id1; exec(0x73656c6563742031)等价于执行select 1。这能有效绕过基于文本匹配的WAF规则因为WAF看到的是十六进制串而非关键字。4.5 参数污染与参数拆分利用应用程序处理多个同名参数的逻辑与WAF处理逻辑的差异。HPPHTTP Parameter Pollution提交id1id2 and 11。WAF可能只检查第一个id1认为安全而ASP程序特别是老旧程序可能取最后一个参数值2 and 11导致注入。参数拆分将一个危险的参数拆分成多个看似无害的部分。例如想传递union select可以尝试par1unipar2onpar3selpar4ect然后在后端代码中如果程序错误地拼接了par1par2par3par4就构成了union select。这需要猜测后端代码逻辑成功率不高但一旦成功绕过效果极佳。4.6 利用数据库特性与非常规语法深入研究特定数据库的“怪癖”。SQL Server中的换行符在SQL Server中某些关键字之间可以用换行符分隔而SQL引擎仍能正确解析。id1 union select 1,2,3 --将union和select分在两行可能绕过单行匹配的规则。注释符使用--是SQL Server的行注释#是MySQL的/*...*/是块注释。尝试在Payload中插入无关注释union/**/select/**/1,2,3/**/from/**/admin。字符串拼接admin最终结果是admin。可以尝试select * from users where usernameadmin。5. 实战串联从探测到获取数据理论说再多不如看一次完整的、串联的实战过程。假设目标为http://target/news.asp?id1部署了WTS-WAF。第一步静默侦察访问news.asp?id999999页面显示“文章不存在”。访问id1正常显示。初步判断为数字型参数且程序对无结果有处理。第二步WAF规则试探id1- 返回WAF拦截页面。确认WAF存在且拦截单引号。id1%27- 同样被拦截。WAF能识别URL编码。id1 and 11- 页面正常显示。and和没被单独拦截。id1 and 12- 页面显示“文章不存在”这是一个黄金信号。说明and 12这个假条件使整个查询无结果程序走到了“文章不存在”的逻辑且WAF没有拦截。这意味着布尔盲注的条件成熟了并且and [条件]这种形式可能可以绕过。第三步构造绕过Payload进行布尔盲注目标猜解当前数据库用户名。判断数据库类型id1 and len(version)0- 页面正常。极可能是SQL Server。猜解用户名长度id1 and len(user)5- 页面正常。id1 and len(user)6- 页面“文章不存在”。因此user长度是5。猜解第一个字符使用二分法。id1 and ascii(substring(user,1,1))100- 正常。id1 and ascii(substring(user,1,1))120- 不存在。id1 and ascii(substring(user,1,1))110- 正常。id1 and ascii(substring(user,1,1))115- 不存在。... 最终定位到ascii(substring(user,1,1))115时页面正常。ASCII 115 对应字母s。引入绕过技巧如果上述substring函数被拦截尝试替换。id1 and ascii(mid(user,1,1))115(mid是Access函数但SQL Server也支持可能绕过对substring的规则)。或者使用id1 and unicode(substring(user,1,1))115。甚至尝试内联注释id1 and ascii(sub/**/string(user,1,1))115。重复猜解依次猜解第2到第5个字符。最终得到user sa。注意user是函数返回的是数据库登录名这里结果是sa系统管理员非常常见。第四步尝试联合查询获取更多数据布尔盲注太慢如果可能联合查询是首选。探测字段数id1 order by 10- 被WAF拦截。order by是敏感关键字。尝试绕过id1 order/**/by 10- 可能通过。或者用id1/**/order/**/by 10。如果通过且当order by 5正常order by 6报错则字段数为5。联合查询id-1 union select 1,2,3,4,5- 极大概率被拦截。综合运用绕过技巧构造最终Payloadid-1 uniOn%0aSeLeCt%09null,system_user,db_name(),null,null%09from%09information_schema.tablesid-1使前一个查询无结果确保显示我们union的结果。uniOn%0aSeLeCt大小写混合并用换行符%0a分隔。%09Tab作为空白符。将想要的数据放在第2、3个字段位置通过order by探测出的可显示位。from information_schema.tables是一个合法的表名用于满足语法。 如果这个Payload成功执行且未被WAF拦截页面原本显示文章标题和内容的位置可能会显示system_user如‘sa’和db_name()数据库名。6. 常见问题、防御建议与深度思考6.1 实战中遇到的典型问题与排查Payload明明正确但页面没变化检查参数类型你以为id是数字型但它可能是字符串型需要闭合引号id1 and 11。检查数据回显位置Union查询的数据可能输出在页面的隐藏标签、JS代码或注释里查看网页源代码。考虑Cookie注入有些ASP程序会将参数存储在Cookie中处理。尝试将Payload放在Cookie里Cookie: id1; sessionxxx; ...。WAF静默拦截WAF可能不返回拦截页面而是将请求重置或返回一个空白页/原页。用and 11和and 12反复对比确认页面是否有极其细微的差异如一个隐藏div、一个空格、响应时间。Access数据库猜解表名、列名太慢怎么办Access没有information_schema需要暴力猜解。提高效率的方法基于字典准备常见的表名字典admin, user, manage, news, product...和列名字典username, password, id, name...。利用已知信息从网站功能推断表名新闻站可能有news表用户中心可能有users表。布尔盲注逻辑优化猜解是否存在某表id1 and (select count(*) from admin)0。如果页面正常说明存在admin表。WAF突然开始封IP降低请求频率在请求间加入随机延迟如3-10秒。切换User-Agent模拟正常浏览器。使用代理池如果条件允许通过多个代理IP轮询发送请求。检查Payload特征是否使用了过于激进或常见的测试向量。回归到更温和、更混淆的Payload。6.2 对开发与防御的启示作为攻击方研究绕过技巧最终目的是为了帮助构建更好的防御。对遗留ASP系统维护者的建议代码层面根治参数化查询Parameterized Queries这是唯一从根本上杜绝SQL注入的方法。无论是ADO的Command对象还是使用参数集合确保用户输入不被解释为SQL代码。输入验证与过滤在参数化查询的基础上增加白名单验证。例如ID参数只允许数字就严格用IsNumeric()判断并转换为数值型。最小权限原则数据库连接账户不应使用sa等高权限账号应为其分配仅能满足应用需求的最小权限。错误处理关闭服务器的详细错误信息在IIS中设置自定义错误页在ASP代码中使用On Error Resume Next并转向通用错误页避免信息泄露。WAF作为补偿性控制WAF是最后一道防线而非唯一防线。应正确配置定期更新规则库并开启日志审计分析拦截记录了解攻击态势。对WAF规则运营者的启示语义分析优于简单匹配规则应能理解uni/**/on sel/**/ect的本质而不是单纯匹配union select字符串。关注异常行为除了单个请求的恶意特征还应关注同一会话/IP在短时间内的大量错误请求盲注特征、对同一参数进行系统性变化如逐字符猜解的行为模式。虚拟补丁对于已发现但无法立即修复的遗留系统漏洞可以在WAF上配置精确的虚拟补丁规则进行临时防护。6.3 我的个人体会与进阶思考经过这次实战我最大的体会是面对“老系统新WAF”这种组合攻击者的优势在于对“老系统”的深刻理解。WAF的规则往往是针对通用模式、流行框架的而ASP老旧站点那些原始的字符串拼接、脆弱的错误处理、特定的数据库函数如Access的mid,asc构成了其独特的攻击面。很多绕过手法本质上是利用了WAF规则与老旧ASP程序实际解析SQL语句方式之间的“认知差”。例如WAF可能严格检测union select但对于ASP程序通过Request.QueryString获取参数后先进行字符串替换如去掉某些字符再拼接SQL的行为WAF是无从知晓的。这就可能产生“畸形”但有效的Payload。因此真正的安全不能依赖单点防御。一个健全的防护体系应该包括安全的编码规范从源头杜绝、及时的打补丁和更新、最小权限的网络与系统配置、以及作为监测和缓解手段的WAF/IDS。对于渗透测试者而言面对这样的目标耐心、细致和对细节的观察力比如页面一个像素的变化、响应时间几毫秒的差异往往比拥有最强大的工具更重要。手工注入的过程虽然枯燥但每一次成功的绕过都是对攻防技术本质的一次深刻理解。