某天,我大哥让我帮忙弄个设备的网页爆破脚本,说是前端加密了,算法看不懂是啥
于是打开网页瞅瞅(不贴登录界面图了,省的得罪人)
先查看网页源代码
一个简单的form表单,126行
仅一个submit按钮,91行
form也没有额外的提交事件
F12打开控制台,看看提交按钮的监听事件
有一个submit事件,点击链接跳转到login.js
又调用了evtMainAuth()
函数,Ctrl+F
搜索login.js
中的evtMainAuth
在265
、266行
对maUser
和maPass
进行了处理,来看下这两个对象是如何定义的
第7行
定义了变量
132
、133行
进行了赋值,返回的是name值为a
和p
的input框,就是登录页面的用户名和密码框
再来看加密函数
用户名仅做了去空字符处理,密码使用了$.fn.en
进行处理,当前页面没有找到这个函数的定义,直接在266行
打断点,然后登录一次
然后点击控制台右侧的进入函数内部(或者直接F11)
先是进入了Jquery
的val()
函数,这个不管
然后来到了jquery.plugin.js
中
找到了fn.en
的定义
d.fn.en = function (a) {
f = l();
return 0 >= this.length && "string" === d.type(a) ? 0 >= a.length ? "" : h(a) : this.each(function () {
var b = d(this), a = b.val(), c;
0 >= a.length || (c = b.data("v"),
"string" === d.type(c) && 0 < c.length && (b.val(c),
b.removeData("v")),
c = h(a),
b.val(c),
b.data("v", a),
b.one("input propertychange", k))
})
}
其中的函数l()
是
function l() {
for (var a = 0; 0 >= a || 255 <= a;)
a = Math.floor(255 * Math.random());
return a
}
函数h()
是
function g(a) {
var b = "";
16 > a && (b = "0");
return b + a.toString(16)
}
function h(a) {
for (var b = [g(f)], e, c = 0, d = a.length; c < d; ++c)
e = a.charCodeAt(c),
255 >= e ? b.push(g(e ^ f)) : (b.push(g(Math.floor(e / 256) ^ f)),
b.push(g(e % 256 ^ f)));
return b.join("").toUpperCase()
}
开始分析加密算法
先看fn.en
函数
第1行
将l()
函数的返回值赋值给定义的变量f
l()
函数代码如下
-
Math.random()
是返回一个0<= number <1
的随机数 -
Math.floor()
是返回小于等于给定数字的最大整数
也就是说,l()
返回了一个0<= number < 255
的随机数
接着看fn.en
,接下来是一个return
语句,但用到了三目运算,为了方便阅读,这里将代码进行格式化
当this.length
为0且a
的变量类型为string
时,返回8~16行
的三目运算结果,否则,返回20~29行
的函数
这里在控制台看一下
-
this
在函数中为函数所属的对象,就是fn
,过程中并没有见到对fn
的length
进行修改,所以恒为0 -
a
是传入的密码,肯定为string
类型
故返回值为8~16行
的三目运算结果
再来看8~16行
的表达式
当a
(传入的密码)长度为0时,返回空字符串,否则返回h(a)
的结果
h()
就是加密的具体函数了,分析一下h()
函数
先看循环的步数,起始是c=0
,结束条件是c<d(a.length)
,每步步长是++c
也就是对a
字符串进行遍历
b
的初始值是[g(f)]
,看看g()
函数
-
当
a
小于16
时,返回的是0
+a的16进制字符串
,例如a=13,返回的是”0d” -
当
a
大于等于16
时,返回的是a的16进制字符串
,例如a=18,返回的是”12”
g
函数也就是将10进制转换为16进制字符串
数组b的初始值也就是[f的16进制字符串],f取值范围是0<=f<255,也就是初始值为”00”至”ff”
e
变量每步会执行e = a.charCodeAt(c)
,也就是遍历获取a
字符串的每个字符的ASCII值
然后进行一个三目运算,e<=255
时,b
数组添加g(e^f)
,否则添加g(Math.floor((e/256)^f)
X^Y
,是按位异或,每一个对应的位,两个不相同返回1,相同则返回0
例如
200^255
200 =1100 1000
255 =1111 1111
0011 0111 =
55 =
55^100
55 =0011 0111
100 =0110 0100
0101 0011 =
83 =
再回到e
,当e>255
的时候,就超出了ASCII的范围,属于UNICODE字符串了,先e/256
再去按位异或f
,再向b
添加e
对256取余后和f
的按位异或结果
当e<=255
,在ASCII时(a-zA-Z0-9
特殊符号都在其中了),仅需正常返回g(e^f)
即可
所以算法流程如图
搞清楚算法后,开始编写加密及解密脚本
对了,还有个数学知识,a^b=c
,那么c^b=a
加密脚本
#!/usr/env python
# author Wax
import random
import sys
def getKey():
return int(255 * random.random())
def octToHexStr(number):
if number < 16:
return '0' + str(hex(number))[2:]
return str(hex(number))[2:]
def en(data, key):
enData = ""
if data == "":
return enData
if key == '':
key = getKey()
enData += octToHexStr(key)
for chr in data:
e = ord(chr)
if e <= 255:
enData += octToHexStr(e^key)
else:
enData += octToHexStr(int(e/256) ^ key)
enData += octToHexStr(e % 256 ^ key)
return enData.upper()
if __name__ == '__main__':
if len(sys.argv) > 1 and len(sys.argv) <=3:
data = sys.argv[1].strip()
key = ''
if sys.argv[2]:
key = int(sys.argv[2])
print('data:'+data)
print('key :'+str(key))
print(en(data, key))
else:
print('usage:')
print('tpython encrypt.py data')
print('tpython encrypt.py data key_oct')
解密脚本
#!/usr/env python
# author Wax
import sys
def de(enData):
key = HexStrToOct(enData[0:2])
enData = enData[2:]
data = ""
while len(enData)/2 > 0:
chrHexStr = enData[0:2]
data += chr(HexStrToOct(chrHexStr)^key)
enData = enData[2:]
return data
def HexStrToOct(hexStr):
return int.from_bytes(bytes.fromhex(hexStr))
if __name__ == '__main__':
if len(sys.argv) == 2:
enData = sys.argv[1]
print(de(enData))
else:
print('usage:')
print('tpython decrypt.py data')
测试一下,先登录一次,密码输入的是12345
加密结果是8DBCBFBEB9B8
前两位8D
就是生成的随机数,十进制是141
测试加解密脚本
ok~
我大哥可以愉快的玩耍了
这里详细分析过程只是为了演示前端加密的分析,实际中可以借助AI的力量帮你分析,效率杠杠滴
原文始发于微信公众号(雁行安全团队):某安全设备前端加解密爆破案例
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论