目录
一、前言
近来在开始学习java的代码审计,故决定审计一波经典的铁人下载系统,安装过程这里就省略了,源码里有安装教程。
二、漏洞挖掘
非框架的代码审计,按照前台–后台,严重–低危,非交互–需交互,跟随代码流程尽量发现高危和易利用漏洞类型为主。
1、重安装漏洞
在之前安装的时候,会出现自动安装失败的情形,但是/install目录的重新安装页面是没有删除的。如果按照系统自带的安装是不存在重新安装漏洞的。让我们分析下安装的代码逻辑。
分析/install/index.html页面,在确认提交后会到/install/install.jsp文件继续跟进install.jsp,调用/install/install_setup.jsp
分析/install/install_setup.jsp发现会import语句
找到安装的主要逻辑代码,在WEB-INF/java/liuxing/util/Install.java中,安装时du方法会判断db_an的值,如果是yes可以安装,no不安装
通过在安装完后,updateConfig方法会将db_an的值设置为no,故虽然install页面没有删除,但是已经不能再次安装了。
所以,使用系统自带的安装功能时,不存在重安装漏洞;但是如果手工导入sql文件安装系统,自己又没有吧db_an的值写成no,没有删除install目录文件时,存在重安装漏洞。
2、SQL注入漏洞
首先测试首页搜索功能,发现调用“so.jsp”文件,且传入的参数为“name”,跟进
分析“so.jsp“文件,搜索传入的”name“参数值会传入”Ruanjianguanli“的“so”方法中,继续跟进
分析WEB-INF/java/liuxing/guanli/Ruanjianguanli.java类的so方法,调用了ruanjianDao.so()函数
ruanjianDao是什么,在Ruanjianguanli.java类的构造函数里,ruanjianDao是RuanjianMySQL的一个实例,继续跟进
分析WEB-INF/java/liuxing/dao/RuanjianMySQL.java类,搜索其中的”so“方法,发现最终的采用的是预编译来执行数据库操作的,这里不存在SQL注入漏洞。(注:并不是采用预编译就不存在漏洞,有的参数可能会忽略)
一般来说,有一处用了预编译,说明很多处都用了预编译的方式执行sql语句(不排除有些参数没有提前使用占位符,而是直接拼接)。我们可以全局搜索(execteQuery、createStatement、PreparedStatement、connection等)函数,排查是否存在直接拼接的SQL语句。
如上图所示,还是有蛮多的,但是存在漏洞的点还有待排查,基本都是需要登录台才能利用。(注:一方面是create Statement,其次方法中传入的 参数是string类型,且参数会拼接进sql语句中),如下示例:
在UserDaoMySQL.java类中有个“Shanchuyonghu“方法,在这个方法中会传入一个String类型的”ids“数组,该数组的第一个元素会拼接进sql语句中,最终拼接的sql语句为” delete from user where id in (‘ids[0],ids[1]…,ids[i]’) “,最终sql语句直接被“executeUpdate”方法直接调用
跟进,谁调用了UserDaoMySQL.java类的“Shanchuyonghu”方法(idea选中方法,右键有个“find user”),我这里是全局搜索,发现”Delete.jsp“和“guanliyonghu.jsp”调用了“Shanchuyonghu”方法,进行分析。
(1)分析Delete.jsp,里面代码的功能为将网页请求的id参数的值直接传入sid数组,sid数组的值直接传入“shanchuyonghu”方法,故id的值是用户可控的,这里存在sql注入漏洞。
Delete.jsp在网页中的位置为“/liuxing/admin/left6/Delete.jsp”,这里需要先登录后台,可以构造时间盲注语句
/liuxing/admin/left6/Delete.jsp?pageNo=1&id=2%27)+or+if(ascii(substr(database(),1,1))%3E107,0,sleep(5))--+
(2)再来分析guanliyouhu.jsp,其代码功能为获取checkbox2参数的值,在前端代码中,checkbox2是表单中的一个选择框,这里是把选中的行数据放到数组里。
在guanliyouhu.jsp中,checkbox2参数的值赋值为str数组,str数组的值赋值给shanchuyonghu方法,我们进一步跟踪能否控制checkbox2参数的值,在guanliyouhu.jsp中,说明了id、pageNo等值的来源,这里是没有办法控制的,故不存在SQL注入点。
3、XSS漏洞
因为系统比较简单,前台能够产生xss的地方比较少,会员注册后,登录,在修改邮箱处,存在XSS漏洞,键入payload:
""><script>alsrt(111)</script>
分析此处功能实现代码,请求接口文件为“denglu1.jsp”,里面将要修改的youxiang和id参数的值传入了“Userguanli”的“xiugaiyonghu2”方法中,跟进
在WEB-INF/java/liuxing/dao/UserDaoMySQL.java类的Xiugaiyonghu2方法中调用了userDao函数,userDao是UserDaoMySQL的一个实例,继续跟踪UserDaoMySQL中Xiugaiyonghu2方法。
在“UserDaoMySQL”中“Xiugaiyonghu2”方法中可以看到,直接将前端传过来的youxiang值写入数据库,没有任何安全防护,从而导致了存储型XSS
在后台管理员功能处修改用户邮箱处也会触发xss,实现方法是”UserDaoMySQL”中“Xiugaiyonghu”方法 ,本质是一样的。
且在前端denglu1.jsp中,邮箱的值是直接从数据库中获取的。
4、访问控制
在没有登录账户的时候,访问/admin/admin.jsp页面,会被重定向到/buzai.htm页面。打开admin.jsp页面,看看里面的跳转逻辑,搜索关键字:
RequestDispatcher
getRequestDispatcher
sendRedirect
setHeader
forward
没有发现跳转的语句,判断是全局Filter的权限认证。查看WEB-INF/web.xml,发现针对所有jsp文件的AuthFilter过滤器。
跟进filter-class,找到WEB-INF/java/liuxing/util/AuthFilter.java文件,分析doFilter函数:
关键语句在于两个if判断:
如果路径不是指定的4个安装系统相关的路径,会尝试从数据库的lujing表中查询web路径。(注:这里需要跟进WEB-INF/java/liuxing/util/LuJing.java文件翻看代码,里面为找到路径返回“false“,没找到路径返回”true”),所以对于数据库中不存在的管理员路径/admin/admin.jsp,返回true。如果这时候访问者没有session,或者读不到admin的session,就会返回true,然后就被重定向到/buzai.htm页面。
所以,这里也不存在越权访问页面什么的漏洞。程序用session中的user和admin属性区分普通用户和管理员用户,猜想
也没有垂直越权之类的漏洞,没有仔细查看。根据session中的id属性区分普通用户,平行越权也放弃了查找(还是我太菜)
5、后台任意文件上传漏洞
在登录后台后,“其他管理”–“添加友情链接”、“软件管理”–“软件发布”页面,都可以上传文件,在web.xml中或者顺着jsp页面调用寻找,都能够找到具体的逻辑代码。
几个类的内部代码看起来差不多,我们以WEB-INF/java/liuxing/util/shanchuan2.java文件为例,关键代码如下:
try {
List fileItems = upload.parseRequest(req);
Iterator iter = fileItems.iterator();
String regExp = ".+\\\\(.+)$";
String[] errorType = new String[]{".exe", ".com", ".cgi", ".asp"};
Pattern p = Pattern.compile(regExp);
while(true) {
FileItem item;
String name;
long size;
do {
do {
if (!iter.hasNext()) {
return;
}
item = (FileItem)iter.next();
if (item.isFormField() && item.getFieldName().equals("date")) {
date = item.getString();
}
} while(item.isFormField());
name = item.getName();
size = item.getSize();
} while((name == null || name.equals("")) && size == 0L);
Matcher m = p.matcher(name);
boolean result = m.find();
if (!result) {
throw new IOException("无法上传");
}
for(int temp = 0; temp < errorType.length; ++temp) {
if (m.group(1).endsWith(errorType[temp])) {
throw new IOException(name + ": wrong type");
}
}
try {
String names = m.group(1);
String men = this.getExt(names);
item.write(new File(this.getServletContext().getRealPath("/") + "wen/" + date + men));
res.sendRedirect("../admin/left3/chenggong2.jsp?" + date + men);
} catch (Exception var21) {
out.println(var21);
}
}
} catch (IOException var22) {
out.println(var22);
} catch (FileUploadException var23) {
out.println(var23);
out.println("文件大小超出限制");
}
}
其中会先判断上传的文件名,要符合正则表达式“.+\\\\(.+)$”,才能够正常上传。即形似xxx\\xx的文件名,估计是为了匹配Windows路径中的\,比如C:\a.jpg。定义了内部禁止的后缀名”.exe”,”.com”,”.cgi”,”.asp”,这就是唯一的过滤方式了。
继续往下看,写文件时,关键的一句代码:
item.write(new File(this.getServletContext().getRealPath("/") + "wen/" + date + men));
即将文件存放在/wen目录下,保存为date+men形式的文件名,两者都是可以控制的,直接修改写shell。
三、总结
还是java基础不行,只能跟着前人脚步走,但这也算是自己一些学习记录吧,后续加油