ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HTTP头注入实战:sqli-labs第18关手工注入与防御详解

HTTP头注入实战:sqli-labs第18关手工注入与防御详解 1. 靶场环境与第十八关概述sqli-labs是一个经典的SQL注入实战靶场对于想深入理解Web安全攻防特别是手工注入技巧的从业者来说是绕不开的必修课。它模拟了各种真实场景下的注入点从最简单的报错注入到复杂的盲注、堆叠注入难度层层递进。今天要拆解的第十八关是一个典型的“HTTP头注入”场景它打破了我们通常只在URL参数或表单字段中寻找注入点的思维定式将战场转移到了HTTP请求的头部信息里。很多刚接触安全测试的朋友在常规位置测试无果后很容易就放弃了殊不知漏洞可能就藏在User-Agent、Referer甚至X-Forwarded-For这些看似“无害”的请求头里。第十八关正是训练我们这种“视野”的绝佳关卡。这一关的界面通常很简单可能只有一个登录框但常规的账号密码注入尝试都会失败。它的核心在于服务器在验证登录后会将部分HTTP头信息比如User-Agent记录到数据库中而这个记录过程存在SQL注入漏洞。因此攻击链是先通过一个有效的登录可能是一个预设的弱口令获得一个有效的会话或触发记录点然后在后续请求的HTTP头中携带恶意载荷。理解这个流程是通关的关键。它模拟了那些记录用户访问日志、分析用户行为但未对输入进行严格过滤的应用场景在现实中具有相当的普遍性。2. 核心漏洞原理与请求流程拆解要攻克第十八关不能闷头乱试必须先把它的后台逻辑猜个八九不离十。根据关卡常见的实现模式我们可以推断出其核心代码逻辑大致如下登录验证前端提交用户名和密码。后端代码会查询数据库验证凭据是否正确。这里通常不是注入点因为开发者会对这些直接来自用户的输入进行基础过滤或使用预编译语句。登录成功后的操作一旦登录成功应用会执行一些“记录”操作。一个非常常见的操作就是将本次登录成功的用户信息连同他此次请求的User-Agent或Referer字符串一起插入到数据库的某个日志表中。代码可能看起来像这样以PHP为例$username $_POST[username]; $uagent $_SERVER[HTTP_USER_AGENT]; // 直接获取HTTP头 $sql INSERT INTO ua_log (username, uagent, ip) VALUES ($username, $uagent, $remote_addr); $result mysqli_query($conn, $sql);漏洞产生点问题就出在第二点。$_SERVER[‘HTTP_USER_AGENT’]这个变量直接来源于客户端浏览器发送的请求头是完全可由用户控制的。开发者往往认为HTTP头是浏览器自动生成的、可信的从而忽略了对其进行与表单数据同等级别的过滤和转义。当这个未经验证的$uagent变量被直接拼接进SQL语句时注入漏洞便产生了。注入位置因此本关的注入点不在登录的username或password字段而在于登录成功之后任何一个会触发该记录逻辑的请求的HTTP头部。通常我们利用User-Agent或Referer头来实施注入。整个攻击流程可以概括为寻找有效登录凭证 - 在登录请求或后续请求的HTTP头中植入SQL注入载荷 - 触发后台的记录逻辑执行恶意SQL。很多人在第一步就卡住了因为总想着用admin’ or ‘1’’1这种payload去爆破登录其实这一关的登录可能只需要一个简单的已知账号密码对比如admin/admin目的是为了让我们“合法”地进入记录流程。注意在实际测试中务必使用Burp Suite、OWASP ZAP这类拦截代理工具。你需要拦截登录成功后的那个请求或者是登录请求本身如果记录发生在登录验证之后然后修改其HTTP头部字段。3. 手工注入实战从探测到数据获取理论清晰后我们进入实战环节。假设我们已经通过某种方式如查看源码提示、常见弱口令获得了登录凭证admin/admin。3.1 环境准备与请求拦截首先确保你的sqli-labs环境如Docker或PHPStudy搭建的运行正常。打开浏览器访问第十八关页面同时打开Burp Suite并配置好代理。在浏览器中输入用户名admin和密码admin点击登录。先不要在意登录后页面显示什么。迅速切换到Burp Suite的Proxy-Intercept标签页确保拦截是开启的Intercept is on。然后在浏览器中刷新登录后的页面或者进行任何可能触发记录的操作有时登录动作本身就会触发。目的是拦截到那个携带了User-Agent的请求。此时Burp Suite会拦截到一个HTTP请求。重点关注请求头部你会看到类似这样的字段GET /sqli-labs/Less-18/ HTTP/1.1 Host: your-target-ip User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 Connection: close ...3.2 漏洞探测与注入点确认现在我们要验证User-Agent头是否真的存在注入。将拦截到的请求发送到Burp Suite的Repeater模块方便我们反复修改和测试。在Repeater中修改User-Agent头的值。最初的探测通常使用单引号‘来触发SQL语法错误。原始请求头User-Agent: Mozilla/5.0 (...)修改为User-Agent: Mozilla/5.0 (...)点击Send发送请求。观察返回的HTTP响应。如果页面返回了数据库的报错信息如“You have an error in your SQL syntax...”那么恭喜你注入点确认了错误信息明确告诉我们单引号破坏了SQL语句的完整性证明User-Agent的值被直接拼接到SQL语句中且未经过滤。如果页面只是空白或显示正常可以尝试在User-Agent末尾添加‘ and ‘1’’1和‘ and ‘1’’2观察页面返回内容是否有差异这常用于盲注判断。但在第十八关通常设计为报错注入所以看到数据库错误是最直接的证据。3.3 利用报错注入提取信息确认注入点后我们利用数据库报错信息来提取数据。这里以MySQL为例常用的报错函数是updatexml()和extractvalue()。原理updatexml()函数用于更新XML文档但如果其第二个参数XPath路径不符合格式就会产生错误并将我们构造的查询结果包含在错误信息中输出。构造Payload 我们需要将想要查询的语句嵌套在updatexml()函数里。基本格式如下 and updatexml(1, concat(0x7e, (你想要执行的SQL查询语句), 0x7e), 1) and 解释一下开头的‘用于闭合原SQL语句中的前一个单引号。and连接我们的恶意条件。updatexml(1, ..., 1)第一个和第三个参数任意重点是第二个参数。concat(0x7e, ..., 0x7e)0x7e是波浪号~的十六进制。用~包裹查询结果是为了在报错信息中更清晰地看到我们的数据边界。末尾的‘用于闭合原SQL语句中User-Agent值后面的那个单引号。实操步骤获取当前数据库名 将User-Agent修改为User-Agent: Mozilla/5.0 (...) and updatexml(1, concat(0x7e, (database()), 0x7e), 1) and 发送请求。在返回页面的错误信息中你会看到类似这样的内容... ‘~security~’ ...这说明当前数据库名是security。获取数据库中的表名 知道数据库名后接下来查询表名。MySQL的系统表information_schema.tables存储了所有表的信息。User-Agent: Mozilla/5.0 (...) and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schemadatabase() limit 0,1), 0x7e), 1) and 这里limit 0,1表示从第0行开始取1行结果即第一个表。发送请求错误信息会显示第一个表名比如emails。 然后修改limit 1,1获取第二个表名limit 2,1获取第三个以此类推。直到找到可能存放用户凭证或敏感信息的表比如users。获取表的列名 假设我们找到了users表接下来获取它的列名。查询information_schema.columns。User-Agent: Mozilla/5.0 (...) and updatexml(1, concat(0x7e, (select column_name from information_schema.columns where table_schemadatabase() and table_nameusers limit 0,1), 0x7e), 1) and 同样使用limit子句遍历你可能会依次得到id,username,password等列名。最终提取数据 现在我们可以从users表中提取username和password了。User-Agent: Mozilla/5.0 (...) and updatexml(1, concat(0x7e, (select concat(username, :, password) from users limit 0,1), 0x7e), 1) and 这个payload会查询username和password并用冒号连接起来。发送请求错误信息会显示第一行数据例如~Dumb:Dumb~。 修改limit 1,1获取第二行直到遍历完所有数据。实操心得updatexml()函数一次只能返回一行中的一个字段或我们concat后的一个字符串。并且它返回的字符串长度有限制约32KB但实际在报错信息中显示的部分更短通常只能显示几十个字符。如果你要查询的数据很长可能会被截断。这时可以使用substring()函数分段读取例如substring((select group_concat(username) from users), 1, 30)。4. 工具辅助与自动化注入思路虽然手工注入能让我们透彻理解原理但在效率至上的渗透测试中合理使用工具是必要的。对于HTTP头注入Sqlmap依然是利器但需要一些特殊配置。使用Sqlmap进行自动化测试保存请求文件在Burp Suite的Proxy-HTTP history中找到那个成功的登录请求或者触发记录的那个GET请求右键点击选择Save item将其保存为一个文本文件比如less18.req。使用Sqlmap加载请求文件Sqlmap可以通过-r参数直接读取包含完整HTTP请求的文件并自动解析其中的注入点。sqlmap -r less18.req --level 3 --risk 2 --batch-r less18.req: 指定请求文件。--level 3: 检测等级提高到3Sqlmap会尝试对User-Agent和Referer等HTTP头进行测试。--risk 2: 风险等级设为2允许Sqlmap使用一些基于时间的注入测试对于盲注很有用。--batch: 以非交互模式运行所有选择都按默认来适合自动化。指定注入点如果Sqlmap没有自动识别出头部的注入点我们可以用-p参数手动指定。sqlmap -r less18.req -p “User-Agent” --batch这会告诉Sqlmap只对User-Agent头部参数进行注入测试。获取数据一旦Sqlmap确认存在注入点后续的操作就简单了使用--dbs枚举数据库-D security --tables枚举表-D security -T users --dump导出数据即可。工具使用注意事项Cookie与会话确保保存的请求文件(.req)中包含有效的Cookie或Session ID因为登录后的操作需要维持会话状态。如果关卡有验证码或Token机制虽然sqli-labs第十八关通常没有工具自动化可能会更复杂。请求频率Sqlmap默认的测试请求非常密集可能会对靶场服务器造成压力甚至触发潜在的WAFWeb应用防火墙规则。在测试生产环境时务必使用--delay参数设置请求间隔并谨慎评估。理解输出不要只盯着最终的数据结果。多观察Sqlmap的测试过程看它发送了哪些Payload触发了什么样的响应这本身就是学习各种注入技巧的绝佳途径。5. 漏洞防御与安全开发启示作为开发者从第十八关的漏洞中应该吸取深刻的教训。HTTP头注入的本质依然是“不可信数据未经验证即拼接进入SQL语句”。根本的解决方案使用参数化查询预编译语句这是防御SQL注入最有效、最根本的方法。无论是PHP的PDO、Python的sqlite3或MySQLdb、Java的PreparedStatement其原理都是将SQL语句的结构模板与数据分离。数据库先编译语句结构再将用户输入的数据作为纯参数传入从根本上杜绝了数据被解释为代码的可能性。以PHP PDO为例的正确写法?php $stmt $pdo-prepare(“INSERT INTO ua_log (username, uagent, ip) VALUES (:username, :uagent, :ip)”); $stmt-bindParam(‘:username’, $username); $stmt-bindParam(‘:uagent’, $_SERVER[‘HTTP_USER_AGENT’]); // 即使来自HTTP头也安全 $stmt-bindParam(‘:ip’, $_SERVER[‘REMOTE_ADDR’]); $stmt-execute(); ?输入验证与过滤辅助手段虽然参数化查询是首选但对输入进行严格的验证和过滤也是良好的安全习惯。白名单验证对于User-Agent虽然格式多变但可以设定一个合理的最大长度限制如500字符防止过长的注入载荷。转义处理如果因历史遗留问题必须使用字符串拼接强烈不建议则必须对输入进行正确的转义。在MySQL中使用mysqli_real_escape_string()函数。但请注意转义并非万能且依赖于正确的字符集设置容易出错。最小化数据库权限运行Web应用的数据库账户应遵循最小权限原则。例如这个用于记录日志的数据库连接账号可能只需要INSERT权限到ua_log表而不需要SELECT、UPDATE、DELETE权限更不需要DROP、FILE等危险权限。这样即使发生注入攻击者能造成的破坏也有限。安全的日志记录实践对于记录User-Agent这类需求可以考虑在存入数据库前进行标准化处理或哈希处理如果不需要原文查询。或者直接将日志写入文件系统而不是数据库可以避免一类注入风险。6. 常见问题与排查技巧实录在实际操作第十八关时你可能会遇到以下几个典型问题问题1登录失败无法进入后续步骤。排查仔细查看关卡页面的前端代码右键查看源代码有时作者会在HTML注释里给出登录提示。尝试最常见的弱口令组合如admin/admin,admin/password,test/test等。sqli-labs的许多关卡默认密码就是admin。问题2修改User-Agent后页面没有报错一切正常。排查确认注入点你是否拦截了正确的请求登录动作本身可能是一个POST请求而记录日志可能是登录成功后跳转的一个GET请求。确保你测试的是触发记录功能的那个请求。尝试其他头部User-Agent可能被过滤了试试Referer头或X-Forwarded-For头。在Burp Repeater中直接添加或修改这些头部字段。尝试盲注Payload可能关卡设计为布尔盲注或时间盲注。尝试在User-Agent后添加‘ and ‘1’’1和‘ and ‘1’’2观察页面返回内容如标题、某个单词、图片是否有细微差别。或者使用时间盲注‘ and sleep(5) and ‘观察响应是否延迟5秒。问题3报错信息中看不到查询结果只显示部分XPATH语法错误。排查这是updatexml()函数的一个限制。它返回的字符串如果过长在报错信息中会被截断。解决方案使用substring()或mid()函数分段读取。例如要获取所有的用户名可以先查询长度再分段截取 and updatexml(1, concat(0x7e, substring((select group_concat(username) from users), 1, 30), 0x7e), 1) and 然后修改substring的第二个和第三个参数起始位置和长度来获取后续部分。问题4使用Sqlmap测试时总是失败或无法识别注入点。排查检查请求文件用文本编辑器打开你保存的.req文件确保它包含完整的HTTP请求包括Host、Cookie等头部并且请求方法是正确的。指定测试参数明确告诉Sqlmap测试哪个头部sqlmap -r less18.req -p “User-Agent,Referer”。调整Level和Risk尝试提高--level和--risk的值最高分别为5和3让Sqlmap进行更全面的测试。处理Cookie如果关卡有会话验证确保你的请求文件中的Cookie是新鲜有效的。可以先用浏览器正常登录一次再从Burp History里复制最新的请求。问题5注入成功但获取的数据不是想要的用户表。排查information_schema.tables里可能有很多表。你需要根据表名来猜测其用途。在sqli-labs的security数据库中常见的敏感表就是users。如果没找到可以尝试user、accounts、admin等常见表名或者用group_concat(table_name)一次性列出所有表名再仔细筛选。攻克第十八关的关键在于思维模式的转变——将HTTP请求的每一个部分都视为潜在的输入向量。这种“头注入”在现实中的WAFWeb应用防火墙绕过、漏洞拼接攻击中时有应用。通过手工一步步拆解你不仅学会了一个技巧更建立起一种全面、立体的安全测试视角。
返回列表