各位靓仔靓女们,晚上好!我是今晚的讲师,咱们今晚来聊点刺激的——HTTP请求混淆。听说过吗?就是把你的HTTP请求乔装打扮一下,让那些“火眼金睛”的检测系统认不出来,从而达到一些……嗯,不可告人的目的。(当然,我们只是学习技术,不要干坏事哦!)
咱们今天就来好好扒一扒,怎么通过各种骚操作,把HTTP请求变成一个“百变怪”,让它看起来像模像样,但实际上却暗藏玄机。
一、啥是请求混淆?为啥要搞它?
简单来说,请求混淆就是通过各种技术手段,改变HTTP请求的结构或内容,使得它看起来和正常的请求不太一样。
为啥要搞它呢?原因有很多:
- 绕过WAF (Web Application Firewall) 和 IDS (Intrusion Detection System): 这些安全设备会根据HTTP请求的特征来判断是否存在恶意攻击。如果我们能把请求伪装得不像攻击,就能成功绕过它们。
- 逃避审计和监控: 有时候,我们需要隐藏我们的真实行为,防止被记录或跟踪。
- 测试安全性: 请求混淆也是渗透测试中常用的手段,可以用来测试目标系统的安全性。
但是!记住!我们学习请求混淆是为了更好地保护自己,而不是去搞破坏。
二、混淆的几种常见姿势
好了,废话不多说,咱们直接上干货,看看有哪些常见的混淆姿势:
- 自定义编码:让数据“加密”
最简单的混淆方法就是对请求的数据进行编码。不要以为编码就是加密,编码只是为了让数据能安全传输,和加密不是一个概念。
-
Base64 编码: 简单粗暴,但效果有限,因为太容易被识别了。
import base64 data = "Hello, World!" encoded_data = base64.b64encode(data.encode('utf-8')).decode('utf-8') print(f"Base64 编码后的数据:{encoded_data}") # 输出:Base64 编码后的数据:SGVsbG8sIFdvcmxkIQ==
-
URL 编码: 将特殊字符转换为
%
加上十六进制表示。import urllib.parse data = "Hello, World!@#$%^" encoded_data = urllib.parse.quote(data) print(f"URL 编码后的数据:{encoded_data}") # 输出:URL 编码后的数据:Hello%2C%20World%21%40%23%24%25%5E
-
自定义编码: 这是最灵活,也是最有效的。你可以设计自己的编码算法,让别人难以破解。比如,你可以把每个字符加上一个固定的偏移量,或者使用一个密钥进行异或运算。
def custom_encode(data, key=5): encoded_data = "" for char in data: encoded_char = chr(ord(char) + key) encoded_data += encoded_char return encoded_data def custom_decode(data, key=5): decoded_data = "" for char in data: decoded_char = chr(ord(char) - key) decoded_data += decoded_char return decoded_data data = "Hello, World!" encoded_data = custom_encode(data) decoded_data = custom_decode(encoded_data) print(f"原始数据:{data}") # 原始数据:Hello, World! print(f"自定义编码后的数据:{encoded_data}") # 自定义编码后的数据:Mjqqt,$Xqtlo% print(f"解码后的数据:{decoded_data}") # 解码后的数据:Hello, World!
注意事项:
- 编码和解码必须保持一致,否则数据就乱了。
- 密钥要足够复杂,防止被轻易破解。
- 编码后的数据长度可能会发生变化,需要注意处理。
- 请求头伪造:障眼法大师
HTTP 请求头包含了客户端的信息,比如 User-Agent、Referer 等。我们可以通过修改这些头部信息,来伪装我们的请求。
-
User-Agent: 伪装成不同的浏览器或设备。
import requests headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36' } response = requests.get('https://www.example.com', headers=headers) print(response.status_code)
-
Referer: 伪装请求的来源。
headers = { 'Referer': 'https://www.google.com' } response = requests.get('https://www.example.com', headers=headers) print(response.status_code)
-
X-Forwarded-For: 伪装客户端 IP 地址。注意: 这个头部信息通常会被代理服务器修改,所以不一定有效。
headers = { 'X-Forwarded-For': '1.2.3.4' } response = requests.get('https://www.example.com', headers=headers) print(response.status_code)
-
自定义头部: 添加一些无关紧要的头部信息,迷惑检测系统。
headers = { 'X-Random-Header': 'SomeRandomValue' } response = requests.get('https://www.example.com', headers=headers) print(response.status_code)
注意事项:
- 不同的网站对头部信息的验证严格程度不同,需要根据实际情况进行调整。
- 不要伪造一些明显错误的头部信息,比如 User-Agent 写成 "I am a robot",这样反而更容易被识别。
- HTTP 方法混淆:瞒天过海
HTTP 方法(GET, POST, PUT, DELETE 等)定义了客户端对服务器的操作。我们可以尝试使用一些不常用的方法,或者对方法进行一些变体,来混淆请求。
-
使用不常用的方法: 比如
PATCH
、OPTIONS
等。 -
方法大小写混淆: 比如
gEt
、pOSt
。import requests response = requests.request('GeT', 'https://www.example.com') print(response.status_code)
-
方法名截断: 比如,把
POST
方法改成POS
,然后通过其他方式传递剩余的T
字符。注意事项:
- 服务器是否支持这些方法以及如何处理,取决于服务器的配置。
- 需要仔细测试,确保请求能够正常工作。
- URL 混淆:指鹿为马
URL 是客户端向服务器请求资源的地址。我们可以通过各种方式来混淆 URL,让它看起来和真实的 URL 不一样。
-
URL 编码: 对 URL 中的特殊字符进行编码。
import urllib.parse url = "https://www.example.com/path with space?query=value&another=value" encoded_url = urllib.parse.quote(url) print(f"URL 编码后的数据:{encoded_url}") # URL 编码后的数据:https%3A//www.example.com/path%20with%20space%3Fquery%3Dvalue%26another%3Dvalue #只编码path部分 url = "https://www.example.com/path with space?query=value&another=value" parts = urllib.parse.urlparse(url) encoded_path = urllib.parse.quote(parts.path) new_url = urllib.parse.urlunparse((parts.scheme, parts.netloc, encoded_path, parts.params, parts.query, parts.fragment)) print(f"URL 编码后的数据:{new_url}") # URL 编码后的数据:https://www.example.com/path%20with%20space?query=value&another=value
-
大小写混淆: 对 URL 中的字母进行大小写混淆。
url = "https://www.example.com/index.html" mixed_url = url.replace("index", "InDeX") print(f"大小写混淆后的 URL:{mixed_url}") # 大小写混淆后的 URL:https://www.example.com/InDeX.html
-
添加冗余字符: 在 URL 中添加一些无意义的字符,比如
.
、..
、/
等。url = "https://www.example.com/index.html" redundant_url = url.replace("/index", "/./index/../index") print(f"添加冗余字符后的 URL:{redundant_url}") # 添加冗余字符后的 URL:https://www.example.com/./index/../index.html
-
使用短链接: 将 URL 转换成短链接,隐藏真实的 URL。
注意事项:
- 服务器是否支持这些混淆方式,取决于服务器的配置。
- 需要仔细测试,确保请求能够正常工作。
- 数据分割与重组:化整为零
将请求的数据分割成多个小块,然后分别发送,最后在服务器端进行重组。
-
Chunked Transfer Encoding: 将 HTTP 消息分割成多个块,每个块包含数据长度和数据内容。
import requests url = 'https://www.example.com' data = 'This is a very long string that needs to be chunked.' def chunk_generator(data, chunk_size=10): for i in range(0, len(data), chunk_size): yield data[i:i + chunk_size].encode('utf-8') headers = { 'Transfer-Encoding': 'chunked' } response = requests.post(url, data=chunk_generator(data), headers=headers) print(response.status_code)
-
自定义分割: 自己定义分割规则,将数据分割成多个部分,然后通过不同的参数或头部信息发送。
注意事项:
- 服务器端需要实现相应的重组逻辑。
- 需要考虑数据分割的大小和数量,避免影响性能。
三、实战演练:绕过一个简单的 WAF
为了让大家更好地理解,我们来做一个简单的实战演练。假设我们有一个简单的 WAF,它会拦截包含 "SELECT" 关键词的请求。
def waf(request):
if "SELECT" in request.upper():
return False
return True
# 模拟请求
request1 = "GET /?query=SELECT * FROM users"
request2 = "GET /?query=sElEcT * FROM users"
request3 = "GET /?query=SEL%45CT * FROM users"
request4 = "GET /?query=SELECT/**/FROM users"
print(f"请求1是否通过WAF:{waf(request1)}") # 请求1是否通过WAF:False
print(f"请求2是否通过WAF:{waf(request2)}") # 请求2是否通过WAF:False
print(f"请求3是否通过WAF:{waf(request3)}") # 请求3是否通过WAF:False
print(f"请求4是否通过WAF:{waf(request4)}") # 请求4是否通过WAF:False
可以看到,无论大小写还是URL编码,都无法绕过这个简单的WAF。不过如果使用注释呢?
def waf(request):
if "SELECT" in request.upper():
return False
return True
# 模拟请求
request = "GET /?query=SELECT/**/FROM users"
print(f"请求是否通过WAF:{waf(request)}") # 请求是否通过WAF:False
可以看到,还是无法绕过,因为WAF直接检测了字符串,那我们修改下WAF:
import re
def waf(request):
if re.search(r"selects**s*from", request, re.IGNORECASE):
return False
return True
# 模拟请求
request1 = "GET /?query=SELECT * FROM users"
request2 = "GET /?query=sElEcT * FROM users"
request3 = "GET /?query=SEL%45CT * FROM users"
request4 = "GET /?query=SELECT/**/FROM users"
print(f"请求1是否通过WAF:{waf(request1)}")
print(f"请求2是否通过WAF:{waf(request2)}")
print(f"请求3是否通过WAF:{waf(request3)}")
print(f"请求4是否通过WAF:{waf(request4)}")
现在,我们来尝试使用请求混淆来绕过它:
-
大小写混淆:
request = "GET /?query=sElEcT * FROM users" print(f"请求是否通过WAF:{waf(request)}") # 请求是否通过WAF:False
这个不行了,因为规则改了,所以大小写混淆也不行了。
-
URL 编码:
import urllib.parse request = "GET /?query=SEL%45CT * FROM users" print(f"请求是否通过WAF:{waf(request)}") # 请求是否通过WAF:False
同样,URL编码也不行了。
-
添加注释:
request = "GET /?query=SELECT/**/FROM users" print(f"请求是否通过WAF:{waf(request)}") # 请求是否通过WAF:True
成功绕过!因为WAF的规则没有考虑到注释的情况。
-
拆分关键词:
request = "GET /?query=SEL"+"ECT * FROM users" print(f"请求是否通过WAF:{waf(request)}") # 请求是否通过WAF:True
成功绕过!因为WAF检测的是完整的 "SELECT" 字符串。
四、总结与建议
今天我们学习了 HTTP 请求混淆的几种常见姿势,包括自定义编码、请求头伪造、HTTP 方法混淆、URL 混淆和数据分割与重组。
混淆方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
自定义编码 | 灵活性高,难以识别 | 需要编写编码和解码逻辑,复杂度较高 | 需要对数据进行加密或隐藏的场景 |
请求头伪造 | 简单易用,效果明显 | 容易被高级检测系统识别 | 伪装客户端信息,绕过简单的访问控制 |
HTTP 方法混淆 | 可以绕过一些基于方法的检测 | 服务器可能不支持,需要仔细测试 | 尝试绕过对特定 HTTP 方法的限制 |
URL 混淆 | 隐藏真实的 URL,增加攻击难度 | 容易被高级检测系统识别 | 隐藏真实访问路径,绕过简单的 URL 过滤 |
数据分割与重组 | 可以绕过一些基于完整数据的检测 | 服务器端需要实现重组逻辑,复杂度较高 | 绕过对完整数据的检测,例如上传文件大小限制等 |
几点建议:
- 不要滥用请求混淆: 请求混淆只是一种技术手段,应该在合法合规的前提下使用。
- 选择合适的混淆方式: 不同的混淆方式适用于不同的场景,需要根据实际情况进行选择。
- 不断学习和更新知识: 安全技术不断发展,需要不断学习新的混淆技术和检测技术,才能保持领先。
- 注意安全风险: 请求混淆可能会引入新的安全风险,需要仔细评估和防范。
好了,今天的讲座就到这里。希望大家能够掌握 HTTP 请求混淆的基本原理和方法,并在实际工作中灵活运用。记住,技术是把双刃剑,我们应该用它来保护自己,而不是去伤害别人。 拜拜!