JS逆向实战-房天下登录系统

本文最后更新于 2024年10月21日 晚上

加密发现

开局一个登录框,开逆!

image

输入任意账号密码登录,抓包,看看载荷

image

pwd是密码参数,被前端加密了

密码合法性前端校验

如果我输入一长串密码,点击登录,发现抓不到登录包,为什么呢?

image

来看看js怎么写的吧,多半是有前端的密码合法性校验,只有先通过了前端的合法性校验,数据包才能发给服务器,才能被抓到包

image

好家伙,一眼就找到了,原来是限制了密码的位数6-16位,否则就不发包,over

逆向加密过程

ok,进入正题

全局搜索pwd,寻找pwd的加密入口点,我自称加密点

image

看到了”encrypt”多半就是这里了,点击跟进,为了确定加密点就是这里,可以在这里下个断,再点击登录触发加密流程,看看是否程序会停在这里,调试一下值,看看是否真的是在经过这个点之后,pwd就由明文变为密文了

(由于我登录次数太多,被封了24h,就没办法放调试结果的截图了,但之前我调试成功了)

image

经测试,这里的确是加密点,分析一下

1
encryptedString(key_to_encode, that.password.val())

encryptedString函数接收key_to_encode和**that.password.val()**两个值,后者不用说,就是原始密码,前者是什么呢,找一找

全局搜索key_to_encod

image

一眼找到

1
var key_to_encode = new RSAKeyPair("010001", "", "978C0A92D2173439707498F0944AA476B1B62595877DD6FA87F6E2AC6DCB3D0BF0B82857439C99B5091192BC134889DFF60C562EC54EFBA4FF2F9D55ADBCCEA4A2FBA80CB398ED501280A007C83AF30C3D1A142D6133C63012B90AB26AC60C898FB66EDC3192C3EC4FF66925A64003B72496099F4F09A9FB72A2CF9E4D770C41");

但是key_to_encod不是一个静态值,也是使用函数计算出的动态值,怎么办呢,不慌,跟进RSAKeyPair函数看看

image

可以看到这个函数中又进行了一些数据流的传递,难道又要继续跟进内层的函数跟踪数据流吗?其实不用,因为RSAKeyPair函数是在RSA.min.js文件中定义的,RSA.min.js文件不就是RSA的加密库吗,现成的,开发图方便直接调用这个加密库,那我也图了个方便,直接调用这个加密库,自写一下入口函数就行了,不用再跟踪数据流的传递了

那么,回到encryptedString函数,现在已知第一个参数就是调用RSA加密库中的函数计算出来的,第二个参数就是原始密码

1
encryptedString(key_to_encode, that.password.val())

那么不就逆出来了吗,可以再跟进encryptedString函数看看,是不是这么一回事

image

果然,encryptedString函数也是在RSA加密库里,这下确定了,整个前端加密就是直接套用的RSA.min.js加密库,不涉及自写算法或者修改的情况,over

模拟还原加密过程

看看RSA.min.js,也就四百多行

image

打开鬼鬼调试,直接全选复制过去,加载代码,成功加载,没有bug

image

现在这个代码段,加密逻辑都有了,还缺点什么呢?

缺少入口函数去调用这些加密逻辑以及缺少key的定义

前把kay的定义复制出来,就是key_to_encode这个,前面找到了它单独在html源码中,我们给它拿出来

image

1
var key_to_encode = new RSAKeyPair("010001", "", "978C0A92D2173439707498F0944AA476B1B62595877DD6FA87F6E2AC6DCB3D0BF0B82857439C99B5091192BC134889DFF60C562EC54EFBA4FF2F9D55ADBCCEA4A2FBA80CB398ED501280A007C83AF30C3D1A142D6133C63012B90AB26AC60C898FB66EDC3192C3EC4FF66925A64003B72496099F4F09A9FB72A2CF9E4D770C41");

给当前代码段加一个入口函数,去调用这些RSA加密库的逻辑,并把key_to_encode定义在里面

怎么写呢?直接找前面说过的加密入口点,把这个函数拿出来,不就可以调用整个RAS.min.js的逻辑了吗

image

1
encryptedString(key_to_encode, pwd)

注意修改一下第二个参数,改为我们自定义的参数,手动传参原始密码,而不是从that.password.val()接收再取值了

整体的入口函数就是这样:

1
2
3
4
5
6
7
 
function main (pwd){
setMaxDigits(129);
var key_to_encode = new RSAKeyPair("010001", "", "978C0A92D2173439707498F0944AA476B1B62595877DD6FA87F6E2AC6DCB3D0BF0B82857439C99B5091192BC134889DFF60C562EC54EFBA4FF2F9D55ADBCCEA4A2FBA80CB398ED501280A007C83AF30C3D1A142D6133C63012B90AB26AC60C898FB66EDC3192C3EC4FF66925A64003B72496099F4F09A9FB72A2CF9E4D770C41");

return encryptedString(key_to_encode, pwd);
}

在鬼鬼里调试一下看看输出

image

可以看到成功模拟还原出整个加密过程,可以实现正确的加密数据了

写脚本批量加密数据

直接开始写脚本,批量加密数据

这种直接套用RSA加密库的前端加密情况,该怎么写脚本呢?很简单,直接使用execjs库,在py中去调用执行JS代码,那么我们直接把RSA.min.js存为一个js文件,再使用py的execjs库调用执行,就不用再自己写加密逻辑了,加密逻辑全部由现成的RSA.min.js加密库完成,py脚本要做的其实就只是实现数据的批量输入与输出。ok不废话了,上脚本

rsa.js

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
function setMaxDigits(n) {
maxDigits = n;
ZERO_ARRAY = new Array(maxDigits);
for (var t = 0; t < ZERO_ARRAY.length; t++)
ZERO_ARRAY[t] = 0;
bigZero = new BigInt;
bigOne = new BigInt;
bigOne.digits[0] = 1
}
function BigInt(n) {
this.digits = typeof n == "boolean" && n == !0 ? null : ZERO_ARRAY.slice(0);
this.isNeg = !1
}
function biFromDecimal(n) {
for (var u = n.charAt(0) == "-", t = u ? 1 : 0, i, f, r; t < n.length && n.charAt(t) == "0"; )
++t;
if (t == n.length)
i = new BigInt;
else {
for (f = n.length - t,
r = f % dpl10,
r == 0 && (r = dpl10),
i = biFromNumber(Number(n.substr(t, r))),
t += r; t < n.length; )
i = biAdd(biMultiply(i, lr10), biFromNumber(Number(n.substr(t, dpl10)))),
t += dpl10;
i.isNeg = u
}
return i
}
function biCopy(n) {
var t = new BigInt(!0);
return t.digits = n.digits.slice(0),
t.isNeg = n.isNeg,
t
}
function biFromNumber(n) {
var t = new BigInt, i;
for (t.isNeg = n < 0,
n = Math.abs(n),
i = 0; n > 0; )
t.digits[i++] = n & maxDigitVal,
n = Math.floor(n / biRadix);
return t
}
function reverseStr(n) {
for (var i = "", t = n.length - 1; t > -1; --t)
i += n.charAt(t);
return i
}
function biToString(n, t) {
var r = new BigInt, i, u;
for (r.digits[0] = t,
i = biDivideModulo(n, r),
u = hexatrigesimalToChar[i[1].digits[0]]; biCompare(i[0], bigZero) == 1; )
i = biDivideModulo(i[0], r),
digit = i[1].digits[0],
u += hexatrigesimalToChar[i[1].digits[0]];
return (n.isNeg ? "-" : "") + reverseStr(u)
}
function biToDecimal(n) {
var i = new BigInt, t, r;
for (i.digits[0] = 10,
t = biDivideModulo(n, i),
r = String(t[1].digits[0]); biCompare(t[0], bigZero) == 1; )
t = biDivideModulo(t[0], i),
r += String(t[1].digits[0]);
return (n.isNeg ? "-" : "") + reverseStr(r)
}
function digitToHex(n) {
var t = "";
for (i = 0; i < 4; ++i)
t += hexToChar[n & 15],
n >>>= 4;
return reverseStr(t)
}
function biToHex(n) {
for (var i = "", r = biHighIndex(n), t = biHighIndex(n); t > -1; --t)
i += digitToHex(n.digits[t]);
return i
}
function charToHex(n) {
var t = 48
, u = t + 9
, i = 97
, f = i + 25
, r = 65;
return n >= t && n <= u ? n - t : n >= r && n <= 90 ? 10 + n - r : n >= i && n <= f ? 10 + n - i : 0
}
function hexToDigit(n) {
for (var t = 0, r = Math.min(n.length, 4), i = 0; i < r; ++i)
t <<= 4,
t |= charToHex(n.charCodeAt(i));
return t
}
function biFromHex(n) {
for (var i = new BigInt, u = n.length, t = u, r = 0; t > 0; t -= 4,
++r)
i.digits[r] = hexToDigit(n.substr(Math.max(t - 4, 0), Math.min(t, 4)));
return i
}
function biFromString(n, t) {
var f = n.charAt(0) == "-", e = f ? 1 : 0, i = new BigInt, r = new BigInt, u;
for (r.digits[0] = 1,
u = n.length - 1; u >= e; u--) {
var o = n.charCodeAt(u)
, s = charToHex(o)
, h = biMultiplyDigit(r, s);
i = biAdd(i, h);
r = biMultiplyDigit(r, t)
}
return i.isNeg = f,
i
}
function biDump(n) {
return (n.isNeg ? "-" : "") + n.digits.join(" ")
}
function biAdd(n, t) {
var r, u, f, i;
if (n.isNeg != t.isNeg)
t.isNeg = !t.isNeg,
r = biSubtract(n, t),
t.isNeg = !t.isNeg;
else {
for (r = new BigInt,
u = 0,
i = 0; i < n.digits.length; ++i)
f = n.digits[i] + t.digits[i] + u,
r.digits[i] = f % biRadix,
u = Number(f >= biRadix);
r.isNeg = n.isNeg
}
return r
}
function biSubtract(n, t) {
var r, f, u, i;
if (n.isNeg != t.isNeg)
t.isNeg = !t.isNeg,
r = biAdd(n, t),
t.isNeg = !t.isNeg;
else {
for (r = new BigInt,
u = 0,
i = 0; i < n.digits.length; ++i)
f = n.digits[i] - t.digits[i] + u,
r.digits[i] = f % biRadix,
r.digits[i] < 0 && (r.digits[i] += biRadix),
u = 0 - Number(f < 0);
if (u == -1) {
for (u = 0,
i = 0; i < n.digits.length; ++i)
f = 0 - r.digits[i] + u,
r.digits[i] = f % biRadix,
r.digits[i] < 0 && (r.digits[i] += biRadix),
u = 0 - Number(f < 0);
r.isNeg = !n.isNeg
} else
r.isNeg = n.isNeg
}
return r
}
function biHighIndex(n) {
for (var t = n.digits.length - 1; t > 0 && n.digits[t] == 0; )
--t;
return t
}
function biNumBits(n) {
for (var i = biHighIndex(n), r = n.digits[i], u = (i + 1) * bitsPerDigit, t = u; t > u - bitsPerDigit; --t) {
if ((r & 32768) != 0)
break;
r <<= 1
}
return t
}
function biMultiply(n, t) {
for (var i = new BigInt, u, o = biHighIndex(n), s = biHighIndex(t), e, f, r = 0; r <= s; ++r) {
for (u = 0,
f = r,
j = 0; j <= o; ++j,
++f)
e = i.digits[f] + n.digits[j] * t.digits[r] + u,
i.digits[f] = e & maxDigitVal,
u = e >>> biRadixBits;
i.digits[r + o + 1] = u
}
return i.isNeg = n.isNeg != t.isNeg,
i
}
function biMultiplyDigit(n, t) {
var u, r, f, i;
for (result = new BigInt,
u = biHighIndex(n),
r = 0,
i = 0; i <= u; ++i)
f = result.digits[i] + n.digits[i] * t + r,
result.digits[i] = f & maxDigitVal,
r = f >>> biRadixBits;
return result.digits[1 + u] = r,
result
}
function arrayCopy(n, t, i, r, u) {
for (var o = Math.min(t + u, n.length), f = t, e = r; f < o; ++f,
++e)
i[e] = n[f]
}
function biShiftLeft(n, t) {
var e = Math.floor(t / bitsPerDigit), i = new BigInt, u, o, r, f;
for (arrayCopy(n.digits, 0, i.digits, e, i.digits.length - e),
u = t % bitsPerDigit,
o = bitsPerDigit - u,
r = i.digits.length - 1,
f = r - 1; r > 0; --r,
--f)
i.digits[r] = i.digits[r] << u & maxDigitVal | (i.digits[f] & highBitMasks[u]) >>> o;
return i.digits[0] = i.digits[r] << u & maxDigitVal,
i.isNeg = n.isNeg,
i
}
function biShiftRight(n, t) {
var e = Math.floor(t / bitsPerDigit), i = new BigInt, u, o, r, f;
for (arrayCopy(n.digits, e, i.digits, 0, n.digits.length - e),
u = t % bitsPerDigit,
o = bitsPerDigit - u,
r = 0,
f = r + 1; r < i.digits.length - 1; ++r,
++f)
i.digits[r] = i.digits[r] >>> u | (i.digits[f] & lowBitMasks[u]) << o;
return i.digits[i.digits.length - 1] >>>= u,
i.isNeg = n.isNeg,
i
}
function biMultiplyByRadixPower(n, t) {
var i = new BigInt;
return arrayCopy(n.digits, 0, i.digits, t, i.digits.length - t),
i
}
function biDivideByRadixPower(n, t) {
var i = new BigInt;
return arrayCopy(n.digits, t, i.digits, 0, i.digits.length - t),
i
}
function biModuloByRadixPower(n, t) {
var i = new BigInt;
return arrayCopy(n.digits, 0, i.digits, 0, t),
i
}
function biCompare(n, t) {
if (n.isNeg != t.isNeg)
return 1 - 2 * Number(n.isNeg);
for (var i = n.digits.length - 1; i >= 0; --i)
if (n.digits[i] != t.digits[i])
return n.isNeg ? 1 - 2 * Number(n.digits[i] > t.digits[i]) : 1 - 2 * Number(n.digits[i] < t.digits[i]);
return 0
}
function biDivideModulo(n, t) {
var a = biNumBits(n), s = biNumBits(t), v = t.isNeg, r, i, u, e, h, o, f, y, p;
if (a < s)
return n.isNeg ? (r = biCopy(bigOne),
r.isNeg = !t.isNeg,
n.isNeg = !1,
t.isNeg = !1,
i = biSubtract(t, n),
n.isNeg = !0,
t.isNeg = v) : (r = new BigInt,
i = biCopy(n)),
[r, i];
for (r = new BigInt,
i = n,
u = Math.ceil(s / bitsPerDigit) - 1,
e = 0; t.digits[u] < biHalfRadix; )
t = biShiftLeft(t, 1),
++e,
++s,
u = Math.ceil(s / bitsPerDigit) - 1;
for (i = biShiftLeft(i, e),
a += e,
h = Math.ceil(a / bitsPerDigit) - 1,
o = biMultiplyByRadixPower(t, h - u); biCompare(i, o) != -1; )
++r.digits[h - u],
i = biSubtract(i, o);
for (f = h; f > u; --f) {
var c = f >= i.digits.length ? 0 : i.digits[f]
, w = f - 1 >= i.digits.length ? 0 : i.digits[f - 1]
, b = f - 2 >= i.digits.length ? 0 : i.digits[f - 2]
, l = u >= t.digits.length ? 0 : t.digits[u]
, k = u - 1 >= t.digits.length ? 0 : t.digits[u - 1];
for (r.digits[f - u - 1] = c == l ? maxDigitVal : Math.floor((c * biRadix + w) / l),
y = r.digits[f - u - 1] * (l * biRadix + k),
p = c * biRadixSquared + (w * biRadix + b); y > p; )
--r.digits[f - u - 1],
y = r.digits[f - u - 1] * (l * biRadix | k),
p = c * biRadix * biRadix + (w * biRadix + b);
o = biMultiplyByRadixPower(t, f - u - 1);
i = biSubtract(i, biMultiplyDigit(o, r.digits[f - u - 1]));
i.isNeg && (i = biAdd(i, o),
--r.digits[f - u - 1])
}
return i = biShiftRight(i, e),
r.isNeg = n.isNeg != v,
n.isNeg && (r = v ? biAdd(r, bigOne) : biSubtract(r, bigOne),
t = biShiftRight(t, e),
i = biSubtract(t, i)),
i.digits[0] == 0 && biHighIndex(i) == 0 && (i.isNeg = !1),
[r, i]
}
function biDivide(n, t) {
return biDivideModulo(n, t)[0]
}
function biModulo(n, t) {
return biDivideModulo(n, t)[1]
}
function biMultiplyMod(n, t, i) {
return biModulo(biMultiply(n, t), i)
}
function biPow(n, t) {
for (var r = bigOne, i = n; ; ) {
if ((t & 1) != 0 && (r = biMultiply(r, i)),
t >>= 1,
t == 0)
break;
i = biMultiply(i, i)
}
return r
}
function biPowMod(n, t, i) {
for (var f = bigOne, u = n, r = t; ; ) {
if ((r.digits[0] & 1) != 0 && (f = biMultiplyMod(f, u, i)),
r = biShiftRight(r, 1),
r.digits[0] == 0 && biHighIndex(r) == 0)
break;
u = biMultiplyMod(u, u, i)
}
return f
}
function BarrettMu(n) {
this.modulus = biCopy(n);
this.k = biHighIndex(this.modulus) + 1;
var t = new BigInt;
t.digits[2 * this.k] = 1;
this.mu = biDivide(t, this.modulus);
this.bkplus1 = new BigInt;
this.bkplus1.digits[this.k + 1] = 1;
this.modulo = BarrettMu_modulo;
this.multiplyMod = BarrettMu_multiplyMod;
this.powMod = BarrettMu_powMod
}
function BarrettMu_modulo(n) {
var r = biDivideByRadixPower(n, this.k - 1), u = biMultiply(r, this.mu), f = biDivideByRadixPower(u, this.k + 1), e = biModuloByRadixPower(n, this.k + 1), o = biMultiply(f, this.modulus), s = biModuloByRadixPower(o, this.k + 1), t = biSubtract(e, s), i;
for (t.isNeg && (t = biAdd(t, this.bkplus1)),
i = biCompare(t, this.modulus) >= 0; i; )
t = biSubtract(t, this.modulus),
i = biCompare(t, this.modulus) >= 0;
return t
}
function BarrettMu_multiplyMod(n, t) {
var i = biMultiply(n, t);
return this.modulo(i)
}
function BarrettMu_powMod(n, t) {
var u = new BigInt, r, i;
for (u.digits[0] = 1,
r = n,
i = t; ; ) {
if ((i.digits[0] & 1) != 0 && (u = this.multiplyMod(u, r)),
i = biShiftRight(i, 1),
i.digits[0] == 0 && biHighIndex(i) == 0)
break;
r = this.multiplyMod(r, r)
}
return u
}
function RSAKeyPair(n, t, i) {
this.e = biFromHex(n);
this.d = biFromHex(t);
this.m = biFromHex(i);
this.digitSize = 2 * biHighIndex(this.m) + 2;
this.chunkSize = this.digitSize - 11;
this.radix = 16;
this.barrett = new BarrettMu(this.m)
}
function twoDigit(n) {
return (n < 10 ? "0" : "") + String(n)
}
function encryptedString(n, t) {
var e, o, s, h, c, i, f, u, v, l, y;
if (n.chunkSize > n.digitSize - 11)
return "Error";
for (var a = [], p = t.length, r = 0; r < p; )
a[r] = t.charCodeAt(r),
r++;
for (e = a.length,
o = "",
r = 0; r < e; r += n.chunkSize) {
for (c = new BigInt,
s = 0,
f = r + n.chunkSize > e ? e % n.chunkSize : n.chunkSize,
u = [],
i = 0; i < f; i++)
u[i] = a[r + f - 1 - i];
for (u[f] = 0,
v = Math.max(8, n.digitSize - 3 - f),
i = 0; i < v; i++)
u[f + 1 + i] = Math.floor(Math.random() * 254) + 1;
for (u[n.digitSize - 2] = 2,
u[n.digitSize - 1] = 0,
h = 0; h < n.digitSize; ++s)
c.digits[s] = u[h++],
c.digits[s] += u[h++] << 8;
l = n.barrett.powMod(c, n.e);
y = n.radix == 16 ? biToHex(l) : biToString(l, n.radix);
o += y + " "
}
return o.substring(0, o.length - 1)
}
function decryptedString(n, t) {
for (var e = t.split(" "), i = "", r, u, o, f = 0; f < e.length; ++f)
for (o = n.radix == 16 ? biFromHex(e[f]) : biFromString(e[f], n.radix),
u = n.barrett.powMod(o, n.d),
r = 0; r <= biHighIndex(u); ++r)
i += String.fromCharCode(u.digits[r] & 255, u.digits[r] >> 8);
return i.charCodeAt(i.length - 1) == 0 && (i = i.substring(0, i.length - 1)),
i
}
var biRadixBase = 2, biRadixBits = 16, bitsPerDigit = biRadixBits, biRadix = 65536, biHalfRadix = biRadix >>> 1, biRadixSquared = biRadix * biRadix, maxDigitVal = biRadix - 1, maxInteger = 9999999999999998, maxDigits, ZERO_ARRAY, bigZero, bigOne, dpl10, lr10, hexatrigesimalToChar, hexToChar, highBitMasks, lowBitMasks;
setMaxDigits(20);
dpl10 = 15;
lr10 = biFromNumber(1e15);
hexatrigesimalToChar = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
hexToChar = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
highBitMasks = [0, 32768, 49152, 57344, 61440, 63488, 64512, 65024, 65280, 65408, 65472, 65504, 65520, 65528, 65532, 65534, 65535];
lowBitMasks = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535];
setMaxDigits(129);

function main (pwd){
setMaxDigits(129);
var key_to_encode = new RSAKeyPair("010001", "", "978C0A92D2173439707498F0944AA476B1B62595877DD6FA87F6E2AC6DCB3D0BF0B82857439C99B5091192BC134889DFF60C562EC54EFBA4FF2F9D55ADBCCEA4A2FBA80CB398ED501280A007C83AF30C3D1A142D6133C63012B90AB26AC60C898FB66EDC3192C3EC4FF66925A64003B72496099F4F09A9FB72A2CF9E4D770C41");
return encryptedString(key_to_encode, pwd);
}

encrypt.py

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
import execjs
import os

# 加载并编译 JavaScript 代码
try:
with open('rsa.js', 'r') as js:
js_code = js.read()
print("成功加载 JavaScript 代码。")
encrypt = execjs.compile(js_code)
print("成功编译 JavaScript 代码。")
except FileNotFoundError:
print("错误:未找到 'rsa.js' 文件。")
exit(1)
except Exception as e:
print(f"加载或编译 JavaScript 代码时出错:{e}")
exit(1)

# 加密函数
def encrypt_data(data):
try:
print(f"正在加密数据:{data[:30]}...") # 打印数据的前30个字符以避免泄露
encrypted = encrypt.call('main', data)
print("数据加密成功。")
return encrypted
except Exception as e:
print(f"加密数据时出错:{e}")
exit(1)

# 读取 data.txt 中的数据
input_file = 'data.txt'
output_file = 'encrypted_data.txt'

try:
with open(input_file, 'r') as f:
data = f.read().strip()
print(f"从 {input_file} 读取的数据:{data[:30]}...") # 打印数据的前30个字符
except FileNotFoundError:
print(f"错误:未找到 '{input_file}' 文件。")
exit(1)
except Exception as e:
print(f"读取文件 {input_file} 时出错:{e}")
exit(1)

# 执行加密
try:
encrypted_data = encrypt_data(data)
except Exception as e:
print(f"加密过程中出错:{e}")
exit(1)

# 将加密结果写入新文件
try:
with open(output_file, 'w') as f:
f.write(encrypted_data)
print(f"加密数据已写入 {output_file}。")
except Exception as e:
print(f"写入文件 {output_file} 时出错:{e}")
exit(1)

执行截图

image

image

image

下面讲讲遇到的几个坑,我也是初学者,相信第一次接触这个的师傅也都遇到过吧……

坑点一:库的安装

由于py脚本执行需要execjs库,一般都是 **pip install execjs **安装,网上的部分文章也是这样的命令

image

但实际情况无法使用这条命令安装成功,会报错

1
2
3
4
5
6
(py39) C:\Users\xxxxx\xxxxx>pip install execjs
Looking in indexes: http://mirrors.aliyun.com/pypi/simple/
ERROR: Could not find a version that satisfies the requirement execjs (from versions: none)
ERROR: No matching distribution found for execjs

(py39) C:\Users\xxxxx\xxxxx>

execjs 库在你使用的 PyPI 镜像源中不可用

使用这个命令,成功安装上了 pip install PyExecJS

1
pip install PyExecJS

坑点二:位数限制未配置导致程序卡死

第一写的脚本,一切正常,但是就是运行到加密数据时,程序卡死了,一直卡在加密数据的地方,无法执行完成和退出

强制退出程序的话是这样:

image

于是我就逐一排查问题:

py的语法是否正确

py的传参是否正确

js的语法是否正确

js的传参是否正确

execjs库是否兼容

其他环境是否正常

js本地再次调试运行(之所以放在最后排查,是因为之前本地所以鬼鬼调试运行过,成功了的)

结果就是在最后一步排查,找到了问题所在:

原来是我的main入口函数中,缺少了一个位数限制配置的代码

再次回到网站HTML源码看看细节:

image

就是这个: setMaxDigits(129);

1
2
3
4
5
function main (pwd){
setMaxDigits(129);
var key_to_encode = new RSAKeyPair("010001", "", "978C0A92D2173439707498F0944AA476B1B62595877DD6FA87F6E2AC6DCB3D0BF0B82857439C99B5091192BC134889DFF60C562EC54EFBA4FF2F9D55ADBCCEA4A2FBA80CB398ED501280A007C83AF30C3D1A142D6133C63012B90AB26AC60C898FB66EDC3192C3EC4FF66925A64003B72496099F4F09A9FB72A2CF9E4D770C41");
return encryptedString(key_to_encode, pwd);
}

在第一次的脚本中,我没有加这一行,因为之前我觉得这个应该没必要吧,就糊弄过去了没有管它,而且之前模拟还原加密过程时使用鬼鬼调试,没有加这一行代码,也没有出现问题,我就以为没问题了,结果后面运行py才发现存在问题

image

这一行是什么用处呢?

1
2
3
4
5
6
7
如果缺少 setMaxDigits(129); 这一行代码导致加密过程卡死,可能是因为加密库在处理大数运算时遇到了未配置的位数限制,造成了以下问题:

位数限制未配置:大数运算库(如 jsbn)通常有一个默认的最大位数限制。如果你的加密操作涉及到的数字位数超过了这个默认限制,而没有调用 setMaxDigits 来调整这个限制,库可能无法正确处理这些大数。这会导致计算无法完成,从而卡死。

库的内部实现:在一些大数库中,未设置足够大的位数可能会导致计算内部的缓冲区溢出或其他资源不足的问题。库内部可能会尝试使用默认的较小位数来处理大数运算,但当实际需求超出这个范围时,可能会导致无限等待或计算卡住。

加密算法的要求:RSA 加密需要处理相当大的数字(例如,2048 位或更大的密钥)。如果库未配置足够大的位数来处理这些加密需求,它可能会陷入无法处理的状态。setMaxDigits 确保库有足够的位数来处理 RSA 密钥的大小。

反正就是缺少这个位数限制配置的话,可能会导致计算密码时程序卡死

现在我们注释掉这一行配置,再看看运行加密脚本是什么情况?

image

image

程序果然卡死了,但试了几次发现这种卡死是有几率的,不一定会每次都卡死,但最好还是加上这一行配置吧

验证加密脚本的正确性

对比一下看看本地加密与网站端加密后的密文是否相似:

  • 网站端的

image

  • 本地的
1
1a6b927316a9eaee01d2956d44de329bc5c565badc88677fe50df825bb90d59be8afffcc0bfcca3b21c6e433e898a5a57e50940047269132e24c15e8532d6461bbc18e42dfd0f8e5b606c9423e6250eddd3592d2dbfd77d0e5c65182c74d4c22bf9ba6e8e77e4ea6dcbf70030bd3d510ed065ed65c14f5477f245dece4602736

嗯!形式上是一致的!不过最好的验证方式是,使用正确的账号密码,把密码使用本地的脚本加密,如果能登录成功,那么就说明加密脚本没问题!

于是果断使用手机号注册一个账号

image

修改密码,保存好新密码

image

输入正确的密码,登录,burp拦截住登录包

image

成功拦下登录包,丢包,不要放行,使得保持未登录状态image

把数据包发送到重放模块,也不要着急发送数据包

先使用之前我们写好的加密脚本encrypt.py,对我正确的密码进行加密,得到加密数据

image

ok,把加密后的密文与数据包中密码参数pwd替换,发包登录,如果登录成功,就说明我们的加密脚本完全没有问题,加密出的数据可被服务器端成功解密

忘了说了还需要过一个验证码

image

登录失败!?翻车了?白忙活了?

image

不会吧……(陷入沉思)

推测是对数据包做了防篡改,就是类似于sign签名的机制,使用某些参数存储用户的唯一会话,推测这种校验字段与数据包中的数据做了绑定,于是就达到了数据包防篡改的目的

看看cookie验证一下我的推测

这是未登录的cookie,就有这么多值

image

解释一下参数的作用(只是大概推测)

这是一个包含多个 Cookie 参数的示例。每个参数在 web 开发和分析中都有不同的用途。下面是这些参数的解释:

  1. global_cookie: 这是一个用户的全局 Cookie,通常用于跟踪用户的会话或存储全局状态信息。
  2. g_sourcepage: 该 Cookie 可能用于记录用户访问的页面来源或渠道。表示用户的来源页和设备类型(例如 PC)。
  3. __utmc: 这是 Google Analytics 的一个 Cookie,用于跟踪用户会话。它帮助 Google Analytics 了解用户在网站上的活动时间。
  4. city: 记录用户所在的城市。bj 表示北京。
  5. __utma: 这是另一个 Google Analytics 的 Cookie,用于记录用户的首次访问时间、上次访问时间和访问次数。
  6. __utmz: 该 Cookie 用于记录用户的来源信息,例如搜索引擎、广告活动或直接访问。表示用户的访问来源是通过 my.fang.com,且是通过推荐链接访问的。
  7. __utmt_t0__utmt_t1: 这些是 Google Analytics 的 Cookie,用于管理和限制请求速率,以避免过多的服务器负担。1 是其值。
  8. token: 用于存储用户的认证令牌或会话 ID。
  9. unique_cookie: 这是一个唯一标识符,用于唯一标识用户。
  10. __utmb: 这是 Google Analytics 的一个 Cookie,用于计算用户的会话持续时间。它与 __utmc 一起使用,帮助分析用户在网站上的活动。

反正分析了一下,就这两个参数很可疑

token:

  • 作用: 通常,token 用于存储用户的会话标识符或认证令牌。它可以是一个生成的唯一值,用于标识用户的会话。虽然它本身不直接用于签名或校验数据包是否被篡改,但它确实在会话管理中发挥重要作用。
  • 安全性: 令牌通常是随机生成的,且与用户会话相关联。如果令牌被篡改或伪造,服务器可以检测到并拒绝请求。

如果我的加密脚本没有错误的话,就是这里做了数据包放窜改校验,推测应该是每一个数据包随机生成一个token,具备类似于sign值的功能,把当前数据包的token与POST的数据做了一个绑定,服务器端只通过与token进行了绑定的数据,其他数据(比如做了改包操作),就被视为不合法的数据,无法被解密出来,也就无法成功登录

over