HttpClient.execute
HttpClient.executeMethod
HttpURLConnection.connect
HttpURLConnection.getInputStream
URL.openStream
...
修复方案SQLi介绍
注入攻击的本质,是程序把用户输入的数据当做代码执行。这里有两个关键条件,第一是用户能够控制输入;第二是用户输入的数据被拼接到要执行的代码中从而被执行。sql注入漏洞则是程序将用户输入数据拼接到了sql语句中,从而攻击者即可构造、改变sql语义从而进行攻击。
漏洞示例
此处以Mybatis框架为例,示例sql片段如下:
select * from books where id= ${id}
对于Mybatis框架下SQL注入漏洞的审计可参考
修复方案
Mybatis框架SQL语句安全写法应使用#{},避免使用动态拼接形式${},ibatis则使用#变量#。安全写法如下:
select * from books where id= #{id}
文件上传漏洞介绍
文件上传过程中,通常因为未校验上传文件后缀类型,导致用户可上传jsp等一些webshell文件。代码审计时可重点关注对上传文件类型是否有足够安全的校验,以及是否限制文件大小等。
漏洞示例
此处以MultipartFile为例,示例代码片段如下:
public String handleFileUpload(MultipartFile file){
String fileName = file.getOriginalFilename();
if (fileName==null) {
return "file is error";
}
String filePath = "/static/images/uploads/"+fileName;
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream =
new BufferedOutputStream(new FileOutputStream(new File(filePath)));
stream.write(bytes);
stream.close();
return "OK";
} catch (Exception e) {
return e.getMessage();
}
} else {
return "You failed to upload " + file.getOriginalFilename() + " because the file was empty.";
}
}
审计函数
java程序中涉及到文件上传的函数,比如:
MultipartFile
...
修复方案Autobinding介绍
Autobinding-自动绑定漏洞,根据不同语言/框架,该漏洞有几个不同的叫法,如下:
软件框架有时允许开发人员自动将HTTP请求参数绑定到程序代码变量或对象中,从而使开发人员更容易地使用该框架。这里攻击者就可以利用这种方法通过构造http请求,将请求参数绑定到对象上,当代码逻辑使用该对象参数时就可能产生一些不可预料的结果。
漏洞示例
示例代码以ZeroNights-HackQuest-2016的demo为例,把示例中的justiceleague程序运行起来,可以看到这个应用菜单栏有about,reg,Sign upjava判断文件是否存在的方法,Forgot password这4个页面组成。我们关注的点是密码找回功能,即怎么样绕过安全问题验证并找回密码。
1)首先看reset方法,把不影响代码逻辑的删掉。这样更简洁易懂:
@Controller
@SessionAttributes("user")
public class ResetPasswordController {
private UserService userService;
...
@RequestMapping(value = "/reset", method = RequestMethod.POST)
public String resetHandler(@RequestParam String username, Model model) {
User user = userService.findByName(username);
if (user == null) {
return "reset";
}
model.addAttribute("user", user);
return "redirect: resetQuestion";
}
这里从参数获取username并检查有没有这个用户,如果有则把这个user对象放到Model中。因为这个Controller使用了@SessionAttributes(“user”),所以同时也会自动把user对象放到session中。然后跳转到resetQuestion密码找回安全问题校验页面。
2)resetQuestion密码找回安全问题校验页面有resetViewQuestionHandler这个方法展现
@RequestMapping(value = "/resetQuestion", method = RequestMethod.GET)
public String resetViewQuestionHandler(@ModelAttribute User user) {
logger.info("Welcome resetQuestion ! " + user);
return "resetQuestion";
}
这里使用了@ModelAttribute User user,实际上这里是从session中获取user对象。但存在问题是如果在请求中添加user对象的成员变量时则会更改user对象对应成员的值。
所以当我们给resetQuestionHandler发送GET请求的时候可以添加“answer=hehe”参数,这样就可以给session中的对象赋值,将原本密码找回的安全问题答案修改成“hehe”。这样在最后一步校验安全问题时即可验证成功并找回密码
审计函数
这种漏洞一般在比较多步骤的流程中出现,比如转账、找密等场景,也可重点留意几个注解如下:
@SessionAttributes
@ModelAttribute
...
更多信息可参考Spring MVC Autobinding漏洞实例初窥
修复方案
Spring MVC中可以使用@InitBinder注解,通过WebDataBinder的方法setAllowedFields、setDisallowedFields设置允许或不允许绑定的参数。
URL重定向介绍
由于Web站点有时需要根据不同的逻辑将用户引向到不同的页面,如典型的登录接口就经常需要在认证成功之后将用户引导到登录之前的页面,整个过程中如果实现不好就可能导致URL重定向问题,攻击者构造恶意跳转的链接,可以向用户发起钓鱼攻击。
漏洞示例
此处以Servlet的redirect 方式为例,示例代码片段如下:
String site = request.getParameter("url");
if(!site.isEmpty()){
response.sendRedirect(site);
}
审计函数
java程序中URL重定向的方法均可留意是否对跳转地址进行校验、重定向函数如下:
sendRedirect
setHeader
forward
...
修复方案CSRF介绍
跨站请求伪造(Cross-Site Request Forgery,CSRF)是一种使已登录用户在不知情的情况下执行某种动作的攻击。因为攻击者看不到伪造请求的响应结果,所以CSRF攻击主要用来执行动作,而非窃取用户数据。当受害者是一个普通用户时,CSRF可以实现在其不知情的情况下转移用户资金、发送邮件等操作;但是如果受害者是一个具有管理员权限的用户时CSRF则可能威胁到整个Web系统的安全。
漏洞示例
由于开发人员对CSRF的了解不足,错把“经过认证的浏览器发起的请求”当成“经过认证的用户发起的请求”,当已认证的用户点击攻击者构造的恶意链接后就“被”执行了相应的操作。例如,一个博客删除文章是通过如下方式实现的:
GET http://blog.com/article/delete.jsp?id=102
当攻击者诱导用户点击下面的链接时,如果该用户登录博客网站的凭证尚未过期,那么他便在不知情的情况下删除了id为102的文章,简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
漏洞审计
此类漏洞一般都会在框架中解决修复,所以在审计csrf漏洞时。首先要熟悉框架对CSRF的防护方案,一般审计时可查看增删改请求重是否有token、formtoken等关键字以及是否有对请求的Referer有进行校验。手动测试时,如果有token等关键则替换token值为自定义值并重放请求,如果没有则替换请求Referer头为自定义链接或置空。重放请求看是否可以成功返回数据从而判断是否存在CSRF漏洞。
修复方案命令执行介绍
由于业务需求,程序有可能要执行系统命令的功能,但如果执行的命令用户可控,业务上有没有做好限制,就可能出现命令执行漏洞。
漏洞示例
此处以getRuntime为例,示例代码片段如下:
String cmd = request.getParameter("cmd");
Runtime.getRuntime().exec(cmd);
审计函数
这种漏洞原理上很简单,重点是找到执行系统命令的函数,看命令是否可控。在一些特殊的业务场景是能判断出是否存在此类功能,这里举个典型的实例场景,有的程序功能需求提供网页截图功能,笔者见过多数是使用phantomjs实现,那势必是需要调用系统命令执行phantomjs并传参实现截图。而参数大多数情况下应该是当前url或其中获取相关参数,此时很有可能存在命令执行漏洞,还有一些其它比较特别的场景可自行脑洞。
java程序中执行系统命令的函数如下:
Runtime.exec
ProcessBuilder.start
GroovyShell.evaluate
...
修复方案权限控制介绍
越权漏洞可以分为水平、垂直越权两种,程序在处理用户请求时未对用户的权限进行校验,使的用户可访问、操作其他相同角色用户的数据,这种情况是水平越权;如果低权限用户可访问、操作高权限用户则的数据,这种情况为垂直越权。
漏洞示例
@RequestMapping(value="/getUserInfo",method = RequestMethod.GET)
public String getUserInfo(Model model, HttpServletRequest request) throws IOException {
String userid = request.getParameter("userid");
if(!userid.isEmpty()){
String info=userModel.getuserInfoByid(userid);
return info;
}
return "";
}
审计函数
水平、垂直越权不需关注特定函数,只要在处理用户操作请求时查看是否有对当前登陆用户权限做校验从而确定是否存在漏洞
修复方案
获取当前登陆用户并校验该用户是否具有当前操作权限,并校验请求操作数据是否属于当前登陆用户,当前登陆用户标识不能从用户可控的请求参数中获取。
批量请求介绍
业务中经常会有使用到发送短信校验码、短信通知、邮件通知等一些功能,这类请求如果不做任何限制,恶意攻击者可能进行批量恶意请求轰炸,大量短信、邮件等通知对正常用户造成困扰,同时也是对公司的资源造成损耗。
除了短信、邮件轰炸等,还有一种情况也需要注意,程序中可能存在很多接口,用来查询账号是否存在、账号名与手机或邮箱、姓名等的匹配关系,这类请求如不做限制也会被恶意用户批量利用,从而获取用户数据关系相关数据。对这类请求在代码审计时可关注是否有对请求做鉴权、和限制即可大致判断是否存在风险。
漏洞示例
@RequestMapping(value="/ifUserExit",method = RequestMethod.GET)
public String ifUserExit(Model model, HttpServletRequest request) throws IOException {
String phone = request.getParameter("phone");
if(! phone.isEmpty()){
boolean ifex=userModel.ifuserExitByPhone(phone);
if (!ifex)
return "用户不存在";
}
return "用户已被注册";
}
修复方案第三方组件安全介绍
这个比较好理解,诸如Struts2、不安全的编辑控件、XML解析器以及可被其它漏洞利用的如commons-collections:3.1等第三方组件java判断文件是否存在的方法,这个可以在程序pom文件中查看是否有引入依赖。即便在代码中没有应用到或很难直接利用,也不应该使用不安全的版本,一个产品的周期很长,很难保证后面不会引入可被利用的漏洞点。
修复方案危险函数自动化搜索脚本javaid.py
审计一个工程一般是需要通篇阅读代码,但是有的时候也需要简单粗暴的方法,就是关注一些可能产生漏洞的危险函数,这里分享一个自己编写的自动化脚本JavaID。通过正则匹配的方式去查找一些危险函数,由于特定的需求,阉割了一些正则,不过可以根据自己的需求在regexp.xml中自由添加。只是个辅助脚本,不能指望靠查找危险函数找到所有漏洞。一些框架安全、逻辑漏洞等等更多的还需要通读代码。
总结
除了上述相关的漏洞,在代码审计的时候有时会遇到一些特别的漏洞,比如开发为了测试方便关闭掉了一些安全校验函数、甚至未彻底清除的一些预留后门及测试管理接口等。除此,框架本身的安全问题也是可以深挖。一些安全校验、安全解决方案也未必就毫无破绽的,即便存在一些安全解决,但开发人员有没有使用以及是否正确使用安全方案都是可能存在问题的点。大公司都有成熟的框架,一些基本的安全问题并不是太多,但设计层面上的安全及流程相关的问题却基本依赖开发的经验。流程相关的漏洞则有必要先熟悉应用本身的设计和逻辑,这块也是潜在的风险点。