Java代码审计-铁人下载系统


  作者: LandGrey ●   创建时间 2018年3月11日 13:11 ●   更新时间 2018年3月26日 21:00
  浏览: 545 次. ●  标签: #分享, #代码审计
您的IP地址: 54.234.228.78

Java代码审计-铁人下载系统

Java代码审计-铁人下载系统 正文

0x00: 简介

初学java代码审计,跟着表哥们脚步,走一遍审计流程,就选了个没有使用Java框架的java系统,作为入门。


0x01: 目的

熟悉代码审计流程,寻找漏洞的思路,入门记录。


0x02: 准备工作

为了验证审计出的漏洞效果,还是要搭建起来系统,不然空说无凭。为了方便,使用JspStudy 2016一体化环境,选择tomcat 8.0,jdk 1.8搭建。查看代码使用IDEA,当然,也可以用jd-gui,反编译class,不过IDEA自动就反编译了,比较方便。

值得注意的是,使用系统自带的安装功能搭建后,打开页面报错,事后想起来可能是自动导入sql文件的路径程序中写死了,和自己部署时根目录位置不一样导致的。

再次通过install/index.html页面重新安装,则显示数据库已经安装。原因是WEB-INF/classes/liuxing_db.properties中的db_an=yes变成了db_an=no,表示数据库已经安装,不会再次安装。

最后发现使用安装提示里的第二种手工安装方法可以正常安装系统,人工导入数据库数据就行了。

为了审计代码时全局搜索方便,可以使用jad批量反编译class文件,使用命令如:

jad -r -d /path/to/store/java -s java -8 /path/to/classes/files/**/*.class

最后,我将反编译出来的java文件,统一存放在了WEB-INF/java目录下,和class文件的原始目录WEB-INF/classes目录相对应。


0x03:发现漏洞

非框架的代码审计,按照前台——后台,严重——低危,非交互——需交,跟随代码流程尽量发现高危和易利用漏洞类型为主。

一:重安装漏洞

像在0x02中说的一样,虽然我使用系统自带的安装功能失败了,但是db_an参数变成了no,虽然/install目录的重新安装页面没删除,但确实使用系统自带的安装功能不存在重新安装漏洞。

跟随/install/index.html页面,找到install.jsp文件,再根据form action,找到install_setup.jsp页面


再根据install_setup.jsp页面上的import语句

import="liuxing.util.Install,java.util.*"

找到安装的主要逻辑源码,在WEB-INF/java/liuxing/util/Install.java中,安装时会判断db_an的值,yes可以安装,no不安装;安装完后会把值置为no,虽然install页面没删除,但是已经不能够再次安装了。


所以,当使用系统自带的install页面安装系统时,不存在重安装漏洞;如果使用手工导入sql文件安装系统,自己又没有把db_an的值写成no,没删除install目录文件时,存在重安装漏洞。


二. SQL注入漏洞

首先尝试搜索功能,进入so.jsp,发现将搜索的参数值传入Ruanjianguanli的so方法中


进入WEB-INF/java/liuxing/guanli/Ruanjianguanli.java文件中,找到so方法;发现是调用了ruanjianDao.so()函数,

ruanjianDao是什么呢?在Ruanjianguanli类的构造函数里,可以找到,ruanjianDao是RuanjianMySQL的一个实例,那么再接着往下跟

然后打开WEB-INF/java/liuxing/dao/RuanjianMySQL.java文件,搜索so方法,并发现最终是采用预编译来执行数据库操作,这里不存在SQL注入漏洞。

public pageModel so(int pageNo, int pageSize, String tiaojian, int lei, String name)


可想而知,有一处用了预编译,说明就有很多处用了预编译方式来执行SQL语句,基本都没有SQL注入漏洞。然后全局搜索createStatement关键字,看看有没有用拼接的SQL语句的。


如上图,最后也发现几个可以注入的地方,但是都需要登录后台,在delete语句中,可以用时间盲注,比如:

delete from user where id in('222') or if(ascii(substr(database(),1,1))>107,0,sleep(3));

语句来测试

利用:

GET /admin/left6/Delete.jsp?pageNo=1&id=2')+or+if(ascii(substr(database(),1,1))>107,0,sleep(3))--+ HTTP/1.1
Host: 192.168.0.103:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.103:8080/admin/admin.jsp
Cookie: JSESSIONID=85D1D94B22AE49D6447AF40408735F2C
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1 

但是后台都登录了,也没必要去纠结这个SQL注入漏洞了,进了后台应该有更容易利用的漏洞。

三. XSS漏洞

由于系统较简单,前台能够产生xss的地方较少,会员注册后,登录;可以在修改邮箱处,用Payload

""><script>alert(0)</script>

触发了前端会员的xss。

而且在后台管理员修改用户资料处,尝试修改该用户资料,也会触发此XSS。


代码层面,查看denlu1.jsp页面代码,跟踪Userguanli的Xiugaiyonghu2方法,里面传入了要修改成的邮箱和用户ID

       if(gai != null && gai.equals("xugai")){
int id = user1.getId();
String youxiang = request.getParameter("youxiang");
Userguanli.getInstance().Xiugaiyonghu2(youxiang,id);
response.sendRedirect("denlu1.jsp");
}

最终到WEB-INF/java/liuxing/dao/UserDaoMySQL.java文件中的Xiugaiyonghu2方法中,直接将前端传过来的youxiang值写入数据库中,没有任何安全防护,从而导致了存储型xss。


四. 访问控制

没有登录管理员帐户,访问/admin/admin.jsp页面,会被重定向到/buzai.htm页面。打开admin.jsp页面,看看里面的跳转逻辑,搜索关键字:

RequestDispatcher
getRequestDispatcher
sendRedirect
setHeader
forward

没有发现跳转语句,判断是全局Filter的权限认证。查看WEB-INF/web.xml,发现针对所有jsp文件的AuthFilter过滤器

<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>liuxing.util.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>*.jsp</url-pattern>

</filter-mapping>

顺着filter-class,找到WEB-INF/java/liuxing/util/AuthFilter.java文件,分析doFilter函数:


关键语句在于以下两个if判断:


if(!targetURL.equals(install) && !targetURL.equals(install1) && !targetURL.equals(install_setup) && !targetURL.equals(install_setup1))
{
    boolean bl = LuJing.getInstance().Lu(targetURL);
    if(bl && (session == null || session.getAttribute("admin") == null))
    {
        response.sendRedirect((new StringBuilder(String.valueOf(request.getContextPath()))).append("/buzai.htm").toString());
        return;
    }
} 

如果路径不是指定的四个安装系统相关的路径,会尝试从数据库中的lujing表中查询存储在数据库中的web路径。这里一个坑被我踩到了,没看代码的情况下,认为找到路径时bl为true,没找到路径时bl为false,结果最后解释不通了。重新进入WEB-INF/java/liuxing/util/LuJing.java文件翻看代码,发现原来是没查到路径才返回true,找到返回false。所以对于数据库中不存在的管理员路径/admin/admin.jsp,返回true。如果这时候访问者没有session,或者读不到admin的session,就会返回true,然后就被重定向到/buzai.htm页面了。

所以,这里也不存在越权访问页面什么的漏洞。程序用session中的user和admin属性区分普通用户和管理员用户,猜想也没有垂直越权之类的漏洞,没有仔细查看。根据session中的id属性区分普通用户,平行越权也放弃了查找。所以不存在访问控制方面的漏洞。


五. 后台任意文件上传漏洞

在"其它管理"—"添加友情链接"处、"软件管理"—"软件发布"页面,都可以上传文件,在web.xml中或者顺着jsp页面调用寻找,都能够找到具体的逻辑代码


内部代码看起来都是一样的,以WEB-INF/java/liuxing/util/shangchuan2.java文件为例,关键代码如下:

try
{
	List fileItems = upload.parseRequest(req);
	Iterator iter = fileItems.iterator();
	String regExp = ".+\\\\(.+)$";
	String errorType[] = {
		".exe", ".com", ".cgi", ".asp"
	};
	Pattern p = Pattern.compile(regExp);
	while(iter.hasNext()) 
	{
		FileItem item = (FileItem)iter.next();
		if(item.isFormField() && item.getFieldName().equals("date"))
			date = item.getString();
		if(!item.isFormField())
		{
			String name = item.getName();
			long size = item.getSize();
			if(name != null && !name.equals("") || size != 0L)
			{
				Matcher m = p.matcher(name);
				boolean result = m.find();
				if(result)
				{
					for(int temp = 0; temp < errorType.length; temp++)
						if(m.group(1).endsWith(errorType[temp]))
							throw new IOException((new StringBuilder(String.valueOf(name))).append(": wrong type").toString());

					try
					{
						String names = m.group(1);
						String men = getExt(names);
						item.write(new File((new StringBuilder(String.valueOf(getServletContext().getRealPath("/")))).append("wen/").append(date).append(men).toString()));
						res.sendRedirect((new StringBuilder("../admin/left3/chenggong2.jsp?")).append(date).append(men).toString());
					}
					catch(Exception e)
					{
						out.println(e);
					}
				} else
				{
					throw new IOException("无法上传");
				}
			}
		}
	}
}

其中会判断上传的文件名,要符合正则表达式 ".+\\\\(.+)$",才能够正常上传。即形似xxx\\xx的文件名,估计是为了匹配Windows路径中的\\,比如C:\\a.jpg。定义了内部禁止的后缀名".exe", ".com", ".cgi", ".asp",这就是唯一的过滤方式了。

继续往下看,写文件时,关键的一句代码:

item.write(new File((new StringBuilder(String.valueOf(getServletContext().getRealPath("/")))).append("wen/").append(date).append(men).toString()));

即将文件存放在/wen目录下,保存为date+men形式的文件名,两者都是可以控制的,直接修改写shell了。


吐槽一下,不清楚为什么要过滤asp,不过滤jsp?其实正常上传jsp文件,注意filename参数的正则匹配就好了


六. 寻找其它漏洞

1. CSRF漏洞

全系统都没有针对CSRF漏洞进行防御,应该存在不少CSRF漏洞,不一一分析了。

2. 其它

命令执行漏洞,因系统没使用什么框架,所以全局搜索关键字getRuntime,没发现存在执行系统命令的情况,不存在命令执行漏洞。XXE、反序列化等按照系统的需求,连相应功能估计也没有,所以放弃了。


附:

源码下载地址

参考文章:

铁人下载系统代码审计

JAVA代码审计之铁人下载系统 v1.0

JAVA代码审计的一些Tips(附脚本)

作者

LandGrey

文人墨客
https://landgrey.me
blog comments powered by Disqus
<