小蓝本消息收集之生成h_sign签名 (二)

admin 2023年9月5日12:08:18评论39 views字数 6152阅读20分30秒阅读模式

小蓝本消息收集之生成h_sign签名 (二)

U51旗下的《小蓝本》在查询公司时,在新媒体的栏目中,包含该公司旗下的小程序和公众号信息。利用此功能,可对目标单位进行小程序信息收集。

小蓝本消息收集之生成h_sign签名 (二)

一、调试分析

在请求的报文中url中,有h_sign对整个请求连接进行签名。签名不正确和没有h_sign参数的情况下,将不能正常访问小蓝本的每个接口。

小蓝本消息收集之生成h_sign签名 (二)

签名不正确的情况:

小蓝本消息收集之生成h_sign签名 (二)

h_sign 的签名逻辑在这份代码里,整个js文件被obfuscator 混淆,不会配合AST反混淆,所以直接逆h_sign的算法更无从说起。

小蓝本消息收集之生成h_sign签名 (二)

全局搜索:_0x295807[_0x1f594e 断点位置:

_0x4af221[0x1] = _0x295807[_0x1f594e(0x4a0, 0x395, 0x418, 0x3b6, 0x282) + 'n'](_0x49a5d7)[_0x29667a(0x2ba, 0xb6, 0x152, 0x20e, 0x35c)];
小蓝本消息收集之生成h_sign签名 (二)

调试代码,让流程走到这里,粗略过一遍sign的签名过程。

二、node补环境

下载签名函数所在的js文件

wget https://h5.u51.com/web.u51.com/storage/static-configs/xlbsiren0906.js

在这个文件的底部直接添加:

window.sign = _0x295807['sign']
a = {"url":"/123/456/test","method":"Get"}
b = window.sign(a).url
console.log(b)

直接在node中运行此js文件,根据报错信息补环境,缺啥就补啥。

小蓝本消息收集之生成h_sign签名 (二)

window是浏览器环境下的全局变量,在nodejs中是没有的,暂时先在xlbsiren0906.js的头部添加:

window = {};

再次运行:

小蓝本消息收集之生成h_sign签名 (二)

第一次看到这个报错,我也不知道是啥情况,在浏览器中调试一遍看看,复制报错中的关键词,去浏览器中搜索源码,打断点调试。

小蓝本消息收集之生成h_sign签名 (二)

在这里下一个断点,刷新页面,让程序执行到这里。

小蓝本消息收集之生成h_sign签名 (二)
_0x4ff10f[(_0x2f4bf8(-0x581, -0x772, -0x530, -0x624, -0x626)) + (_0x2f4bf8(-0x638, -0x4c4, -0x687, -0x5a5, -0x6d0))](_0x2f4bf8(-0x736, -0x620, -0x700, -0x5f3, -0x714) + _0x198eda(-0x435, -0x460, -0x423, -0x4c7, -0x5b5) + _0x198eda(-0x6a3, -0x6ed, -0x528, -0x67e, -0x5ac) + _0x198eda(-0x4d4, -0x457, -0x529, -0x556, -0x48e) + _0x2f4bf8(-0x55c, -0x5be, -0x3f7, -0x4eb, -0x3bf) + _0x26acd4(-0x6f0, -0x5c9, -0x5d2, -0x5a7, -0x6ac) + _0x80bb0f(-0x505, -0x49b, -0x590, -0x556, -0x5d5) + _0x26acd4(-0x4da, -0x58d, -0x5cf, -0x4f8, -0x572));

复制到控制台执行一下看看情况

/^([^ ]+( +[^ ]+)+)+[^ ]}/

在控制台挨个执行,逐步还原一下这里被混淆的代码

(_0x2f4bf8(-0x581, -0x772, -0x530, -0x624, -0x626)) + (_0x2f4bf8(-0x638, -0x4c4, -0x687, -0x5a5, -0x6d0)) ==> RegExp

小蓝本消息收集之生成h_sign签名 (二)

将还原出来的替换原来的代码,在执行一下看看是否与之前相等。

_0x2f4bf8(-0x736, -0x620, -0x700, -0x5f3, -0x714) + _0x198eda(-0x435, -0x460, -0x423, -0x4c7, -0x5b5) + _0x198eda(-0x6a3, -0x6ed, -0x528, -0x67e, -0x5ac) + _0x198eda(-0x4d4, -0x457, -0x529, -0x556, -0x48e) + _0x2f4bf8(-0x55c, -0x5be, -0x3f7, -0x4eb, -0x3bf) + _0x26acd4(-0x6f0, -0x5c9, -0x5d2, -0x5a7, -0x6ac) + _0x80bb0f(-0x505, -0x49b, -0x590, -0x556, -0x5d5) + _0x26acd4(-0x4da, -0x58d, -0x5cf, -0x4f8, -0x572)
 ==> '^([^ ]+( +[^ ]+)+)+[^ ]}'

这段的运算结果是:^([^ ]+( +[^ ]+)+)+[^ ]} 最后可以整个还原成:

new _0x4ff10f['RegExp']('^([^ ]+( +[^ ]+)+)+[^ ]}');

在源文件中替换

小蓝本消息收集之生成h_sign签名 (二)

继续在node执行

小蓝本消息收集之生成h_sign签名 (二)

这个_0x295807的具体分析请见上一篇文章。将_0x295807导出为全局试试。

window._0x295807={}

继续在node中执行:

小蓝本消息收集之生成h_sign签名 (二)

补navigator,在文件首部加入:

navigator = {};

在node接着跑

小蓝本消息收集之生成h_sign签名 (二)

在浏览器中调试,误打误撞,将:

window[_0x418ec3]||window[_0x228d87(0x842,0x78d,0x6aa,0x7a5,0x8dd)+_0x987442(0x6e3,0x745,0x628,0x779,0x5ee)+'nt'][_0x16ee8a(0x710,0x78d,0x751,0x77c,0x717)+_0x228d87(0x861,0x745,0x5fe,0x6dd,0x66c)+_0x11ef68(0x7c5,0x724,0x7eb,0x70c,0x63e)+_0x2dca6a(0x989,0x892,0x79c,0x8ec,0x8f1)+_0x16ee8a(0x951,0x865,0x8b1,0x98d,0x7ea)][_0x11ef68(0x93b,0x8f9,0x97a,0x849,0xa13)+_0x11ef68(0x9fa,0x986,0x947,0xa2e,0xac8)+_0x228d87(0x62b,0x77f,0x82f,0x766,0x809)+_0x228d87(0x8e8,0x872,0x89c,0x86d,0x775)](_0x418ec3)

还原为:

window['document']['documentElement']['getAttribute'](_0x418ec3)

执行的结果为:

null

绕了一圈,直接将这里置为:false

小蓝本消息收集之生成h_sign签名 (二)

接着在node中运行,现在能正常生产 sign 签名了。验签失败!

小蓝本消息收集之生成h_sign签名 (二)

可惜,在burp重发实验,这次生成的签名错误,验签不通过。

回到浏览器,重新调试生成sign签名所在的函数:

void 0x0 !== _0x295807 && (_0x295807[_0x582a37(-0x1a0, -0x2d2, -0x23f, -0x270, -0x334) + 'n'] = function(_0x23cbc8) {

在这里下个断点,调试发现,在下图中的if里面有一次判断,在最后return时还有一个三目运算:

(_0x585dc5[_0x16c6cc(-0x119, -0x1aa, -0x109, -0x226, -0x20e) + _0x16c6cc(-0x285, -0x4b1, -0x3a0, -0x43c, -0x35a) + 't'][_0x51fec2(-0x320, -0x2bd, -0x143, -0x167, -0x1c5) + _0x48657c(-0x3aa, -0x137, -0x13e, -0x123, -0x24b) + _0x48657c(-0x33b, -0x354, -0x2f9, -0x238, -0x34a)](_0x10aa71[_0x51fec2(-0x31e, -0x22a, -0xbc, -0x1fc, -0x1e6) + 'it']('?')[0x0], 0x64) ? _0x23cbc8[_0x48657c(-0x311, -0x316, -0x36a, -0x321, -0x399)] = _0x3d54d4[_0x51fec2(-0xf5, -0x2f3, -0x12b, -0x314, -0x20e) + _0x5c833f(-0x499, -0x427, -0x410, -0x46a, -0x35a) + 't'][_0x16c6cc(-0x21d, -0x2a2, -0x163, -0x21f, -0x1f1) + 'n'](_0x10aa71, _0x367d77) : _0x23cbc8[_0x48779e(-0x2bc, -0x2e9, -0x3ee, -0x3fb, -0x399)] = _0x3d54d4[_0x16c6cc(-0x2d6, -0x2a2, -0x1f4, -0x31c, -0x20e) + _0x48779e(-0x413, -0x402, -0x2ab, -0x292, -0x35a) + 't'][_0x48779e(-0x296, -0x413, -0x495, -0x2d6, -0x3c3) + _0x48779e(-0x343, -0x3fd, -0x29a, -0x409, -0x363) + 'gn'](_0x10aa71, _0x367d77)),_0x23cbc8;
小蓝本消息收集之生成h_sign签名 (二)
小蓝本消息收集之生成h_sign签名 (二)

(_0x585dc5['default']['getDetect'](_0x10aa71['split']('?')[0x0], 0x64)) 这个表示式是在检测环境,在浏览器中执行为:true,在node中就为false,这就是为什么前面直接在node中执行计算出的sign签名不正确的原因。最后,将这段代码直接替换为:(true ? _0x23cbc8['url'] = _0x3d54d4['default']['sign'](_0x10aa71, _0x367d77) : _0x23cbc8['url'] = _0x3d54d4['default']['fakesign'](_0x10aa71, _0x367d77)),_0x23cbc8

或者_0x23cbc8['url'] = _0x3d54d4['default']['sign'](_0x10aa71, _0x367d77)),_0x23cbc8

中间的环境检测代码就懒得去补全了,就让这个代码调用_0x3d54d4['default']['sign']

在burp确认h_sign签名是否有效,现在可以正确获取到数据了。

小蓝本消息收集之生成h_sign签名 (二)

三、工程化利用

该脚本用到了BestToYou 大佬们开发的BestV8 来补环境。

项目地址:GitHub - BestToYou/bestV8_release: 一个可以跑js的逆向工具。

import sys
from ctypes import *

class Sign:
     def __init__(self) -> None:
          self.cur = None
          if sys.platform == "darwin":
                self.cur = cdll.LoadLibrary("./src/bestV8_mac_intel.dylib")
          elif sys.platform == "linux":
                self.cur = cdll.LoadLibrary("./src/bestV8_x64.so")
          elif sys.platform == "win32":
                self.cur = cdll.LoadLibrary("./src/bestV8_win64.dll")
          else:
                raise Exception("unknown systerm!")
          self._jsfile = self._get_jsfile()

     def _get_encrypt_value(self,data):
          result = bytes(20000)
          self.cur.runJs.argtypes = (c_char_p, c_char_p)
          for x in range(1):
               self.cur.runJs(create_string_buffer(data.encode('utf8')), result)
               return result.rstrip(b"x00").decode('utf-8')
     def _get_jsfile(self):
           try:
              data = open("./src/xlbsiren0906.js","r").read()
              return data
           except Exception as e:
                print(e)   
     def sign(self,url):
          jsfile = self._jsfile
          exec_jsfile = jsfile.replace("_kqsec_r1is_", url, 1)
          return self._get_encrypt_value(exec_jsfile)



if __name__ == "__main__":
     url = "/api.xiaolanben.com/xlb-gateway/blue-book/company/companyData?eid=q072badde03989d63245bcda8e97c0ad5&page=0&type=newMedias&pageSize=100"
     test = Sign()
     a = test.sign(url)
     print("===start===n"+"sign: "+a+"n====end====")
小蓝本消息收集之生成h_sign签名 (二)

代码请见:https://github.com/r1is/xiaolanben_h_sign/tree/main/lib


小蓝本消息收集之生成h_sign签名 (二)


原文始发于微信公众号(KQsec):小蓝本消息收集之生成h_sign签名 (二)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年9月5日12:08:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   小蓝本消息收集之生成h_sign签名 (二)http://cn-sec.com/archives/2007048.html

发表评论

匿名网友 填写信息