+If you like the poject, you can star java-sec-code project to support me. With your support, I will be able to make `Java sec code` better 😎.
diff --git a/README_zh.md b/README_zh.md
index 70e68837..b5c658c3 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -2,7 +2,11 @@
对于学习Java漏洞代码来说,`Java Sec Code`是一个非常强大且友好的项目。
-[英文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README.md) 😋[阿里集团安全紫军招聘](https://talent.alibaba.com/off-campus-position/937731?trace=qrcode_share)
+[英文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README.md) 😋
+
+## 招聘
+
+[Alibaba招聘-安全攻防/研究(P5-P7)](https://github.com/JoyChou93/java-sec-code/wiki/Alibaba-Purple-Team-Job-Description)
## 介绍
@@ -36,12 +40,14 @@ joychou/joychou123
- [Log4j](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Log4j.java)
- [ooxmlXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java)
- [PathTraversal](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/PathTraversal.java)
+- [QLExpress](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/QLExpress.java)
- [RCE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Rce.java)
- Runtime
- ProcessBuilder
- ScriptEngine
- Yaml Deserialize
- Groovy
+- [Shiro](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Shiro.java)
- [SpEL](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SpEL.java)
- [SQL Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SQLI.java)
- [SSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SSRF.java)
@@ -137,7 +143,7 @@ Viarus
例子:
```
-http://localhost:8080/java-sec-code-1.0.0/rce/exec?cmd=whoami
+http://localhost:8080/java-sec-code-1.0.0/rce/runtime/exec?cmd=whoami
```
返回:
@@ -193,12 +199,7 @@ Tomcat默认JSESSION会话有效时间为30分钟,所以30分钟不操作会
核心开发者: [JoyChou](https://github.com/JoyChou93).其他开发者:[lightless](https://github.com/lightless233), [Anemone95](https://github.com/Anemone95)。欢迎各位提交PR。
-## 捐赠
-
-如果你喜欢这个项目,你可以捐款来支持我。 有了你的支持,我将能够更好地制作`Java sec code`项目。
-
-### Alipay
+## 支持
-扫描支付宝二维码支持`Java sec code`。
+如果你喜欢这个项目,你可以star该项目支持我。 有了你的支持,我将能够更好地制作`Java sec code`项目。
-
diff --git a/docker-compose.yml b/docker-compose.yml
index cb3f8efa..7e9c878e 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,9 +1,11 @@
-version : '2'
+version : '3'
services:
jsc:
image: joychou/jsc:latest
+ command: ["java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000", "-jar", "jsc.jar"]
ports:
- "8080:8080"
+ - "8000:8000"
links:
- j_mysql
diff --git a/java-sec-code.iml b/java-sec-code.iml
index 6e093293..5c58c92b 100644
--- a/java-sec-code.iml
+++ b/java-sec-code.iml
@@ -1,226 +1,14 @@
-+ * T(java.lang.Runtime).getRuntime().exec("open -a Calculator") */ - @GetMapping("/spel/vuln") - public String rce(String expression) { + @RequestMapping("/spel/vuln1") + public String spel_vuln1(String value) { ExpressionParser parser = new SpelExpressionParser(); - // fix method: SimpleEvaluationContext - return parser.parseExpression(expression).getValue().toString(); + return parser.parseExpression(value).getValue().toString(); + } + + /** + * Use Spel to execute cmd.
+ * #{T(java.lang.Runtime).getRuntime().exec('open -a Calculator')}
+ * Exploit must add #{} if using TemplateParserContext.
+ */
+ @RequestMapping("spel/vuln2")
+ public String spel_vuln2(String value) {
+ StandardEvaluationContext context = new StandardEvaluationContext();
+ SpelExpressionParser parser = new SpelExpressionParser();
+ Expression expression = parser.parseExpression(value, new TemplateParserContext());
+ Object x = expression.getValue(context); // trigger vulnerability point
+ return x.toString(); // response
+ }
+
+ /**
+ * Use SimpleEvaluationContext to fix.
+ */
+ @RequestMapping("spel/sec")
+ public String spel_sec(String value) {
+ SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
+ SpelExpressionParser parser = new SpelExpressionParser();
+ Expression expression = parser.parseExpression(value, new TemplateParserContext());
+ Object x = expression.getValue(context);
+ return x.toString();
}
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
- String expression = "T(java.lang.Runtime).getRuntime().exec(\"open -a Calculator\")";
+ String expression = "1+1";
String result = parser.parseExpression(expression).getValue().toString();
System.out.println(result);
}
+
}
diff --git a/src/main/java/org/joychou/controller/Test.java b/src/main/java/org/joychou/controller/Test.java
deleted file mode 100644
index 3b75c7d0..00000000
--- a/src/main/java/org/joychou/controller/Test.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.joychou.controller;
-
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-
-@RestController
-@RequestMapping("/test")
-public class Test {
-
- @RequestMapping(value = "/")
- public String Index(HttpServletResponse response, String empId) {
-
- System.out.println(empId);
- Cookie cookie = new Cookie("XSRF-TOKEN", "123");
- cookie.setDomain("taobao.com");
- cookie.setMaxAge(-1); // forever time
- response.addCookie(cookie);
- return "success";
- }
-
-
- @RequestMapping(value = "/aa")
- public void test(HttpServletResponse response, String empId) {
-
- System.out.println(empId);
- Cookie cookie = new Cookie("XSRF-TOKEN", "123");
- cookie.setDomain("taobao.com");
- cookie.setMaxAge(-1); // forever time
- response.addCookie(cookie);
- }
-}
diff --git a/src/main/java/org/joychou/controller/URLWhiteList.java b/src/main/java/org/joychou/controller/URLWhiteList.java
index 35d37576..156cc73d 100644
--- a/src/main/java/org/joychou/controller/URLWhiteList.java
+++ b/src/main/java/org/joychou/controller/URLWhiteList.java
@@ -6,7 +6,8 @@
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
-import java.net.MalformedURLException;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.regex.Matcher;
@@ -86,20 +87,21 @@ public String regex(@RequestParam("url") String url) {
/**
- * The bypass of using java.net.URL to getHost.
+ * The bypass of using {@link java.net.URL} to getHost.
*
- * Bypass poc1: curl -v 'http://localhost:8080/url/vuln/url_bypass?url=http://evel.com%5c@www.joychou.org/a.html' - * Bypass poc2: curl -v 'http://localhost:8080/url/vuln/url_bypass?url=http://evil.com%5cwww.joychou.org/a.html' + * bypass 1 + * bypass 2 + * *
- * More details: https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass
+ * More details
*/
@GetMapping("/vuln/url_bypass")
- public String url_bypass(String url) throws MalformedURLException {
+ public void url_bypass(String url, HttpServletResponse res) throws IOException {
logger.info("url: " + url);
if (!SecurityUtil.isHttp(url)) {
- return "Url is not http or https";
+ return;
}
URL u = new URL(url);
@@ -109,11 +111,10 @@ public String url_bypass(String url) throws MalformedURLException {
// endsWith .
for (String domain : domainwhitelist) {
if (host.endsWith("." + domain)) {
- return "Good url.";
+ res.sendRedirect(url);
}
}
- return "Bad url.";
}
diff --git a/src/main/java/org/joychou/controller/XStreamRce.java b/src/main/java/org/joychou/controller/XStreamRce.java
index 62616e95..aa3469bd 100644
--- a/src/main/java/org/joychou/controller/XStreamRce.java
+++ b/src/main/java/org/joychou/controller/XStreamRce.java
@@ -2,6 +2,7 @@
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
+import com.thoughtworks.xstream.security.AnyTypePermission;
import org.joychou.dao.User;
import org.joychou.util.WebUtils;
import org.springframework.web.bind.annotation.PostMapping;
@@ -24,20 +25,9 @@ public class XStreamRce {
public String parseXml(HttpServletRequest request) throws Exception {
String xml = WebUtils.getRequestBody(request);
XStream xstream = new XStream(new DomDriver());
+ xstream.addPermission(AnyTypePermission.ANY); // This will cause all XStream versions to be affected.
xstream.fromXML(xml);
return "xstream";
}
- public static void main(String[] args) {
- User user = new User();
- user.setId(0);
- user.setUsername("admin");
-
- XStream xstream = new XStream(new DomDriver());
- String xml = xstream.toXML(user); // Serialize
- System.out.println(xml);
-
- user = (User) xstream.fromXML(xml); // Deserialize
- System.out.println(user.getId() + ": " + user.getUsername());
- }
}
diff --git a/src/main/java/org/joychou/controller/XXE.java b/src/main/java/org/joychou/controller/XXE.java
index 14b6a232..58e90739 100644
--- a/src/main/java/org/joychou/controller/XXE.java
+++ b/src/main/java/org/joychou/controller/XXE.java
@@ -4,6 +4,9 @@
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.data.web.ProjectedPayload;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@@ -27,6 +30,7 @@
import org.apache.commons.digester3.Digester;
import org.jdom2.input.SAXBuilder;
import org.joychou.util.WebUtils;
+import org.xmlbeam.annotation.XBRead;
/**
* Java xxe vuln and security code.
@@ -38,8 +42,8 @@
@RequestMapping("/xxe")
public class XXE {
- private static Logger logger = LoggerFactory.getLogger(XXE.class);
- private static String EXCEPT = "xxe except";
+ private static final Logger logger = LoggerFactory.getLogger(XXE.class);
+ private static final String EXCEPT = "xxe except";
@PostMapping("/xmlReader/vuln")
public String xmlReaderVuln(HttpServletRequest request) {
@@ -226,16 +230,15 @@ public String DigesterSec(HttpServletRequest request) {
}
- // 有回显
- @RequestMapping(value = "/DocumentBuilder/vuln01", method = RequestMethod.POST)
- public String DocumentBuilderVuln01(HttpServletRequest request) {
+ /**
+ * Use request.getInputStream to support UTF16 encoding.
+ */
+ @RequestMapping(value = "/DocumentBuilder/vuln", method = RequestMethod.POST)
+ public String DocumentBuilderVuln(HttpServletRequest request) {
try {
- String body = WebUtils.getRequestBody(request);
- logger.info(body);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
- StringReader sr = new StringReader(body);
- InputSource is = new InputSource(sr);
+ InputSource is = new InputSource(request.getInputStream());
Document document = db.parse(is); // parse xml
// 遍历xml节点name和value
@@ -249,7 +252,6 @@ public String DocumentBuilderVuln01(HttpServletRequest request) {
buf.append(String.format("%s: %s\n", node.getNodeName(), node.getTextContent()));
}
}
- sr.close();
return buf.toString();
} catch (Exception e) {
e.printStackTrace();
@@ -258,43 +260,6 @@ public String DocumentBuilderVuln01(HttpServletRequest request) {
}
}
-
- // 有回显
- @RequestMapping(value = "/DocumentBuilder/vuln02", method = RequestMethod.POST)
- public String DocumentBuilderVuln02(HttpServletRequest request) {
- try {
- String body = WebUtils.getRequestBody(request);
- logger.info(body);
-
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- DocumentBuilder db = dbf.newDocumentBuilder();
- StringReader sr = new StringReader(body);
- InputSource is = new InputSource(sr);
- Document document = db.parse(is); // parse xml
-
- // 遍历xml节点name和value
- StringBuilder result = new StringBuilder();
- NodeList rootNodeList = document.getChildNodes();
- for (int i = 0; i < rootNodeList.getLength(); i++) {
- Node rootNode = rootNodeList.item(i);
- NodeList child = rootNode.getChildNodes();
- for (int j = 0; j < child.getLength(); j++) {
- Node node = child.item(j);
- // 正常解析XML,需要判断是否是ELEMENT_NODE类型。否则会出现多余的的节点。
- if (child.item(j).getNodeType() == Node.ELEMENT_NODE) {
- result.append(String.format("%s: %s\n", node.getNodeName(), node.getFirstChild()));
- }
- }
- }
- sr.close();
- return result.toString();
- } catch (Exception e) {
- logger.error(e.toString());
- return EXCEPT;
- }
- }
-
-
@RequestMapping(value = "/DocumentBuilder/Sec", method = RequestMethod.POST)
public String DocumentBuilderSec(HttpServletRequest request) {
try {
@@ -447,7 +412,32 @@ private static void response(NodeList rootNodeList){
}
}
- public static void main(String[] args) {
+ /**
+ * Receiving POST requests supporting both JSON and XML.
+ * CVE-2018-1259
+ */
+ @PostMapping(value = "/xmlbeam/vuln")
+ HttpEntity Normal: Bypass: Hello . Welcome to login java-sec-code application. Application Infomation
- Swagger
- CmdInject
- JSONP
- Picture Upload
- File Upload
- Cors
- PathTraversal
- SqlInject
- SSRF
- RCE
- ooxml XXE
- xlsx-streamer XXE
- Hello . Welcome to login java-sec-code application. Application Infomation
+ Swagger
+ CmdInject
+ JSONP
+ Picture Upload
+ File Upload
+ Cors
+ PathTraversal
+ SqlInject
+ SSRF
+ RCE
+ ooxml XXE
+ xlsx-streamer XXE
+ actuator env
+
- JWTCreateToken
- GetUserFromJWTToken
- ...
+ JWTCreateToken
+ GetUserFromJWTToken
+ ...
*
- *
* @return decimal ip
*/
@@ -177,15 +178,17 @@ public static String host2ip(String host) {
return "";
}
- // convert octal to decimal
+ // convert octal to decimal
if(isOctalIP(host)) {
host = decimalIp;
}
try {
- InetAddress IpAddress = InetAddress.getByName(host); // send dns request
+ // send dns request
+ InetAddress IpAddress = InetAddress.getByName(host);
return IpAddress.getHostAddress();
} catch (Exception e) {
+ logger.error("host2ip exception " + e.getMessage());
return "";
}
}
@@ -196,44 +199,57 @@ public static String host2ip(String host) {
* @return Octal ip returns true, others return false. 012.23.78.233 return true. 012.0x17.78.233 return false.
*/
public static boolean isOctalIP(String host) {
- String[] ipParts = host.split("\\.");
- StringBuilder newDecimalIP = new StringBuilder();
- boolean is_octal = false;
-
- // Octal ip only has number and dot character.
- if (isNumberOrDot(host)) {
-
- if (ipParts.length != 1 && ipParts.length != 4) {
- return false;
- }
-
- // 000000001205647351
- if( ipParts.length == 1 && host.startsWith("0") ) {
- decimalIp = Integer.valueOf(host, 8).toString();
- return true;
- }
+ try{
+ String[] ipParts = host.split("\\.");
+ StringBuilder newDecimalIP = new StringBuilder();
+ boolean is_octal = false;
+
+ // Octal ip only has number and dot character.
+ if (isNumberOrDot(host)) {
+
+ // not support ipv6
+ if (ipParts.length > 4) {
+ logger.error("Illegal ipv4: " + host);
+ return false;
+ }
- // 0000012.23.78.233
- for(String ip : ipParts) {
- if (!isNumber(ip)){
- throw new SSRFException("Illegal host: " + host + ".");
+ // 01205647351
+ if( ipParts.length == 1 && host.startsWith("0") ) {
+ decimalIp = Integer.valueOf(host, 8).toString();
+ return true;
}
- if (ip.startsWith("0")) {
- if (Integer.valueOf(ip, 8) >= 256){
- throw new SSRFException("Illegal host: " + host + ".\t" + ip + " is above 255.");
+
+ // 012.23.78.233
+ for(String ip : ipParts) {
+ if (!isNumber(ip)){
+ logger.error("Illegal ipv4: " + host);
+ return false;
}
- newDecimalIP.append(Integer.valueOf(ip, 8)).append(".");
- is_octal = true;
- }else{
- if (Integer.valueOf(ip, 10) >= 256) {
- throw new SSRFException("Illegal host: " + host + ".\t" + ip + " is above 255.");
+ // start with "0", but not "0"
+ if (ip.startsWith("0") && !ip.equals("0")) {
+ if (Integer.valueOf(ip, 8) >= 256){
+ logger.error("Illegal ipv4: " + host);
+ return false;
+ }
+ newDecimalIP.append(Integer.valueOf(ip, 8)).append(".");
+ is_octal = true;
+ }else{
+ if (Integer.valueOf(ip, 10) >= 256) {
+ logger.error("Illegal ipv4: " + host);
+ return false;
+ }
+ newDecimalIP.append(ip).append(".");
}
- newDecimalIP.append(ip).append(".");
}
+ // delete last char .
+ decimalIp = newDecimalIP.substring(0, newDecimalIP.lastIndexOf("."));
}
- decimalIp = newDecimalIP.substring(0, newDecimalIP.lastIndexOf("."));
+ return is_octal;
+ } catch (Exception e){
+ logger.error("SSRFChecker isOctalIP exception: " + e.getMessage());
+ return false;
}
- return is_octal;
+
}
/**
@@ -246,7 +262,7 @@ private static boolean isNumber(String str) {
}
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
- if (ch < 48 || ch > 57) {
+ if (ch < '0' || ch > '9') {
return false;
}
}
@@ -261,7 +277,7 @@ private static boolean isNumber(String str) {
private static boolean isNumberOrDot(String s) {
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
- if ((ch < 48 || ch > 57) && ch != 46){
+ if ((ch < '0' || ch > '9') && ch != '.'){
return false;
}
}
diff --git a/src/main/java/org/joychou/util/CookieUtils.java b/src/main/java/org/joychou/util/CookieUtils.java
index eddae0db..f63b6e42 100644
--- a/src/main/java/org/joychou/util/CookieUtils.java
+++ b/src/main/java/org/joychou/util/CookieUtils.java
@@ -21,4 +21,17 @@ public static boolean deleteCookie(HttpServletResponse res, String cookieName) {
return false;
}
}
+
+ public static boolean addCookie(HttpServletResponse res, String cookieName, String cookieValue) {
+ try {
+ Cookie cookie = new Cookie(cookieName, cookieValue);
+ cookie.setMaxAge(1000);
+ cookie.setPath("/");
+ res.addCookie(cookie);
+ return true;
+ } catch (Exception e) {
+ log.error(e.toString());
+ return false;
+ }
+ }
}
diff --git a/src/main/java/org/joychou/util/HttpUtils.java b/src/main/java/org/joychou/util/HttpUtils.java
index 2c248b29..c1eac95c 100644
--- a/src/main/java/org/joychou/util/HttpUtils.java
+++ b/src/main/java/org/joychou/util/HttpUtils.java
@@ -94,7 +94,6 @@ public static String URLConnection(String url) {
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
- // BufferedReader in = new BufferedReader(new InputStreamReader(u.openConnection().getInputStream()));
String inputLine;
StringBuilder html = new StringBuilder();
diff --git a/src/main/java/org/joychou/util/WebUtils.java b/src/main/java/org/joychou/util/WebUtils.java
index 4816df8e..0445f310 100644
--- a/src/main/java/org/joychou/util/WebUtils.java
+++ b/src/main/java/org/joychou/util/WebUtils.java
@@ -2,9 +2,7 @@
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
import com.google.common.base.Preconditions;
import org.springframework.web.util.HtmlUtils;
@@ -17,7 +15,6 @@ public static String getRequestBody(HttpServletRequest request) throws IOExcepti
return convertStreamToString(in);
}
-
// https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java
public static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index d5c6d5ab..326a2b76 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -9,8 +9,6 @@ logging.level.org.joychou.mapper=debug
# Spring Boot Actuator Config
management.security.enabled=false
-endpoints.enabled=true
-
# logging.config=classpath:logback-online.xml
@@ -48,10 +46,14 @@ swagger.enable = true
### no need to login page begins ###
-joychou.no.need.login.url = /css/**, /js/**, /xxe/**, /rce/**, /deserialize/**, /test/**, /ws/**
+joychou.no.need.login.url = /css/**, /js/**, /xxe/**, /rce/**, /deserialize/**, /test/**, /ws/**, /shiro/**, /ssrf/**, /spel/**, /qlexpress/**
### no need to login page ends ###
# http header max size
#server.max-http-header-size=30000
+
+# Fake aksk. Simulate actuator info leak.
+jsc.accessKey.id=LTAI5tSAEPX3Z5N2Yt8ogc2y
+jsc.accessKey.secret=W1Poxj09wN0Zu6dDsS0on3SIUhOhK7
\ No newline at end of file
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html
index 8511d5fd..7e7061be 100644
--- a/src/main/resources/templates/index.html
+++ b/src/main/resources/templates/index.html
@@ -5,29 +5,30 @@