├── README.md ├── eyewitness_parser ├── README.md ├── LICENSE.md └── eyewitness_parse.rb ├── nmap_scripts ├── nmapscrape.rb ├── LICENSE.md ├── README.md └── nmap_parser.py ├── wmi_loggedin_user_enumerator ├── README.md ├── LICENSE.md └── wmi_loggedin_users.rb ├── ike_aggressive_scanner ├── README.md ├── LICENSE.md └── ike-aggressive-scanner.rb ├── LICENSE ├── iSMTP ├── LICENSE.md ├── README.md └── iSMTP.py ├── iWebAudit ├── LICENSE.md ├── README.md └── iWebAudit.py ├── ifconfig ├── LICENSE.md ├── README.md ├── ifconfigs.rb └── mac-ifconfigs.rb ├── pyfoca ├── LICENSE.md ├── README.md └── pyfoca.py ├── smbspider ├── LICENSE.md ├── README.md └── smbspider.py ├── name_formatter ├── LICENSE ├── README.md └── name_formatter.rb └── vhost_lookup ├── LICENSE.md ├── README.md └── vhost_lookup.rb /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | This repository contains a random variety of penetration testing tools that consultants may find useful on their engagements. 4 | 5 | Feel free to make any changes, suggest features, or provide comments. 6 | 7 | Contact information 8 | === 9 | Facebook: https://www.facebook.com/vonahisec
10 | Twitter: https://www.twitter.com/vonahisec
11 | LinkedIn: https://www.linkedin.com/company/vonahisec
12 | Instagram: https://www.instagram.com/vonahisec -------------------------------------------------------------------------------- /eyewitness_parser/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | If you've ever used the EyeWitness tool to analyze web applications (request and responses) and to take screenshots, this tool basically takes the HTML reports and convert them over to a text file. It's most useful for when you want to browse through the applications based on their titles. For example, if there's 900 web applications, by organizing the titles, you'll be able to efficiently go through the applications and assess only a sample of the applications that are similar (e.g. 30 of 300 same printers, etc). 4 | 5 | Instructions 6 | === 7 | Place this ruby file in the directory that contains the HTML reports, and simply run it afterwards: 8 |

 9 | [07.22.2017/21:34:09] root@box $ ./eyewitness_parse.rb
10 | 
-------------------------------------------------------------------------------- /nmap_scripts/nmapscrape.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This script simply parses a greppable nmap file for opened TCP 4 | # and UDP ports. Run the file and target the nmap file for results. 5 | # 6 | # Author: Alton Johnson 7 | # Updated: 08/10/2016 8 | # 9 | 10 | $banner = "\n " + "-" * 54 + "\n Nmap Parser v2.0, Alton Johnson (alton@vonahi.io)\n " + "-" * 54 11 | $banner += "\n\n Usage: #{$0} /path/to/results.gnmap\n\n" 12 | 13 | (puts $banner; exit(0)) if ARGV.first.nil? 14 | port_files = {} 15 | 16 | Dir.mkdir 'open-ports' unless File.exists? "open-ports" 17 | File.open(ARGV.first) { |f| 18 | f.read.split("\n").each do |l| 19 | ipaddr = l.split(" ")[1] 20 | l.scan(/(\d+)\/open/).flatten.each do |p| 21 | port_files[p] = File.open("open-ports/#{p}.txt", 'a') unless port_files.key? p 22 | port_files[p].write("#{ipaddr}\n") 23 | end 24 | end 25 | port_files.values.each {|pf| pf.close} 26 | puts "\n [Nmap Port Parser] Completed. Check the 'open-ports' folder.\n\n" 27 | } 28 | -------------------------------------------------------------------------------- /wmi_loggedin_user_enumerator/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | This tool was designed to assist with enumerating a list of domain-attached computers to identify what users are logged on to them. A penetration tester may want to do this for a variety of reasons, including looking for domain administrator accounts, accounts that may have access to data of interest, etc. 4 | 5 | Instructions 6 | === 7 | Usage menu: 8 |

 9 | [07.22.2017/21:50:56] root@box $ ./wmi_loggedin_users.rb 
10 | 
11 |  ---------------------------------------------------------------------------
12 |   Logged In User Enumerator (via WMIC) - Alton Johnson (alton@vonahi.io)
13 |  ---------------------------------------------------------------------------
14 | 
15 |  Usage: ./wmi_loggedin_users.rb -f  -d  -u  -p 
16 | 
17 |   -f  File containing IP addresses to scan.
18 |   -d  Domain used for the valid account.
19 |   -u  Administrator account that will be used for logging in.
20 |   -p  Password for the administrator account.
21 | 
-------------------------------------------------------------------------------- /ike_aggressive_scanner/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | Kali Linux comes with the tool ike-scan pre-installed already. However, what happens if you need to run this against 50 VPN gateways? Sure you can wrap it in a for loop, but this tool simplifies this process by outputting the PSK in a file (if found) for each respective device, and also provides a nice output table for you. The table looks great for reporting purposes. :) 4 | 5 | Instructions 6 | === 7 | Create a file that contains a list of IP addresses (of VPN gateways) and then run the tool. Here's an example of the usage menu: 8 |

 9 | [07.22.2017/21:34:09] root@box $ ./ike-aggressive-scanner.rb 
10 | 
11 |   --------------------------------------------------------------------
12 |     IKE Aggressive Mode Scanner - Alton Johnson (alton@vonahi.io)
13 |   --------------------------------------------------------------------
14 | 
15 |   Usage: ./ike-aggressive-scanner.rb -f vpn_gateways.txt
16 | 
17 |   -f     Specifies file containing IP addresses for scanning.
18 | 
19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /iSMTP/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /iWebAudit/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /ifconfig/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 altjx 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 | -------------------------------------------------------------------------------- /pyfoca/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /smbspider/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /name_formatter/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /nmap_scripts/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /vhost_lookup/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /eyewitness_parser/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /ike_aggressive_scanner/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /wmi_loggedin_user_enumerator/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vonahi Security 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 | -------------------------------------------------------------------------------- /ifconfig/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | Sometimes when you run ifconfig on both Mac OS and also UNIX/Linux, you like to just get straight to the point. Sometimes it takes you a few seconds to look for the MAC address and/or IP address for a specific interface. These tools basically simplify that process for you. 4 | 5 | Instructions 6 | === 7 | Usage menu (from Linux CLI): 8 |

 9 | [07.22.2017/21:34:09] root@box $ ./ifconfigs.rb 
10 | +-----------+--------------+---------------------+---------------+-------------------+
11 | |       1337 H4x0Rz Linux Ifconfig Parser - Alton Johnson (@altonjx)                 |
12 | +-----------+--------------+---------------------+---------------+-------------------+
13 | | Interface | IPv4 Address | Subnet Mask         | Broadcast     | MAC Address       |
14 | +-----------+--------------+---------------------+---------------+-------------------+
15 | | ens33:    | 192.168.1.10 | 255.255.255.0 (/24) | 192.168.1.255 | 00:0c:29:56:d8:ea |
16 | | lo:       | 127.0.0.1    | 255.0.0.0 (/8)      |               |                   |
17 | +-----------+--------------+---------------------+---------------+-------------------+
18 | 
19 | 
20 | -------------------------------------------------------------------------------- /iWebAudit/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | Many times, on an internal penetration test, you run across numerous web servers that you don't care about. As opposed to opening up every web server to see what they are, iWebAudit will scan a list of web servers and grab all their titles. By only reading a little bit from each server's response, this makes reporting only the titles much faster. 4 | 5 | Instructions 6 | === 7 | Usage menu: 8 |

 9 | [07.22.2017/21:34:09] root@box $ ./iWebAudit.py
10 | 
11 | ------------------------------------------------------------------------------
12 |  iWebAudit v1.5 - Web Page Title Analyzer, Alton Johnson (alton@vonahi.io)  
13 | ------------------------------------------------------------------------------ 
14 | 
15 |  Usage: iWebAudit -f <file> -o <output file> -v -h -t 5
16 | 
17 |    -f <file>   Supports list of IPs and/or http[s]:// formatted IPs.
18 |    -o <file>   Outputs the results to a file of your choice.
19 |    -h          Enables HTTP PUT Method Discovery. (optional)
20 |    -t <secs>   Sets default timeout. Default is 5. (optional)
21 |    -v 	       Displays details as script runs. (optional)
22 | 
23 | -------------------------------------------------------------------------------- /nmap_scripts/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | These Nmap scripts were created to make parsing Nmap files much more convenient. For example, with nmap_parser.py, it simply creates a text file, named whatever port that was found to be opened, and it contains a list of IP addresses that has that port open. It's much easier to feed other tools a list of IP addresses that have a port opened this way. 4 | 5 | With the nmapscrape.rb script, this allows for you to parse a greppable Nmap output into a nicely formatted table. 6 | 7 | Instructions 8 | === 9 | With regards to the Nmap parser script, here's an example of the usage menu: 10 | 11 |

12 |  ------------------------------------------------------------------------
13 |   nmapparse 1.0 - Nmap Output Parser, Alton Johnson (alton@vonahi.io)
14 |  ------------------------------------------------------------------------
15 |  
16 |  Usage: ./nmapparse.py results.gnmap
17 | 
18 |  Note: This script must point to a grepable output file from nmap to work properly.
19 | 
20 | 21 | With regards to the Nmap scrape script, here's an example of the usage menu: 22 | 23 |

24 |  ----------------------------------------------------
25 |  Nmap Parser v1.0, Alton Johnson (alton@vonahi.io) 
26 |  ----------------------------------------------------
27 | 
28 |  Usage: nmapscrape.py <gnmap file>
29 | 
-------------------------------------------------------------------------------- /ifconfig/ifconfigs.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'terminal-table' 3 | require 'ipaddr' 4 | 5 | ifconfig = `ifconfig`.split("\n\n") 6 | 7 | oint = '' 8 | int = '' 9 | omac = '' 10 | 11 | # Begin the table. 12 | table = Terminal::Table.new do |t| 13 | # Define the table headers. 14 | t << ['Interface','IPv4 Address','Subnet Mask','Broadcast','MAC Address'] 15 | t.add_separator 16 | 17 | ifconfig.each do |line| 18 | int = "" 19 | # Regex used to grab for specific information. 20 | unless !int.empty? 21 | int = line.split(" ")[0] 22 | end 23 | ipaddr = line.scan(/\b(?:\d{1,3}\.){3}\d{1,3}\b/)[0] 24 | netmask = line.scan(/\b(?:\d{1,3}\.){3}\d{1,3}\b/)[1] 25 | broadcast = line.scan(/\b(?:\d{1,3}\.){3}\d{1,3}\b/)[2] 26 | mac = line.scan(/(?:[A-Fa-f0-9]{2}[:-]){5}(?:[A-Fa-f0-9]{2})/)[0] 27 | 28 | # Clean up some stuff 29 | oint = int unless int.empty? 30 | ipaddr = '' if ipaddr.nil? 31 | broadcast = '' if broadcast.nil? 32 | omac = mac unless mac.nil? 33 | 34 | # Add the data to a table. 35 | unless oint.empty? and ipaddr.empty? and netmask.empty? and broadcast.empty? and omac.empty? 36 | ipaddr = '' if ipaddr.nil? 37 | netmask = '' if netmask.nil? 38 | unless ipaddr.empty? or netmask.empty? 39 | cidr = IPAddr.new(netmask).to_i.to_s(2).count("1") 40 | t << [oint, ipaddr, "#{netmask} (/#{cidr})", broadcast, omac] 41 | omac = '' 42 | end 43 | end 44 | end 45 | end 46 | 47 | table.title = "1337 H4x0Rz Linux Ifconfig Parser - Alton Johnson (@altonjx)" 48 | puts table 49 | -------------------------------------------------------------------------------- /eyewitness_parser/eyewitness_parse.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This tool simply takes the HTML pages that EyeWitness produces 4 | # and converts it into a beautiful table to output for CLI. 5 | # Looks great for screenshots, parsing, grepping, etc. 6 | # 7 | # Author: Alton Johnson 8 | # Updated: 04/24/2018 9 | # 10 | 11 | require 'nokogiri' 12 | require 'terminal-table' 13 | 14 | report_files = Dir.glob("./report*.html") 15 | 16 | table = Terminal::Table.new do |t| 17 | t.headings = ['url','response code', 'title', 'powered by','server', 'report'] 18 | report_files.each do |report_name| 19 | t.add_separator unless report_files.index(report_name) == 0 20 | report = Nokogiri::HTML(File.open(report_name)) 21 | report.xpath('//td[contains(., "http://")] | //td[contains(., "https://")]').each do |data| 22 | data = data.to_s.split ("\n") 23 | url = title = response = powered_by = server = "" 24 | data.each do |d| 25 | d = d.gsub("", "").gsub("
", "") 26 | url = Nokogiri::HTML(d).xpath("//a").text if url.empty? and Nokogiri::HTML(d).xpath("//a").text.include? "http" 27 | title = d.gsub("Page Title: ", "") if title.empty? and d.include? "Page Title: " 28 | powered_by = d.gsub("x-powered-by: ", "") if powered_by.empty? and d.include? "x-powered-by: " 29 | server = d.gsub("server: ", "") if server.empty? and d.include? "server: " 30 | response = d.gsub("Response Code: ", "") if response.empty? and d.include? "Response Code: " 31 | end 32 | t.add_row [url, response, title[0..40], powered_by[0..20], server[0..20], report_name] 33 | end 34 | end 35 | end 36 | 37 | puts table 38 | -------------------------------------------------------------------------------- /name_formatter/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | This is just an extremely quick and light script that simply formats a list of names into formats that may be used for authentication-based attacks. For example, you may want to format a list of names harvested from LinkedIn into a username list for Active Directory or Exchange brute force attacks. 4 | 5 | Prerequisites 6 | === 7 | You'll need the optparse gem in order to get this working, or maybe not. You can install it with the following command: 8 |

 9 | gem install optparse
10 | 
11 | 12 | Instructions 13 | === 14 | Simply run the script with the following syntax. 15 |

16 | [root:vonahisec-kali:...ting_scripts/name_formatter]# ./name_formatter.rb
17 | 
18 |  - Name Formatter v1.0
19 |  - Author: Alton Johnson (@altonjx)
20 |  - Contact: alton.jx@gmail.com
21 | 
22 |  Required
23 | 
24 |  	--target	Specify file containing names or first/last name in quotes.
25 | 
26 |  Additional Options (sample name: Alton Johnson):
27 | 
28 | 	--option1	Format names using Alton.Johnson@domain.com
29 | 	--option2	Format names using AJohnson@domain.com.
30 | 	--option3	Format names using AltonJ@domain.com.
31 | 	--option4	All of the above.
32 | 	--domain	Domain for email format, e.g. test@domain.com (optional)
33 | 
34 |  Examples:
35 | 
36 |   --target "Alton Johnson" --option1 --option2
37 |   --target names.txt --option4
38 | 
39 | 40 | Here's an example of the output when using it: 41 | 42 |

43 | [root:vonahisec-kali:...ting_scripts/name_formatter]# cat > names.txt
44 | Alton Johnson
45 | Hello World
46 | ^C
47 | [root:vonahisec-kali:...ting_scripts/name_formatter]# ./name_formatter.rb --option4 --target names.txt --domain @gmail.com
48 | Alton.Johnson@gmail.com
49 | AJohnson@gmail.com
50 | AltonJ@gmail.com
51 | Hello.World@gmail.com
52 | HWorld@gmail.com
53 | HelloW@gmail.com
54 | 
-------------------------------------------------------------------------------- /pyfoca/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | If you're familiar with the Windows FOCA application, this is basically a python version of it. Pyfoca will use Google to discover files with extensions such as .pdf, .xls, .doc, etc. and download them. Once downloaded, it will extract all metadata which, in many cases, include usernames you can use for password attacks. 4 | 5 | Instructions 6 | === 7 |
8 | Usage menu: 9 |

10 |  -------------------------------------------------------------------------------
11 |   pyfoca v1.6 - Document Metadata Extractor, Alton Johnson (alton@vonahi.io)
12 |  -------------------------------------------------------------------------------
13 | 
14 |  Usage: ./pyfoca.py <OPTIONS>
15 | 
16 |  Domain options:
17 | 
18 |     -d <domain>      Harvests all documents from a domain (saves to pyfoca-downloads/).
19 | 				     Afterwards, extract metadata.
20 | 
21 |  Parse file/dir:
22 | 
23 |     -f <file>     Extracts metadata specifically from one file. (Cannot use with '-d')
24 |     -w <dir>      Extracts metadata from files within specified directory. (Cannot use with '-d')
25 | 
26 |  Foca Export Parsing:
27 | 
28 |     -r <directory>      Parses data exported from FOCA. Provide directory containing exported files.
29 | 
30 |  Misc:
31 | 
32 |     -x                     After parsing metadata, delete files downloaded from the domain.
33 |     -e <pdf|doc|xls|all>   Search based on provided extension(s). Separate with comma. (Default is all.)
34 |     -p <number>            Searches x amount of google pages (per extension). (Default is 2.)
35 |     -t <secs>              Sets timeout value. (Default is 5.)
36 |     -v                     Prints status messages for files that are downloaded.
37 | 
38 |  Supported extensions are: .pdf, .doc, .docx, .xls, .xlsx, and .ppt
39 |  Example: ./pyfoca.py -d www.domain.com -e pdf,doc -p 3
40 | 
-------------------------------------------------------------------------------- /ike_aggressive_scanner/ike-aggressive-scanner.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # 4 | # This script simply scans a list of systems to determine if IKE Aggressive Mode 5 | # is supported. If so, it writes the hashed PSK value to a file. 6 | # 7 | # I simply stumbled upon like 20 systems with port 500/UDP opened, and didn't feel 8 | # like doing it manually or running a for loop, so that's where this came from. 9 | # 10 | # Author: Alton Johnson 11 | # Updated: 06/29/2015 12 | # 13 | 14 | begin 15 | require 'terminal-table' 16 | require 'getopt/std' 17 | rescue 18 | puts "It appears that you may be missing some prerequisites for this script." 19 | puts "Run the following commands:" 20 | puts "\t\t gem install terminal-table" 21 | puts "\t\t gem install getopt/std" 22 | end 23 | 24 | @banner = %Q{ 25 | -------------------------------------------------------------------- 26 | IKE Aggressive Mode Scanner - Alton Johnson (alton@vonahi.io) 27 | -------------------------------------------------------------------- 28 | 29 | } 30 | 31 | def help 32 | puts @banner 33 | 34 | puts " Usage: #{$0} -f vpn_gateways.txt\n\n" 35 | puts " -f \t Specifies file containing IP addresses for scanning." 36 | puts 37 | exit 38 | end 39 | 40 | def start_scanning(filename) 41 | ips = File.open(filename).read.split 42 | 43 | results = [] 44 | 45 | # Use ike-scan on all IP addresses in file. 46 | ips.each do |ip| 47 | if `ike-scan #{ip} -A --id=test -Pike-aggressive-mode-#{ip}.txt`.include? "Aggressive Mode" 48 | results << [ip, "\e[34;1mAggressive Mode supported\e[00;0m", "ike-aggressive-mode-#{ip}.txt"] 49 | else 50 | results << [ip, 'Not supported', 'n/a'] 51 | end 52 | end 53 | 54 | # Stdout. 55 | table = Terminal::Table.new :title => "IKE Aggressive Mode Scanner - Alton Johnson (alton@vonahi.io)", 56 | :headings => ['IP Address','Status', 'Saved file to'], :rows => results 57 | 58 | puts table 59 | end 60 | 61 | if $0 == __FILE__ 62 | 63 | if ARGV.length == 0 64 | help 65 | end 66 | 67 | opt = Getopt::Std.getopts('f:') 68 | 69 | fail "You must specify a file with -f. If you only have one host, just use ike-scan :)" unless opt['f'] 70 | 71 | start_scanning(opt['f']) 72 | 73 | end 74 | -------------------------------------------------------------------------------- /name_formatter/name_formatter.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'optparse' 3 | def help 4 | puts 5 | puts " - Name Formatter v1.0" 6 | puts " - Author: Alton Johnson (@altonjx)" 7 | puts " - Contact: alton.jx@gmail.com" 8 | puts 9 | puts " Required" 10 | puts 11 | puts " \t--target\tSpecify file containing names or first/last name in quotes." 12 | puts 13 | puts " Additional Options (sample name: Alton Johnson):" 14 | puts 15 | puts "\t--option1\tFormat names using Alton.Johnson@domain.com" 16 | puts "\t--option2\tFormat names using AJohnson@domain.com." 17 | puts "\t--option3\tFormat names using AltonJ@domain.com." 18 | puts "\t--option4\tAll of the above." 19 | puts "\t--domain\tDomain for email format, e.g. test@domain.com (optional)" 20 | puts 21 | puts " Examples:" 22 | puts 23 | puts " #{ARGV.first} --target \"Alton Johnson\" --option1 --option2" 24 | puts " #{ARGV.first} --target names.txt --option4" 25 | puts 26 | exit 27 | end 28 | if $0 == __FILE__ 29 | if ARGV.length == 0 30 | help 31 | end 32 | options = {:format_types => [], :target => nil, :domain => nil} 33 | avail_options = [1,2,3,4] 34 | OptionParser.new do |parser| 35 | avail_options.each do |ao| 36 | parser.on("--option#{ao}") {|d| options[:format_types] << ao} 37 | end 38 | parser.on("--target=TARGET") do |target| 39 | options[:target] = target 40 | end 41 | parser.on("--domain=DOMAIN") do |domain| 42 | options[:domain] = domain 43 | end 44 | end.parse! 45 | fail "No target specified. Please provide a target with option --target." if options[:target].nil? 46 | if File.exists? options[:target] 47 | target_names = File.open(options[:target]).read.split("\n") 48 | else 49 | target_names = [options[:target]] 50 | end 51 | domain = options[:domain] ||= "" 52 | target_names.each do |target_name| 53 | fname = target_name.split(" ")[0] 54 | lname = target_name.split(" ")[-1] 55 | options[:format_types].each do |option| 56 | case option 57 | when 1 58 | puts "#{fname}.#{lname}#{domain}" 59 | when 2 60 | puts "#{fname[0]}#{lname}#{domain}" 61 | when 3 62 | puts "#{fname}#{lname[0]}#{domain}" 63 | when 4 64 | puts "#{fname}.#{lname}#{domain}" 65 | puts "#{fname[0]}#{lname}#{domain}" 66 | puts "#{fname}#{lname[0]}#{domain}" 67 | end 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /smbspider/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | Smbspider is a pretty smart when it comes to spidering Windows systems on internal networks. Once you get your hands on some credentials, you can pass them around with smbspider to try spidering systems that the user account has access to. In many cases, you'll end up quickly finding all types of sensitive data hanging out on employees' workstations. 4 | 5 | Instructions 6 | === 7 | Usage video: http://www.youtube.com/watch?v=skVZwynHECw 8 | Usage menu: 9 | 10 |

11 |  ************************************************************
12 |  *                  _                                       *
13 |  *                 | |       //  \\                         *
14 |  *    ___ _ __ ___ | |__    _\\()//_                        *
15 |  *   / __| '_ ` _ \| '_ \  / //  \\ \                       *
16 |  *   \__ \ | | | | | |_) |   |\__/|                         *
17 |  *   |___/_| |_| |_|_.__/                                   *
18 |  *                                                          *
19 |  * SMB Spider v2.4, Alton Johnson (alton@vonahi.io)         *
20 |  ************************************************************
21 | 
22 |  Usage: /root/scripts/ipwn/smbspider.py 
23 | 
24 |  Target(s) (required): 
25 | 
26 |    -h    Provide IP address or a text file containing IPs.
27 |        Supported formats: IP, smb://ip/share, \\ip\share\
28 | 
29 |  Credentials (required): 
30 | 
31 |    -u    Specify a valid username to authenticate to the system(s).
32 |    -p    Specify the password which goes with the username.
33 |    -P    Use -P to provide password hash if cleartext password isn't known.
34 |    -d    If using a domain account, provide domain name.
35 | 
36 |  Shares (optional):
37 | 
38 |    -s   Specify shares (separate by comma) or specify "profile" to spider user profiles.
39 |    -f    Specify a list of shares from a file.
40 | 
41 |  Other (optional):
42 | 
43 |    -w      Avoid verbose output. Output successful spider results to smbspider_host_share_user.txt.
44 |        This option is HIGHLY recommended if numerous systems are being scanned.
45 |    -n      ** Ignore authentication check prior to spidering.
46 |    -g    Grab (download) files that match strings provided in text file. (Case sensitive.)
47 |        ** Examples: *assword.doc, *assw*.doc, pass*.xls, etc.
48 | 
49 | 
-------------------------------------------------------------------------------- /iSMTP/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | There's been countless times when I've needed to test for SMTP user enumeration (RCPT TO and VRFY), internal spoofing, and open relay. I've never found a tool that tested for all three and with great flexibility. iSMTP does just that, making it much easier to knock that process out of the way. 4 | 5 | Instructions 6 | === 7 | Usage menu: 8 |

 9 | [07.22.2017/21:34:09] root@box $ ./iSMTP.py
10 | 
11 |  ---------------------------------------------------------------------
12 |   SMTP v1.6 - SMTP Server Tester, Alton Johnson (alton@vonahi.io)
13 |  ---------------------------------------------------------------------
14 |  
15 |  Usage: ./iSMTP.py <OPTIONS>
16 | 
17 |  Required:
18 | 
19 |    -f <import file>  Imports a list of SMTP servers for testing.
20 |                      (Cannot use with '-h'.)
21 |    -h <host>         The target IP and port (IP:port).
22 |                      (Cannot use with '-f'.)
23 | 
24 |  Spoofing:
25 | 
26 |    -i <consultant email>   The consultant's email address.
27 |    -s <sndr email>         The sender's email address.
28 |    -r <rcpt email>         The recipient's email address.
29 |       --sr <email>         Specifies both the sender's and recipient's email address.
30 |    -S <sndr name>          The sender's first and last name.
31 |    -R <rcpt name>          The recipient's first and last name.
32 |       --SR <name>          Specifies both the sender's and recipient's first and last name.
33 |    -m                      Enables SMTP spoof testing.
34 |    -a                      Includes .txt attachment with spoofed email.
35 | 
36 |  SMTP enumeration:
37 | 
38 |    -e <file>   Enable SMTP user enumeration testing and imports email list.
39 |    -l <1|2|3>  Specifies enumeration type (1 = VRFY, 2 = RCPT TO, 3 = all).
40 |                (Default is 3.) 
41 | 
42 |  SMTP relay:
43 | 
44 |    -i <consultant email>   The consultant's email address.
45 |    -x                      Enables SMTP external relay testing.
46 | 
47 |  Misc:
48 | 
49 |    -t <secs>   The timeout value. (Default is 10.)
50 |    -o          Creates "ismtp-results" directory and writes output to
51 |                ismtp-results/smtp_<service>_<ip>(port).txt
52 | 
53 |  Note: Any combination of options is supported (e.g., enumeration, relay, both, all, etc.).
54 | 
-------------------------------------------------------------------------------- /nmap_scripts/nmap_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # This tool was simply written to convert the gnmap output from Nmap 4 | # and present it in a nice table for command line. 5 | # 6 | # This table can then be used to greb for specific content, 7 | # sort, parse, etc. 8 | # 9 | # Author: Alton Johnson 10 | # Updated: 08/10/2016 11 | # 12 | 13 | import re 14 | from sys import argv 15 | 16 | class colors: 17 | lightblue = "\033[1;36m" 18 | blue = "\033[1;34m" 19 | normal = "\033[0;00m" 20 | red = "\033[1;31m" 21 | white = "\033[1;37m" 22 | green = "\033[1;32m" 23 | 24 | try: 25 | from prettytable import * 26 | except Exception: 27 | print colors.red + " Error: The 'prettytables' python module isn't installed." 28 | print " Download prettytables and then run the script again. Link: https://code.google.com/p/prettytable/" 29 | exit() 30 | 31 | banner = "\n " + "-" * 72 + "\n " + colors.white + " nmapparse 1.0 - Nmap Output Parser, Alton Johnson (alton@vonahi.io)\n " + colors.normal + "-" * 72 + "\n " 32 | 33 | def help(): 34 | print banner 35 | print " Usage: %s results.gnmap" % argv[0] 36 | print "\n Note: This script must point to a grepable output file from nmap to work properly.\n" 37 | exit() 38 | 39 | 40 | def start(argv): 41 | table = PrettyTable(["IP Address","Port","Service","Version"]) 42 | if len(argv) == 0: 43 | help() 44 | contents = sorted(open(argv[0]).read().split('\n')) 45 | data = [] 46 | 47 | class colors: 48 | blue = "\033[1;34m" 49 | normal = "\033[0;00m" 50 | 51 | for item in contents: 52 | ip_addr = item[item.find(":")+2:item.find("(")-1] 53 | info = re.findall("(?<=Ports: )(.*?)(?=Ignored)", item) 54 | if len(info) == 0: 55 | info = re.findall("(?<=Ports: )(.*?)(?=Seq Index)", item) 56 | if len(info) == 0: 57 | info = re.findall("(?<=Ports: )(.*?)(?=$)", item) 58 | if len(info) != 0: 59 | for i in info: 60 | result = i.split(',') 61 | for x in result: 62 | port = re.findall("([0-9]+/open/.*?)/", x) 63 | if "[]" in str(port): 64 | continue 65 | port = port[0].replace("/open", "") 66 | service = re.findall("(?<=//)(.*?)(?=/)", x)[0] 67 | version = x.split("/")[-2] 68 | if len(version) > 40: 69 | version = version[:40] 70 | if len(version) == 0: 71 | version = "-" 72 | table.add_row([ip_addr, port, service, version]) 73 | print table 74 | 75 | 76 | try: 77 | start(argv[1:]) 78 | except Exception, err: 79 | print err 80 | -------------------------------------------------------------------------------- /wmi_loggedin_user_enumerator/wmi_loggedin_users.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # 3 | # This script can be used to enumerate users that are logged on 4 | # to a remote system by leveraging Windows Management Instrumentation (WMI) 5 | # 6 | # Author: Alton Johnson 7 | # 8 | 9 | begin 10 | require 'terminal-table' 11 | require 'getopt/std' 12 | rescue Exception => e 13 | puts "\n Error loading required gems." 14 | puts " Install required gems using the following commands:\n 15 | gem install terminal-table\n\tgem install getopt\n\n" 16 | exit 17 | end 18 | 19 | def help_menu 20 | puts %Q{ 21 | --------------------------------------------------------------------------- 22 | Logged In User Enumerator (via WMIC) - Alton Johnson (alton.jx@gmail.com) 23 | --------------------------------------------------------------------------- 24 | 25 | Usage: #{$0} -f -d -u -p 26 | 27 | \t-f\tFile containing IP addresses to scan. 28 | \t-d\tDomain used for the valid account. 29 | \t-u\tAdministrator account that will be used for logging in. 30 | \t-p\tPassword for the administrator account. 31 | } 32 | exit 33 | end 34 | 35 | def start(filename, domain, username, password) 36 | iplist = File.open(filename,'r').read.split 37 | 38 | # Extract logged in users via WMIC. 39 | table = Terminal::Table.new do |t| 40 | t << ["IP Address","Domain", "Username"] 41 | t.add_separator 42 | 43 | iplist.each do |ip| 44 | separate = 0 45 | users = [] 46 | excluded_users = ["SYSTEM","IUSR"] 47 | 48 | # Prints status. 49 | puts " [*] Extracting users from #{ip}" 50 | 51 | data = `wmic -U #{domain}/#{username}%#{password} //#{ip} "select * from Win32_LoggedOnUser"`.split 52 | for row in data 53 | user_domain = row.scan(/Domain="(.*?)"/im)[0] 54 | user = row.scan(/Name="(.*?)"/im)[0] 55 | unless user_domain.nil? or user.nil? 56 | t << [ip, user_domain[0], user[0]] unless users.include? user or excluded_users.include? user[0] 57 | users << user 58 | separate = 1 59 | end 60 | end 61 | t.add_separator unless separate == 0 or ip == iplist[-1] 62 | end 63 | end 64 | puts 65 | puts table 66 | end 67 | 68 | if $0 == __FILE__ 69 | if ARGV.length == 0 70 | help_menu 71 | end 72 | 73 | opt = Getopt::Std.getopts("f:u:p:d:") 74 | 75 | fail "One or more required options are missing." unless opt["f"] or opt["u"] or opt["d"] or opt["p"] 76 | begin 77 | start(opt["f"], opt["d"], opt["u"], opt["p"]) 78 | rescue Exception => e 79 | puts e 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /vhost_lookup/README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | === 3 | VHostLookup is a script designed to assist with identifying virtual hosts. For example, if you have an IP address of a domain and want to see what other domains are hosted on that same exact server, you can use this tool to get that information. 4 | 5 | Instructions 6 | === 7 | Simply provide the script an IP address of a potential virtual host. 8 | Usage example: 9 |

10 | [07.22.2017/21:49:31] root@box $ host altonj.com
11 | altonj.com has address 68.65.120.201
12 | altonj.com mail is handled by 10 mx2.privateemail.com.
13 | altonj.com mail is handled by 10 mx1.privateemail.com.
14 | [07.22.2017/21:49:42] root@box $ ./vhost_lookup.rb -i 68.65.120.201
15 | 
16 |  ---------------------------------------------------
17 |   VHost Lookup - Alton Johnson (alton@vonahi.io)
18 |  ---------------------------------------------------
19 |  
20 |  [*] Finding virtualhosts for: 68.65.120.201
21 |  [*] 10 potential domain(s) identified to match IP. Parsing results.
22 |  [*] Results parsed. Performing additional IP lookups on each domain.
23 |  [*] Complete! Printing table.
24 | 
25 | +-------------------------+-------------------------+-----------------+----------------------+
26 | | Query                   | Additional DNS Name     | DNS Record Type | DNS Entry            |
27 | +-------------------------+-------------------------+-----------------+----------------------+
28 | | www.eddiescalzones.com  | www.eddiescalzones.com  | CNAME           | eddiescalzones.com.  |
29 | | www.eddiescalzones.com  | eddiescalzones.com      | A               | 68.65.120.201        |
30 | | beachlineraceway.com    | beachlineraceway.com    | A               | 68.65.120.201        |
31 | | zebiak.website          | zebiak.website          | A               | 68.65.120.201        |
32 | | tableandchairdirect.com | tableandchairdirect.com | A               | 68.65.120.201        |
33 | | gisesb.com              | gisesb.com              | A               | 68.65.120.201        |
34 | | wazivision.com          | wazivision.com          | A               | 68.65.120.201        |
35 | | hellscanyoninn.com      | hellscanyoninn.com      | A               | 68.65.120.201        |
36 | | www.inglenookhearth.com | www.inglenookhearth.com | CNAME           | inglenookhearth.com. |
37 | | www.inglenookhearth.com | inglenookhearth.com     | A               | 68.65.120.201        |
38 | | zeroplaneualberta.com   | zeroplaneualberta.com   | A               | 68.65.120.201        |
39 | | fatfreezing.club        | fatfreezing.club        | A               | 68.65.120.201        |
40 | +-------------------------+-------------------------+-----------------+----------------------+
41 | 
-------------------------------------------------------------------------------- /ifconfig/mac-ifconfigs.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'terminal-table' 3 | require 'ipaddr' 4 | 5 | ifconfig = `ifconfig`.split("\n") 6 | 7 | oint = '' 8 | int = '' 9 | omac = '' 10 | 11 | # Convert hexadecimal netmask to decimal. 12 | def convert_netmask(mask) 13 | first_octet = mask[0..1].to_i(16) 14 | second_octet = mask[2..3].to_i(16) 15 | third_octet = mask[4..5].to_i(16) 16 | fourth_octet = mask[6..7].to_i(16) 17 | mask = "#{first_octet}.#{second_octet}.#{third_octet}.#{fourth_octet}" 18 | return mask 19 | end 20 | 21 | data = [] 22 | 23 | # Begin the table. 24 | table = Terminal::Table.new do |t| 25 | # Define the table headers. 26 | t << ['Interface','IPv4 Address','Subnet Mask','Broadcast','MAC Address'] 27 | t.add_separator 28 | 29 | ifconfig.each do |line| 30 | # Regex used to grab for specific information. 31 | int = line.scan(/(.*?): flags=/) 32 | ipaddr = line.scan(/inet \b(?:\d{1,3}\.){3}\d{1,3}\b/) 33 | netmask = line.scan(/0x\h\h\h\h\h\h\h\h/) 34 | broadcast = line.scan(/broadcast \b(?:\d{1,3}\.){3}\d{1,3}\b/) 35 | mac = line.scan(/(?:[A-Fa-f0-9]{2}[:-]){5}(?:[A-Fa-f0-9]{2})/) 36 | 37 | # Fix the variables if the regex matches aren't empty. 38 | int = int unless int.nil? 39 | ipaddr = ipaddr[0][5..-1] unless ipaddr.empty? 40 | netmask = convert_netmask(netmask[0][2..-1]) unless netmask.empty? 41 | broadcast = broadcast[0][10..-1] unless broadcast.empty? 42 | omac = mac[0] unless mac.empty? 43 | 44 | # Clean up some stuff. 45 | int = int[0][0] unless int.empty? 46 | oint = int unless int.empty? 47 | broadcast = '' if broadcast.empty? 48 | ipaddr = '' if ipaddr.empty? 49 | netmask = '' if netmask.empty? 50 | 51 | # Add the data to a table. 52 | unless oint.empty? and ipaddr.empty? and netmask.empty? and broadcast.empty? and omac.empty? 53 | unless ipaddr.empty? and omac.empty? 54 | cidr = "(/" +IPAddr.new(netmask).to_i.to_s(2).count("1").to_s + ")" unless ipaddr.empty? 55 | data << [oint, ipaddr, "#{netmask} #{cidr}", broadcast, omac] 56 | omac = '' 57 | end 58 | end 59 | end 60 | 61 | # Remove weird entries and then add them to our terminal table. 62 | data.each do |d| 63 | lastline = data[data.index(d)-1] 64 | if lastline[0] == d[0] and d[-1].empty? 65 | d[-1] = lastline[-1] 66 | data.delete_at(data.index(lastline)) 67 | end 68 | end 69 | 70 | # Add data array to the terminal table. 71 | data.each do |entry| 72 | t << entry 73 | end 74 | 75 | # Grab public IP address 76 | public_ip = `curl "https://api.ipify.org" -s` 77 | t.add_separator 78 | t.add_row ["Public IP Address", public_ip, "", "", ""] 79 | 80 | end 81 | table.title = "1337 H4x0Rz Mac OS Ifconfig Parser - Alton Johnson (@altonjx)" 82 | puts table 83 | -------------------------------------------------------------------------------- /vhost_lookup/vhost_lookup.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | ################################################################# 4 | # # 5 | # This ruby script simply uses Bing to grab domains which may # 6 | # be associated with a VirtualHost. This is extremely useful if # 7 | # you have a single IP address and it answers for several # 8 | # domains. For further reference, research Apache VirtualHosts. # 9 | # # 10 | # Author: Alton Johnson # 11 | ################################################################# 12 | 13 | begin 14 | ['terminal-table', 'net/http', 'getopt/std'].each(&method(:require)) 15 | rescue 16 | puts " Error: Apparently, you're missing a gem. Install required gems using the following commands:" 17 | puts "\t gem install terminal-table" 18 | puts "\t gem install getopt" 19 | puts 20 | end 21 | 22 | $banner = %Q{ 23 | --------------------------------------------------- 24 | VHost Lookup - Alton Johnson (alton@vonahi.io) 25 | --------------------------------------------------- 26 | 27 | } 28 | 29 | class VHostLookup 30 | def initialize(ipaddr, strict, pages) 31 | @ipaddr = ipaddr 32 | @pages = pages 33 | @domains = [] 34 | @strict = strict 35 | end 36 | 37 | # Submit the bing request and obtain all domains associated with IP. 38 | def begin 39 | puts $banner 40 | puts " [*] Finding virtualhosts for: #{@ipaddr}" 41 | cookies = '' 42 | url = URI("http://www.bing.com/search?q=ip:#{@ipaddr}") 43 | res = Net::HTTP.get_response(url) 44 | res.get_fields('set-cookie').each {|cookie| cookies << cookie.split('; ')[0] + '; '} 45 | 46 | http = Net::HTTP.new(url.host, url.port) 47 | headers = { 48 | 'Cookie' => cookies 49 | } 50 | resp = http.get(url.request_uri, headers) 51 | parse_body(resp.body.scan(/(.*?)<\/cite>/)) 52 | end 53 | 54 | def parse_body(domains) 55 | puts " [*] #{domains.length} potential domain(s) identified to match IP. Parsing results." 56 | domains.each {|domain| 57 | @domains << domain[0].gsub("","").gsub("","").gsub("https://", "").split("/")[0] 58 | } 59 | lookup_additional_ips 60 | end 61 | 62 | def lookup_additional_ips 63 | puts ' [*] Results parsed. Performing additional IP lookups on each domain.' 64 | @domain_matchings = [] 65 | @domains.each do |domain| 66 | output = `dig @8.8.8.8 A #{domain}`.split("\n") 67 | hash = Hash[output.map.with_index.to_a] 68 | if output.include? ';; ANSWER SECTION:' 69 | results = output[hash[';; ANSWER SECTION:']+1..-6] 70 | results.each do |result| 71 | result = result.split("\t") 72 | a = result[0].split(" ")[0][0..-2] # DNS name. 73 | begin 74 | b = result[-2].split(" ")[-1] # DNS record. 75 | rescue 76 | next 77 | end 78 | c = result[-1] # DNS entry. 79 | if @strict == 1 80 | @domain_matchings << [domain, a, b, c] if c == @ipaddr 81 | else 82 | @domain_matchings << [domain, a, b, c] 83 | end 84 | end 85 | end 86 | end 87 | stdout 88 | end 89 | 90 | def stdout 91 | if @domain_matchings.length == 0 and @domains.uniq.length == 0 92 | puts " [*] No results matches specific criteria." 93 | puts 94 | elsif @domains.uniq.length != 0 and @domain_matchings.length == 0 95 | puts " [*] Printing out domains that were found associated with IP address." 96 | puts 97 | @domains.uniq.each do |domain| 98 | puts " [*] #{domain}" 99 | end 100 | puts 101 | else 102 | puts " [*] Complete! Printing table.\n\n" 103 | table = Terminal::Table.new do |t| 104 | t << ['Query','Additional DNS Name','DNS Record Type','DNS Entry'] 105 | t.add_separator 106 | @domain_matchings.each do |entry| 107 | t << entry 108 | end 109 | end 110 | puts table 111 | puts 112 | end 113 | end 114 | end 115 | 116 | def help_menu 117 | 118 | puts %Q{#{$banner} Usage: #{$0} -i 199.83.134.95 119 | 120 | \t-i\tIP address to look up other domains (virtualhosts) for. 121 | \t-s\tOnly show domains associated with IP address provided. 122 | } 123 | exit 124 | end 125 | 126 | # Begin things. 127 | if $0 == __FILE__ 128 | if ARGV.length == 0 129 | help_menu 130 | end 131 | 132 | opt = Getopt::Std.getopts("i:s") 133 | strict = '' 134 | fail "You forgot to provide an IP address with -i." unless opt['i'] 135 | if opt['s'] 136 | strict = 1 137 | else 138 | strict = 0 139 | end 140 | s = VHostLookup.new(opt['i'], strict, 5) 141 | s.begin 142 | end 143 | -------------------------------------------------------------------------------- /iWebAudit/iWebAudit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This program automates the process of auditing web servers on internal 4 | # penetration tests. As opposed to opening numerous sites manually to view what they're 5 | # hosting, you can use this script to grab the HTTP titles from every page. 6 | # After you're done, you can easily prioritize what you want to start looking 7 | # at first starting from a nice, formatted list of IPs and page titles 8 | # 9 | # Author: Alton Johnson (alton@vonahi.io) 10 | # Updated: 01/23/2014 11 | # 12 | # Note: Many thanks to Victor M. and Mark T. for assistance 13 | # 14 | # Future consideration: HTTP Put Method Detection [completed]; auto-update attempt via wiki 15 | # 16 | 17 | from sys import argv 18 | import urllib2 19 | import getopt 20 | import time 21 | import httplib 22 | import socket 23 | 24 | container = list() 25 | spaces = [0,0,0] 26 | 27 | # Defines the colors which can be called later. Can identify colors using echo -e "\033[x;nnm" 28 | # from terminal. x = 1 (bold) or 0 (non-bold). n = numbers from 01 = 99 (must be two digits). 29 | class colors: 30 | lightblue = '\033[1;36m' 31 | blue = '\033[1;34m' 32 | normal = '\033[0;00m' 33 | red = '\033[1;31m' 34 | white = '\033[1;37m' 35 | 36 | # Prints out the help function, which guides users how to use the script. 37 | def help(): 38 | print "\n", "-" * 78 39 | print colors.white, "iWebAudit v1.5 - Web Page Title Analyzer, Alton Johnson (alton@vonahi.io) ", colors.normal 40 | print "-" * 78, "\n" 41 | print " Usage: iWebAudit -f -o -v -h -t 5\n" 42 | print "\t-f \tSupports list of IPs and/or http[s]:// formatted IPs." 43 | print "\t-o \tOutputs the results to a file of your choice." 44 | print "\t-h \t\tEnables HTTP PUT Method Discovery. (optional)" 45 | print "\t-t \tSets default timeout. Default is 5. (optional)" 46 | print "\t-v \t\tDisplays details as script runs. (optional)\n" 47 | 48 | # Takes the response (html source) from http_request and parses it for title 49 | def FindTitle(source): 50 | source = source.replace("\n", "").replace("\r", "").replace("\t", "").split("<") 51 | for i in range(0,len(source)): 52 | if 'title>' in source[i].lower(): 53 | if not (source[i][source[i].lower().find("title>")+6:]): 54 | return "-" 55 | else: 56 | return source[i][source[i].lower().find('title>')+6:] 57 | break 58 | elif 'title ' in source[i].lower(): # alt for code such as " in parsed_title: 62 | return parsed_title 63 | break 64 | return "-" 65 | 66 | def http_method(url): 67 | if 'http://' in url: 68 | url = url[url.find("http://")+7:] 69 | elif 'https://' in url: 70 | url = url[url.find("https://")+8:] 71 | connection = httplib.HTTPConnection(url) 72 | connection.request('OPTIONS','/') 73 | response = connection.getresponse() 74 | http_parse = response.getheader('allow') 75 | if http_parse: 76 | if "PUT" in http_parse: 77 | return "PUT" 78 | else: 79 | return "-" 80 | else: 81 | return "-" 82 | 83 | # Checks to see what format the current line is in and connects accordingly 84 | def http_request(request): 85 | if 'http://' in request: 86 | request = urllib2.Request(request) # construct rqst 87 | elif 'https://' in request: 88 | request = urllib2.Request(request) # construct rqst 89 | else: 90 | request = urllib2.Request('http://%s' %request) # construct rqst 91 | 92 | request.add_header('User-Agent','Lewlsauce, Inc.') 93 | try: 94 | response = urllib2.urlopen(request) 95 | return response.read() 96 | except urllib2.URLError, err: 97 | return "-" 98 | except urllib2.HTTPError, err: 99 | return "-" 100 | except Exception, err: 101 | return "-" 102 | 103 | # Takes argv (everything provided after the program name) and passes it to this function. 104 | def start(argv): 105 | if len(argv) < 2: 106 | help() 107 | exit() 108 | try: 109 | opts, args = getopt.getopt(argv, 'vhf:o:t:') #colons are needed if option is req'd. 110 | except getopt.GetoptError: 111 | help() 112 | exit() 113 | # Define default variables so script doesn't fail if "if" statements aren't true. 114 | use_https = False 115 | output_file = "" 116 | verbose = False 117 | length = 0 118 | httpmethod = False 119 | cur_time = time.time() 120 | timeout = 5 121 | socket.setdefaulttimeout(timeout) 122 | # Check which options and agruments have been provided to CLI. 123 | for opt, arg in opts: 124 | if opt == '-f': 125 | http_file = open(arg) 126 | elif opt == '-o': 127 | output_file = open(arg, 'w') 128 | elif opt == '-v': 129 | verbose = True 130 | elif opt == '-h': 131 | httpmethod = True 132 | elif opt == '-t': 133 | socket.setdefaulttimeout(float(arg)) 134 | file_contents = http_file.read().split() # Converted to list; assigned to variable. 135 | 136 | # setting the space for the first column; used for verbose mode 137 | for line in file_contents: 138 | if len(line) > spaces[0]: 139 | spaces[0] = len(line) 140 | spaces[0] += 3 141 | 142 | # grabbing titles, adding to container, and outputting (if verbose is enabled) 143 | for site in file_contents: 144 | try: 145 | title = FindTitle(http_request(site)) # pass http_request data to FindTitle function 146 | if httpmethod: 147 | method = http_method(site) 148 | else: 149 | method = False 150 | container.append([site,method,title]) 151 | if output_file != "": 152 | if method: 153 | output_file.write("%s%s%s%s\n" % (site," " * (spaces[0]-len(site)),method + " " * (6 - len(method)),title)) 154 | else: 155 | output_file.write("%s%s%s\n" % (site, " " * (spaces[0]-len(site)),title)) 156 | if verbose: 157 | output(verbose,site,method,title) 158 | except Exception, err: 159 | print err 160 | 161 | #adds the necessary space lengths for each field 162 | if method: 163 | for i in container: 164 | i[1] = i[1] + " " 165 | for num in range(0,len(spaces)): 166 | if len(i[num]) > spaces[num]: 167 | spaces[num] = len(i[num]) 168 | else: 169 | for i in container: 170 | i[1] = '' 171 | for num in range(0,len(spaces)): 172 | if len(i[num]) > spaces[num]: 173 | spaces[num] = len(i[num]) 174 | if not verbose: 175 | output(verbose) 176 | print "-" * 5 177 | print "Completed in: %.1fs" % (time.time() - cur_time) 178 | 179 | # this determines whether to print status line after line (verbose), or wait until file is finished 180 | def output(verbose,site='',httpmethod='',title=''): 181 | if not verbose: 182 | if not httpmethod: 183 | httpmethod = ' ' 184 | for i in container: 185 | for num in range(0,len(i)): 186 | print i[num] + " " * (spaces[num]-len(i[num])), 187 | print 188 | else: 189 | site = site + " " * (spaces[0]-len(site)) 190 | title = " " + title 191 | if httpmethod: 192 | httpmethod = httpmethod + " " * (5 - len(httpmethod)) 193 | print site + httpmethod + title 194 | else: 195 | print site + title 196 | 197 | if __name__ == "__main__": 198 | try: 199 | start(argv[1:]) 200 | except KeyboardInterrupt: 201 | print "\nClosed by user. Now exiting... (ctrl+c)" 202 | exit() 203 | -------------------------------------------------------------------------------- /smbspider/smbspider.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # 4 | # This post-exploitation script can be used to spider numerous systems 5 | # to identify sensitive and/or confidential data. A good scenario to 6 | # use this script is when you have admin credentials to tons of 7 | # Windows systems, and you want to look for files containing data such 8 | # as PII, network password documents, etc. For the most part, 9 | # this script uses smbclient, parses the results, and prints 10 | # out the results in a nice format for you. 11 | # 12 | # Author: Alton Johnson <alton@vonahi.io 13 | # Version: 2.4 14 | # Updated: 01/23/2014 15 | # 16 | 17 | import commands, time, getopt, re, os 18 | from sys import argv 19 | 20 | start_time = time.time() 21 | 22 | class colors: 23 | red = "\033[1;31m" 24 | blue = "\033[1;34m" 25 | norm = "\033[0;00m" 26 | green = "\033[1;32m" 27 | 28 | banner = "\n " + "*" * 56 29 | banner += "\n * _ *" 30 | banner += "\n * | | // \\\\ *" 31 | banner += "\n * ___ _ __ ___ | |__ _\\\\()//_ *" 32 | banner += "\n * / __| '_ ` _ \| '_ \ / // \\\\ \ *" 33 | banner += "\n * \__ \ | | | | | |_) | |\__/| *" 34 | banner += "\n * |___/_| |_| |_|_.__/ *" 35 | banner += "\n * *" 36 | banner += "\n * SMB Spider v2.4, Alton Johnson (alton@vonahi.io) *" 37 | banner += "\n " + "*" * 56 + "\n" 38 | 39 | def help(): 40 | print banner 41 | print " Usage: %s <OPTIONS>" % argv[0] 42 | print colors.red + "\n Target(s) (required): \n" + colors.norm 43 | print "\t -h <host>\t Provide IP address or a text file containing IPs." 44 | print "\t\t\t Supported formats: IP, smb://ip/share, \\\\ip\\share\\" 45 | print colors.red + "\n Credentials (required): \n" + colors.norm 46 | print "\t -u <user>\t Specify a valid username to authenticate to the system(s)." 47 | print "\t -p <pass>\t Specify the password which goes with the username." 48 | print "\t -P <hash>\t Use -P to provide password hash if cleartext password isn't known." 49 | print "\t -d <domain>\t If using a domain account, provide domain name." 50 | print colors.green + "\n Shares (optional):\n" + colors.norm 51 | print "\t -s <share>\t Specify shares (separate by comma) or specify \"profile\" to spider user profiles." 52 | print "\t -f <file>\t Specify a list of shares from a file." 53 | print colors.green + "\n Other (optional):\n" + colors.norm 54 | print "\t -w \t\t Avoid verbose output. Output successful spider results to smbspider_host_share_user.txt." 55 | print "\t\t\t This option is HIGHLY recommended if numerous systems are being scanned." 56 | print "\t -n \t\t ** Ignore authentication check prior to spidering." 57 | print "\t -g <file> \t Grab (download) files that match strings provided in text file. (Case sensitive.)" 58 | print "\t\t\t ** Examples: *assword.doc, *assw*.doc, pass*.xls, etc." 59 | print colors.norm 60 | exit() 61 | 62 | def start(argv): 63 | if len(argv) < 1: 64 | help() 65 | try: 66 | opts, args = getopt.getopt(argv, "u:p:d:h:s:f:P:wng:") 67 | except getopt.GetoptError, err: 68 | print colors.red + "\n [-] Error: " + str(err) + colors.norm 69 | 70 | # set default variables to prevent errors later in script 71 | sensitive_strings = [] 72 | smb_user = "" 73 | smb_pass = "" 74 | smb_domain = "" 75 | smb_host = [] 76 | smb_share = ["profile"] 77 | pth = False 78 | output = False 79 | unique_systems = [] 80 | ignorecheck = False 81 | inputfile = False 82 | 83 | #parse through arguments 84 | for opt, arg in opts: 85 | if opt == "-u": 86 | smb_user = arg 87 | elif opt == "-p": 88 | smb_pass = arg 89 | elif opt == "-d": 90 | smb_domain = arg 91 | elif opt == "-h": 92 | try: 93 | smb_host = open(arg).read().split('\n') 94 | inputfile = True 95 | except: 96 | if "\\\\" in arg and "\\" not in arg[-1:]: 97 | test = arg[2:].replace("\\","\\") 98 | smb_host.append("\\\\%s\\" % test) 99 | else: 100 | smb_host.append(arg) 101 | elif opt == "-f": 102 | smb_share = open(arg).read().split() 103 | elif opt == "-s": 104 | smb_share = arg.split(',') 105 | elif opt == "-P": 106 | if arg[-3:] == ":::": 107 | arg = arg[:-3] 108 | smb_pass = arg 109 | pth = True 110 | elif opt == "-w": 111 | output = True 112 | elif opt == "-n": 113 | ignorecheck = True 114 | elif opt == "-g": 115 | sensitive_strings = open(arg).read().split("\n")[:-1] 116 | 117 | #check options before proceeding 118 | if (not smb_user or not smb_pass or not smb_host): 119 | print colors.red + "\n [-] " + colors.norm + "Error: Please check to ensure that all required options are provided." 120 | help() 121 | if pth: 122 | result = commands.getoutput("pth-smbclient") 123 | if "not found" in result.lower(): 124 | print colors.red + "\n [-] " + colors.norm + "Error: The passing-the-hash package was not found. Therefore, you cannot pass hashes." 125 | print "Please run \"apt-get install passing-the-hash\" to fix this error and try running the script again.\n" 126 | exit() 127 | 128 | #make smb_domain, smb_user, and smb_pass one variable 129 | if smb_domain: 130 | credentials = smb_domain + "\\\\" + smb_user + " " + smb_pass 131 | else: 132 | credentials = smb_user + " " + smb_pass 133 | for system in smb_host: 134 | if "\\" in system or "//" in system: 135 | if "\\" in system: 136 | sys = system[system.find("\\")+2:] 137 | sys = sys[:sys.find("\\")] 138 | else: 139 | sys = system[system.find("/")+2:] 140 | sys = sys[:sys.find("/")] 141 | if sys not in unique_systems: 142 | unique_systems.append(sys) 143 | else: 144 | unique_systems.append(system) 145 | #start spidering 146 | print banner 147 | unique_systems = [i for i in unique_systems if i != ''] #remove blank elements from list 148 | print " [*] Spidering %s system(s)..." % len(unique_systems) 149 | begin = spider(credentials, smb_host, smb_share, pth, output, ignorecheck, inputfile, sensitive_strings) 150 | begin.start_spidering() 151 | 152 | class spider: 153 | def __init__(self, credentials, hosts, shares, pth, output, ignorecheck, inputfile, sensitive_strings): 154 | self.list_of_hosts = hosts 155 | self.list_of_shares = shares 156 | self.credentials = credentials 157 | self.smb_host = "" 158 | self.smb_share = "" 159 | self.skip_host = "" 160 | self.pth = pth 161 | self.outputfile = output 162 | self.blacklisted = [] 163 | self.ignorecheck = ignorecheck 164 | self.inputfile = inputfile 165 | self.smb_download = True 166 | self.file_locations = [] 167 | self.sensitive_strings = sensitive_strings 168 | self.profile = False 169 | 170 | def start_spidering(self): 171 | share = "" 172 | self.total_hosts = 0 173 | empty_share_error = colors.red + " [-] " + colors.norm + "Error: Empty share detected for host %s. Skipping share." 174 | for test_host in self.list_of_hosts: 175 | temp = test_host 176 | if ("//" in temp or "\\\\" in temp) and self.list_of_shares[0] != "profile": 177 | print colors.red + " [-] " + colors.norm + "Error: You cannot specify a share if your target(s) contains \\\\<ip>\\<share> or //<ip>/<share>\n" 178 | exit() 179 | for host in self.list_of_hosts: 180 | self.total_hosts += 1 181 | tmp_share = host.replace("/","") 182 | tmp_share = host.replace("\\","") 183 | orig_host = host # ensures that we can check the original host value later on if we need to 184 | if "\\\\" in host: # this checks to see if host is in the format of something like \\192.168.0.1\C$ 185 | host = host[2:] 186 | host = host[:host.find("\\")] 187 | elif "smb://" in host: # this checks to see if the host contains a format such as smb://192.168.0.1/C$ 188 | host = host[6:] 189 | host = host[:host.find("/")] 190 | if self.skip_host == host: 191 | self.blacklisted.append(host) 192 | continue 193 | if len(self.list_of_shares) == 1 and ("//" in orig_host or "\\\\" in orig_host): 194 | if "//" in orig_host: 195 | share = orig_host[orig_host.rfind("/")+1:] 196 | elif "\\\\" in orig_host: 197 | if orig_host[-1] == "\\": 198 | temp = orig_host[:-1] 199 | share = temp[temp.rfind("\\")+1:] 200 | self.smb_host = host 201 | self.smb_share = share 202 | else: 203 | for share in self.list_of_shares: 204 | if self.skip_host == host: 205 | self.blacklisted.append(host) 206 | break 207 | self.smb_host = host 208 | self.smb_share = share 209 | tmp_share = tmp_share.replace(self.smb_host,"") 210 | tmp_share = tmp_share.replace("smb:///","") 211 | if len(tmp_share) == 0 and (self.smb_share != "profile" and len(self.smb_share) == 0): 212 | print empty_share_error % self.smb_host 213 | continue 214 | if len(self.list_of_shares) > 1: 215 | for x in self.list_of_shares: 216 | self.smb_share = x 217 | print "\n [*] Attempting to spider smb://%s/%s" % (self.smb_host, self.smb_share.replace("profile","<user profiles>")) 218 | self.spider_host() 219 | else: 220 | print "\n [*] Attempting to spider smb://%s/%s " % (self.smb_host, self.smb_share.replace("profile","<user profiles>")) 221 | self.spider_host() 222 | if self.list_of_shares[0] == "profile": 223 | if self.inputfile: 224 | print " [*] Finished with smb://%s/<user profiles>. [Remaining: %s] " % (self.smb_host, str(len(self.list_of_hosts)-self.total_hosts-1)) 225 | else: 226 | print " [*] Finished with smb://%s/<user profiles>. [Remaining: %s] " % (self.smb_host, str(len(self.list_of_hosts)-self.total_hosts)) 227 | else: 228 | print " [*] Finished with smb://%s/%s. [Remaining: %s] " % (self.smb_host, self.smb_share, str(len(self.list_of_hosts)-self.total_hosts)) 229 | if self.smb_download: self.start_downloading() 230 | 231 | def start_downloading(self): 232 | if len(self.sensitive_strings) == 0: return 233 | print "\n" + colors.blue + " [*] " + colors.norm + "Attempting to download files that were deemed sensitive." 234 | if not os.path.exists('smbspider-downloads'): 235 | os.makedirs('smbspider-downloads') 236 | for f in self.file_locations: 237 | host = f[2:] 238 | host = str(host[:host.find("\\")]) 239 | share = f[len(host)+3:] 240 | share = share[:share.find("\\")] 241 | full_path = f.replace("\\\\%s\\%s\\" % (host, share), "").strip() 242 | file_name = full_path[full_path.rfind("\\")+1:] 243 | for s in self.sensitive_strings: 244 | if s in file_name: 245 | result = commands.getoutput("%s -c \"get \\\"%s\\\" \\\"%s_%s\\\"\" //%s/%s -U %s " % (self.smbclient(), full_path.replace("\\","\\\\"), \ 246 | host,file_name, host, share, self.credentials)) 247 | print colors.blue + " [*] " + colors.norm + "Downloaded: %s from smb://%s/%s" % (file_name, host, share) 248 | commands.getoutput("mv \"%s_%s\" \"smbspider-downloads/%s\"" % (host, file_name, host, file_name)) 249 | else: 250 | temp_file = s.split("*") 251 | all_match = 0 252 | for tmp in temp_file: 253 | if tmp in full_path: 254 | all_match = 1 255 | else: 256 | all_match = 0 257 | break 258 | if all_match == 1: 259 | result = commands.getoutput("%s -c \"get \\\"%s\\\" \\\"%s_%s\\\"\" //%s/%s -U %s " % (self.smbclient(), full_path.replace("\\","\\\\"), \ 260 | host,file_name, host, share, self.credentials)) 261 | print colors.blue + " [*] " + colors.norm + "Downloaded: %s from smb://%s/%s" % (file_name, host, share) 262 | commands.getoutput("mv \"%s_%s\" \"smbspider-downloads/%s_%s\"" % (host, file_name, host, file_name)) 263 | 264 | def parse_result(self, result): 265 | ############################################################ 266 | # this small section removes all of the unnecessary crap. a bit ugly, i know! :x 267 | errors = ["O_SUCH_F","ACCESS_DEN", 268 | "US_OBJECT_NAME_IN", "US_INVALID_NETWORK_RE", "CT_NAME_NOT", 269 | "not present","CONNECTION_REFUSED" 270 | ] 271 | result = result.split('\n') 272 | purge = [] 273 | trash = [" . ", " .. ", "Domain=", " D", "blocks of size", 274 | "wrapper called", "Substituting user supplied"] 275 | for num in range(0,len(result)): 276 | for d in trash: 277 | if d in result[num] or len(result[num]) < 2: 278 | purge.append(num) 279 | purge = list(set(purge)) 280 | purge = sorted(purge, reverse=True) 281 | for i in purge: 282 | del result[i] 283 | ############################################################ 284 | directory = "" 285 | filename = "" 286 | file_locations = [] 287 | file_change = False 288 | for x in result: 289 | if x[0] == "\\": 290 | directory = x 291 | file_change = False 292 | else: 293 | filename = x[2:] 294 | filename = filename[:filename.find(" ")] 295 | file_change = True 296 | fail = 0 297 | if not file_change: continue 298 | for error in errors: 299 | if error in filename: 300 | fail = 1 301 | if fail == 0 and len(filename) > 0: 302 | if not self.outputfile: 303 | file_complete_path = "\\\\%s\%s" % (self.smb_host,self.smb_share) + directory + "\\" + filename 304 | print colors.blue + " [*] " + colors.norm + file_complete_path 305 | else: 306 | if not os.path.exists('smbspider'): 307 | os.makedirs('smbspider') 308 | 309 | if self.profile: 310 | lawl_share = "profile" 311 | else: 312 | lawl_share = self.smb_share 313 | output = open("smbspider/smbspider_%s_%s_%s.txt" % (self.smb_host, lawl_share, self.credentials.split()[0]), 'a') 314 | file_complete_path = colors.blue + " [*] " + colors.norm + "\\\\%s\%s" % (self.smb_host,lawl_share) + directory + "\\" + filename + "\n" 315 | output.write(file_complete_path) 316 | output.close() 317 | if self.smb_download: 318 | self.file_locations.append(file_complete_path[file_complete_path.find("\\\\"):]) 319 | 320 | def fingerprint_fs(self): 321 | result = commands.getoutput("%s -c \"ls Users\\*\" //%s/C$ -U %s" % (self.smbclient(), self.smb_host, self.credentials)).split() 322 | if self.check_errors(result[-1]): 323 | return "error" 324 | if "NT_STATUS_OBJECT_NAME_NOT_FOUND" in result: 325 | return "old" 326 | else: 327 | return "new" 328 | 329 | def find_users(self, result): 330 | result = result.split('\n') 331 | purge = [] 332 | users = [] 333 | for num in range(0,len(result)): # cleans some stuff up a bit. 334 | if " . " in result[num] or " .. " in result[num] or "Domain=" in result[num]\ 335 | or len(result[num]) < 2 or "blocks of size" in result[num]: 336 | purge.append(num) 337 | purge = sorted(purge, reverse=True) 338 | for i in purge: 339 | del result[i] 340 | 341 | #clean up users list a little bit 342 | for i in result: 343 | user = i[:i.find(" D")] 344 | user = user[2:user.rfind(re.sub(r'\W+', '', user)[-1])+1] 345 | users.append(user) 346 | return users 347 | 348 | def check_errors(self, result): 349 | access_error = { 350 | "UNREACHABLE":" [-] Error [%s]: Check to ensure that host is online and that share is accessible." % self.smb_host, 351 | "UNSUCCESSFUL":" [-] Error [%s]: Check to ensure that host is online and that share is accessible." % self.smb_host, 352 | "TIMEOUT":" [-] Error [%s]: Check to ensure that host is online and that share is accessible." % self.smb_host, 353 | "LOGON_SERVER":" [-] Error %s Cannot contact logon server. Skipping host." % self.smb_host 354 | } 355 | for err in access_error: 356 | if err in result: 357 | print colors.red + access_error[err] + colors.norm 358 | self.skip_host = self.smb_host 359 | return True 360 | 361 | 362 | if "LOGON_FAIL" in result.split()[-1] and not self.ignorecheck: 363 | print colors.red + " [-] " + colors.norm + "Error [%s]: Invalid credentials. Please correct credentials and try again." % self.smb_host 364 | exit() 365 | elif "ACCESS_DENIED" in result.split()[-1]: 366 | print colors.red + " [-] " + colors.norm + "Error [%s]: Valid credentials, but no access. Try another account." % self.smb_host 367 | elif "BAD_NETWORK" in result.split()[-1] or "CONNECTION_REFUSED" in result.split()[-1]: 368 | print colors.red + " [-] " + colors.norm + "Error: Invalid share -> smb://%s/%s" % (self.smb_host,self.smb_share) 369 | return True 370 | 371 | 372 | def smbclient(self): 373 | if self.pth: 374 | return "pth-smbclient" 375 | else: 376 | return "smbclient" 377 | 378 | def spider_host(self): 379 | if self.smb_share.lower() == "profile": 380 | self.smb_share = "C$" 381 | self.profile = True 382 | if self.fingerprint_fs() == "error": 383 | return 384 | elif self.fingerprint_fs() == "old": 385 | folders = ['My Documents','Desktop','Documents'] 386 | result = commands.getoutput("%s -c \"ls \\\"Documents and Settings\\*\" //%s/C$ -U %s" % (self.smbclient(), self.smb_host, self.credentials)) 387 | if self.check_errors(result): 388 | return 389 | users = self.find_users(result) 390 | for user in users: 391 | for folder in folders: 392 | result = commands.getoutput("%s -c \"recurse;ls \\\"Documents and Settings\\%s\\%s\" //%s/C$ -U %s"\ 393 | % (self.smbclient(), user, folder, self.smb_host, self.credentials)) 394 | self.parse_result(result) 395 | else: 396 | folders = ['Documents','Desktop','Music','Videos','Downloads','Pictures'] 397 | result = commands.getoutput("%s -c \"ls \\\"Users\\*\" //%s/C$ -U %s" % (self.smbclient(), self.smb_host, self.credentials)) 398 | if self.check_errors(result): 399 | return 400 | users = self.find_users(result) 401 | for user in users: 402 | for folder in folders: 403 | result = commands.getoutput("%s -c \"recurse;ls \\\"Users\\%s\\%s\" //%s/C$ -U %s" % (self.smbclient(), user, folder, self.smb_host, self.credentials)) 404 | self.parse_result(result) 405 | else: 406 | result = commands.getoutput("%s -c \"recurse;ls\" \"//%s/%s\" -U %s" % (self.smbclient(), self.smb_host, self.smb_share, self.credentials)) 407 | if self.check_errors(result): 408 | return 409 | self.parse_result(result) 410 | 411 | if __name__ == "__main__": 412 | try: 413 | start(argv[1:]) 414 | except KeyboardInterrupt: 415 | print "\nExiting. Interrupted by user (ctrl-c)." 416 | exit() 417 | except Exception, err: 418 | print err 419 | exit() 420 | 421 | print "\n-----" 422 | print "Completed in: %.1fs" % (time.time() - start_time) 423 | -------------------------------------------------------------------------------- /iSMTP/iSMTP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | ################################################################################################################# 4 | # # 5 | # This script was designed to be seemingly easy if you're used to ispoof by Stone. # 6 | # This script supports a variety of things that we typically test for during an external pentest. # 7 | # Overall, you can automate the process of SMTP User Enumeration, SMTP Spoofing, and/or SMTP relay. # 8 | # Any combination of the techniques can be used -- in other words, it's as flexible as you need it to be. # 9 | # # 10 | # Author: Alton Johnson <alton@vonahi.io # # 11 | # Updated: 05-24-2013 # 12 | # Version: 1.6 # 13 | # # 14 | # While this tool supports a variety of options, there is still a lot of room for improvement. # 15 | # Please report any bugs and/or suggestions to me. # 16 | # # 17 | ################################################################################################################# 18 | 19 | from sys import argv 20 | import time, smtplib, getopt, socket, os 21 | 22 | class colors: 23 | lightblue = "\033[1;36m" 24 | blue = "\033[1;34m" 25 | normal = "\033[0;00m" 26 | red = "\033[1;31m" 27 | white = "\033[1;37m" 28 | green = "\033[1;32m" 29 | 30 | start_time = time.time() 31 | banner = "\n " + "-" * 69 + "\n " + colors.white + " iSMTP v1.6 - SMTP Server Tester, Alton Johnson (alton@vonahi.io)\n " + colors.normal + "-" * 69 + "\n " 32 | split_service = "\n " + colors.white + "-" * 10 + " starting next test " + "-" * 10 + colors.normal + "\n" 33 | split_target = "\n " + colors.white + "=" * 23 + " starting next target " + "=" * 23 + colors.normal + "\n" 34 | 35 | def help(): 36 | print banner 37 | print " Usage: ./iSMTP.py <OPTIONS>\n" 38 | print colors.red + " Required:\n" + colors.normal 39 | print "\t-f <import file>\tImports a list of SMTP servers for testing.\n\t\t\t\t(Cannot use with '-h'.)" 40 | print "\t-h <host>\t\tThe target IP and port (IP:port).\n\t\t\t\t(Cannot use with '-f'.)" 41 | print colors.green + "\n Spoofing:\n" + colors.normal 42 | print "\t-i <consultant email>\tThe consultant's email address." 43 | print "\t-s <sndr email>\t\tThe sender's email address." 44 | print "\t-r <rcpt email>\t\tThe recipient's email address." 45 | print "\t --sr <email>\t\tSpecifies both the sender's and recipient's email address." 46 | print "\t-S <sndr name>\t\tThe sender's first and last name." 47 | print "\t-R <rcpt name>\t\tThe recipient's first and last name." 48 | print "\t --SR <name>\t\tSpecifies both the sender's and recipient's first and last name." 49 | print "\t-m\t\t\tEnables SMTP spoof testing." 50 | print "\t-a\t\t\tIncludes .txt attachment with spoofed email." 51 | print colors.green + "\n SMTP enumeration:\n" + colors.normal 52 | print "\t-e <file>\tEnable SMTP user enumeration testing and imports email list." 53 | print "\t-l <1|2|3>\tSpecifies enumeration type (1 = VRFY, 2 = RCPT TO, 3 = all).\n\t\t\t(Default is 3.)" 54 | print colors.green + "\n SMTP relay:\n" + colors.normal 55 | print "\t-i <consultant email>\tThe consultant's email address." 56 | print "\t-x\t\t\tEnables SMTP external relay testing." 57 | print colors.green + "\n Misc:\n" + colors.normal 58 | print "\t-t <secs>\tThe timeout value. (Default is 10.)" 59 | print "\t-o\t\tCreates \"ismtp-results\" directory and writes output to\n\t\t\tismtp-results/smtp_<service>_<ip>(port).txt\n" 60 | print " Note: Any combination of options is supported (e.g., enumeration, relay, both, all, etc.).\n" 61 | 62 | def output_write(smtp_host,smtp_port,data,output,smtp_test): 63 | if output: 64 | if not os.path.exists('ismtp-results'): 65 | os.makedirs('ismtp-results') 66 | output_file = open('ismtp-results/%s_%s(%s).txt' % (smtp_test,smtp_host,smtp_port), 'w') 67 | output_file.write(data) 68 | output_file.write("%s" % ("-" * 5)) 69 | output_file.write("\nCompleted in: %.1fs\n" % (time.time() - start_time)) 70 | output_file.close() 71 | print " Output file created." 72 | 73 | def smtp_relay(smtp_host,smtp_port,consultant_email): 74 | print " Testing SMTP server [external relay]: %s:%s\n" % (smtp_host, smtp_port) 75 | smtp_rlog = "\n Testing SMTP server [external relay]: %s:%s\n" % (smtp_host, smtp_port) 76 | 77 | #grabs the domain name from consultant email 78 | consultant_domain = consultant_email[consultant_email.rfind("@")+1:] 79 | 80 | try: 81 | server = smtplib.SMTP(smtp_host, smtp_port) 82 | # server.docmd returns ['status code','message'] 83 | response = server.docmd("helo",consultant_domain) 84 | print " - Submitted 'helo %s' : %s" % (consultant_domain,str(response[0])) 85 | smtp_rlog += "\n - Submitted 'helo example.com' : %s" % str(response[0]) 86 | response = server.docmd("mail from:","<%s>" % consultant_email) 87 | print " - Submitted 'mail from: <%s>' : %s" % (consultant_email, str(response[0])) 88 | smtp_rlog += "\n - Submitted 'mail from: <%s>' : %s" % (consultant_email, str(response[0])) 89 | response = server.docmd("rcpt to:","<%s>" % consultant_email) 90 | print " - Submitted 'rcpt to: <%s>' : %s" % (consultant_email, response[0]) 91 | smtp_rlog += "\n - Submitted 'rcpt to: <%s>' : %s" % (consultant_email, response[0]) 92 | if response[0] != 250: 93 | print colors.red + "\n External SMTP relay access denied." + colors.normal 94 | smtp_rlog += colors.red + "\n\n External SMTP relay access denied." + colors.normal 95 | else: 96 | print colors.blue + "\n External SMTP relay enabled." + colors.normal 97 | smtp_rlog += colors.blue + "\n\n External SMTP relay enabled." + colors.normal 98 | server.quit() 99 | except smtplib.SMTPException, err: 100 | if "421" in str(err): 101 | print colors.red + " Error: Service rejected connection attempt." + colors.normal 102 | smtp_rlog = colors.red + "\n Error: Service rejected connection attempt." + colors.normal 103 | else: 104 | print colors.red + str(err) + colors.normal 105 | smtp_rlog += "\n" + colors.red + str(err) + colors.normal 106 | except socket.timeout: 107 | print colors.red + " Error: The system timed out while trying to connect to the SMTP server." + colors.normal 108 | smtp_rlog += colors.red + "\n Error: The system while timed out trying to connect to the SMTP server." + colors.normal 109 | except Exception, err: 110 | print colors.red + str(err) + colors.normal 111 | smtp_rlog += "\n" + colors.red + str(err) + colors.normal 112 | 113 | print "\n Completed external SMTP relay test." 114 | smtp_rlog += "\n\n Completed external SMTP relay test.\n\n" 115 | 116 | #return log in case output is enabled 117 | return smtp_rlog 118 | 119 | def smtp_spoof(smtp_host,smtp_port,consultant_email,sndr_email,rcpt_email,sndr_name,rcpt_name,spoof_attach): 120 | print " Testing SMTP server [internal spoof]: %s:%s\n" % (smtp_host, smtp_port) 121 | smtp_slog = "\n Testing SMTP server [internal spoof]: %s:%s\n" % (smtp_host, smtp_port) 122 | 123 | # grab domain from target mail server's banner 124 | try: 125 | s = socket.socket() 126 | s.connect((smtp_host,smtp_port)) 127 | response = s.recv(1024) 128 | domain = response.split(' ')[1].split('.')[-2] + "." + response.split(' ')[1].split('.')[-1] 129 | s.close() 130 | except socket.timeout: 131 | print colors.red + " Error: The system timed out while trying to connect to the SMTP server." + colors.normal 132 | smtp_slog += "\n" + colors.red + " Error: The system timed out while trying to connect to the SMTP server." + colors.normal 133 | print "\n Completed SMTP internal spoof test." 134 | smtp_slog += "\n\n Completed SMTP internal spoof test.\n\n" 135 | return smtp_slog 136 | except Exception, err: 137 | if "range" in str(err): 138 | domain = 'example.com' 139 | else: 140 | print colors.red + " Please check the SMTP server for typos: %s" % smtp_host + colors.normal 141 | smtp_slog += colors.red + "\n Error: Please check the SMTP server for typos: %s" % smtp_host + colors.normal 142 | print colors.red + " If your SMTP servers do not contain a typo, please report this error to Alton." + colors.normal 143 | print "\n Completed SMTP internal spoof test." 144 | smtp_slog += "\n\n Completed SMTP internal spoof test.\n\n" 145 | return smtp_slog 146 | 147 | try: 148 | smtp_subj = "SMTP Server Test" 149 | smtp_msg = "\r\n%s:\r\n\r\nThis message is part of a security assessment. If this message is received, \nplease take a screenshot and forward it \nto %s.\r\n\r\nThis message was delivered through %s:%s." % (rcpt_name, consultant_email, smtp_host, str(smtp_port)) 150 | if spoof_attach: 151 | smtp_data = "From: %s <%s>\r\nTo: %s <%s>\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed; boundary=\"000Message000\"\r\n\r\n--000Message000\r\n%s\r\n\r\n--000Message000\r\nContent-Type: application/octet-stream; name=\"Attachment.txt\"\r\n\r\nSecurity Assessment (with attachment).\r\n\r\n--000Message000--\r\n." % (sndr_name, sndr_email, rcpt_name, rcpt_email, smtp_subj,smtp_msg) 152 | else: 153 | smtp_data = "From: %s <%s>\r\nTo: %s <%s>\r\nSubject: %s\r\n%s\r\n." % (sndr_name, sndr_email, rcpt_name, rcpt_email, smtp_subj,smtp_msg) 154 | server = smtplib.SMTP(smtp_host, smtp_port) 155 | # server.docmd returns ['status code','message'] 156 | response = server.docmd("helo",domain) 157 | print " - Submitted 'helo %s' : %s" % (domain,str(response[0])) 158 | smtp_slog += "\n - Submitted 'helo %s' : %s" % (domain, str(response[0])) 159 | response = server.docmd("mail from:", "<%s>" % sndr_email) 160 | print " - Submitted 'mail from' : %s" % str(response[0]) 161 | smtp_slog += "\n - Submitted 'mail from' : %s" % str(response[0]) 162 | response = server.docmd("rcpt to:", "<%s>" % rcpt_email) 163 | print " - Submitted 'rcpt to' : %s" % str(response[0]) 164 | smtp_slog += "\n - Submitted 'rcpt to' : %s" % str(response[0]) 165 | if str(response[0])[0] == '5': 166 | print colors.red + "\n Error providing recipient email address: %s" % response[1] + colors.normal 167 | smtp_slog += colors.red + "\n\n Error providing recipient email address: %s" % response[1] + colors.normal 168 | server.quit() 169 | print "\n Completed SMTP internal spoof test." 170 | smtp_slog += "\n\n Completed SMTP internal spoof test.\n\n" 171 | return smtp_slog 172 | response = server.docmd("data") 173 | print " - Submitted 'data' : %s" % str(response[0]) 174 | smtp_slog += "\n - Submitted 'data' : %s" % str(response[0]) 175 | if spoof_attach: 176 | print " - Adding attachment..." 177 | smtp_slog += "\n - Adding attachment..." 178 | print " - Submitting message...\n" 179 | smtp_slog += "\n - Submitting message...\n" 180 | response = server.docmd("%s" % smtp_data) 181 | if str(response[0]) != '250': 182 | print colors.red + "\n Error submitting message: %s" % response[1] + colors.normal 183 | smtp_slog += colors.red + "\n\n Error submitting message: %s" % response[1] + colors.normal 184 | server.quit() 185 | print "\n Completed SMTP internal spoof test." 186 | smtp_slog += "\n\n Completed SMTP internal spoof test.\n\n" 187 | return smtp_slog 188 | if spoof_attach: 189 | modded_data = smtp_data.split('\n') 190 | del modded_data[3:7] 191 | del modded_data[12:] 192 | modded_data.insert(3, "Attachment: Attachment.txt") 193 | for i in modded_data: 194 | print colors.blue + " | " + i + colors.normal 195 | smtp_slog += colors.blue + "\n | " + i + colors.normal 196 | smtp_slog += "\n" 197 | print 198 | else: 199 | print colors.blue + " | " + smtp_data.replace("\n", "\n | ")[:-3] + colors.normal 200 | smtp_slog += colors.blue + "\n | " + smtp_data.replace("\n", "\n | ")[:-3] + colors.normal 201 | print " - Message complete" 202 | smtp_slog += "\n - Message complete" 203 | print " - Successfully submitted message: %s" % str(response[0]) 204 | smtp_slog += "\n - Successfully submitted message: %s" % str(response[0]) 205 | server.quit() 206 | except socket.timeout: 207 | pass 208 | except Exception, err: 209 | if "421" in str(err): 210 | print colors.red + " Error: Service rejected connection attempt." + colors.normal 211 | smtp_slog = colors.red + "\n Error: Service rejected connection attempt." + colors.normal 212 | else: 213 | print colors.red + " Error: " + str(err) + colors.normal 214 | smtp_slog += colors.red + "\n Error: " + str(err) + colors.normal 215 | 216 | print "\n Completed SMTP internal spoof test." 217 | smtp_slog += "\n\n Completed SMTP internal spoof test.\n\n" 218 | 219 | #return log in case output is enabled 220 | return smtp_slog 221 | 222 | def smtp_enumeration(smtp_host,smtp_port,email_list,enum_level): 223 | print " Testing SMTP server [user enumeration]: %s:%s" % (smtp_host,smtp_port) 224 | print " Emails provided for testing: %s\n" % str(len(email_list)) 225 | smtp_elog = "\n Testing SMTP server [user enumeration]: %s:%s" % (smtp_host,smtp_port) 226 | smtp_elog += "\n Emails provided for testing: %s\n" % str(len(email_list)) 227 | validc = 0 228 | 229 | #grab domain from target mail server's banner 230 | try: 231 | s = socket.socket() 232 | s.connect((smtp_host,smtp_port)) 233 | response = s.recv(1024) 234 | domain = response.split(' ')[1].split('.')[-2] + "." + response.split(' ')[1].split('.')[-1] 235 | s.close() 236 | except socket.timeout: 237 | print colors.red + " Error: The system timed out while trying to connect to the SMTP server." + colors.normal 238 | smtp_elog += "\n" + colors.red + " Error: The system timed out while trying to connect to the SMTP server." + colors.normal 239 | print "\n Completed SMTP user enumeration test." 240 | smtp_elog += "\n\n Completed SMTP user enumeration test.\n\n" 241 | return smtp_elog 242 | except Exception, err: 243 | if "list index" in str(err): 244 | domain = 'example.com' 245 | else: 246 | print colors.red + " Please check the SMTP server for typos: %s" % smtp_host + colors.normal 247 | smtp_elog += colors.red + "\n Error: Please check the SMTP server for typos: %s" % smtp_host + colors.normal 248 | print colors.red + " If your SMTP servers do not contain a typo, please report this error to Alton." + colors.normal 249 | print "\n Completed SMTP user enumeration test." 250 | smtp_elog += "\n\n Completed SMTP user enumeration test.\n\n" 251 | return smtp_elog 252 | try: 253 | server = smtplib.SMTP(smtp_host,smtp_port) 254 | response = server.docmd('helo',domain) 255 | except Exception, err: 256 | if "421" in str(err): 257 | print colors.red + " Error: Service rejected connection attempt." + colors.normal 258 | smtp_elog = colors.red + "\n Error: Service rejected connection attempt." + colors.normal 259 | else: 260 | print colors.red + " Error: " + str(err) + colors.normal 261 | smtp_elog += colors.red + "\n Error: " + str(err) + colors.normal 262 | return smtp_elog 263 | 264 | # set spaces to format output 265 | offset = 0 266 | for line in email_list: 267 | if len(line) > offset: 268 | offset = len(line) + 3 269 | 270 | # begin testing via SMTP VRFY 271 | # server.docmd returns ['status code','message'] 272 | if enum_level == 1 or enum_level == 3: 273 | print " Performing SMTP VRFY test...\n" 274 | smtp_elog += "\n Performing SMTP VRFY test...\n" 275 | fail = 0 276 | for i in email_list: 277 | try: 278 | if "@" in i: 279 | response = server.docmd('VRFY', '%s' % i[:i.find("@")]) 280 | else: 281 | response = server.docmd('VRFY', i) 282 | if response[0] == 502 or response[0] == 252 or (response[0] == 550 and "user unknown" not in response[1].lower()): 283 | if "disabled" in str(response[1]) or "Cannot VRFY user" in str(response[1]): 284 | print colors.red + " Server is not vulnerable to SMTP VRFY user enumeration." + colors.normal 285 | smtp_elog += colors.red + "\n Server is not vulnerable to SMTP VRFY user enumeration." + colors.normal 286 | else: 287 | print colors.red + " Error: %s." % response[1] + colors.normal 288 | smtp_elog += colors.red + "\n Error: %s." % response[1] + "\n" + colors.normal 289 | break 290 | elif response[0] == 250: 291 | print colors.blue + " [+] %s " % i + "-" * (offset-len(i)) + " [ success ]" + colors.normal 292 | smtp_elog += colors.blue + "\n [+] %s " % i + "-" * (offset-len(i)) + " [ success ]" + colors.normal 293 | fail = 0 294 | else: 295 | if fail == 15: 296 | print colors.red + "\n Error: Too many consistent failures. Probably not vulnerable to SMTP VRFY. Skipping... " + colors.normal 297 | smtp_elog += colors.red + "\n\n Error: Too many consistent failures. Probably not vulnerable to SMTP VRFY. Skipping... \n" + colors.normal 298 | break 299 | print colors.red + " [-] %s " % i + "-" * (offset-len(i)) + " [ invalid ]" + colors.normal 300 | smtp_elog += colors.red + "\n [-] %s " % i + "-" * (offset-len(i)) + " [ invalid ]" + colors.normal 301 | fail+= 1 302 | # print colors.red + " Error: %s:%s" % (response[0],response[1]) + colors.normal 303 | # smtp_elog += colors.red + "\n Error: %s:%s" % (response[0],response[1]) + colors.normal 304 | except Exception, err: 305 | if "unexpectedly closed" in str(err): 306 | print colors.red + " Error: Attempting to reconnect..." + colors.normal 307 | smtp_elog += colors.red + "\n Error: Attempting to reconnect..." + colors.normal 308 | try: 309 | server = smtplib.SMTP(smtp_host,smtp_port) 310 | response = server.docmd('helo',domain) 311 | continue 312 | except Exception: 313 | print colors.red + " Error: Cannot reconnect. Quitting..." + colors.normal 314 | smtp_elog += colors.red + "\n Error: Cannot reconnect. Quitting...\n" + colors.normal 315 | break 316 | else: 317 | print colors.red + " Error: " + colors.red + str(err) + "\n" + colors.normal 318 | smtp_elog += colors.red + "\n Error: " + colors.red + str(err) + "\n\n" + colors.normal 319 | print 320 | # begin testing via SMTP RCPT TO 321 | # server.docmd returns ['status code','message'] 322 | if enum_level == 2 or enum_level == 3: 323 | print " Performing SMTP RCPT TO test...\n" 324 | smtp_elog += "\n Performing SMTP RCPT TO test...\n" 325 | 326 | try: 327 | response = server.docmd('mail from:', '<pentest@company.com>') 328 | if str(response[0])[0] == '5': 329 | print colors.red + " Error: %s" % response[1] + colors.normal 330 | smtp_elog += colors.red +"\n\n Error: %s" % response[1] + colors.normal 331 | server.quit() 332 | print "\n Completed SMTP user enumeration test." 333 | smtp_elog += "\n\n Completed SMTP user enumeration test.\n\n" 334 | return smtp_elog 335 | email_domain = email_list[1][email_list[1].find("@"):] 336 | response = server.docmd("rcpt to:", "<invalidemail34598374%s>" % email_domain) 337 | if str(response[0])[0] == '2' or response[0] == 554: 338 | print colors.red + " Server is not vulnerable to SMTP RCPT TO user enumeration." + colors.normal 339 | smtp_elog += colors.red + "\n Sever is not vulnerable to SMTP RCPT TO user enumeration." + colors.normal 340 | server.quit() 341 | print "\n Completed SMTP user enumeration test." 342 | smtp_elog += "\n\n Completed SMTP user enumeration test.\n\n" 343 | return smtp_elog 344 | for n in email_list: 345 | if "@" not in n: 346 | print colors.red + " [-] %s " % n + "-" * (offset-len(n)) + " skipped (invalid email format)" + colors.normal 347 | smtp_elog += colors.red + "\n [-] %s " % n + "-" * (offset-len(n)) + " skipped (invalid email format)" + colors.normal 348 | continue 349 | try: 350 | response = server.docmd('rcpt to:', '<%s>' % n) 351 | except socket.timeout: 352 | print colors.red + " [-] %s " % n + "-" * (offset-len(n)) + " timeout" + colors.normal 353 | smtp_elog += colors.red + " [-] %s " % n + "-" * (offset-len(n)) + " timeout" + colors.normal 354 | continue 355 | except Exception: 356 | print colors.red + " Error: Attempting to reconnect..." + colors.normal 357 | smtp_elog += colors.red + "\n Error: Attempting to reconnect..." + colors.normal 358 | try: 359 | server = smtplib.SMTP(smtp_host,smtp_port) 360 | response = server.docmd('helo',domain) 361 | response = server.docmd('mail from:', '<pentest@company.com>') 362 | continue 363 | except Exception: 364 | print colors.red + " Error: Cannot reconnect. Quitting..." + colors.normal 365 | smtp_elog += colors.red + "\n Error: Cannot reconnect. Quitting..." + colors.normal 366 | break 367 | if response[0] == 250: 368 | print colors.blue + " [+] %s " % n + "-" * (offset-len(n)) + " [ valid ]" + colors.normal 369 | smtp_elog += colors.blue + "\n [+] %s " % n + "-" * (offset-len(n)) + " [ valid ]" + colors.normal 370 | else: 371 | print colors.red + " [-] %s " % n + "-" * (offset-len(n)) + " [ invalid ]" + colors.normal 372 | smtp_elog += colors.red + "\n [-] %s " % n + "-" * (offset-len(n)) + " [ invalid ]" + colors.normal 373 | validc = 0 374 | print 375 | except Exception, err: 376 | if "timed out" in str(err): 377 | print colors.red + " Error: Timed out. Try increasing the default timeout value to 10+ secs.\n" + colors.normal 378 | smtp_elog += colors.red + "\n Error: Timed out. Try increasing the default timeout value to 10+ secs.\n\n" + colors.normal 379 | else: 380 | print colors.red + " Error: \n" + str(err) + colors.normal 381 | smtp_elog += colors.red + "\n Error: \n\n" + str(err) + colors.normal 382 | print " Completed SMTP user enumeration test." 383 | smtp_elog += "\n Completed SMTP user enumeration test.\n\n" 384 | 385 | try: 386 | server.quit() 387 | except Exception, err: 388 | pass 389 | return smtp_elog 390 | 391 | def start(argv): 392 | if len(argv) < 1: 393 | help() 394 | exit() 395 | try: 396 | opts, args = getopt.getopt(argv, "h:i:s:r:S:R:moxe:l:f:t:a", ['sr=','SR=']) 397 | except getopt.GetoptError, err: 398 | print colors.red + "\n Error: %s" % err + colors.normal 399 | help() 400 | exit() 401 | 402 | # set default variables (needed in case if statement isn't met) 403 | smtp_host = '' 404 | smtp_port = 25 405 | consultant_email = '' 406 | sndr_email = '' 407 | rcpt_email = '' 408 | sndr_name = '' 409 | rcpt_name = '' 410 | smtp_enum = False 411 | smtp_list = False 412 | email_list = '' 413 | socket.setdefaulttimeout(10) 414 | relay_test = False 415 | output = False 416 | enum_level = 3 417 | spoof_test = False 418 | spoof_attach = False 419 | 420 | # for loop stdin to determine what arguments are provided 421 | for opt, arg in opts: 422 | if opt == "-i": 423 | consultant_email = arg 424 | elif opt == "-s": 425 | sndr_email = arg 426 | elif opt == "-r": 427 | rcpt_email = arg 428 | elif opt == "-S": 429 | sndr_name = arg 430 | elif opt == "-R": 431 | rcpt_name = arg 432 | elif opt == "-f": 433 | if smtp_host != "": 434 | print colors.blue + "\n Error: You cannot use '-f' with '-h'!" + colors.normal 435 | help() 436 | exit() 437 | try: 438 | smtpfile = open(arg) 439 | smtp_file = smtpfile.read().split() 440 | except Exception, err: 441 | print colors.red + "\n Error: %s\n" % err + colors.normal 442 | exit() 443 | smtp_list = True 444 | elif opt == "-h": 445 | if ":" in arg: 446 | smtp_host = arg.split(":")[0] 447 | smtp_port = int(arg.split(":")[1]) 448 | else: 449 | smtp_host = arg 450 | if smtp_list: 451 | smtpfile.close() 452 | print colors.red + "\n Error: You cannot use '-h' with '-f'!" + colors.normal 453 | help() 454 | exit() 455 | elif opt == "-t": 456 | socket.setdefaulttimeout(float(arg)) 457 | elif opt == "-e": 458 | smtp_enum = True 459 | try: 460 | email_file = open(arg) 461 | email_list = email_file.read().split() 462 | except Exception, err: 463 | print colors.red + "\n Error: %s\n" % err + colors.normal 464 | exit() 465 | elif opt == "-x": 466 | relay_test = True 467 | elif opt == "-o": 468 | output = True 469 | elif opt == "-l": 470 | enum_level = int(arg) 471 | elif opt == "--SR": 472 | sndr_name = arg 473 | rcpt_name = arg 474 | elif opt == "--sr": 475 | sndr_email = arg 476 | rcpt_email = arg 477 | elif opt == "-m": 478 | spoof_test = True 479 | elif opt == "-a": 480 | spoof_attach = True 481 | 482 | #assign required parameters depending on the test being conducted (for error checking as well) 483 | spoof_options = {'SMTP Port':smtp_port,'consultant email address':consultant_email,'sender email address':sndr_email,'recipient email address':rcpt_email,'sender name':sndr_name,'recipient name':rcpt_name} 484 | enum_options = {'email list':email_list,'SMTP port':smtp_port} 485 | relay_options = {'consultant email address':consultant_email,'SMTP port':smtp_port} 486 | 487 | # checks for errors before processing arguments 488 | if smtp_host == "" and smtp_list == False: 489 | print colors.red + "\n Error: You must provide either an SMTP server or an imported list of SMTP servers." + colors.normal 490 | help() 491 | exit() 492 | if smtp_enum == False and spoof_test == False and relay_test == False: 493 | print colors.red + "\n Error: You didn't enable any options such as spoof (-m), relay (-x), and/or enumeration (-e <list>)." + colors.normal 494 | help() 495 | exit() 496 | if smtp_enum == True: 497 | for b in enum_options: 498 | if enum_options[b] == "": 499 | print colors.red + "\n Error: While providing SMTP enumeration arguments, you forgot to provide the %s." % b + colors.normal 500 | help() 501 | exit() 502 | if relay_test == True: 503 | for y in relay_options: 504 | if relay_options[y] == "": 505 | print colors.red + "\n Error: While providing SMTP relay arguments, you forgot to provide the %s." % y + colors.normal 506 | help() 507 | exit() 508 | if spoof_test == True: 509 | for x in spoof_options: 510 | if spoof_options[x] == "": 511 | print colors.red + "\n Error: While providing SMTP spoofing arguments, you forgot to provide the %s." % x + colors.normal 512 | help() 513 | exit() 514 | 515 | #banner will print (only one time throughout entire execution) if all looks well 516 | print banner 517 | 518 | # performs either SMTP enumeration, SMTP spoofing, SMTP relay, or whatever combination requested 519 | if smtp_list: 520 | for i in smtp_file: 521 | if smtp_file.index(i) > 0: 522 | print split_target 523 | if spoof_test: 524 | output_write(i,smtp_port,smtp_spoof(i,smtp_port,consultant_email,sndr_email,rcpt_email,sndr_name,rcpt_name,spoof_attach),output,'smtp_spoof') 525 | if relay_test: 526 | if spoof_test: 527 | print split_service 528 | output_write(i,smtp_port,smtp_relay(i,smtp_port,consultant_email),output,'smtp_relay') 529 | if smtp_enum: 530 | if spoof_test or relay_test: 531 | print split_service 532 | output_write(i,smtp_port,smtp_enumeration(i,smtp_port,email_list,enum_level),output,'smtp_enum') 533 | smtpfile.close() 534 | if smtp_enum: 535 | email_file.close() 536 | else: 537 | if spoof_test: 538 | output_write(smtp_host,smtp_port,smtp_spoof(smtp_host,smtp_port,consultant_email,sndr_email,rcpt_email,sndr_name,rcpt_name,spoof_attach),output,'smtp_spoof') 539 | if relay_test: 540 | if spoof_test: 541 | print split_service 542 | output_write(smtp_host,smtp_port,smtp_relay(smtp_host,smtp_port,consultant_email),output,'smtp_relay') 543 | if smtp_enum: 544 | if spoof_test or relay_test: 545 | print split_service 546 | output_write(smtp_host,smtp_port,smtp_enumeration(smtp_host,smtp_port,email_list,enum_level),output,'smtp_enum') 547 | email_file.close() 548 | 549 | if __name__ == "__main__": 550 | try: 551 | start(argv[1:]) 552 | except KeyboardInterrupt: 553 | print "\nExiting. Closed by user (ctrl-c)" 554 | exit() 555 | 556 | print "\n" + "-" * 5 557 | print "Completed in: %.1fs\n" % (time.time() - start_time) 558 | -------------------------------------------------------------------------------- /pyfoca/pyfoca.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | ############################################################################################################# 4 | # # 5 | # This reason behind creating this script is due to the issues # 6 | # I've experienced in the FOCA application such as with downloading files. # 7 | # Additionally, taking screen captures for reports can turn into three and # 8 | # four captures depending on what metadata was gathered. Therefore, this script # 9 | # can perform some of the same functions as FOCA, and outputs the data into a nicely # 10 | # formatted table. # 11 | # # 12 | # For those of you who have already used FOCA in the past to perform metadata # 13 | # extraction, you can export the data you've received (e.g., user, software, printers, folders, etc.) # 14 | # into a directory, and have this script # 15 | # to parse those files and print it to a table. This will make the screenshot look much better. # 16 | # # 17 | # Author: Alton Johnson <alton@vonahi.io> # 18 | # Updated: 06/06/2013 # 19 | # Version: 1.6 # 20 | # # 21 | ############################################################################################################# 22 | 23 | import getopt, os, httplib, socket, urllib2, re, time, commands,sys 24 | from sys import argv 25 | 26 | curr_time = time.time() 27 | totalFiles = 0 28 | extractedFrom = 0 29 | 30 | class colors: 31 | white = "\033[1;37m" 32 | normal = "\033[0;00m" 33 | red = "\033[1;31m" 34 | blue = "\033[1;34m" 35 | green = "\033[1;32m" 36 | 37 | try: 38 | import pyPdf 39 | from pyPdf import PdfFileReader 40 | except Exception, err: 41 | print colors.red + " Warning: To obtain maximum data from PDF documents, it's highly recommended that you install the pyPDF python module." 42 | print " pyPDF can be downloaded from http://pybrary.net/pyPdf/" + colors.normal 43 | 44 | banner = '\n ' + "-" * 79 + colors.white + '\n pyfoca v1.6 - Document Metadata Extractor, Alton Johnson (alton@vonahi.io)\n ' + colors.normal + "-" * 79 + "\n" 45 | class metaparser: 46 | def __init__(self, fileName, workingDir, domainName, pageResults,exts,report_dir,del_files,verbose): 47 | self.fileName = fileName 48 | self.container = list() 49 | self.offset = [0] 50 | self.data_exists = [0] 51 | self.top_row = [' | File Name','Creation Date','Author','Produced By','Modification Date','Last Saved By'] 52 | self.top_rowf = ['Folders','Operating System(s)','Printers','Software','Users','Emails'] 53 | self.domainName = domainName 54 | self.workingDir = workingDir 55 | self.pageResults = pageResults 56 | self.totalSuccess = 0 57 | self.exts = exts 58 | self.report_dir = report_dir 59 | self.del_files = del_files 60 | self.verbose = verbose 61 | 62 | if self.report_dir == "": 63 | while len(self.offset) < len(self.top_row): 64 | self.offset.append(0) 65 | self.data_exists.append(0) 66 | else: 67 | while len(self.offset) < len(self.top_rowf): 68 | self.offset.append(0) 69 | self.data_exists.append(0) 70 | def processFile(self, curr_file): 71 | global extractedFrom 72 | author = '-' 73 | date = '-' 74 | generator = '-' 75 | created = '-' 76 | producer = '-' 77 | modded = '-' 78 | last_saved = '-' 79 | if ".pdf" in curr_file: 80 | try: 81 | pdfFile = PdfFileReader(file(curr_file, 'rb')) 82 | if pdfFile.getIsEncrypted(): 83 | pdfFile.decrypt('') 84 | docInfo = pdfFile.getDocumentInfo() 85 | if not docInfo: 86 | return 87 | last_saved = '-' 88 | #looks at the entire dictionary to parse for information 89 | if "/CreationDate" in docInfo: 90 | data = docInfo["/CreationDate"].strip("D:|'") 91 | year = data[0:4] 92 | date = data[4:6] + "/" + data[6:8] 93 | created_time = data[8:10] + ":" + data[10:12] 94 | created_time = time.strftime("%I:%M %p", time.strptime(created_time, "%H:%M")) 95 | created = date + "/" + year + " " + created_time 96 | if "/Author" in docInfo: 97 | author = docInfo["/Author"] + " " 98 | if len(author) <=1: 99 | author = "-" 100 | if "/Producer" in docInfo: 101 | producer = docInfo["/Producer"].strip("(Windows)") 102 | producer = re.sub(r'[^\w]', ' ', producer) 103 | if len(producer) == 0: 104 | producer = "-" 105 | while True: 106 | if " " in producer: 107 | producer = producer.replace(" ", " ") 108 | else: 109 | break 110 | if "/ModDate" in docInfo: 111 | data = docInfo["/ModDate"].strip("D:|'") 112 | year = data[0:4] 113 | date = data[4:6] + "/" + data[6:8] 114 | modded_time = data[8:10] + ":" + data[10:12] 115 | modded_time = time.strftime("%I:%M %p", time.strptime(modded_time, "%H:%M")) 116 | modded = date + "/" + year + " " + modded_time 117 | 118 | #strips '/' off file name (if it includes directory name) 119 | if "/" in curr_file: 120 | curr_file = curr_file[curr_file.rfind("/")+1:] 121 | if "\\" in curr_file: 122 | curr_file = curr_file.replace("\\","") 123 | 124 | #trim information if it's too long 125 | if len(curr_file) > 15: # trims file name 126 | curr_file = curr_file[:15] + "..." + curr_file[-13:] 127 | if len(producer) > 30: 128 | producer = producer[:20] + " [snipped] " 129 | if len(author) > 20: 130 | author = author[:20] + " [snipped] " 131 | 132 | #appends each piece of information. output will show ONLY if at least ONE file has data in a column 133 | self.container.append([" | " + curr_file,created,author,producer,modded,last_saved]) 134 | except Exception, err: 135 | return 136 | else: 137 | try: 138 | curr_file = curr_file.replace(" ","\ ").replace("(", "\(").replace(")", "\)") 139 | output = commands.getoutput('extract -V ' + curr_file).split('\n') 140 | if "extract: not found" in output[0]: 141 | print colors.red + " Error: This script requires the extract command." 142 | print " Please install extract by typing \'apt-get install extract\' in terminal.\n" + colors.normal 143 | exit() 144 | for i in output: 145 | if "creator" in i: 146 | author = i[i.find("-")+2:] 147 | rem_alphanumeric = re.compile('\W') 148 | author = re.sub(rem_alphanumeric, ' ', author) 149 | while True: 150 | if " " in author: 151 | author = author.replace(" ", " ") 152 | elif author[0] == " ": 153 | author = author[1:] 154 | else: 155 | break 156 | elif "date" in i and "creation" not in i: 157 | year = i[i.find('-')+2:(i.find('-')+2)+4] 158 | date = i[i.find(year)+5:(i.find(year)+5)+5].replace("-","/") 159 | modded_time = i[i.find(":")-2:i.rfind(":")-1] 160 | modded_time = time.strftime("%I:%M %p", time.strptime(modded_time, "%H:%M")) 161 | modded = date + "/" + year + " " + modded_time 162 | elif 'generator' in i: 163 | producer = i[i.find('-')+2:] 164 | elif 'creation' in i: 165 | year = i[i.find('-')+2:(i.find('-')+2)+4] 166 | date = i[i.find(year)+5:(i.find(year)+5)+5].replace("-","/") 167 | created_time = i[i.find(":")-2:i.rfind(":")-1] 168 | created_time = time.strftime("%I:%M %p", time.strptime(created_time, "%H:%M")) 169 | created = date + "/" + year + " " + created_time 170 | elif 'last saved' in i: 171 | last_saved = i[i.find('-')+2:] 172 | if "/" in curr_file: 173 | curr_file = curr_file[curr_file.rfind("/")+1:] 174 | if "\\" in curr_file: 175 | curr_file = curr_file.replace("\\","") 176 | #trim the file name if it's longer than 15 characters 177 | if len(curr_file) > 15: 178 | curr_file = curr_file[:9] + "..." + curr_file[-13:] 179 | if author != "-" or date != "-" or generator != "-" or created != "-" or producer != "-" or modded != "-" or last_saved != "-": 180 | self.container.append([" | " + curr_file,created,author,producer,modded,last_saved]) 181 | except Exception, err: 182 | if "command not found" in str(err): 183 | print colors.red + "\n Error: This program requires the \"extract\" command, and it cannot be found." 184 | print " Please install extract by using 'apt-get install extract' from terminal." + colors.normal 185 | exit() 186 | # print colors.red + curr_file + " --------------- " + str(err) + colors.normal 187 | return 188 | extractedFrom = len(self.container) 189 | 190 | def parseReport(self, folders_file, OS_file, printers_file, software_file, users_file, emails_file): 191 | supported_options = [folders_file, OS_file, printers_file, software_file, users_file, emails_file] 192 | #grab variable with most lines -- this determines how many times we append to container 193 | max_lines = 0 194 | for i in supported_options: 195 | if len(i) > max_lines: 196 | max_lines = len(i) 197 | for ln in range(0,max_lines): 198 | if ln < len(folders_file): 199 | add_folder = folders_file[ln].replace("%20", " ") 200 | else: 201 | add_folder = "-" 202 | if ln < len(OS_file): 203 | add_os = OS_file[ln] 204 | else: 205 | add_os = "-" 206 | if ln < len(printers_file): 207 | add_printer = printers_file[ln] 208 | else: 209 | add_printer = "-" 210 | if ln < len(software_file): 211 | add_software = software_file[ln] 212 | else: 213 | add_software = "-" 214 | if ln < len(users_file): 215 | add_user = users_file[ln] 216 | else: 217 | add_user = "-" 218 | if ln < len(emails_file): 219 | add_email = emails_file[ln] 220 | else: 221 | add_email = "-" 222 | self.container.append([add_folder, add_os, add_printer, add_software, add_user, add_email]) 223 | 224 | def grabMeta(self): 225 | print banner 226 | global totalFiles 227 | foundFile = False 228 | files = [] 229 | # FOCA file types 230 | folders_file = [] 231 | OS_file = [] 232 | printers_file = [] 233 | software_file = [] 234 | users_file = [] 235 | emails_file = [] 236 | self.foca_filetypes = [] 237 | if self.report_dir: 238 | if self.report_dir == ".": 239 | self.report_dir = "./" 240 | self.report_dir = self.report_dir.replace(" ", "\ ") 241 | print " Reading files..." 242 | for dirname, dirnames, filenames in os.walk(self.report_dir): 243 | for z in filenames: 244 | try: 245 | new_open = open(dirname + z) 246 | file_contents = new_open.read().replace("\r", "").replace("\t","").split('\n') 247 | while '' in file_contents: 248 | file_contents.remove('') 249 | if "Metadata" in file_contents[0]: 250 | self.foca_files = file_contents[1][file_contents[1].find("(")+1:file_contents[1].find("/")] 251 | for ext in file_contents: 252 | if ext != "": 253 | if "." == file_contents[file_contents.index(ext)][0]: 254 | self.foca_filetypes.append(file_contents[file_contents.index(ext)]) 255 | elif "folders" in file_contents[0]: 256 | for i in file_contents[1:]: 257 | if i not in folders_file: 258 | folders_file.append(i) 259 | folders_file.sort() 260 | elif "operating systems" in file_contents[0]: 261 | for a in file_contents[1:]: 262 | if a not in OS_file: 263 | OS_file.append(a) 264 | OS_file.sort() 265 | elif "printers" in file_contents[0]: 266 | for b in file_contents[1:]: 267 | if b not in printers_file: 268 | printers_file.append(b) 269 | printers_file.sort() 270 | elif "software" in file_contents[0]: 271 | for c in file_contents[1:]: 272 | if c not in software_file: 273 | software_file.append(c) 274 | software_file.sort() 275 | elif "users" in file_contents[0]: 276 | for d in file_contents[1:]: 277 | if d not in users_file: 278 | users_file.append(d) 279 | users_file.sort() 280 | elif "emails" in file_contents[0]: 281 | for e in file_contents[1:]: 282 | if e not in emails_file: 283 | emails_file.append(e) 284 | emails_file.sort() 285 | new_open.close() 286 | except Exception, err: 287 | # print err 288 | pass 289 | if len(emails_file) == 0 and len(users_file) == 0 and len(software_file) == 0 and len(printers_file) == 0 and len(OS_file) == 0 and len(folders_file) == 0: 290 | print colors.red + " Error: There are no supported files within the specified directory.\n" + colors.normal 291 | exit() 292 | self.parseReport(folders_file, OS_file, printers_file, software_file, users_file, emails_file) 293 | elif self.workingDir != "": 294 | if self.workingDir == ".": 295 | self.workingDir = "./" 296 | for dirname, dirnames, filenames in os.walk(self.workingDir): 297 | if len(filenames) == 0: 298 | print colors.red + " Error: There are no files within the specified directory.\n" + colors.normal 299 | exit() 300 | for i in filenames: 301 | for ext in self.exts: 302 | if ext in i: 303 | foundFile = True 304 | curr_file = dirname + i 305 | curr_file = curr_file.replace(" ","\ ").replace("(", "\(").replace(")", "\)") 306 | self.processFile(curr_file) 307 | totalFiles += 1 308 | if foundFile == False: 309 | print colors.red + "\n Error: Sorry, no supported files were located within the specified directory. Please try another file or directory.\n" + colors.normal 310 | exit() 311 | elif self.fileName != "": 312 | self.fileName = self.fileName.replace(" ", "\ ").replace("(", "\(").replace(")", "\)") 313 | self.processFile(self.fileName) 314 | totalFiles += 1 315 | elif self.domainName != "": 316 | print " Domain: %s" % self.domainName 317 | print " Attempting to gather links from google searches..." 318 | conn = httplib.HTTPConnection('www.google.com') 319 | total_count = 0 320 | for e in self.exts: 321 | count = 0 322 | while count < self.pageResults: 323 | conn.request("GET","/search?q=site:" + self.domainName + "+ext:" + e + "&start=%s0" % str(count)) 324 | r1 = conn.getresponse() 325 | contents = r1.read() 326 | new_pattern = "(?P<url>https?://[^:]+\.%s)" % e 327 | new_pattern = re.findall(new_pattern,contents) 328 | for n in new_pattern: 329 | if n not in files: 330 | files.append(n) 331 | count += 1 332 | total_count += 1 333 | totalFiles = len(files) 334 | if len(files) == 0: 335 | print " No files were located within Google based on the extension(s) and domain you provided.\n" 336 | exit() 337 | print " Discovered " + str(len(files)) + " files from " + str(total_count) + " total google searches..." 338 | #create pyfoca-downloads directory if it doesn't exist 339 | if not os.path.exists('pyfoca-downloads'): 340 | print " Creating pyfoca-downloads folder..." 341 | os.makedirs('pyfoca-downloads') 342 | 343 | #set max amount of spaces for pdf file names 344 | spaces = 0 345 | for item in files: 346 | item = item[item.rfind("/")+1:] 347 | if len(item) > 10: 348 | short_file = item[:10] + "..." + item[-10:] 349 | else: 350 | short_file = item 351 | if len(short_file) > spaces: 352 | spaces = len(short_file) + 3 353 | 354 | print " Attempting to download files..." 355 | if self.verbose == False: 356 | print " Please wait..." 357 | #download each file that we added to the 'files' variable 358 | print " -------------------------------" 359 | for f in files: 360 | if "..." in f: 361 | del files[files.index(f)] 362 | continue 363 | pdf_name = f[f.rfind("/")+1:] 364 | print f 365 | try: 366 | response = urllib2.urlopen(f) 367 | source = response.read() 368 | write_file = open('pyfoca-downloads/%s' % pdf_name, 'w') 369 | write_file.write(source) 370 | write_file.close() 371 | name = pdf_name.replace("(", "\(").replace(")", "\)") 372 | filesize = commands.getoutput('ls -lh pyfoca-downloads/%s | awk \'{print $5}\'' % name) 373 | if len(pdf_name) > 10: 374 | short_file = pdf_name[:10] + "..." + pdf_name[-10:] 375 | else: 376 | short_file = pdf_name 377 | if self.verbose == True: 378 | print; print colors.blue + " [+] " + short_file, "-" * (spaces-len(short_file)), "success", "[%s of %s] [size: %s]" % (str(files.index(f)+1),str(len(files)), filesize) + colors.normal 379 | except Exception, err: 380 | if self.verbose == True: 381 | print colors.red + " [-] " + short_file, "-" * (spaces-len(short_file)), "fail", "[%s of %s]" % (str(files.index(f)+1),str(len(files))) + colors.normal 382 | totalFiles -= 1 383 | continue 384 | print 385 | for e in files: 386 | pdf_name = e[e.rfind("/")+1:] 387 | self.processFile('pyfoca-downloads/%s' % pdf_name) 388 | 389 | def printMeta(self): 390 | #check to see if user requested FOCA file parsing; if so, print statistics first 391 | if self.report_dir != "": 392 | print " User specified option for \"FOCA\" text file parsing. Printing details..." 393 | print " ----------------------------------------------------------------------" 394 | for i in self.foca_filetypes: 395 | print " Total " + i[:i.find("(")-1] + " files: " + i[i.find("(")+1:i.find(")")] 396 | print 397 | #for self.data_exists, add 1 to any column with data, and 0 to column without data 398 | for i in self.container: 399 | for num in range(0,len(self.top_row)): 400 | if i[num] != "-": 401 | self.data_exists[num] = 1 402 | #check self.data_exists for empty columns, and remove them. 403 | restart_check = 1 404 | while restart_check == 1: 405 | restart_check = 0 406 | for data in self.data_exists: 407 | if data == 0: 408 | if self.report_dir == "": 409 | del self.top_row[self.data_exists.index(data)] 410 | else: 411 | del self.top_rowf[self.data_exists.index(data)] 412 | del self.offset[self.data_exists.index(data)] 413 | for citem in self.container: 414 | del citem[self.data_exists.index(data)] 415 | del self.data_exists[self.data_exists.index(0)] 416 | restart_check = 1 417 | 418 | totalFiles = len(self.container) 419 | 420 | #states that no data exists if nothing really does. this prevents the output of self.top_row from showing with nothing in the table. 421 | if len(self.container) == 0: 422 | print colors.red + " Either no data was found on Google, or there were issues opening the documents." 423 | print colors.red + " Ensure that the 'extract' tool is installed by running 'sudo apt-get install extract'\n" + colors.normal 424 | exit() 425 | 426 | #goes through each item in container and make sure max spaces are correct 427 | for item in self.container: 428 | for num in range(0,len(item)): 429 | if "|" not in item[0]: 430 | item[0] = " | " + item[0] 431 | if len(item[num]) > self.offset[num]: 432 | self.offset[num] = len(item[num]) + 1 433 | if self.report_dir == "": 434 | for x in range(0,len(self.offset)): 435 | if len(self.top_row[x]) > self.offset[x]: 436 | self.offset[x] = len(self.top_row[x]) + 1 437 | else: 438 | for x in range(0,len(self.offset)): 439 | if "|" not in self.top_rowf[0]: 440 | self.top_rowf[0] = " | " + self.top_rowf[0] 441 | if len(self.top_rowf[x]) > self.offset[x]: 442 | self.offset[x] = len(self.top_rowf[x]) + 1 443 | 444 | #prints the top row (formatted according to the # of spaces set from above code) 445 | if self.report_dir == "": 446 | top_bottom_lines = " " + "-" * (sum(self.offset) + len(self.top_row) + len(self.top_row)-2) 447 | print top_bottom_lines 448 | for top in self.top_row: 449 | print top + " " * (self.offset[self.top_row.index(top)] - len(top)) + "|", 450 | print "\n" + top_bottom_lines 451 | else: 452 | top_bottom_lines = " " + "-" * (sum(self.offset) + len(self.top_rowf) + len(self.top_rowf)-2) 453 | print top_bottom_lines 454 | for top in self.top_rowf: 455 | print top + " " * (self.offset[self.top_rowf.index(top)] - len(top)) + "|", 456 | print "\n" + top_bottom_lines 457 | 458 | #prints the metadata details for each file 459 | for item in self.container: 460 | for num in range(0,len(item)): 461 | print item[num] + " " * (self.offset[num] - len(item[num])) + "|", 462 | if item == self.container[-1]: 463 | print "\n" + top_bottom_lines + "\n" 464 | else: 465 | print 466 | 467 | print " " + "--" * 5 468 | if self.report_dir == "": 469 | if self.del_files: 470 | print " Deleting pyfoca-downloads folder..." 471 | commands.getoutput('rm pyfoca-downloads/ -rf') 472 | print " Extracted data from %s file(s)." % str(totalFiles) 473 | else: 474 | print " Extracted data from %s file(s)." % self.foca_files 475 | 476 | def help(): 477 | print banner 478 | print " Usage: ./pyfoca.py <OPTIONS> \n" 479 | print colors.green + " Domain options:\n" + colors.normal 480 | print "\t -d <domain>\t\tHarvests all documents from a domain (saves to pyfoca-downloads/). \n\t\t\t\tAfterwards, extract metadata." 481 | print colors.green + "\n Parse file/dir:\n" + colors.normal 482 | print "\t -f <file>\t\tExtracts metadata specifically from one file. (Cannot use with '-d')" 483 | print "\t -w <dir>\t\tExtracts metadata from files within specified directory. (Cannot use with '-d')" 484 | print colors.green + "\n Foca Export Parsing:\n" + colors.normal 485 | print "\t -r <directory>\t\tParses data exported from FOCA. Provide directory containing exported files." 486 | print colors.green + "\n Misc:\n" + colors.normal 487 | print "\t -x\t\t\tAfter parsing metadata, delete files downloaded from the domain." 488 | print "\t -e <pdf|doc|xls|all>\tSearch based on provided extension(s). Separate with comma. (Default is all.) " 489 | print "\t -p <number>\t\tSearches x amount of google pages (per extension). (Default is 2.)" 490 | print "\t -t <secs>\t\tSets timeout value. (Default is 5.)" 491 | print "\t -v\t\t\tPrints status messages for files that are downloaded." 492 | print "\n Supported extensions are: .pdf, .doc, .docx, .xls, .xlsx, and .ppt" 493 | print " Example: ./pyfoca.py -d www.domain.com -e pdf,doc -p 3\n" 494 | exit() 495 | 496 | def main(argv): 497 | if len(argv) < 2: 498 | help() 499 | try: 500 | opts, args = getopt.getopt(argv, 'vxf:d:r:w:p:t:e:') 501 | except getopt.GetoptError: 502 | help() 503 | 504 | fileName = '' 505 | workingDir = '' 506 | domainName = '' 507 | pageResults = 2 508 | verbose = False 509 | socket.setdefaulttimeout(5) 510 | exts = ['all'] 511 | supported_exts = ['all','pdf','doc','docx','xls','xlsx','ppt'] 512 | report_dir = '' 513 | del_files = False 514 | for opt, arg in opts: 515 | if opt == "-f": 516 | fileName = arg 517 | elif opt == "-w": 518 | workingDir = arg 519 | elif opt == "-d": 520 | domainName = arg 521 | elif opt == "-p": 522 | pageResults = int(arg) 523 | elif opt == "-t": 524 | socket.setdefaulttimeout(float(arg)) 525 | elif opt == "-e": 526 | exts = arg.split(',') 527 | elif opt == "-r": 528 | report_dir = arg 529 | elif opt == "-x": 530 | del_files = True 531 | elif opt == "-v": 532 | verbose = True 533 | 534 | #checks for errors before submitting for processing 535 | if domainName != "" and (fileName != "" or workingDir != ""): 536 | print colors.red + "\n Error: You have provided a domain name, yet you also have provided a file and/or working directory." 537 | print " You can only use the domain name option by itself." + colors.normal 538 | help() 539 | if fileName != "" and (workingDir != "" or domainName != ""): 540 | print colors.red + "\n Error: You have provided a file name, yet you also have provided a working directory and/or domain name." 541 | print " You can only use the file name option by itself." + colors.normal 542 | help() 543 | if workingDir != "" and (fileName != "" or domainName != ""): 544 | print colors.red + "\n Error: You have provided a working directory, yet you have also provided a file name and/or domain name." 545 | print " You can only use the working directory option by itself." + colors.normal 546 | help() 547 | if report_dir and (workingDir != "" or fileName != "" or domainName != ""): 548 | print colors.red + "\n Error: You've enabled report mode. Therefore, you can only provide a directory with files exported from FOCA." 549 | print " It appears that you've enabled report mode, along with some other options (e.g., directory, file name, domain name, etc.)." 550 | print " Please check your options and try again." + colors.normal 551 | help() 552 | if del_files == True and domainName == "": 553 | print colors.red + "\n Error: You've provided the '-x' option when you have no domain name specified. Please check your options." + colors.normal 554 | help() 555 | for i in exts: 556 | if i.lower() not in supported_exts: 557 | print colors.red + "\n Error: You've provided an unsupported extension. Please try again." + colors.normal 558 | help() 559 | if fileName != "": 560 | try: 561 | with open(fileName) as f: pass 562 | except IOError, err: 563 | print colors.red + "\n Error: " + str(err) + "\n" 564 | exit() 565 | if " " in fileName: 566 | fileName = fileName.replace(" ","\ ") 567 | if "all" in exts: 568 | exts = supported_exts[1:] 569 | startparse = metaparser(fileName, workingDir, domainName, pageResults,exts,report_dir,del_files,verbose) 570 | startparse.grabMeta() 571 | startparse.printMeta() 572 | 573 | if __name__ == "__main__": 574 | try: 575 | main(argv[1:]) 576 | except KeyboardInterrupt: 577 | print "\n Exiting. Interrupted by user (ctrl-c)." 578 | if os.path.exists('pyfoca-downloads'): 579 | del_folder = raw_input(" Remove pyfoca-downloads folder? [Y/n] ") 580 | if "n" not in del_folder: 581 | commands.getoutput('rm pyfoca-downloads/ -r') 582 | print 583 | exit() 584 | 585 | print " Completed in: %.1fs\n" % (time.time() - curr_time) 586 | --------------------------------------------------------------------------------