记一次Java Servlet实战审计

0x00 背景

某次渗透中,遇到个jsp的站,弱密码进到后台以后,却发现功能很少,并不好搞,简单排查后并无突破,然而老大给的要求是尽快搞到shell。。。
一番梳理后,我的想法是:要么通过漏洞拿下后台,要么搞到源码直接白盒开冲。白盒的话很香,对审计能力提升也大,所以可以按下面几步来试试:

  • 目录扫描。使用dirbuster的字典directory-list-2.3-medium.txt指定后缀jsp来扫,工具就随便用个顺手的,dirseach

  • 网盘泄露。这套系统并不开源,估计厂家发版的时候放了不少在网盘,简单一搜就有结果了。在网盘搜索引擎上,搜到了厂家安装包,但似乎是PE文件,还得安装,且不清楚是否有混淆/加密,暂搁置。

内心OS:这年头——可不敢乱点。

  • Github、gitlab泄露。尝试了多个关键词,均无果

  • Fofa找同类型的站。这个无需多说,不管是发版的时候没有把备份文件删除,还是运维的疏忽大意,都是很容易被发现的。同时,在fofa上,用favicon.ico,或是用title来搜,准确率都相当惊人


那工具方面,首先是选用了broken5师傅的https://github.com/broken5/WebAliveScan,1024个线程猛冲之后,却并无发现。。。
考虑是不是字典不够牛,接着使用dirsearch自带的字典(约17,000条),

# 只要字典大没有拿不下
python3 dirsearch3.py -e "jsp" -l ip_port.txt -t 50 --plain-text-report=ip_port_DirScan.txt  -q

跑目标列表花了一个上午,终于有了收获——web.rar,香!
天黑了,打开IDEA!天亮了,关闭IDEA。

我发现jsp的代码虽然不难懂,可基础知识不牢,代码审起来简直让人打脑壳,于是有了下面这第一章。

0x01 Servlet简介


正常情况下的目录结构,长下面这样

exampleApp
└─images
└─WEB-INF                             
   ├─classes     # 包含了所有的 Servlet 类和其他类文件重要
       └─com  
          └─example
   
   └─lib         # 项目依赖包的储存位置.jar文件
└─web.xml         # Servlet的配置文件重要

定义路由
路由的定义,可以在这两个地方进行:Servlet注解web.xml, 配置的时候二选一即可。

Ⅰ Servlet中的注解

@WebServlet("/Hello")
public class HelloServlet extends HttpServlet{
  //处理 GET 方法请求的方法
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=UTF-8");
    //实现的代码
  }

  //处理POST方法请求的方法
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html;charset=UTF-8");
    //实现的代码
  }
}

Servlet3.0之前,需要在web.xml中配置,才能够使用Servlet,这一块由于路由跟逻辑并不在一块儿实现,比较陌生,因此我们重点看下它。

Ⅱ 配置文件web.xml

Java项目中的web.xml,可以配置web的路由,里面的属性很多,咱们主要关心两条:

  1. <servlet-class></servlet-class>对应类的名字
  2. <url-pattern>,</url-pattern>路由。为servlet提供一个缺省的URL:http://host/webAppPre fix/servlet/ServletName

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!DOCTYPE web-app
     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
    <servlet>
     <servlet-name>HelloServlet</servlet-name>
     <servlet-class>com.example.HelloServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
     <servlet-name>HelloServlet</servlet-name>
     <url-pattern>/Hello</url-pattern>
    </servlet-mapping>
    </web-app>
    

    ## Ⅲ jsp:useBean标签
    同时,在jsp代码的开头,看到大量使用<jsp:useBean...的代码,见图片中的第2~4行。

    这东西,叫**<jsp:useBean>**标签,它的定义,整理出来是这样的:

    <jsp:useBean>标签可以在JSP中声明一个JavaBean,然后使用。

    • 声明后,JavaBean对象就成了脚本变量,可以通过脚本元素或其他自定义标签来访问。
    • <jsp:useBean>标签的语法格式如下:
      <jsp:useBean id="HttpSession" scope="session" class="example.HttpSession"/>
      
      • id值,可随意定义,只要跟上下文不重复;在习惯上,跟class的最后一级相同(HttpSession);
      • scope值,可以是pagerequestsessionapplication,分别对应不同的作用范围,注意不要将【需要经常变动的 bean】 的 scope设为 applicationsession
      • class值,指定对应的java类;一般是WEB-INF/classes/为起点的一个相对路径(用作路径分隔符)

JavaBean,个人认为就是Java的一种对象,遵循一些规范,有一些特征。

因此,在审计时,只需先在jsp文件中找敏感函数,根据当前文件中的标签定义,找到定义函数的.class,完成漏洞的确认就好了。用IDEA可以很方便地进行查看。

Ⅳ 取HTTP参数

JSP中取request参数的写法,虽然非常好理解,但对小白来说,也是有一些需要注意的点。

<%@ page contentType="text/html; charset=gb2312" language="java" errorPage="" %>
    ...
<% 
String id;  
id = request.getParameter("id");

// 处理中文
String name =new String(request.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
...

上面这段代码中,使用request.getParameter来接受client发送的HTTP参数。但对于id参数,无论你以GET、还是POST方法来提交的,都能被server接收。
也就是说:request.getParameter是兼容POST/GET参数滴!有点像PHP中的$_REQUEST方法。
同时,对于中文数据,需要对其转码,才能正常显示。
当然,JSP中还有其它取参数的写法,由于并未在本次渗透中出现,也就不再赘述。感兴趣的师傅可以自行了解。

Ⅴ 文件的包含

此外,在项目中还出现了文件包含的写法

<%@ include file="check.jsp"%>

经过一番学习,这个跟PHP中的文件包含有点像

那看看check.jsp的内容吧,

很明显,下面这段代码一出现,就意味着本页面的功能属于后台功能了。

<%@ include file="check.jsp"%>

不过呢,我还有一点没搞懂,又没看到有exit函数,为啥include以后的代码,走到out.print以后就不再执行了呢?估计跟servlet的生命周期有关。有了解的师傅麻烦在评论区抬一手。
OK,基础知识补充得差不多了,下面开冲,尝试完成前台RCE的挖掘。

0x02 后台SQLi

轻松找到一处注入,无任何过滤。

不过,考虑到可能需要SQLMAP作自动化利用——删除型的注入点,还是算了。

那么,又重新找到一处拼接表名的注入点。

但问题又来了,这个测试环境的数据库文件,我之前作目录扫描的时候是搞了一份的,找半天咋找不到task_开头的数据表呢???
没办法,只能FUZZ了,采用SecLists里面的raft-large-words.txt来进行FUZZ。哈哈,果然没用!

最后,审计呗,找到一处完美注入点,既不伤害数据库,又不需要花里胡哨地FUZZ。SQLMAP跑的结果如下:

注意到是DBA权限,且通过大小写判断目标是Windows环境。
对我来说,一是想到可以用UNC地址进行带外注入(可以,但在联合注入面前,没必要);
二是写webshell。一般而言,windows通过注入写webshell比linux更难一些,因为路径相对更难猜。不过,由于我手上有系统的部分源码,很快翻找到了web路径,是C:\example,尝试使用--os-shell写shell,并不成功;一开始还猜测是当前环境更换了盘符,结果26个字母捋了一遍,都不成功。。。

0x03 路径泄露 => 后台getshell

天无绝人之路啊。我回想起目标环境的Tomcat似乎很拉跨,并未对报错进行屏蔽,经常露源码出来,嘿嘿!

通过让后台备份功能产生报错,成功获得路径d:\exam\bak\
接着通过SQLMAP --os-shell参数,也是不费工夫就拿到了SQLMAP的shell,允许上传任意文件了。

0x04 前台getshell

话又说回来,目前这个目标是拿下来了,可毕竟是弱密码,难免被诟病“不讲武德”。
于是,全局搜索没有包含check.jsp的代码,发现一处前台SQL注入。。。

好的,前台getshell的方式有了,不过要知道目标环境的web路径才行,但考虑到该产品差不多采用的都是销售OEM服务器的形式,基本不会出太大岔子,所以就到此为止吧。

感谢观看,本文主要是简单的代码审计,有很多思路不到位的地方,请各位师傅不吝指点!

Refs

点击收藏 | 1 关注 | 1
登录 后跟帖