diff --git a/src/test/java/org/olat/util/xss/client/CharsetUtil.java b/src/test/java/org/olat/util/xss/client/CharsetUtil.java index 70120a36efce7efa62205d0225049c4e418e11dd..c68295293a8c5a787c4aa51461e6623c4f5213ef 100644 --- a/src/test/java/org/olat/util/xss/client/CharsetUtil.java +++ b/src/test/java/org/olat/util/xss/client/CharsetUtil.java @@ -20,10 +20,23 @@ package org.olat.util.xss.client; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + /** * * @author jkraehemann, joel.kraehemann@frentix.com, frentix.com */ public class CharsetUtil { - + public static byte[] encode(String str, String charsetName){ + Charset charset = Charset.forName(charsetName); + + return(charset.encode(str).array()); + } + + public static String decode(byte[] str, String charsetName){ + Charset charset = Charset.forName(charsetName); + + return(charset.decode(ByteBuffer.wrap(str)).toString()); + } } diff --git a/src/test/java/org/olat/util/xss/client/HttpClient.java b/src/test/java/org/olat/util/xss/client/HttpClient.java deleted file mode 100644 index 82b14ce0501f865bb7449a8f60a9d566fd97f38e..0000000000000000000000000000000000000000 --- a/src/test/java/org/olat/util/xss/client/HttpClient.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * <a href="http://www.openolat.org"> - * OpenOLAT - Online Learning and Training</a><br> - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); <br> - * you may not use this file except in compliance with the License.<br> - * You may obtain a copy of the License at the - * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> - * <p> - * Unless required by applicable law or agreed to in writing,<br> - * software distributed under the License is distributed on an "AS IS" BASIS, <br> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> - * See the License for the specific language governing permissions and <br> - * limitations under the License. - * <p> - * Initial code contributed and copyrighted by<br> - * frentix GmbH, http://www.frentix.com - * <p> - */ - -package org.olat.util.xss.client; - -import java.io.IOException; - -/** - * - * @author jkraehemann, joel.kraehemann@frentix.com, frentix.com - */ -public interface HttpClient { - public void connect(String host, int port); - - public void setHttpHeader(byte[] buffer); - - public void httpGet(byte[] data); - public void httpPut(byte[] data); - public void httpDelete(byte[] data); - public void httpPost(byte[] data); -} diff --git a/src/test/java/org/olat/util/xss/client/HttpUtil.java b/src/test/java/org/olat/util/xss/client/HttpUtil.java index 987faf21d6a05a98b297d007df3916efae5f4797..0c61a399287c612e25a95643dacee62d3e71a029 100644 --- a/src/test/java/org/olat/util/xss/client/HttpUtil.java +++ b/src/test/java/org/olat/util/xss/client/HttpUtil.java @@ -20,13 +20,35 @@ package org.olat.util.xss.client; +import java.io.IOException; +import java.io.StringWriter; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.olat.util.FunctionalEPortfolioUtil; /** * * @author jkraehemann, joel.kraehemann@frentix.com, frentix.com */ public class HttpUtil { + + public final static String DEFAULT_HIJACKED_USER_AGENT = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)"; + + private String hijackedUserAgent; + + public HttpUtil() { + this.hijackedUserAgent = DEFAULT_HIJACKED_USER_AGENT; + } + enum HttpMethod { HTTP_PUT, HTTP_DELETE, @@ -34,11 +56,103 @@ public class HttpUtil { HTTP_POST, }; - public static byte[] createHttpHeader(HttpMethod method, HashSet<String> parameter, String headerEncoding, String bodyEncoding){ - byte[] header = null; + public byte[] createHttpGetHeader(String path, String host, + String jsessionId, String headerEncoding){ + + VelocityContext context = new VelocityContext(); + + context.put("path", path); + context.put("host", host); + context.put("userAgent", hijackedUserAgent); + context.put("jsessionId", jsessionId); + + VelocityEngine engine = null; + + engine = new VelocityEngine(); + + StringWriter sw = new StringWriter(); + + try { + engine.evaluate(context, sw, "xssClient_HTTP_GET-Header", HttpUtil.class.getResourceAsStream("xssClient_HTTP_GET-Header.vm")); + + } catch (ParseErrorException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (MethodInvocationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ResourceNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + byte[] header = CharsetUtil.encode(sw.toString(), headerEncoding); + + return(header); + } + + public byte[] createHttpPostHeader(String path, String host, + String jsessionId, String headerEncoding, String bodyEncoding, HashMap<String,String> parameters){ + + StringBuffer stringBuffer = new StringBuffer(); + + Set<Entry<String,String>> keys = parameters.entrySet(); + Iterator<Entry<String,String>> iter = keys.iterator(); + + while(iter.hasNext()){ + Entry<String,String> entry = iter.next(); + + stringBuffer.append(entry.getKey()) + .append("=") + .append(CharsetUtil.encode(entry.getValue(), bodyEncoding)) + .append('\n'); + } + + VelocityContext context = new VelocityContext(); + + context.put("path", path); + context.put("host", host); + context.put("userAgent", hijackedUserAgent); + context.put("contentLength", Integer.toString(stringBuffer.length())); + context.put("jsessionId", jsessionId); + context.put("parameters", stringBuffer.toString()); + + VelocityEngine engine = null; + + engine = new VelocityEngine(); + + StringWriter sw = new StringWriter(); + + try { + engine.evaluate(context, sw, "xssClient_HTTP_POST-Header", HttpUtil.class.getResourceAsStream("xssClient_HTTP_POST-Header.vm")); + + } catch (ParseErrorException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (MethodInvocationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ResourceNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } - //TODO:JK: implement me + byte[] header = CharsetUtil.encode(sw.toString(), headerEncoding); return(header); } + + public String getHijackedUserAgent() { + return hijackedUserAgent; + } + + public void setHijackedUserAgent(String hijackedUserAgent) { + this.hijackedUserAgent = hijackedUserAgent; + } } diff --git a/src/test/java/org/olat/util/xss/client/XssClient.java b/src/test/java/org/olat/util/xss/client/XssClient.java index 19c2bf6bf0bf9595fd4696c01fb004076dd53ec7..27a99e0cdc15f10a69978db57a7342b7d8d386cf 100644 --- a/src/test/java/org/olat/util/xss/client/XssClient.java +++ b/src/test/java/org/olat/util/xss/client/XssClient.java @@ -20,11 +20,15 @@ package org.olat.util.xss.client; +import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.net.InetSocketAddress; import org.apache.xmlrpc.webserver.XmlRpcServlet; @@ -39,7 +43,7 @@ import org.olat.util.xss.client.HttpUtil.HttpMethod; * * @author jkraehemann, joel.kraehemann@frentix.com, frentix.com */ -public class XssClient extends XmlRpcServlet implements HttpClient { +public class XssClient extends XmlRpcServlet { final static String DEFAULT_ENCODING = "UTF-8"; @@ -48,13 +52,33 @@ public class XssClient extends XmlRpcServlet implements HttpClient { final static String DEFAULT_BODY_ENCODING = "UTF-16"; final static String DEFAULT_SCRIPT_ENCODING = "UTF-7"; + final static int DEFAULT_FIELD_LENGTH_LIMITATION = 255; + final static int DEFAULT_THREAD_COUNT = 100; final static int DEFAULT_FAKE_USER_COUNT = 100; final static int DEFAULT_CONCURRENT_USER_COUNT = 100; + final static int DEFAULT_DISTRIBUTED_CHUNK_SIZE = 65535; + + final static String DEFAULT_ESCAPING_PATTERN = "\\\\\\//"; + final static String DEFAULT_CLOSING_TAGS_PATTERN = "</body></html>${\"xssCommonInjectionCode\"}"; + final static String DEFAULT_CLOSING_JSON_PATTERN = "',xssAlert: ${\"xssJSonInjectionCode\"};{"; + final static String DEFAULT_COMMENT_OUT_PATTERN = "${\"xssInlineInjectionCode\"}<!--"; + final static String DEFAULT_SCRIPTIFY_PATTERN = "${\"xssInlineInjectionCode\"}<javascript>"; + final static String DEFAULT_FRAMEIFY_PATTERN = "${\"xssInlineInjectionCode\"}<frame src=\"javascript:void(){window.document.body}\" />"; + final static String DEFAULT_IFRAMEIFY_PATTERN = "${\"xssInlineInjectionCode\"}<iframe src=\"javascript:void(){window.document.body}\" />"; + final static String DEFAULT_TOPLEVEL_FRAME = "<iframe style=\"z-index: -1;\" src=\"javascript:void(){${\"xssSnippedInjectionCode\"}}\">"; + final static String DEFAULT_B_MAIN_ONLY_PATTERN = "<div id=\"b_main\" class=\"javascript:void(){${\"xssSnippedInjectionCode\"}}\"/>"; enum XssStrategy{ TRICK_ESCAPING, CLOSE_TAGS, + COMMENT_OUT, + SCRIPTIFY, + FRAMEIFY, + IFRAMIFY, + TOPLEVEL_FRAME, + B_MAIN_ONLY, + CLOSE_JSON, MASQUERADE_ENCODING, FAKE_USERS, CONCURRENT_USERS, @@ -77,12 +101,20 @@ public class XssClient extends XmlRpcServlet implements HttpClient { private Socket connection; private OutputStream out; + private int fieldLengthLimitation; + private int threadCount; private int fakeUserCount; private int concurrentUserCount; + private int distributedChunkSize; + + private String escapingPattern; + private String closingTagsPattern; private List<Script> scripts; + private HttpUtil httpUtil; + public XssClient(){ this.defaultEncoding = DEFAULT_ENCODING; @@ -94,12 +126,20 @@ public class XssClient extends XmlRpcServlet implements HttpClient { this.connection = new Socket(); this.out = null; + this.fieldLengthLimitation = DEFAULT_FIELD_LENGTH_LIMITATION; + this.threadCount = DEFAULT_THREAD_COUNT; this.fakeUserCount = DEFAULT_FAKE_USER_COUNT; this.concurrentUserCount = DEFAULT_CONCURRENT_USER_COUNT; + this.distributedChunkSize = DEFAULT_DISTRIBUTED_CHUNK_SIZE; + + this.escapingPattern = DEFAULT_ESCAPING_PATTERN; + this.closingTagsPattern = DEFAULT_CLOSING_TAGS_PATTERN; this.scripts = new ArrayList<Script>(); + this.httpUtil = new HttpUtil(); + reloadScripts(); } @@ -117,51 +157,155 @@ public class XssClient extends XmlRpcServlet implements HttpClient { scripts.add(script); } - @Override - public void connect(String host, int port) { - // TODO Auto-generated method stub - + public void connect(String host, int port) throws IOException { + connection.connect(new InetSocketAddress(host, port)); + out = connection.getOutputStream(); } - @Override public void setHttpHeader(byte[] buffer) { this.header = buffer; } - - @Override - public void httpGet(byte[] data) { - // TODO Auto-generated method stub + + private HashMap<String,String> trickEscaping(HashMap<String,String> parameter, int space){ + + if(parameter == null){ + return(null); + } + + HashMap<String,String> injectionCode = new HashMap<String,String>(); + Iterator<String> iter = parameter.keySet().iterator(); + int iNext = escapingPattern.length(); + + while(iter.hasNext()){ + String key = iter.next(); + StringBuffer stringBuffer = new StringBuffer(); + + for(int i = 0; + iNext < fieldLengthLimitation && + iNext < distributedChunkSize && + iNext < space; + i = iNext){ + stringBuffer.append(escapingPattern); + + iNext = i + escapingPattern.length(); + } + + injectionCode.put(key, stringBuffer.toString()); + } + return(injectionCode); } - @Override - public void httpPut(byte[] data) { - // TODO Auto-generated method stub + private HashMap<String,String> closeTags(HashMap<String,String> parameter, int space){ + + if(parameter == null){ + return(null); + } + + HashMap<String,String> injectionCode = new HashMap<String,String>(); + Iterator<String> iter = parameter.keySet().iterator(); + int iNext = escapingPattern.length(); + while(iter.hasNext()){ + String key = iter.next(); + StringBuffer stringBuffer = new StringBuffer(); + + for(int i = 0; + iNext < fieldLengthLimitation && + iNext < distributedChunkSize && + iNext < space; + i = iNext){ + stringBuffer.append(closingTagsPattern); + + iNext = i + closingTagsPattern.length(); + } + + injectionCode.put(key, stringBuffer.toString()); + } + + return(injectionCode); } - - @Override - public void httpDelete(byte[] data) { - // TODO Auto-generated method stub + + private HashMap<String,String> commentOut(HashMap<String,String> parameter){ + HashMap<String,String> injectionCode = new HashMap<String,String>(); + + //TODO:JK: implement me + + return(injectionCode); } - - @Override - public void httpPost(byte[] data) { - // TODO Auto-generated method stub + + private HashMap<String,String> scriptify(HashMap<String,String> parameter){ + HashMap<String,String> injectionCode = new HashMap<String,String>(); + + //TODO:JK: implement me + + return(injectionCode); } - - public void attack(String path, HttpMethod method, HashSet<String> parameter, XssStrategy strategy, String snipped){ - this.attack("localhost", 8080, path, method, parameter, strategy, snipped); + + private HashMap<String,String> frameify(HashMap<String,String> parameter){ + HashMap<String,String> injectionCode = new HashMap<String,String>(); + + //TODO:JK: implement me + + return(injectionCode); + } - private void attack(String host, int port, String path, HttpMethod method, HashSet<String> parameter, XssStrategy strategy, String snipped){ + private HashMap<String,String> b_main_only(HashMap<String,String> parameter){ + HashMap<String,String> injectionCode = new HashMap<String,String>(); + + //TODO:JK: implement me + + return(injectionCode); + + } + + private HashMap<String,String> applyStrategy(XssStrategy[] strategy, HashMap<String,String> parameter){ + HashMap<String,String> injectionCode = new HashMap<String,String>(); + + //TODO:JK: implement me + + return(injectionCode); + } + + public void attack(String path, + HttpMethod method, HashMap<String,String> parameter, String jsessionId, + XssStrategy[] strategy, String snipped, boolean distributed) throws IOException{ + this.attack("localhost", 8080, path, + method, parameter, jsessionId, + strategy, snipped, distributed); + } + + private void attack(String host, int port, String path, + HttpMethod method, HashMap<String,String> parameter, String jsessionId, + XssStrategy[] strategy, String snipped, boolean distributed) throws IOException { connect(host, port); - byte[] header = HttpUtil.createHttpHeader(method, parameter, getClientEncoding(), getBodyEncoding()); + //TODO:JK: implement other methods + byte[] header = null; + switch(method){ + case HTTP_GET: + { + header = httpUtil.createHttpGetHeader(path, host + ":" + port, + jsessionId, getClientEncoding()); + } + break; + case HTTP_POST: + { + HashMap<String,String> injectionCode = applyStrategy(strategy, parameter); + + header = httpUtil.createHttpPostHeader(path, host + ":" + port, + jsessionId, getClientEncoding(), getBodyEncoding(), injectionCode); + } + break; + default: + break; + } + out.write(header); } public String getDefaultEncoding() { @@ -236,6 +380,14 @@ public class XssClient extends XmlRpcServlet implements HttpClient { this.out = out; } + public int getFieldLengthLimitation() { + return fieldLengthLimitation; + } + + public void setFieldLengthLimitation(int fieldLengthLimitation) { + this.fieldLengthLimitation = fieldLengthLimitation; + } + public int getThreadCount() { return threadCount; } @@ -260,6 +412,30 @@ public class XssClient extends XmlRpcServlet implements HttpClient { this.concurrentUserCount = concurrentUserCount; } + public int getDistributedChunkSize() { + return distributedChunkSize; + } + + public void setDistributedChunkSize(int distributedChunkSize) { + this.distributedChunkSize = distributedChunkSize; + } + + public String getEscapingPattern() { + return escapingPattern; + } + + public void setEscapingPattern(String escapingPattern) { + this.escapingPattern = escapingPattern; + } + + public String getClosingTagsPattern() { + return closingTagsPattern; + } + + public void setClosingTagsPattern(String closingTagsPattern) { + this.closingTagsPattern = closingTagsPattern; + } + public List<Script> getScripts() { return scripts; } diff --git a/src/test/java/org/olat/util/xss/client/_ressources/xssClient_HTTP_GET-Header.vm b/src/test/java/org/olat/util/xss/client/_ressources/xssClient_HTTP_GET-Header.vm new file mode 100644 index 0000000000000000000000000000000000000000..bc43edbdb7488fda53b079c2a6b95d70c886fdb2 --- /dev/null +++ b/src/test/java/org/olat/util/xss/client/_ressources/xssClient_HTTP_GET-Header.vm @@ -0,0 +1,12 @@ +GET ${"path"} HTTP/1.1 +Host: ${"host"} +User-Agent: ${"userAgent"} +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Cookie: JSESSIONID=${"jsessionId"} +Pragma: no-cache +Cache-Control: no-cache diff --git a/src/test/java/org/olat/util/xss/client/_ressources/xssClient_HTTP_POST-Header.vm b/src/test/java/org/olat/util/xss/client/_ressources/xssClient_HTTP_POST-Header.vm new file mode 100644 index 0000000000000000000000000000000000000000..b08421162c1f71845f22e570ac291fbb513c274e --- /dev/null +++ b/src/test/java/org/olat/util/xss/client/_ressources/xssClient_HTTP_POST-Header.vm @@ -0,0 +1,16 @@ +POST ${"path"} HTTP/1.1 +Host: ${"host"} +User-Agent: ${"userAgent"} +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Content-Type: application/x-www-form-urlencoded +Content-Length: ${"contentLength"} +Cookie: JSESSIONID=${"jsessionId"} +Pragma: no-cache +Cache-Control: no-cache + +${"parameters"}