学习笔记-JAVA代码审计

JAVA代码审计


免责声明

本文档仅供学习和研究使用,请勿使用文中的技术源码用于非法用途,任何人造成的任何负面影响,与本人无关.


相关文章

相关资源


硬编码

通用关键词


JAVA反序列化


SPel注入


Autobinding

描述

Autobinding-自动绑定漏洞,根据不同语言/框架,该漏洞有几个不同的叫法,如下:

  • Mass Assignment: Ruby on Rails, NodeJS
  • Autobinding: Spring MVC, ASP.NET MVC
  • Object injection: PHP(对象注入、反序列化漏洞)

软件框架有时允许开发人员自动将 HTTP 请求参数绑定到程序代码变量或对象中,从而使开发人员更容易地使用该框架。这里攻击者就可以利用这种方法通过构造 http 请求,将请求参数绑定到对象上,当代码逻辑使用该对象参数时就可能产生一些不可预料的结果。

相关文章

漏洞示例

示例代码以 ZeroNights-HackQuest-2016 的 demo 为例,把示例中的 justiceleague 程序运行起来,可以看到这个应用菜单栏有 about,reg,Sign up,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 设置允许或不允许绑定的参数。


SSRF

描述

相对于 php,在 java 中 SSRF 的利用局限较大,一般利用 http 协议来探测端口,利用 file 协议读取任意文件。常见的类中如 HttpURLConnection,URLConnection,HttpClients 中只支持 sun.net.www.protocol (java 1.8) 里的所有协议: http,https,file,ftp,mailto,jar,netdoc。

相关文章

漏洞示例

此处以 HttpURLConnection 为例,示例代码片段如下:

String url = request.getParameter("picurl");
StringBuffer response = new StringBuffer();

URL pic = new URL(url);
HttpURLConnection con = (HttpURLConnection) pic.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
     response.append(inputLine);
}
in.close();
modelMap.put("resp",response.toString());
return "getimg.htm";

URLConnection类

//urlConnection ssrf vul
String url = request.getParameter("url");
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //发起请求,触发漏洞
String inputLine;
StringBuffer html = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
     html.append(inputLine);
}
System.out.println("html:" + html.toString());
in.close();

ImageIO类

// ImageIO ssrf vul
String url = request.getParameter("url");
URL u = new URL(url);
BufferedImage img = ImageIO.read(u); // 发起请求,触发漏洞

其他类

// Request漏洞示例
String url = request.getParameter("url");
return Request.Get(url).execute().returnContent().toString();//发起请求

// openStream漏洞示例
String url = request.getParameter("url");
URL u = new URL(url);
inputStream = u.openStream();  //发起请求


// OkHttpClient漏洞示例
String url = request.getParameter("url");
OkHttpClient client = new OkHttpClient();
com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
client.newCall(ok_http).execute();  //发起请求

// HttpClients漏洞示例
String url = request.getParameter("url");
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = client.execute(httpGet); //发起请求

审计函数

Java 中能发起网络请求的类:

  • HttpClient 类
  • HttpURLConnection 类
  • URLConnection 类
  • URL 类
  • OkHttp 类
  • ImageIO 类
  • Request 类 (Request 是对 HttpClient 类进行了封装的类,类似于 Python 的 requests 库。)

其中,仅支持 HTTP/HTTPS 协议的类(即类名或封装的类名带 http):

  • HttpClient 类
  • HttpURLConnection 类
  • OkHttp 类
  • Request 类

支持 sun.net.www.protocol 所有协议的类:

  • URLConnection 类
  • URL 类
  • ImageIO 类

程序中发起 HTTP 请求操作一般在获取远程图片、页面分享收藏等业务场景, 在代码审计时可重点关注一些 HTTP 请求操作函数,如下:

HttpClient.execute
HttpClient.executeMethod
HttpURLConnection.connect
HttpURLConnection.getInputStream
URL.openStream
URLConnection.getInputStream
Request.Get.execute
Request.Post.execute
ImageIO.read
OkHttpClient.newCall.execute
HttpServletRequest
BasicHttpRequest

搜索正则

HttpClient\.execute|HttpClient\.executeMethod|HttpURLConnection\.connect|HttpURLConnection\.getInputStream|URL\.openStream

更多内容


SQLi

相关文章

漏洞示例

以 Mybatis 为例

select * from books where id= ${id}

修复方案

Mybatis 框架 SQL 语句安全写法应使用 #{} , 避免使用动态拼接形式 ${}。安全写法如下:

select * from books where id= #{id}

使用预编译,也可以预防 SQL 注入,例如

public UserInfo UserInfoFoundDao(String id){

            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            UserInfo userinfo = null;
            try{
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/sql","root","root");
                String sql = "select * from userinfo where id = ?";
                ps = conn.prepareStatement(sql);

                ps.setInt(1,id);

                rs = ps.executeQuery();

                while(rs.next()){
                    userinfo = new UserInfo();
                    userinfo.setId(rs.getString("id"));
                    userinfo.setName(rs.getString("name"));
                    userinfo.setAge(rs.getInt("age"));
                    userinfo.setContent(rs.getString("content"));
                    userinfo.setAddress(rs.getString("address"));
                }
                ...

            return userinfo;
        }
}

搜索正则

Mybatis
order by \$\{.*\}|like \$\{.*\}
\$\{.*\}

更多内容


SSTI

相关文章

更多内容

FreeMarker SSTI

描述

模板文件存放在 Web 服务器上,当访问指定模版文件时, FreeMarker 会动态转换模板,用最新的数据内容替换模板中 ${...} 的部分,然后返回渲染结果。

FreeMarker 基础

FreeMarker SSTI POC

<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
<#assign value="freemarker.template.utility.Execute"?new()>${value("open /Applications/Calculator.app")}
<#assign value="freemarker.template.utility.Execute"?new()>${value("id")}
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","calc.exe").start()}
<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc.exe")</@value>

Velocity SSTI

Thymeleaf SSTI

描述

Thymeleaf 具有预处理表达式的功能,预处理是在正常表达式之前完成的表达式的执行,允许修改最终将执行的表达式。

预处理的表达式与普通表达式完全一样,但被双下划线符号(如 __${expression}__ )包围。

预处理可以解析执行表达式,也就是说找到一个可以控制预处理表达式的地方,让其解析执行我们的 payload 即可达到任意代码执行

Thymeleaf 基础

相关文章

调试样本

thymeleaf SSTI POC

其实就是 SpEL 注入的 payload

${T(java.lang.Runtime).getRuntime().exec("open -a Calculator")}

文件上传漏洞

相关文章

漏洞示例

此处以 MultipartFile 为例,示例代码片段如下:

    public String vul1(@RequestPart MultipartFile file) throws IOException {
        String fileName = file.getOriginalFilename();
        String filePath = path + fileName;
        File dest = new File(filePath);
        Files.copy(file.getInputStream(), dest.toPath());
        return "文件上传成功 : " + dest.getAbsolutePath();
    }

这里没有对后缀名做检测,同时还存在路径穿越漏洞,攻击者可以直接写计划任务到 /etc/cron.d/ 目录下

审计函数

MultipartFile
newStandardMultipartFile
getOriginalFilename

搜索正则

file\.getOriginalFilename\(\)|文件上传|文件|上传|uploadfile|upload

更多内容

getOriginalFilename()

描述

在使用 SpringBoot 中当没有自己手动配置的情况下默认使用的是 StandardMultipartFile. 在这种情况下直接通过 getOriginalFilename 方法获取文件名后,不进行处理就使用会造成目录穿越漏洞。

如果配置了使用 CommonsMultipartFile 的 getOriginalFilename 方法对文件名进行了处理,在 windows 可以使用 ../..\\..\\ 绕过造成目录穿越漏洞,官方在 Spring >= 4.1.9.RELEASE 修复该问题.

相关文章

相关的 issue


URL重定向

漏洞示例

示例代码片段如下:

    public String vul(String url) {
        log.info(url);
        return"redirect:" + url;
    }

这里没有对传入的参数 url 做任何判断,直接进行了重定向

审计函数

java 程序中 URL 重定向的方法均可留意是否对跳转地址进行校验、重定向函数如下:

redirect:
sendRedirect
setHeader
forward
...

搜索正则

redirect\:|sendRedirect|setHeader|forward|getHost\(\)

更多内容

getHost()

相关文章

反斜杠绕过

有些业务使用 java.net.URL 包中的 getHost() 方法获取了将要跳转 URL 的 host,判断 host 是否为目标域

    @RequestMapping("/redirect/3")
    public String vul3(String url) {

        String host = "";
        try {
            host = new URL(url).getHost();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        if (host.endsWith(".ffffffff0x.com")){
            return"redirect:https://" + host;
        }else{
            return"redirect:/error";
        }
    }

getHost() 方法可以被反斜杠绕过,即 ?url=http://www.baidu.com%5Cwww.ffffffff0x.com 会被代码认为是将要跳转到 ffffffff0x.com,而实际跳转到 www.baidu.com\www.ffffffff0x.com,最终还是跳到 www.baidu.com 的服务器。

通过 # 井号绕过

getHost() 方法的结果在不同 JDK 版本中对井号 #的处理结果不同,通常井号被用作页面锚点,对于 https://www.aaa.com#www.bbb.com?x=123 这个 URL,较高版本的 JDK 中,取出结果为 www.aaa.com,低版本中为 www.aaa.com#www.bbb.com,从而低版本又可绕过 endsWith(".bbb.com") 方法,成功跳转。

比如 JDK1.8.0_221 对 '' 可以绕过,'#' 不可以绕过。JDK1.6.0_45 '' 和 '#' 都可以绕过.


CSRF

漏洞示例

由于开发人员对 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
...

搜索正则

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 "用户已被注册";
}

修复方案

  • 对同一个用户发起这类请求的频率、每小时及每天发送量在服务端做限制,不可在前端实现限制

XXE

漏洞示例

此处以 org.dom4j.io.SAXReader 为例,仅展示部分代码片段:

String xmldata = request.getParameter("data");
SAXReader sax = new SAXReader();
// 创建一个SAXReader对象
Document document = sax.read(new ByteArrayInputStream(xmldata.getBytes()));
// 获取document对象,如果文档无节点,则会抛出Exception提前结束
Element root = document.getRootElement(); //获取根节点
List rowList = root.selectNodes("//msg");
Iterator<?> iter1 = rowList.iterator();
if (iter1.hasNext()) {
    Element beanNode = (Element) iter1.next();
    modelMap.put("success",true);
    modelMap.put("resp",beanNode.getTextTrim());
}
...

审计函数

XML 解析一般在导入配置、数据传输接口等场景可能会用到,涉及到 XML 文件处理的场景可留意下 XML 解析器是否禁用外部实体,从而判断是否存在 XXE。部分 XML 解析接口如下:

javax.xml.parsers.DocumentBuilder
javax.xml.stream.XMLStreamReader
org.jdom.input.SAXBuilder
org.jdom2.input.SAXBuilder
javax.xml.parsers.SAXParser
org.dom4j.io.SAXReader
org.xml.sax.XMLReader
javax.xml.transform.sax.SAXSource
javax.xml.transform.TransformerFactory
javax.xml.transform.sax.SAXTransformerFactory
javax.xml.validation.SchemaFactory
javax.xml.bind.Unmarshaller
javax.xml.xpath.XPathExpression
...

更多内容


第三方组件安全

描述

这个比较好理解,诸如 Struts2、不安全的编辑控件、XML 解析器以及可被其它漏洞利用的如 commons-collections:3.1 等第三方组件,这个可以在程序 pom 文件中查看是否有引入依赖。即便在代码中没有应用到或很难直接利用,也不应该使用不安全的版本,一个产品的周期很长,很难保证后面不会引入可被利用的漏洞点。

相关工具

修复方案

  • 使用最新或安全版本的第三方组件

点击关注,共同学习!
安全狗的自我修养

github haidragon

https://github.com/haidragon

posted @ 2022-11-01 12:31  syscallwww  阅读(1365)  评论(0编辑  收藏  举报