JAVA代码审计(part1)

时间:2022-03-21 00:57:33

ST测试是一项非常有意思的工作,需要通过一些常规或非常规的手段和手法来达到ST的目的(合法途径),java代码审计是ST测试中重要的一个环节,一些无法通过工具扫描出来的漏洞需要人工去审查核实,是否存在漏洞点,漏洞点是否可控。

SQL注入、XSS、XXE、SSRF、CSRF、逻辑漏洞漏洞均可通过人工代码审计的方式来找到漏洞存在点。

一、敏感函数

1、命令执行函数

Runtime.getRuntime().exec()

ProcessBuilder().start()

GroovyShell.evaluate() 

2、文件上传函数

ServletFileUpload()

FileItemStream()

MultipartFile()


3、SSRF函数

一类只支持http,https协议例如:HttpURLConnection、HttpClient、Request、Okhttp;

一类支持所有协议例如:URLConnection、URL

4、反序列化

readObject方法

5、SQL注入

常见的可操控变量:name、id、password、pwd、select、search等。

寻找敏感功能点,通读功能点代码

6、JSONP劫持

*设置任何域都能访问

jsonp跨域设置

response.setHeader(“Access-Control-Allow-Origin”,"*");

callback

7、CORS

*设置任何域都能访问

CORS跨域设置:response.setHeader(“Access-Control-Allow-Origin”,"*");response.setHeader(“Access-Control-Allow-Credentials”,“true”);

8、Session时间失效

Session.setMaxinactiveinterval();

9、登陆退出Session未销毁

session.invalidate(); //销毁所有的Web应用
sessionsession.removeAttribute(); //移除指定的Web应用
sessionapplication
pageContent


二、获取URL数据方法

1、通过OpenConnection()方法

2、通过OpenStream()方法

两种方法效果一样,但是Openstream()方法的实现是调用了openconnection()生成的一个URLConnection()对象,通过这个对象调用getinputStream()方法,使用这个方法实现生成了URLConnection对象的好处是可以调用和这个对象的一些方法和获取对象的一些字段。——如果确定使用URL指向的绝对是文本并且编码格式为ASII时,使用OpenStream()方法比较方便。


三、SQL注入审计

1、Statement方法

JDBC下有两种方法执行SQL语句——StatementPrepareStatement(预编译)

Statement下的SQL语句

SELECT * FROM users WHERE username = '"+username + "' AND password = '"+password+"'

JDBC使用Statement是不安全的,应为万能密码的存在——1‘or’1’=‘1,万能密码形式:‘or’=‘or’*所以在使用Statement的情况下需要做好过滤。


JDBC使用PrepareStatement不仅仅提高了程序执行的效率还提高了安全性。

2、PrepareStatement方法(预编译)

预编译原理:先编译SQL语句,不管输入的结果是什么,预编译的语句只作为一个字符串来执行,然而SQL注入????是对编译过程产生破环,执行阶段只是将输入的字符串作为数据来处理,所以不需要对SQL语句进行二次解析。

PrepareStatement预编译防御SQL注入的写法是通过?作为占位符对SQL语句进行了预编译,因为?作为占位符已经告诉数据库整个SQL语句的结构,所以?处传入的是参数,而不会是SQL语句,所以即使attacker传入SQL语句也不会背数据库所解析。

PrepareStatement和Statement的区别在于PrepareStatement会对SQL语句进行预编译,预编译的好处不仅在于在一定程度上防止了sql注入,还减少了sql语句的编译次数,提高了性能

String sql = "select * from uuu where id=?";
//预编译sql语句
PreparedStatement prepare = connection.prepareStatement(sql);
prepare.setInt(1,1);
//传参,第一个1是第一个?为索引,第二个1是传入的参数
ResultSet resultSet = prepare.executeQuery();
//返回结果集,封装了全部的产部的查询结果

首先规定好SQL语句的结构,然后在对占位符进行数据的插入,这样就会对sql语句进行防御,????者构造的paylaod会被解释成普通的字符串,我们可以通过过输出查看最终会变成什么sql语句。


四、XSS漏洞审计

XSS产生是由于后台未对用户进行校验/过滤,直接将用户输入的内容返回到前端从而导致JS代码于client任意执行。

1、setAttribute方法

通过setAttribute方法在同一个request周期中保存变量使用,使用时再通过getAttribute方法取出。

@WebServlet("/demo")
public class xssServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html");// 设置响应类型
String content = request.getParameter("content"); //获取content传参数据
request.setAttribute("content", content); //content共享到request域
request.getRequestDispatcher("/WEB-INF/pages/xss.jsp").forward(request, response); //转发到xxs.jsp页面中

}
}

JSP中:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title></title>
${requestScope.content}
</head>
<body>
</body>
</html>

2、ModelAndView方法

ModelAndView可以返回参数到指定页面的request作用域中或者指定返回的页面名称。

@RequestMapping("/test")
public ModelAndView test(){
ModelAndView mav=new ModelAndView("XSS");
mav.addObject("time", new Date());
mav.getModel().put("name", "UUP");

return mav;
}

JSP中:

time:${requestScope.time} <br/>
name:${name }

可通过检查ModelAndView中传入的参数是不是可控的或者有没有其他字节过滤来进行代码审计。

参考:​​​​https://www.cnblogs.com/N0r4h/p/15859970.html​


3、反射型XSS

@RequestMapping("/reflect")
@ResponseBody
public static String reflect(String xss) {
return xss;
}

Payload

?xss=<script>alert('xss')</script>

会在页面中弹框,但是并未写入数据库进行解析


4、存储型Xss

@RequestMapping("/stored/store")
@ResponseBody
public String store(String xss, HttpServletResponse response) {
Cookie cookie = new Cookie("xss", xss);
response.addCookie(cookie);
return "Set param into cookie";
}

使用payload

JAVA代码审计(part1)

当后端获取cookie并返回的时候

@RequestMapping("/stored/show")
@ResponseBody
public String show(@CookieValue("xss") String xss) {
return xss;
}

再次访问网址时会成功弹窗,这时表示恶意代码已经写入数据库,并被解析。


5、存储型XSS漏洞修复方法

@RequestMapping("/safe")

@ResponseBody

publicstaticStringsafe(Stringxss) {

returnencode(xss);

}


privatestaticStringencode(Stringorigin) {

/* origin = StringUtils.replace(origin, "&", "&amp;");

origin = StringUtils.replace(origin, "<", "&lt;");

origin = StringUtils.replace(origin, ">", "&gt;");

origin = StringUtils.replace(origin, "\"", "&quot;");

origin = StringUtils.replace(origin, "'", "&#x27;");

origin = StringUtils.replace(origin, "/", "&#x2F;");*/

origin=StringUtils.replace(origin, "&", "&");

origin=StringUtils.replace(origin, "<", "<");

origin=StringUtils.replace(origin, ">", ">");

origin=StringUtils.replace(origin, "\"", "\");

origin=StringUtils.replace(origin, "'", "'");

origin=StringUtils.replace(origin, "/", "/");

returnorigin;

}

通过字符转义的方式进行字符串过滤,基本杜绝XSS漏洞。如果少写了一个,可能就会被利用绕过。


6、StringUtils.replace()用法

StringUtils.replaceOnce("sshhhss", "ss", "p");//只替换一次-->结果是:phhhss
StringUtils.replace("sshhhs", "ss", "p");//全部替换 ---> 结果是:phhhs
StringUtils.replace("sshhhsshss", "ss", "7777",2);//max:最大替换次数--> 结果是:7777hhh7777hss
StringUtils.replaceChars("sshhhs", "ss", "p");//替换所有字符,区别于replace --->结果是:pphhhp而不是pphhhs


五、SSRF漏洞审计

漏洞形成原因:URL对象用openconnection()打开连接,获得URLConnection类对象。用InputStream()获取字节流

1、SSRF/HttpURLConnection

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

从打开的连接获取一个 InputStream,可以从中得到 URL 请求的响应流。在调用这个方法时,会自动调用 URLConnection.connect() 方法,也就是建立连接。所以一旦调用 getInputStream() 连接就已经建立好了,不管后续做什么操作,这个 URL 请求都已经发出去了。

然后InputStreamReader()将字节流转化成字符流,BufferedReader()将字符流以缓存形式输出的方式来快速获取网络数据流,最终一行一行的输入到 html 变量中,输出到浏览器。


2、SSRF/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();


3、SSRF/ImageIO方法

// ImageIO ssrf vul String url = request.getParameter("url");

URL u = new URL(url);

BufferedImage img = ImageIO.read(u); // 发起请求,触发漏洞


4、SSRF/other

Request漏洞

String url = request.getParameter("url");

return Request.Get(url).execute().returnContent().toString();//发起请求


URL类中的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); //发起请求