请求混淆 (Request Obfuscation):如何通过自定义编码、请求头伪造等方式混淆 HTTP 请求,以逃避检测?

各位靓仔靓女们,晚上好!我是今晚的讲师,咱们今晚来聊点刺激的——HTTP请求混淆。听说过吗?就是把你的HTTP请求乔装打扮一下,让那些“火眼金睛”的检测系统认不出来,从而达到一些……嗯,不可告人的目的。(当然,我们只是学习技术,不要干坏事哦!)

咱们今天就来好好扒一扒,怎么通过各种骚操作,把HTTP请求变成一个“百变怪”,让它看起来像模像样,但实际上却暗藏玄机。

一、啥是请求混淆?为啥要搞它?

简单来说,请求混淆就是通过各种技术手段,改变HTTP请求的结构或内容,使得它看起来和正常的请求不太一样。

为啥要搞它呢?原因有很多:

  • 绕过WAF (Web Application Firewall) 和 IDS (Intrusion Detection System): 这些安全设备会根据HTTP请求的特征来判断是否存在恶意攻击。如果我们能把请求伪装得不像攻击,就能成功绕过它们。
  • 逃避审计和监控: 有时候,我们需要隐藏我们的真实行为,防止被记录或跟踪。
  • 测试安全性: 请求混淆也是渗透测试中常用的手段,可以用来测试目标系统的安全性。

但是!记住!我们学习请求混淆是为了更好地保护自己,而不是去搞破坏。

二、混淆的几种常见姿势

好了,废话不多说,咱们直接上干货,看看有哪些常见的混淆姿势:

  1. 自定义编码:让数据“加密”

最简单的混淆方法就是对请求的数据进行编码。不要以为编码就是加密,编码只是为了让数据能安全传输,和加密不是一个概念。

  • 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!

    注意事项:

    • 编码和解码必须保持一致,否则数据就乱了。
    • 密钥要足够复杂,防止被轻易破解。
    • 编码后的数据长度可能会发生变化,需要注意处理。
  1. 请求头伪造:障眼法大师

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",这样反而更容易被识别。
  1. HTTP 方法混淆:瞒天过海

HTTP 方法(GET, POST, PUT, DELETE 等)定义了客户端对服务器的操作。我们可以尝试使用一些不常用的方法,或者对方法进行一些变体,来混淆请求。

  • 使用不常用的方法: 比如 PATCHOPTIONS 等。

  • 方法大小写混淆: 比如 gEtpOSt

    import requests
    
    response = requests.request('GeT', 'https://www.example.com')
    print(response.status_code)
  • 方法名截断: 比如,把 POST 方法改成 POS,然后通过其他方式传递剩余的 T 字符。

    注意事项:

    • 服务器是否支持这些方法以及如何处理,取决于服务器的配置。
    • 需要仔细测试,确保请求能够正常工作。
  1. 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。

    注意事项:

    • 服务器是否支持这些混淆方式,取决于服务器的配置。
    • 需要仔细测试,确保请求能够正常工作。
  1. 数据分割与重组:化整为零

将请求的数据分割成多个小块,然后分别发送,最后在服务器端进行重组。

  • 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 请求混淆的基本原理和方法,并在实际工作中灵活运用。记住,技术是把双刃剑,我们应该用它来保护自己,而不是去伤害别人。 拜拜!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注