├── .gitignore ├── LICENSE ├── README.md └── getExploit.py /.gitignore: -------------------------------------------------------------------------------- 1 | archive.zip 2 | exploits/* 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Giovanny Andres Gongora Granada 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # getExploit 2 | 3 | Python script to explore exploits from exploit-db.com. Exist a similar script in Kali Linux, but in difference this python script will have provide more flexibility at search and download time. 4 | 5 | ## Usage 6 | 7 | Run the `getExploit.py` script to enter at text interface. 8 | 9 | **NOTE:** If running for the first time, the script will automatically download the latest exploits archive 10 | at startup. 11 | 12 | If you want to update the database downloaded from exploit-db.com, run `getExploit.py updatedb` and it will start the download of the latest exploit list from the website. 13 | 14 | ### Searching exploits 15 | 16 | The `search` command allows you to search for a given pattern in any field of the original exploit-db's 17 | CSV file. The search query must be in the form of `field_name:pattern` couples, if no field name is 18 | given, `description` is the default. 19 | 20 | Available fields are: 21 | * `id` - the internal exploit's ID 22 | * `file` - the path where the exploit file can be found 23 | * `description` - informations about exploit and targetted software 24 | * `date` - the date the exploit was released 25 | * `author` - well, self-explanatory, huh? 26 | * `platform` - the platform type the exploit runs on 27 | * `type` - exploit classification, possible values are: 28 | * `local` 29 | * `shellcode` 30 | * `dos` 31 | * `remote` 32 | * `webapps` 33 | 34 | If the pattern you want to search contains spaces, you can quote it using either single or double 35 | quotes (see screenshot below). 36 | 37 | It is also possible to search using a regular expression by enclosing your pattern in quotes 38 | (simple or double) and prefixing it with 'r'. 39 | 40 | To sum it up, here are the possible search formats: 41 | 42 | * `description:zabbix` - single word substring search 43 | * `description:'zabbix 2.'` / `description:"zabbix 2."` - quoted pattern substring search 44 | * `description:r'za\w\wix'` / `description:r"za\w\wix"` - regular expression search 45 | 46 | ### Getting exploit infos 47 | 48 | To show all the available details about an exploit, use the `info` command. This command takes a 49 | single argument, which is the ID of the exploit you want details for. 50 | 51 | ### Updating database 52 | 53 | Running the `updatedb` command will download the latest exploits archive from exploit-db.com and 54 | extract it in an `exploits` folder in current directory. 55 | 56 | ## Licensing 57 | 58 | This script is licensed under MIT terms. 59 | 60 | -------------------------------------------------------------------------------- /getExploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | # Made by Giovanny A. Gongora G. (gioyik) 4 | 5 | # define 6 | __app__ = 'getExploit' 7 | __author__ = 'Giovanny Andres Gongora Granada' 8 | __date__ = '2015-01-02' 9 | __license__ = 'MIT' 10 | 11 | # imports 12 | import cmd 13 | import csv 14 | import os 15 | import re 16 | import shutil 17 | import sys 18 | import zipfile 19 | from copy import copy 20 | from collections import defaultdict 21 | from urllib2 import urlopen 22 | 23 | MYSELF = os.path.realpath(__file__) 24 | if os.path.islink(MYSELF): MYSELF = os.readlink(MYSELF) 25 | CURRENT_DIR = os.path.realpath(os.path.dirname(MYSELF)) 26 | EXPLOITS_DIR = os.path.join(CURRENT_DIR, 'exploits') 27 | EXPLOITS_CSV = os.path.join(EXPLOITS_DIR, 'exploit-database-master/files.csv') 28 | ARCHIVE_PATH = os.path.join(CURRENT_DIR, 'archive.zip') 29 | ARCHIVE_URL = "https://github.com/offensive-security/exploit-database/archive/master.zip?raw" 30 | 31 | 32 | class ExploitSearch(cmd.Cmd): 33 | prompt = '\033[1;31mgetExploit\033[0m\033[1;32m>\033[0m ' 34 | intro = ( 35 | '\n' 36 | '#[ getExploit.py ]#\n' 37 | '#[ Search exploits from exploit-db database ]#\n' 38 | ) 39 | fields = ('id', 'file', 'description', 'date', 'author', 'platform', 'type', 'port') 40 | search_regexes = { 41 | 'plain': [ 42 | re.compile(r'(\w+):(?!r[\'"])([^ \'"]+)'), 43 | re.compile(r'(\w+):(?:\'|")([^"\']+)') 44 | ], 45 | 'regex': [ 46 | re.compile(r'(\w+):r(?:\'|")([^"\']+)') 47 | ] 48 | } 49 | unquoted_search_re = re.compile(r'(\w+):(?!r[\'"])([^ \'"]+)') 50 | quoted_search_re = re.compile(r'(\w+):(?:\'|")([^"\']+)') 51 | regex_search_re = re.compile(r'(\w+):r(?:\'|")([^"\']+)') 52 | highlighted_fields_map = { 53 | 'id': ['id'], 54 | 'description': ['description'], 55 | 'file': ['type', 'platform'] 56 | } 57 | 58 | def __init__(self, csv_file=EXPLOITS_CSV): 59 | self.csv_file = csv_file 60 | self.exploits = [] 61 | self.load_csv() 62 | self.fields_value_completion = { 63 | 'platform': set([e['platform'] for e in self.exploits]), 64 | 'type': set([e['type'] for e in self.exploits]), 65 | 'port': set([e['port'] for e in self.exploits]) 66 | } 67 | cmd.Cmd.__init__(self) 68 | 69 | def download_archive(self): 70 | resp = urlopen(ARCHIVE_URL) 71 | file_size = int(resp.info().getheaders("Content-Length")[0]) 72 | downloaded_size = 0 73 | block_size = 4096 74 | with open(ARCHIVE_PATH, 'wb') as outfile: 75 | buff = resp.read(block_size) 76 | while buff: 77 | outfile.write(buff) 78 | downloaded_size += len(buff) 79 | downloaded_part = float(downloaded_size) / file_size 80 | progress_size = int(downloaded_part * 50) 81 | status = '[{0}{1}] {2:.2%}'.format( 82 | '#' * progress_size, 83 | ' ' * (50 - progress_size), 84 | downloaded_part 85 | ) 86 | sys.stdout.write(status) 87 | sys.stdout.write('\b' * (len(status)+1)) 88 | buff = resp.read(block_size) 89 | sys.stdout.write('\n') 90 | 91 | def parse_args(self, args): 92 | search_args = { 93 | 'plain': {}, 94 | 'regex': {} 95 | } 96 | if ':' not in args: 97 | search_args['plain']['description'] = args 98 | else: 99 | for search_type, regexes in self.search_regexes.iteritems(): 100 | for regex in regexes: 101 | result = regex.findall(args) 102 | if result is not None: 103 | for field_name, pattern in result: 104 | search_args[search_type][field_name] = pattern 105 | return search_args 106 | 107 | def load_csv(self): 108 | if not os.path.exists(self.csv_file): 109 | print "Database not found, updating now!\n" 110 | self.updatedb() 111 | with open(self.csv_file) as infile: 112 | reader = csv.reader(infile) 113 | header = reader.next() 114 | for entry in reader: 115 | exploit = dict(zip(header, entry)) 116 | if exploit['port'] == '0': 117 | exploit['port'] = 'n/a' 118 | if not exploit['platform']: 119 | exploit['platform'] = 'n/a' 120 | if '//' in exploit['file']: 121 | exploit['file'] = exploit['file'].replace('//', '/') 122 | self.exploits.append(exploit) 123 | 124 | def search(self, search_params): 125 | matches = [] 126 | args = self.parse_args(search_params) 127 | for exploit in self.exploits: 128 | matching = True 129 | for search_type, search_args in args.iteritems(): 130 | if search_type == 'plain': 131 | for field_name, pattern in search_args.iteritems(): 132 | if pattern.lower() not in exploit[field_name].lower(): 133 | matching = False 134 | break 135 | if not matching: 136 | break 137 | elif search_type == 'regex': 138 | for field_name, pattern in search_args.iteritems(): 139 | if re.search(pattern, exploit[field_name], flags=re.I) is None: 140 | matching = False 141 | break 142 | if not matching: 143 | break 144 | if matching: 145 | matches.append((exploit, args)) 146 | return matches 147 | 148 | def do_search(self, line): 149 | """ 150 | search - search database for exploits 151 | Usage: search field:pattern [field:pattern, ...] 152 | """ 153 | results = self.search(line) 154 | for result, args in results: 155 | flattened_args = {} 156 | for search_type in ('plain', 'regex'): 157 | for k, v in args[search_type].iteritems(): 158 | flattened_args[k] = v 159 | result = copy(result) 160 | for field_name, search_vals in self.highlighted_fields_map.iteritems(): 161 | for search_val in search_vals: 162 | if search_val in flattened_args: 163 | pattern = flattened_args[search_val] 164 | result[field_name] = re.sub( 165 | pattern if (field_name in args['regex']) else re.escape(pattern), 166 | lambda matchobj: '\033[1;33m' + matchobj.group(0) + '\033[0m', 167 | result[field_name], 168 | flags=re.I 169 | ) 170 | result_str = "[%s] %s - %s" % (result['id'], result['description'], result['file']) 171 | print result_str 172 | print '' 173 | 174 | def complete_search(self, text, line, begidx, endidx): 175 | last_arg = line.split()[-1] 176 | if last_arg: 177 | if ':' in last_arg: 178 | field_name, pattern = last_arg.split(':') 179 | if pattern: 180 | return [ 181 | f for f in self.fields_value_completion[field_name] 182 | if f.startswith(pattern) 183 | ] 184 | return [f for f in self.fields_value_completion[field_name]] 185 | return [f+':' for f in self.fields if f.startswith(last_arg)] 186 | return [f + ':' for f in self.fields] 187 | 188 | def info(self, exploit_id): 189 | for exploit in self.exploits: 190 | if exploit['id'] == exploit_id: 191 | return exploit 192 | return None 193 | 194 | def do_info(self, line): 195 | """ 196 | info - get details about given exploit 197 | Usage: info [exploitID] 198 | """ 199 | result = self.info(line) 200 | if result is None: 201 | print "No exploits in the database with this ID: %s\n" % line 202 | return 203 | desc_len = len(result['description']) 204 | fstring = "{:-<13} | {:-<%d}" % (desc_len+2) 205 | print ("{:=^%d}" % (desc_len+18)).format(' #%s ' % result['id']) 206 | print fstring.format('Filename ', result['file']+' ') 207 | print fstring.format('Description ', result['description']+' ') 208 | print fstring.format('Date ', result['date']+' ') 209 | print fstring.format('Author ', result['author']+' ') 210 | print fstring.format('Platform ', result['platform']+' ') 211 | print fstring.format('Type ', result['type']+' ') 212 | print fstring.format('Port ', result['port']+' ') 213 | print (desc_len+18)*'='+'\n' 214 | 215 | def complete_info(self, text, line, begidx, endidx): 216 | if not text: 217 | return [e['id'] for e in self.exploits] 218 | else: 219 | return [e['id'] for e in self.exploits if e['id'].startswith(text)] 220 | 221 | def updatedb(self): 222 | print "Downloading latest exploits archive..." 223 | self.download_archive() 224 | print "Extracting files..." 225 | if os.path.exists(EXPLOITS_DIR): 226 | shutil.rmtree(EXPLOITS_DIR) 227 | os.mkdir(EXPLOITS_DIR) 228 | with zipfile.ZipFile(ARCHIVE_PATH, 'r') as infile: 229 | infile.extractall(path=EXPLOITS_DIR) 230 | os.remove(ARCHIVE_PATH) 231 | os.chmod(EXPLOITS_CSV, 0644) 232 | self.exploits = [] 233 | self.load_csv() 234 | print "OK\n" 235 | 236 | def do_updatedb(self, line): 237 | """ 238 | updatedb - update local exploits database 239 | Usage: updatedb 240 | """ 241 | self.updatedb() 242 | 243 | def do_EOF(self, line): 244 | return True 245 | 246 | 247 | def main(restarted=False): 248 | es = ExploitSearch() 249 | if restarted: 250 | es.intro = '\n' 251 | try: 252 | es.cmdloop() 253 | except KeyboardInterrupt: 254 | main(True) 255 | 256 | 257 | if __name__ == '__main__': 258 | main() 259 | --------------------------------------------------------------------------------