├── 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 " % argv[0]
42 | print colors.red + "\n Target(s) (required): \n" + colors.norm
43 | print "\t -h \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 \t Specify a valid username to authenticate to the system(s)."
47 | print "\t -p \t Specify the password which goes with the username."
48 | print "\t -P \t Use -P to provide password hash if cleartext password isn't known."
49 | print "\t -d \t If using a domain account, provide domain name."
50 | print colors.green + "\n Shares (optional):\n" + colors.norm
51 | print "\t -s \t Specify shares (separate by comma) or specify \"profile\" to spider user profiles."
52 | print "\t -f \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 \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 \\\\\\ or ///\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",""))
218 | self.spider_host()
219 | else:
220 | print "\n [*] Attempting to spider smb://%s/%s " % (self.smb_host, self.smb_share.replace("profile",""))
221 | self.spider_host()
222 | if self.list_of_shares[0] == "profile":
223 | if self.inputfile:
224 | print " [*] Finished with smb://%s/. [Remaining: %s] " % (self.smb_host, str(len(self.list_of_hosts)-self.total_hosts-1))
225 | else:
226 | print " [*] Finished with smb://%s/. [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 \n"
38 | print colors.red + " Required:\n" + colors.normal
39 | print "\t-f \tImports a list of SMTP servers for testing.\n\t\t\t\t(Cannot use with '-h'.)"
40 | print "\t-h \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 \tThe consultant's email address."
43 | print "\t-s \t\tThe sender's email address."
44 | print "\t-r \t\tThe recipient's email address."
45 | print "\t --sr \t\tSpecifies both the sender's and recipient's email address."
46 | print "\t-S \t\tThe sender's first and last name."
47 | print "\t-R \t\tThe recipient's first and last name."
48 | print "\t --SR \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 \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 \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 \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__(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:', '')
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:", "" % 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:', '')
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 )." + 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 #
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 = "(?Phttps?://[^:]+\.%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 \n"
479 | print colors.green + " Domain options:\n" + colors.normal
480 | print "\t -d \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 \t\tExtracts metadata specifically from one file. (Cannot use with '-d')"
483 | print "\t -w \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 \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 \tSearch based on provided extension(s). Separate with comma. (Default is all.) "
489 | print "\t -p \t\tSearches x amount of google pages (per extension). (Default is 2.)"
490 | print "\t -t \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 |
--------------------------------------------------------------------------------