第一弹
加解密分析与实现
本次测试为代码审计+渗透,所以可以直接分析加密方法。
抓包后发现请求响应均加密
经分析目标系统使用AES+RSA进行加密。
- 客户端使用RSA公钥加密随机生成的AES秘钥,通过header里的
encrypted-key
字段进行发送。
- 使用随机生成的AES秘钥加密请求与响应
所以解密时需要先用RSA私钥解密encrypted-key
字段拿到AES秘钥,再使用AES秘钥解密请求与响应即可。
首先是加解密代码实现,代码如下:
rsa_decrypt()
函数用RSA私钥解密。
aes_encrypt()
、aes_decrypt()
函数实现AES加解密。
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
def rsa_decrypt(encrypt_msg):
pri_s="MIIExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=="
private_key = RSA.import_key(base64.b64decode(pri_s))
cipher = PKCS1_cipher.new(private_key)
back_text = cipher.decrypt(base64.b64decode(encrypt_msg), 0)
return back_text.decode('utf-8')
def aes_encrypt(plaintext, key):
cipher = AES.new(key.encode(), AES.MODE_ECB)
padded_plaintext = pad(plaintext.encode(), AES.block_size)
ciphertext = cipher.encrypt(padded_plaintext)
b64_ciphertext = base64.b64encode(ciphertext).decode()
return b64_ciphertext
def aes_decrypt(ciphertext, key):
cipher = AES.new(key.encode(), AES.MODE_ECB)
plaintext = cipher.decrypt(base64.b64decode(ciphertext))
unpadded_plaintext = unpad(plaintext, AES.block_size)
return unpadded_plaintext.decode()
if __name__ == '__main__':
encrypt_text = "oJfxAkwLXgEQubahEx+sQQz58fXrcFm3PTwBbt7b51cz6Q2onz8Zf5hEyGsuaNaG6D+d/814cHU7y/ASMzf7bYt4XieJNTRxrc1bIGloZ6xjbJOjWvi+QLLueuaI37NWJZnnFgNjZVai5pFzUoKEpVptBixsBnytEQoOlhbxPrvDmcgqMzjD4mL0/3Jy/IWGw+lq3KTo8AATxaxHFbOOe9od1Uf9fTHpKxy5FcvPMSC9AlgfB4N/dDrHP/I1cWXGm3tct8j6t1pSiLh1lOZiAZkA4LDUCU0/tfqKBf4DUyHCs6PyvJX7nBmdrT46xocVb72dRpRJxOQ3c/iToJnV5Q=="
aes_key = rsa_decrypt(encrypt_text)
print("AESKEY:", aes_key)
# aes
encrypt_text = "uzdGewzerdgrUjoP7BCYeiZt5o3bXp9g5dCE0X+VrS16iWfc8uXRxLjtFpFJj4O2+gqjKqlFpI8Ofy51tOdhc5belXH/s1YxaMVuCLg9V5X6V3Xeu11S0e9XCUJ8y1vJJvWk21bSguJFBfhBe4QhBxUw9Qn6T2a096LWj0JAmkLtj2itLVxQAJW+R49J61Vw39WVCEr1BDufT1DfcQcwWw=="
decrypted_text = aes_decrypt(encrypt_text, aes_key)
print("\nDecrypted text:", decrypted_text)
encrypted_text = aes_encrypt(decrypted_text, aes_key)
print("\nEncrypted text:", encrypted_text)
运行脚本成功解密AESKEY并且解密请求。
注:这里如果没有私钥我们也可以通过hook或者调试拿到aeskey以及其对应的encrypted-key,然后固定aeskey和encrypted-key即可,像第二弹一样,客户端生成aeskey的时候我们就固定它的值(不过这里是pc应用不会搞…)。
mitmproxy实现自动加解密
首先使用mitmproxy解密请求,代码如下:
# mitmproxy --mode upstream:http://127.0.0.1:8080 -s .\de_proxy.py --listen-port 8989 --ssl-insecure
from mitmproxy import http, ctx
import json
from RSAde import *
class DecryptPassword:
# 解密请求包
def request(self, flow: http.HTTPFlow) -> None:
if "xxx.cn" in flow.request.headers["Host"] and flow.request.method=="POST":
# 解析请求体中的数据
req_data = flow.request.text
# 获取encrypted-key,解密获得aeskey
encrypted_key = flow.request.headers["encrypted-key"]
aes_key = rsa_decrypt(encrypted_key)
print("\n\n\n\n\n\n\n"+aes_key)
print(req_data)
# 使用aeskey解密消息
decrypted_text = aes_decrypt(req_data, aes_key)
print(decrypted_text)
flow.request.text = decrypted_text
addons = [
DecryptPassword()
]
使用如下命令跑上面的解密脚本。
mitmproxy --mode upstream:http://127.0.0.1:8080 -s .\de_proxy.py --listen-port 8989 --ssl-insecure
# --mode upstream:http://127.0.0.1:8080表示将流量转发到指定的上游代理服务器
# --listen-port 8989表示监听端口号为8989
# --ssl-insecure表示忽略SSL证书验证,即接受所有SSL连接而不校验证书的有效性。
目标为PC端应用,使用Proxifer转发流量至mitmproxy监听的8989端口,mitmproxy解密后再将请求发给burp。
此时,请求包已经是明文了
接下来需要第二个mitmproxy实现加密请求发给服务器,同时解密响应包内容。
# mitmproxy -s .\en_proxy.py --listen-port 8990 --ssl-insecure
from mitmproxy import http, ctx
import json
from RSAde import *
class EncryptPassword:
# 加密请求包
def request(self, flow: http.HTTPFlow) -> None:
if "xxx.cn" in flow.request.headers["Host"] and flow.request.method=="POST":
# 解析请求体中的数据
req_data = flow.request.text
# 获取encrypted-key,解密获得aeskey
encrypted_key = flow.request.headers["encrypted-key"]
aes_key = rsa_decrypt(encrypted_key)
# 使用aeskey加密消息
encrypted_text = aes_encrypt(req_data, aes_key)
flow.request.text = encrypted_text
def response(self, flow: http.HTTPFlow):
if flow.request.method == 'POST':
req_data = flow.request.text
# 获取encrypted-key,解密获得aeskey
encrypted_key = flow.request.headers["encrypted-key"]
aes_key = rsa_decrypt(encrypted_key)
# 处理响应
datas = flow.response.get_content()
#print("\n\n\n\n\n\n\n"+aes_key)
try:
datas = json.loads(datas.decode())
except:
pass
decrypted_text = aes_decrypt(datas, aes_key)
flow.response.raw_content = decrypted_text
addons = [
EncryptPassword()
]
运行命令为,监听的端口为8990
mitmproxy -s .\en_proxy.py --listen-port 8990 --ssl-insecure
burp设置下一级代理为8990,此时mitmproxy接收到burp发来的明文请求,将其再次AES加密发给服务器,同时将服务器发过来的数据进行解密。
再次抓包,此时请求响应均是解密的
下面就可以愉快的测试了,运气不错,直接扫出来个SQL注入和Fastjson的RCE
第二弹
加解密分析与实现
请求响应均为加密的
一看这个就知道是RSA+AES,param是真正的参数,使用AES加密,key是用RSA公钥加密的AES秘钥。
加密代码在common.js,下面是生成随机AES加密key的js代码。
利用burp固定aes加密的秘钥为return '0123456789123456';
固定aes秘钥后,key也固定了,后端用私钥解密出来永远都是0123456789123456
key=8a65c9b957a9f5c75bec431aa3c94b30c5a9fda6f89515c4552c00d0a4f4d69e011435a33deebe92bb2725860ed40a534f458bae0f86f9242ec5675da594d571cf1e121c8ab8b86f13a53a9c6c6622557a2644407822b9d75658a7bb48ef887d904883948578055136c391e1cdc1e4cdf75d23a354a96fcfa87683aaeab8a819
加密函数调试过程:
打断点在aes加密函数处
调用堆栈处找到调用加密的上一个函数
加密解密均能正常调用
这里实现aes加解密使用crypto-js实现的
var CryptoJS = require('crypto-js')
// crypto-js加密
function cryptoEncryption(aseKey,message){ //aseKey为密钥(必须为:8/16/32位),message为要加密的密文
var encrypt = CryptoJS.AES.encrypt(message,CryptoJS.enc.Utf8.parse(aseKey), {
mode:CryptoJS.mode.ECB,
padding:CryptoJS.pad.Pkcs7
}).toString();
return encrypt
}
// crypto-js解密
function cryptoDecrypt(aseKey,message){
var decrypt = CryptoJS.AES.decrypt(message, CryptoJS.enc.Utf8.parse(aseKey), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
return decrypt
}
var aseKey = "12345678" //密钥一致才能进行解密
var encrpytText = "abc12345";
var decryptText = 'KLqoT18E3l+OoDFLwS8DsA=='
console.log(cryptoEncryption(aseKey,encrpytText)); //调用加密方法
console.log(cryptoDecrypt(aseKey,decryptText));//调用解密方法
使用python的execjs调用js里的加解密函数
import execjs
js = open('aes.js', encoding='utf-8')
com = execjs.compile(js.read(), cwd="./")
def aes_encrypt(aes_key, data):
return com.call("cryptoEncryption", aes_key, data)
def aes_decrypt(aes_key, data):
return com.call("cryptoDecrypt", aes_key, data)
print(aes_encrypt("12345678", "123"))
print(aes_decrypt("12345678", "+ahiJogQTpDxQ8yIGrMGlw=="))
后面直接使用mitmproxy解密数据包即可
mitmproxy实现自动加解密
解密请求包的代码:
# mitmproxy --mode upstream:http://127.0.0.1:8080 -s .\aes_dproxy.py --listen-port 8989 --ssl-insecure
from mitmproxy import http, ctx
import urllib
import base64
from aes import *
class DecryptPassword:
# 解密请求包
def request(self, flow: http.HTTPFlow) -> None:
if "192.18.101.76" in flow.request.headers["Host"] and flow.request.method=="POST":
# 解析请求体中的post数据
req_data = flow.request.text
print("\n\n\n\n\n"+req_data)
print(flow.request.content)
# 判断是否包含password参数
if 'param' in req_data:
req_data=urllib.parse.unquote(req_data)
# print(req_data)
# 进行解密操作
password = req_data.split("&")[0]
password = password.replace("param=","")
print(password)
# 对参数进行解密
decrypted_password = aes_decrypt("0123456789123456", password)
decrypted_password = base64.b64decode(decrypted_password)
print(decrypted_password)
# print(decrypted_password)
# 修改JSON数据中的password参数值
req_data = "param="+decrypted_password.decode()+"&key=8a65c9b957a9f5c75bec431aa3c94b30c5a9fda6f89515c4552c00d0a4f4d69e011435a33deebe92bb2725860ed40a534f458bae0f86f9242ec5675da594d571cf1e121c8ab8b86f13a53a9c6c6622557a2644407822b9d75658a7bb48ef887d904883948578055136c391e1cdc1e4cdf75d23a354a96fcfa87683aaeab8a819"
print(req_data)
# 将修改后的JSON数据转换成字符串形式
# 更新请求体中的数据
flow.request.text = req_data
addons = [
DecryptPassword()
]
加密请求包发给服务器,解密响应包的代码(由于用内网客户机器,实现太麻烦,解密响应包的没搞了):
# mitmproxy -s .\aes_eproxy.py --listen-port 8990 --ssl-insecure
from mitmproxy import http, ctx
import urllib
import base64
import mitmproxy
from aes import *
class EncryptPassword:
# 加密请求包
def request(self, flow: http.HTTPFlow) -> None:
if "192.18.101.76" in flow.request.headers["Host"]:
# 解析请求体中的JSON数据
req_data = flow.request.text
print("\n\n\n\n\n"+req_data)
# 判断是否包含password参数
if 'param' in req_data:
req_data=urllib.parse.unquote(req_data)
print(req_data)
password = req_data.split("&")[0]
password = password.replace("param=","")
print(password)
# 对参数进行解密
encrypted_password = base64.b64encode(password.encode()).decode()
print(encrypted_password)
encrypted_password = aes_encrypt("0123456789123456", encrypted_password)
print(encrypted_password)
# 修改JSON数据中的password参数值
req_data = "param="+encrypted_password+"&key=8a65c9b957a9f5c75bec431aa3c94b30c5a9fda6f89515c4552c00d0a4f4d69e011435a33deebe92bb2725860ed40a534f458bae0f86f9242ec5675da594d571cf1e121c8ab8b86f13a53a9c6c6622557a2644407822b9d75658a7bb48ef887d904883948578055136c391e1cdc1e4cdf75d23a354a96fcfa87683aaeab8a819"
print(req_data)
# 将修改后的JSON数据转换成字符串形式
# 更新请求体中的数据
flow.request.text = req_data
# 解密请求包
# def response(self, flow:HTTPFlow):
# # 解析请求体中的post数据
# req_data = json.loads(flow.response.text)
# flow.response.set_text('success')
# print("\n\n\n\n\n\n\n"+req_data)
# if 'result' in req_data.keys():
# password=req_data["result"]["data"]
# print(password)
# decrypted_password = aes_decrypt("0123456789123456", password)
# print(decrypted_password)
# req_data["result"]["data"] = decrypted_password
# flow.response.text = req_data
addons = [
EncryptPassword()
]
如下图所示,实现了自动解密,然后加密请求包给服务器。
可对账号密码进行爆破枚举等其他操作