某服务器平台sm系列算法分析

VSole2022-12-28 13:56:20

样品网址:aHR0cHM6Ly9mdXd1Lm5oc2EuZ292LmNuL25hdGlvbmFsSGFsbFN0LyMvc2VhcmNoL21lZGljYWw/Y29kZT05MDAwMCZmbGFnPWZhbHNlJmdiRmxhZz10cnVl

打开网站后,需要的就是中间显示的数据

分析数据来自于哪个接口就跳过了,因为不重要,这里直接说结果

其中数据来自于【queryFixedHospital】这个接口,本次要分析的就是请求头中所有【x-tif】开头的参数,以及请求体中的【encData】和【signData】的生成算法

然后随便搜索请求体中的【encData】或者【signData】,都可以直接定位到【app.1654997618917.js】这个js文件

这里可以直接找到所有参数的生成的地方,好像比较顺利,接着从上往下开始分析

【paasid】是定值,【timestamp】是当前时间戳,【nonce】是8位随机值,这三个都非常容易看出来,而【signature】就是【s(g)】的结果,g就是时间戳拼接随机值再拼接时间戳,s就是sha256函数。请求头的参数非常容易,接下来看看请求体的参数。

在signData函数内部下一个断点

其中比较重要的是【v(i)】的函数,这里生成了一段字符串来计算签名

这个函数和查询参数编码的功能类似,除了data参数,并且在最后拼接了一个定值字符串,这里用python进行简单的复现

 复制代码 隐藏代码
def v(e):    t = []
    for n in e:
        if n == 'data':
            data = e[n].copy()
            for each in e[n]:
                if not data[each]:
                    del data[each]
                else:
                    data[each] = str(data[each])
            t.append(n + '=' + json.dumps(data, separators=(',', ':')))
        else:
            t.append(n + '=' + str(e[n]))
    t.append('key=NMVFVILMKT13GEMD3BKPKCTBOQBPZR2P')
    return '&'.join(t)

获取到这段字符串后继续往下走,就进入到了【doSignature】函数

这里的第一个参数就是前面的字符串,第二个参数就是私钥

来到这里有一个判断,因为前面传入的hash参数恒为真,所以要先对签名的内容计算hash,计算hash用到的是【y】函数,网上查看来源

这里可以看到,【y】函数就是sm3算法。继续进入到【y】函数分析

可以看到这里更新了两次数据,相当于是计算这两个数据的hash,首先是r参数,这个是【getZ】函数的返回值。另一个是a参数,就是前面传入的字符串,这个前面已经分析了,那么继续进入到【getZ】函数查看

这里又是一个sm3算法,这次更新的参数就比较多了,包括一个【1234567812345678】的一个固定值,以及sm2算法的初始化ecc表,还有传入的私钥。这里已经可以发现,所有的参数其实都是定值(私钥一般不改的情况下),那么这里的返回值也是一个固定的值【fde9a74125ca149ca75f4c2ccdaeed3e7d0b4b8c0f2c9e35530b9fe9a3ba1233】,代码中可以写成固定值,这里只是说明【getZ】函数的算法,用python还原getZ函数

 复制代码 隐藏代码
def getZ(crypto):    sign_data = bytes()
    n = '1234567812345678'.encode()
    sign_data += bytes([0, 8 * len(n)])
    sign_data += n
    sign_data += bytes.fromhex(crypto.ecc_table['a'])
    sign_data += bytes.fromhex(crypto.ecc_table['b'])
    sign_data += bytes.fromhex(crypto.ecc_table['g'])
    sign_data += bytes.fromhex(crypto.public_key[2:])
    return bytes.fromhex(sm3.sm3_hash(list(sign_data)))
    # 可以写成固定值    # return bytes.fromhex('fde9a74125ca149ca75f4c2ccdaeed3e7d0b4b8c0f2c9e35530b9fe9a3ba1233')

那么将【getZ】的返回值和前面的字符串一起计算sm3,就得到了消息hash

接着将消息hash计算sm2签名,就得到了【signData】了,最后分析【encData】参数。

【encData】这里固定传入了sm4,那么必定走sm4的分支,进入函数继续分析

这个函数比较短,主要是一个b函数,一个w函数,其中b函数是用来计算一个密钥

用python还原也比较简单

 复制代码 隐藏代码
def b(e, t):    crypto = sm4.CryptSM4()
    crypto.set_key(e[:16].encode(), sm4.SM4_ENCRYPT)
    return crypto.crypt_ecb(t.encode()).hex().upper()[:16]

拿到密钥后,直接使用sm4算法加密就可以得到【encData】了,现在所有参数都已经能够获取了,就可以发送请求了。

不过请求的响应也是加密的,幸好的是解密就一个sm4算法,key和前面的是一样的,那么直接解密就可以了,完整代码

 复制代码 隐藏代码
import requests_htmlimport randomimport timeimport jsonimport base64from Crypto.Hash import SHA256from gmssl import sm2, sm3, sm4, func
publicKey = base64.b64decode("BEKaw3Qtc31LG/hTPHFPlriKuAn/nzTWl8LiRxLw4iQiSUIyuglptFxNkdCiNXcXvkqTH79Rh/A2sEFU6hjeK3k=".encode()).hex()
privateKey = base64.b64decode("AJxKNdmspMaPGj+onJNoQ0cgWk2E3CYFWKBJhpcJrAtC".encode()).hex()
appSecret = 'NMVFVILMKT13GEMD3BKPKCTBOQBPZR2P'appCode = 'T98HPCGN5ZVVQBS8LZQNOAEXVI9GYHKQ'def main():    requests = requests_html.HTMLSession()
    key = b(appCode, appSecret).encode()
    s = str(int(time.time()))
    c = ''.join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZzbcdefghijklmnopqrstuvwxyz0123456789", k=8))
    headers = {
        'x-tif-paasid': 'undefined',
        '"x-tif-timestamp': s,
        'x-tif-nonce': c,
        '"x-tif-signature': SHA256.new((s + c + s).encode()).hexdigest(),
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'    }
    data = {
        'appCode': appCode,
        'data': {
            'addr': '',
            'medinsLvCode': '',
            'medinsName': '',
            'medinsTypeCode': '',
            'openElec': '',
            'pageNum': 1,
            'pageSize': 10,
            'regnCode': '110000'        },
        'encType': 'SM4',
        'signType': 'SM2',
        'timestamp': int(s),
        'version': '1.0.0'    }
    crypto = sm2.CryptSM2(privateKey, publicKey)
    data['signData'] = base64.b64encode(bytes.fromhex(crypto.sign(bytes.fromhex(sm3.sm3_hash(list(getZ(crypto) + v(data).encode()))), func.random_hex(crypto.para_len)))).decode()
    crypto = sm4.CryptSM4()
    crypto.set_key(key, sm4.SM4_ENCRYPT)
    data['data'] = {
        'encData': crypto.crypt_ecb(json.dumps(data['data']).encode()).hex().upper()
    }
    data = {
        'data': data
    }
    response = requests.post('https://fuwu.nhsa.gov.cn/ebus/fuwu/api/nthl/api/CommQuery/queryFixedHospital', json=data, headers=headers).json()
    crypto = sm4.CryptSM4()
    crypto.set_key(key, sm4.SM4_DECRYPT)
    data = json.loads(crypto.crypt_ecb(bytes.fromhex(response['data']['data']['encData'])).decode())
    print(data)def getZ(crypto):    sign_data = bytes()
    n = '1234567812345678'.encode()
    sign_data += bytes([0, 8 * len(n)])
    sign_data += n
    sign_data += bytes.fromhex(crypto.ecc_table['a'])
    sign_data += bytes.fromhex(crypto.ecc_table['b'])
    sign_data += bytes.fromhex(crypto.ecc_table['g'])
    sign_data += bytes.fromhex(crypto.public_key[2:])
    return bytes.fromhex(sm3.sm3_hash(list(sign_data)))
    # 可以写成固定值    # return bytes.fromhex('fde9a74125ca149ca75f4c2ccdaeed3e7d0b4b8c0f2c9e35530b9fe9a3ba1233')def v(e):    t = []
    for n in e:
        if n == 'data':
            data = e[n].copy()
            for each in e[n]:
                if not data[each]:
                    del data[each]
                else:
                    data[each] = str(data[each])
            t.append(n + '=' + json.dumps(data, separators=(',', ':')))
        else:
            t.append(n + '=' + str(e[n]))
    t.append('key=' + appSecret)
    return '&'.join(t)def b(e, t):    crypto = sm4.CryptSM4()
    crypto.set_key(e[:16].encode(), sm4.SM4_ENCRYPT)
    return crypto.crypt_ecb(t.encode()).hex().upper()[:16]if __name__ == '__main__':
    main()

成功获取到结果,完成!!!

datasign函数
本作品采用《CC 协议》,转载必须注明作者和本文链接
根据代码得出 sb的值为页码+时间戳 "page=21652931584"。再次hook Sign函数查看参数和返回值并与fiddler对比。此时已经可以断定,这个Sign就是翻页的加密函数 。我们用python实现一下。此时直接调用sign生成加密参数,抓取100页的值相加得出flag:#!
初衷是刷抖音太多,发现不能在点赞过的视频列表中直接搜索,就想自己实现下,把这个过程做了下记录,当学习笔记了,纯技术交流用。
请求头的参数非常容易,接下来看看请求体的参数。在signData函数内部下一个断点其中比较重要的是的函数,这里生成了一段字符串来计算签名这个函数和查询参数编码的功能类似,除了data参数,并且在最后拼接了一个定值字符串,这里用python进行简单的复现 复制代码 隐藏代码。拿到密钥后,直接使用sm4算法加密就可以得到了,现在所有参数都已经能够获取了,就可以发送请求了。
意料之中一大堆参数,反复几次总结需分析的参数应该为以下几个:X-Sign、wToken、X-Ca-Signature、X-Access-Token、X-Ca-Timestamp. 跟进得:public static Map z { String str; String a2 = wo3.a; HashMap hashMap = new HashMap(); hashMap.put; hashMap.put; hashMap.put; if (!TextUtils.isEmpty) { hashMap.put; no3.a; } hashMap.put("Authorization", StringUtils.isEmpty(it3.g()) ?= '\t') || charAt >= 127) { str2.replace; } } hashMap.put; hashMap.put("X-ConnectionType", y
js逆向一直没有相关了解,虽然目前渗透遇见的不是很多,大多数遇见的要么不加密,要么无法实现其加密流程,不过最近看到了一个较为简单的站点正好能够逆向出来,就做了简单记录。本文旨在介绍js逆向的一些基础思路,希望能对初学js前端逆向的师傅有所帮助。
日常渗透测试中,在进行数据包的重放或者是篡改数据包时,会碰到一些存在数据加密或加签的站点,这时我们就得寻找加签或者加密的算法,而这寻找过程往往有一定难度。 总的来讲,对于数据解密或者加签破解的难度:app>web≥wxapp,同时api接口都是相同的,为降低不必要的挖洞难度,可将目光放到微信小程序上。本次就是以一个微信小程序站点开展的测试。
Ida的trace功能,这种方法本身是非常好的一种方法,因为大部分的情况下这种方法更有效也更简单,但目前这种方式已知存在一些问题导致这种方式的使用受限。①慢,ida trace的方式会很慢,所以一般都是在缩小范围后再使用这种trace的方式来操作。
准备◆iOS 12.5.5◆frida 14.0.0◆ipa 5rG96L2m5LmL5a62 11.33.5抓包1.抓包使用 charles,请自行安装并配置证书。使用假账密测试抓包 123456 / 123456,能够抓包成功。分析1、登录页面需要输入3个信息,分别是账号 / 密码 / 验证码,对应字段 logincode / userpwd / validcode。
无意中看到ch1ng师傅的文章觉得很有趣,不得不感叹师傅太厉害了,但我一看那长篇的函数总觉得会有更骚的东西,所幸还真的有,借此机会就发出来一探究竟,同时也不得不感慨下RFC文档的妙处,当然本文针对的技术也仅仅只是在流量层面上waf的绕过。Pre很神奇对吧,当然这不是终点,接下来我们就来一探究竟。前置这里简单说一下师傅的思路部署与处理上传war的servlet是?
MRCTF2022 stuuuuub 题解
2023-02-07 10:15:04
Overview学了这么一段时间的Android,难得见到的一道比较对口的逆向题。e.c()通过执行which su命令后读取输出来检查是否有su文件。读取res.dat文件后调用了decodeSo函数进行解密存放在应用的数据目录下的libnative.so,而decodeSo是libstub.so里的native函数。但是在libstub.so里却没有直接找到decodeSo函数,因此应该是JNI_OnLoad里动态注册的。decode String另外libstub.so使用了Ollvm的字符串加密和控制流平坦化。这里参考官方给的WP中使用了AndroidNativeEmu框架。
VSole
网络安全专家