├── .gitignore ├── LICENSE ├── README.md ├── ansible ├── README.md ├── attack.sh ├── cleanup.yml ├── crawl.sh ├── main.yml ├── webgoat-container-7.0.1-war-exec.jar └── webgoat.sh ├── dist └── sheepdog-1.0-SNAPSHOT.jar ├── pom.xml └── src └── main └── java └── com └── contrastsecurity └── sheepdog ├── AttackThread.java ├── ServerThread.java ├── Sheepdog.java ├── StreamGobbler.java └── sheepdog.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .settings 3 | .classpath 4 | .project 5 | *retry 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Contrast Security OSS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sheepdog 2 | ======== 3 | 4 | Image of SheepDogCat 5 | 6 | Sheepdog is a simple tool to generate normal and attack traffic for OWASP WebGoat. It can be used with security technologies like WAF and RASP in demonstrations and to verify that they are doing a tiny piece of what they are supposed to do. Sheepdog is not intended to be an exhaustive set of security tests. It has some basic SQL injection, XSS, path traversal, and that kind of thing. 7 | 8 | Simply start WebGoat with: 9 | 10 | > java -jar webgoat-container-7.0.1-war-exec.jar 11 | 12 | Then in another window start sheepdog with: 13 | 14 | > java -jar sheepdog-1.0.jar 15 | 16 | There are several configurable properties that you can use to simulate a variety of crawls/attacks. Note that SheepDog sends an X-Forwarded-For header with random IP address for each attack thread. 17 | 18 | > Usage: java -jar sheepdog.jar 19 | > 20 | > -t threads (default 3) 21 | > 22 | > -s seconds (default 60) 23 | > 24 | > -d delay milliseconds between requests (default -1) 25 | > 26 | > -a attack percentage (default 50) 27 | > 28 | > -p port for WebGoat (default 8080) 29 | > 30 | > -v verbose 31 | 32 | 33 | ## Sample usage 34 | 35 | $ java -jar target/sheepdog-1.0-SNAPSHOT.jar -t 3 -s 3600 -d 1000 -a 50 36 | Usage: java -jar sheepdog.jar [-t -s -d -a -p -v] 37 | Using default value for flag 'p', using 8080 38 | Starting 3 attack threads, each with: 39 | 3600 seconds 40 | 1000ms delay between requests 41 | 50% attack parameters 42 | target: http://localhost:8080/WebGoat/ 43 | 44 | Starting AttackThread (110.104.52.59) 45 | Starting AttackThread (93.65.24.224) 46 | Starting AttackThread (161.144.64.146) 47 | 48 | POST from 238.20.254.102 to http://localhost:8080/WebGoat/j_spring_security_check 49 | [username=guest, password=guest] 50 | HTTP/1.1 302 Found 51 | 52 | POST from 93.65.24.224 to http://localhost:8080/WebGoat/j_spring_security_check 53 | [username=guest, password=guest] 54 | HTTP/1.1 302 Found 55 | 56 | POST from 161.144.64.146 to http://localhost:8080/WebGoat/j_spring_security_check 57 | [username=guest, password=guest] 58 | HTTP/1.1 302 Found 59 | 60 | POST from 161.144.64.146 to http://localhost:8080/WebGoat/attack?Screen=733&menu=1200 61 | [SUBMIT=zoees822] 62 | HTTP/1.1 200 OK 63 | 64 | POST from 93.65.24.224 to http://localhost:8080/WebGoat/attack?Screen=534&menu=1900 65 | [id=sztol903, SUBMIT=rjeee272] 66 | HTTP/1.1 200 OK 67 | 68 | POST from 161.144.64.146 to http://localhost:8080/WebGoat/attack?Screen=726&menu=200&stage=1 69 | [action=' or 112=112--] 70 | HTTP/1.1 200 OK 71 | 72 | POST from 161.144.64.146 to http://localhost:8080/WebGoat/attack?Screen=737&menu=1100&stage=3 73 | [action=' or 1+2=3 --] 74 | HTTP/1.1 200 OK 75 | 76 | POST from 93.65.24.224 to http://localhost:8080/WebGoat/attack?Screen=498&menu=1300 77 | [clear_user=>, clear_pass=>, Submit=ctyna446] 78 | HTTP/1.1 200 OK 79 | 80 | 81 | 82 | ## Who made this? 83 | This project is sponsored by [Contrast Security](http://www.contrastsecurity.com/) and released under the MIT license. 84 | 85 | ![Contrast Security Logo](https://www.contrastsecurity.com/hs-fs/hubfs/Contrast_Security-2016/Image/LOGOContrastSec.SVG.svg?t=1488557782361&width=199&name=LOGOContrastSec.SVG.svg "Contrast Logo") 86 | -------------------------------------------------------------------------------- /ansible/README.md: -------------------------------------------------------------------------------- 1 | # Ansible Playbooks to Deploy Sheepdog with WebGoat7 2 | 3 | These two ansible playbooks automate the deployment and cleanup of Sheepdog and WebGoat7 to quickly setup a demo environment using the Contrast Security Secure DevOps Platform. 4 | 5 | ## To use this playbook, follow the instructions below 6 | 7 | ### Create a '~.contrast.cfg file' 8 | The playbook relies on API credentials in this file. 9 | ``` 10 | username: [username] 11 | service_key: [service_key] 12 | teamserver_url: [teamserver_url] 13 | teamserver_organization: [teamserver_organization] 14 | teamserver_url: [teamserver_url] 15 | api_key: [api_key] 16 | 17 | #These might not be required for the playbook to work. 18 | #@todo Validate the need for these fields 19 | agent_username: [agent_username] 20 | agent_service_key: [agent_service_key] 21 | ``` 22 | 23 | ### Clone this github repo. 24 | ``` 25 | $ git clone git@github.com:Contrast-Security-OSS/sheepdog.git 26 | ``` 27 | ### Run the ansible playbook 28 | ``` 29 | $ ansible-playbook ./sheepdog/ansible/main.yml 30 | ``` 31 | 32 | ### Execute attack.sh 33 | **Note:** ```attack.sh``` will take approximatley 30 minutes to run 34 | 35 | ``` 36 | $ cd ~/webgoat7 37 | $ ./attack.sh 38 | ``` 39 | 40 | 41 | ### Demo: Start webgoat7 attack is done 42 | ``` 43 | $ ./webgoat.sh 44 | ``` 45 | 46 | Then browse to webgoat at http://localhost:8080/WebGoat/ 47 | 48 | ## Cleanup 49 | ``` 50 | ansible-playbook ./sheepdog/ansible/cleanup.yml 51 | ``` 52 | -------------------------------------------------------------------------------- /ansible/attack.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # The port which webgoat will use 4 | wgport=${SHEEPDOG_WG_PORT:-8080} 5 | 6 | # Try to verify the webgoat port is open 7 | nc=/usr/bin/nc 8 | 9 | if test -x "$nc"; then 10 | if "$nc" -z localhost $wgport; then 11 | echo PORT $wgport is in use! 12 | echo Try setting \$SHEEPDOG_WG_PORT 13 | exit 1 14 | fi 15 | else 16 | echo '' 17 | echo '###################################' 18 | echo 'WARNING WARNING WARNING WARNING' 19 | echo '###################################' 20 | echo 'Unable to determine if there is a port confict.' 21 | echo '' 22 | echo "Webgoat will run on port $wgport" 23 | echo '' 24 | echo 'If you see no route coverage after sheepdog runs, you may need to' 25 | echo 'specify a different port with $SHEEPDOG_WG_PORT.' 26 | echo '' 27 | fi 28 | 29 | # trap ctrl-c and call ctrl_c() 30 | trap ctrl_c INT 31 | 32 | function ctrl_c() { 33 | echo "** Trapped CTRL-C" 34 | if [ -n $pid1 ]; then kill $pid1; fi 35 | if [ -n $pid2 ]; then kill $pid2; fi 36 | exit 1 37 | } 38 | 39 | function webgoat { 40 | java -javaagent:contrast.jar -Dcontrast.dir=working -Dcontrast.standalone.appname=$app -Dcontrast.override.appname="$app" -Dcontrast.path="/$path" -Dcontrast.server="$server" -Dcontrast.log.daily=true -jar webgoat-container-7.0.1-war-exec.jar -httpPort=$wgport > /dev/null 2>&1 & 41 | } 42 | 43 | function sheepdog { 44 | java -jar sheepdog-1.0-SNAPSHOT.jar -t 3 -s 3600 -d 1500 250 -a 90 -p $wgport > /dev/null 2>&1 & 45 | 46 | } 47 | 48 | declare -a configs=( 49 | "WebGoat7|DEV-WG7|TEST-WG7|STAGE-WG7|PROD-WG7" 50 | "OracleFS|DEV-OFS|TEST-OFS|PROD-OFS" 51 | "CustomerCare|DEV-CCB|TEST-CCB|PROD-CCB" 52 | "WebStore|DEV-WS|STAGE-WS|PROD-WS" 53 | "MedicalRecords|DEV-EMR|STAGE-EMR|PROD-EMR" 54 | "WebPOS|DEV-WPOS|STAGE-WPOS|PROD-WPOS" 55 | "TradingFloor|DEV-TF|TEST-TF|PROD-TF" ) 56 | 57 | for config in "${configs[@]}" 58 | do 59 | IFS='|' read -a item <<<"$config" 60 | app=${item[0]} 61 | 62 | for ((i = 1; i < ${#item[@]}; i++)) 63 | do 64 | server=${item[i]} 65 | path=$(echo "/$app" | tr '[:upper:]' '[:lower:]') 66 | echo "Starting $app on $server using $path" 67 | webgoat 68 | pid1=$! 69 | sleep 60 70 | echo "Attacking $app on $server using $path" 71 | sheepdog 72 | pid2=$! 73 | sleep 120 74 | kill -KILL $pid1 75 | kill -KILL $pid2 76 | echo "----------------" 77 | done 78 | 79 | done 80 | -------------------------------------------------------------------------------- /ansible/cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Cleanup WebGoat7 and Sheepdog 4 | hosts: localhost 5 | gather_facts: false 6 | vars: 7 | wg7_destination: ~/webgoat7 8 | tasks: 9 | - name: Remove Webgoat7 destination directory if it exists 10 | file: 11 | path: '{{wg7_destination}}' 12 | state: absent 13 | - name: Remove cron job for WebGoat7 attack 14 | cron: 15 | name: "Attack WwebGoat7" 16 | state: absent 17 | -------------------------------------------------------------------------------- /ansible/crawl.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # The port which webgoat will use 4 | wgport=${SHEEPDOG_WG_PORT:-8080} 5 | 6 | # Try to verify the webgoat port is open 7 | nc=/usr/bin/nc 8 | 9 | if test -x "$nc"; then 10 | if "$nc" -z localhost $wgport; then 11 | echo PORT $wgport is in use! 12 | echo Try setting \$SHEEPDOG_WG_PORT 13 | exit 1 14 | fi 15 | else 16 | echo '' 17 | echo '###################################' 18 | echo 'WARNING WARNING WARNING WARNING' 19 | echo '###################################' 20 | echo 'Unable to determine if there is a port confict.' 21 | echo '' 22 | echo "Webgoat will run on port $wgport" 23 | echo '' 24 | echo 'If you see no route coverage after sheepdog runs, you may need to' 25 | echo 'specify a different port with $SHEEPDOG_WG_PORT.' 26 | echo '' 27 | fi 28 | 29 | # trap ctrl-c and call ctrl_c() 30 | trap ctrl_c INT 31 | 32 | function ctrl_c() { 33 | echo "** Trapped CTRL-C" 34 | if [ -n $pid1 ]; then kill $pid1; fi 35 | if [ -n $pid2 ]; then kill $pid2; fi 36 | exit 1 37 | } 38 | 39 | function webgoat { 40 | java -javaagent:contrast.jar -Dcontrast.dir=working -Dcontrast.standalone.appname=$app -Dcontrast.override.appname="$app" -Dcontrast.path="/$path" -Dcontrast.server="$server" -Dcontrast.log.daily=true -jar webgoat-container-7.0.1-war-exec.jar -httpPort=$wgport > /dev/null 2>&1 & 41 | } 42 | 43 | function sheepdog { 44 | java -jar sheepdog-1.0-SNAPSHOT.jar -t 3 -s 3600 -d 1500 250 -a 0 -p $wgport > /dev/null 2>&1 & 45 | 46 | } 47 | 48 | declare -a configs=( 49 | "WebGoat7|DEV-WG7|TEST-WG7|STAGE-WG7|PROD-WG7" 50 | "OracleFS|DEV-OFS|TEST-OFS|PROD-OFS" 51 | "CustomerCare|DEV-CCB|TEST-CCB|PROD-CCB" 52 | "WebStore|DEV-WS|STAGE-WS|PROD-WS" 53 | "MedicalRecords|DEV-EMR|STAGE-EMR|PROD-EMR" 54 | "WebPOS|DEV-WPOS|STAGE-WPOS|PROD-WPOS" 55 | "TradingFloor|DEV-TF|TEST-TF|PROD-TF" ) 56 | 57 | for config in "${configs[@]}" 58 | do 59 | IFS='|' read -a item <<<"$config" 60 | app=${item[0]} 61 | 62 | for ((i = 1; i < ${#item[@]}; i++)) 63 | do 64 | server=${item[i]} 65 | path=$(echo "/$app" | tr '[:upper:]' '[:lower:]') 66 | echo "Starting $app on $server using $path" 67 | webgoat 68 | pid1=$! 69 | sleep 60 70 | echo "Attacking $app on $server using $path" 71 | sheepdog 72 | pid2=$! 73 | sleep 120 74 | kill -KILL $pid1 75 | kill -KILL $pid2 76 | echo "----------------" 77 | done 78 | 79 | done 80 | -------------------------------------------------------------------------------- /ansible/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: WebGoat7 Demo With Sheepdog 3 | hosts: localhost 4 | vars: 5 | wg7_destination: ~/webgoat7 6 | agent_type: java 7 | 8 | #@todo: gracefully fail if there is no file and produce a message. 9 | gather_facts: false 10 | tasks: 11 | - name: Include vars from .contrast.cfg 12 | include_vars: 13 | file: ~/.contrast.cfg 14 | name: contrast 15 | 16 | - name: Create Webgoat7 destination directory if it does not exist 17 | file: 18 | path: '{{wg7_destination}}' 19 | state: directory 20 | mode: 0755 21 | - name: Deploy scripts 22 | copy: 23 | src: '{{item}}' 24 | dest: '{{wg7_destination}}' 25 | mode: 0755 26 | with_items: 27 | - attack.sh 28 | - crawl.sh 29 | - webgoat.sh 30 | - name: Deploy sheepdog container 31 | copy: 32 | src: ../dist/sheepdog-1.0-SNAPSHOT.jar 33 | dest: '{{wg7_destination}}' 34 | - name: Copy webgoat container from our local source 35 | # get_url: 36 | # url: https://github.com/WebGoat/WebGoat/releases/download/7.0.1/webgoat-container-7.0.1-war-exec.jar 37 | # Changed this to include the file contained herein as the md5 of the static jar vs. the one in github are differeht. 38 | # Changed by Vihar on Apr 9. 39 | # Working with Jeff to determine pkgdiff between the two snapshots. 40 | copy: 41 | src: webgoat-container-7.0.1-war-exec.jar 42 | dest: '{{wg7_destination}}' 43 | 44 | 45 | #Download contrast.jar file from Teamserver 46 | - name: Create Authorization 47 | set_fact: 48 | authorization_key: "{{ contrast.username }}:{{ contrast.service_key }}" 49 | 50 | - name: Download Agent from TeamServer 51 | get_url: 52 | url: "{{ contrast.teamserver_url }}/Contrast/api/ng/{{ contrast.teamserver_organization }}/agents/default/{{ agent_type }}" 53 | dest: "{{wg7_destination}}/contrast.jar" 54 | headers: 'Accept:application/json,API-Key:{{ contrast.api_key }},Authorization:{{ authorization_key | b64encode }}' 55 | 56 | # - name: Setup cron job 57 | # cron: 58 | # name: "Attack WwebGoat7" 59 | # minute: "0" 60 | # hour: "0" 61 | # job: "{{wg7_destination}}/attack.sh" 62 | -------------------------------------------------------------------------------- /ansible/webgoat-container-7.0.1-war-exec.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/sheepdog/07522bff2ebe844ff261184b48cd749c67cf638d/ansible/webgoat-container-7.0.1-war-exec.jar -------------------------------------------------------------------------------- /ansible/webgoat.sh: -------------------------------------------------------------------------------- 1 | java -javaagent:contrast.jar -Dcontrast.path=~/webgoat7 -Dcontrast.dir=working -Dcontrast.server=PROD-DEMO -Dcontrast.server.activity.period=5000 -Dcontrast.log.daily=true -jar webgoat-container-7.0.1-war-exec.jar -httpPort=8080 2 | -------------------------------------------------------------------------------- /dist/sheepdog-1.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Contrast-Security-OSS/sheepdog/07522bff2ebe844ff261184b48cd749c67cf638d/dist/sheepdog-1.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | com.contrastsecurity 6 | sheepdog 7 | jar 8 | 9 | Contrast Sheepdog 10 | 11 | Contrast Sheepdog Project 12 | 13 | 14 | 15 | Contrast Security 16 | http://contrastsecurity.com 17 | 18 | 19 | 1.0-SNAPSHOT 20 | 21 | 22 | 23 | Jeff Williams 24 | Contrast Security 25 | jeff.williams@contrastsecurity.com 26 | 27 | Developer 28 | 29 | 30 | 31 | 32 | http://maven.apache.org 33 | 34 | 35 | junit 36 | junit 37 | 3.8.1 38 | test 39 | 40 | 41 | org.apache.httpcomponents 42 | httpclient 43 | 4.5.2 44 | 45 | 46 | commons-cli 47 | commons-cli 48 | 1.3.1 49 | 50 | 51 | commons-lang 52 | commons-lang 53 | 2.5 54 | 55 | 56 | commons-io 57 | commons-io 58 | 2.2 59 | 60 | 61 | com.google.code.gson 62 | gson 63 | 2.4 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-dependency-plugin 72 | 73 | 74 | copy-dependencies 75 | prepare-package 76 | 77 | copy-dependencies 78 | 79 | 80 | ${project.build.directory}/lib 81 | false 82 | false 83 | true 84 | 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-jar-plugin 91 | 3.0.2 92 | 93 | 94 | 95 | true 96 | lib/ 97 | com.contrastsecurity.sheepdog.Sheepdog 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-shade-plugin 105 | 2.3 106 | 107 | 108 | package 109 | 110 | shade 111 | 112 | 113 | 114 | 115 | com.contrastsecurity.sheepdog.Sheepdog 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/main/java/com/contrastsecurity/sheepdog/AttackThread.java: -------------------------------------------------------------------------------- 1 | package com.contrastsecurity.sheepdog; 2 | 3 | import java.security.SecureRandom; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | import org.apache.commons.lang.time.StopWatch; 10 | import org.apache.http.Consts; 11 | import org.apache.http.HttpEntity; 12 | import org.apache.http.NameValuePair; 13 | import org.apache.http.client.CookieStore; 14 | import org.apache.http.client.config.CookieSpecs; 15 | import org.apache.http.client.config.RequestConfig; 16 | import org.apache.http.client.entity.UrlEncodedFormEntity; 17 | import org.apache.http.client.methods.CloseableHttpResponse; 18 | import org.apache.http.client.methods.HttpGet; 19 | import org.apache.http.client.methods.HttpPost; 20 | import org.apache.http.impl.client.BasicCookieStore; 21 | import org.apache.http.impl.client.CloseableHttpClient; 22 | import org.apache.http.impl.client.HttpClients; 23 | import org.apache.http.message.BasicNameValuePair; 24 | import org.apache.http.util.EntityUtils; 25 | 26 | public class AttackThread extends Thread { 27 | 28 | private CloseableHttpClient httpclient; 29 | private CookieStore cookieStore; 30 | private StopWatch watch; 31 | 32 | /* Scan settings */ 33 | private String baseUrl; 34 | private String address; 35 | private List lessons; 36 | private int scanDuration; 37 | private int attackPercent; 38 | private long requestDelay; 39 | private boolean verbose; 40 | 41 | /* Statistics */ 42 | private int requestCount; 43 | private long totalScanTime; 44 | private double highest; 45 | private double lowest; 46 | 47 | public AttackThread( String baseUrl, String address, int scanDuration, long requestDelay, int attackPercent, boolean verbose ) { 48 | this.baseUrl = baseUrl; 49 | this.address = address; 50 | this.scanDuration = scanDuration; 51 | this.requestDelay = requestDelay; 52 | this.attackPercent = attackPercent; 53 | this.verbose = verbose; 54 | 55 | this.highest = -1; 56 | this.lowest = -1; 57 | 58 | this.watch = new StopWatch(); 59 | 60 | try { 61 | cookieStore = new BasicCookieStore(); 62 | RequestConfig globalConfig = RequestConfig.custom() 63 | .setCookieSpec(CookieSpecs.DEFAULT) 64 | .build(); 65 | httpclient = HttpClients.custom() 66 | .setDefaultCookieStore(cookieStore) 67 | .setDefaultRequestConfig(globalConfig) 68 | .build(); 69 | 70 | // login and get list of lessons 71 | List credentials = new ArrayList(); 72 | credentials.add(new BasicNameValuePair("username", "guest")); 73 | credentials.add(new BasicNameValuePair("password", "guest")); 74 | 75 | sendPost( "j_spring_security_check", credentials ); 76 | sendGet( "welcome.mvc", false ); 77 | 78 | String json = sendGet( "service/lessonmenu.mvc", false ); 79 | lessons = parseLessons( json ); 80 | 81 | } catch (Exception e ) { 82 | e.printStackTrace(); 83 | } 84 | } 85 | 86 | public void run() { 87 | long start = System.currentTimeMillis(); 88 | while( System.currentTimeMillis() - start < scanDuration * 1000 ) { 89 | try { 90 | long delay = this.requestDelay != -1 ? this.requestDelay : RANDOM.nextInt(1000); 91 | if(delay > 0) { 92 | Thread.sleep( delay ); 93 | } 94 | String page = lessons.get( RANDOM.nextInt( lessons.size() ) ); 95 | String[] parts = page.split( "/" ); 96 | String lesson = "attack?Screen=" + parts[1] + "&menu=" + parts[2]; 97 | if ( parts.length > 3 ) { 98 | lesson += "&stage=" + parts[3]; 99 | } 100 | String form = sendGet( lesson, false ); 101 | scan( lesson, form, attackPercent ); 102 | } catch( Exception e ) { 103 | e.printStackTrace(); 104 | } 105 | } 106 | } 107 | 108 | private void scan(String lesson, String form, int attackPercent ) throws Exception { 109 | List fields = parseForm( form ); 110 | boolean attack = RANDOM.nextBoolean(); 111 | permute( fields, attack, attackPercent ); 112 | sendPost( lesson, fields ); 113 | } 114 | 115 | private void permute(List fields, boolean attack, int attackPercent ) { 116 | for ( int i=0; i parseLessons(String lessons) { 132 | List allMatches = new ArrayList(); 133 | Matcher m = Pattern.compile("#(attack.*?)\"").matcher(lessons); 134 | while (m.find()) { 135 | String page = m.group(); 136 | if(verbose) { 137 | System.out.println(">>>>" + page ); 138 | } 139 | allMatches.add(page.substring(1,page.length()-1)); 140 | } 141 | return allMatches; 142 | } 143 | 144 | 145 | 146 | public String sendGet(String url, boolean xhr ) throws Exception { 147 | HttpGet httpGet = new HttpGet(baseUrl + url); 148 | // System.out.println( "SENDING: " + httpGet.getURI() ); 149 | httpGet.addHeader("X-Forwarded-For", address ); 150 | if ( xhr ) { 151 | httpGet.addHeader("X-Requested-With","XMLHttpRequest"); 152 | } 153 | watch.reset(); 154 | watch.start(); 155 | CloseableHttpResponse response = httpclient.execute(httpGet); 156 | HttpEntity entity = response.getEntity(); 157 | String content = EntityUtils.toString(entity); 158 | watch.stop(); 159 | long elapsed = watch.getTime(); 160 | checkHighestLowest(elapsed); 161 | this.totalScanTime += elapsed; 162 | this.requestCount++; 163 | response.close(); 164 | return content; 165 | } 166 | 167 | 168 | 169 | public String sendPost(String url, List fields ) throws Exception { 170 | UrlEncodedFormEntity entity = new UrlEncodedFormEntity(fields, Consts.UTF_8); 171 | HttpPost httpPost = new HttpPost(baseUrl + url); 172 | if(verbose) { 173 | System.out.println( "POST from " + address + " to " + httpPost.getURI() ); 174 | System.out.println( " " + fields ); 175 | } 176 | httpPost.addHeader("X-Forwarded-For", address ); 177 | httpPost.setEntity(entity); 178 | 179 | watch.reset(); 180 | watch.start(); 181 | CloseableHttpResponse response = httpclient.execute(httpPost); 182 | String content = EntityUtils.toString(response.getEntity()); 183 | watch.stop(); 184 | long elapsed = watch.getTime(); 185 | checkHighestLowest(elapsed); 186 | totalScanTime += elapsed; 187 | response.close(); 188 | if(verbose) { 189 | System.out.println( " " + response.getStatusLine() ); 190 | System.out.println(); 191 | } 192 | requestCount++; 193 | return content; 194 | } 195 | 196 | private void checkHighestLowest(long elapsed) { 197 | if(highest == -1 || elapsed > highest) { 198 | highest = elapsed; 199 | } 200 | if(lowest == -1 || elapsed < lowest) { 201 | lowest = elapsed; 202 | } 203 | } 204 | 205 | public long getTotalScanTime() { 206 | return totalScanTime; 207 | } 208 | 209 | public int getRequestCount() { 210 | return requestCount; 211 | } 212 | 213 | private static String getAttack() { 214 | return FRAGS[ RANDOM.nextInt(FRAGS.length) ]; 215 | } 216 | 217 | private static List parseForm( String content ) { 218 | List fields = new ArrayList(); 219 | int formStart = content.indexOf( "" ); 221 | if ( formStart != -1 && formStop != -1 ) { 222 | String formContent = content.substring( formStart, formStop ); 223 | String[] tags = formContent.split( ">"); 224 | for ( String tag: tags ) { 225 | tag = tag.trim(); 226 | if ( tag.startsWith (" ls -lisa", 295 | "\"' & ping 192.168.0.1", 296 | "]>&xxe;", 297 | "]>&xxe;" 298 | }; 299 | } 300 | -------------------------------------------------------------------------------- /src/main/java/com/contrastsecurity/sheepdog/ServerThread.java: -------------------------------------------------------------------------------- 1 | package com.contrastsecurity.sheepdog; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | 7 | public class ServerThread extends Thread { 8 | 9 | public static void main( String[] args ) throws Exception { 10 | 11 | int servers = 0; 12 | try { 13 | servers = Integer.parseInt( args[0] ); 14 | } catch( Exception e ) {} 15 | 16 | List urls = new ArrayList(); 17 | 18 | for ( int i=0; i> " + cmd ); 33 | Process p = Runtime.getRuntime().exec( cmd ); 34 | StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), "ERROR-" + i); 35 | StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), "OUTPUT-"+i); 36 | errorGobbler.start(); 37 | outputGobbler.start(); 38 | while ( !errorGobbler.isFound() ) { 39 | System.out.println( "Waiting for " + servername+ ":" + httpPort + " to start" ); 40 | Thread.sleep( 5000 ); 41 | } 42 | } 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/contrastsecurity/sheepdog/Sheepdog.java: -------------------------------------------------------------------------------- 1 | package com.contrastsecurity.sheepdog; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.security.SecureRandom; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.apache.commons.cli.CommandLine; 12 | import org.apache.commons.cli.CommandLineParser; 13 | import org.apache.commons.cli.DefaultParser; 14 | import org.apache.commons.cli.Options; 15 | import org.apache.commons.io.FileUtils; 16 | 17 | import com.google.gson.Gson; 18 | import com.google.gson.GsonBuilder; 19 | 20 | 21 | public class Sheepdog { 22 | 23 | private static SecureRandom sr = new SecureRandom(); 24 | 25 | // usage: java Sheepdog #attackThreads #duration #rate 26 | public static void main(String[] args) throws Exception { 27 | 28 | System.out.println( "Usage: java -jar sheepdog.jar [-t -s -d -a -p -v]"); 29 | 30 | Options options = new Options(); 31 | options.addOption("t", true, "number of concurrent threads"); 32 | options.addOption("o", true, "output file"); 33 | options.addOption("s", true, "duration (seconds)"); 34 | options.addOption("d", true, "delay (milliseconds, -1 for random delay)"); 35 | options.addOption("a", true, "attack percentage (0-100)"); 36 | options.addOption("p", true, "server port (8080 by default)"); 37 | options.addOption("v", false, "verbose"); 38 | 39 | CommandLineParser parser = new DefaultParser(); 40 | CommandLine cmd = parser.parse( options, args); 41 | 42 | int maxThreads = readOption(cmd, "t", 3); 43 | int duration = readOption(cmd, "s", 60); 44 | int delay = readOption(cmd, "d", -1); 45 | int attackPercent = readOption(cmd, "a", 50); 46 | int port = readOption(cmd, "p", 8080); 47 | String baseUrl = "http://localhost:" + port + "/WebGoat/"; 48 | 49 | // Starting server threads (TBD) -- See ServerThread.java 50 | System.out.println( "Starting " + maxThreads + " attack threads, each with:" ); 51 | System.out.println( " " + duration + " seconds" ); 52 | if(delay != -1) { 53 | System.out.println( " " + delay + "ms delay between requests" ); 54 | } else { 55 | System.out.println( " random delay between requests" ); 56 | } 57 | System.out.println( " " + attackPercent + "% attack parameters" ); 58 | System.out.println( " target: " + baseUrl ); 59 | System.out.println(); 60 | 61 | List threads = new ArrayList(); 62 | for(int i=0;i threads, String outputFile) { 82 | List> stats = new ArrayList>(); 83 | for(int i=0;i threadStats = new HashMap(); 86 | threadStats.put("count", thread.getRequestCount()); 87 | threadStats.put("elapsed", thread.getTotalScanTime()); 88 | stats.add(threadStats); 89 | } 90 | 91 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 92 | String json = gson.toJson(stats); 93 | try { 94 | FileUtils.write(new File(outputFile), json); 95 | } catch (IOException e) { 96 | System.err.println("Problem writing statistics to " + outputFile); 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | private static boolean threadsAreRunning(List threads) { 102 | for(AttackThread t : threads) { 103 | if(t.isAlive()) { 104 | return true; 105 | } 106 | } 107 | return false; 108 | } 109 | 110 | private static void sleep(long ms) { 111 | try { 112 | Thread.sleep(ms); 113 | } catch (Exception e) { } 114 | } 115 | 116 | private static int readOption(CommandLine cmd, String flag, int defaultValue) { 117 | int rc = defaultValue; 118 | try { 119 | rc = Integer.parseInt(cmd.getOptionValue(flag)); 120 | } catch (Exception e) { 121 | System.err.println("Using default value for flag '" + flag + "', using " + defaultValue); 122 | } 123 | return rc; 124 | } 125 | 126 | private static String getRandomAddress() { 127 | StringBuilder sb=new StringBuilder(); 128 | sb.append( sr.nextInt(256) ); 129 | sb.append( "." ); 130 | sb.append( sr.nextInt(256) ); 131 | sb.append( "." ); 132 | sb.append( sr.nextInt(256) ); 133 | sb.append( "." ); 134 | sb.append( sr.nextInt(256) ); 135 | return sb.toString(); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/contrastsecurity/sheepdog/StreamGobbler.java: -------------------------------------------------------------------------------- 1 | package com.contrastsecurity.sheepdog; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | 8 | class StreamGobbler extends Thread { 9 | InputStream is; 10 | String type; 11 | boolean found = false; 12 | 13 | StreamGobbler(InputStream is, String type) { 14 | this.is = is; 15 | this.type = type; 16 | } 17 | 18 | public void run() { 19 | try { 20 | InputStreamReader isr = new InputStreamReader(is); 21 | BufferedReader br = new BufferedReader(isr); 22 | String line = null; 23 | while ((line = br.readLine()) != null) { 24 | if ( line.startsWith( "INFO: Starting ProtocolHandler [\"http-bio-" ) ) { 25 | found = true; 26 | } 27 | System.out.println(type + ">" + line); 28 | } 29 | } catch (IOException ioe) { 30 | ioe.printStackTrace(); 31 | } 32 | } 33 | 34 | public boolean isFound() { 35 | return found; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/com/contrastsecurity/sheepdog/sheepdog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | --------------------------------------------------------------------------------