├── requirements.txt ├── __pycache__ └── banner.cpython-310.pyc ├── banner.py ├── README.md └── guillotine.py /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | argparse 3 | prettytable 4 | -------------------------------------------------------------------------------- /__pycache__/banner.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gohanckz/guillotine/HEAD/__pycache__/banner.cpython-310.pyc -------------------------------------------------------------------------------- /banner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #_*_ coding: utf8 _*_ 3 | 4 | #------------------------------------------------------------ 5 | # ---- SECURITY HEADERS HTTP FINDER ----| 6 | # ---- Gohanckz ----| 7 | # ---- Contact : Gohanckz@gmail.com ----| 8 | # ---- Version : 2.0 ----| 9 | #------------------------------------------------------------ 10 | 11 | 12 | def banner(): 13 | print(" ______ ") 14 | print(" |\____/|, _ ") 15 | print(" | | \ {\\, ") 16 | print(" | | `,__.'\` ") 17 | print(" ___|______|____""', :__. ") 18 | print(" / | (__) | / `, `. ") 19 | print(" / !______| L\J' `. ") 20 | print(" :_______________________________i. ") 21 | print(" | GUILLOTINE | ") 22 | print(" | By Gohanckz | ") 23 | print(" !________________________________! ") 24 | 25 | 26 | if __name__=='__main__': 27 | banner() 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guillotine 2 | 3 | Guillotine - HTTP Security Headers Finder 4 | 5 | 6 | 7 | ##### Finds the security headers that are not enabled in a specific domain. 8 | 9 | 10 | 11 | 12 | ### HTTP Security Headers List 13 | 14 | You can detect the following HTTP security headers: 15 | 16 | - [x] Strict-Transport-Security 17 | - [x] X-Frame-Options 18 | - [x] X-Content-Type-Options 19 | - [x] Content-Security-Policy 20 | - [x] X-Permitted-Cross-Domain-Policies 21 | - [x] Referrer-Policy 22 | - [x] Clear-Site-Data 23 | - [x] Cross-Origin-Embedder-Policy 24 | - [x] Cross-Origin-Opener-Policy 25 | - [x] Cross-Origin-Resource-Policy 26 | - [x] Cache-Control 27 | 28 | **note:** you can add security headers by directly modifying the code. 29 | 30 | **referer:** https://owasp.org/www-project-secure-headers/ 31 | 32 | ### INSTALL 33 | 34 | 1. Clone the repository 35 | 36 | ``` 37 | git clone https://github.com/Gohanckz/guillotine.git 38 | ``` 39 | 40 | 2. Move in to repository 41 | 42 | ``` 43 | cd guillotine 44 | ``` 45 | 46 | 3. Install the requirements. 47 | 48 | ``` 49 | pip3 install -r requirements.txt 50 | ``` 51 | 52 | ### USAGE 53 | 54 | The use is very simple. 55 | 56 | 1. Show http security headers enabled and missing 57 | ``` 58 | python guillotine.py -t https://www.domain.com 59 | ``` 60 | 61 | 2. Show and compare headers with recommended versions. 62 | ``` 63 | python guillotine.py -t https://www.domain.com --compare-versions 64 | ``` 65 | 66 | 3. Show full response 67 | ``` 68 | python guillotine.py -t https://www.domain.com -v 69 | ``` 70 | 71 | 4. Use BASIC Authenticacion to retrieve the site 72 | ``` 73 | python guillotine.py -t https://www.domain.com --basic : 74 | ``` 75 | 76 | 5. Use NTLM Authenticacion to retrieve the site 77 | ``` 78 | python guillotine.py -t https://www.domain.com --ntlm [\\]: 79 | ``` 80 | 81 | DEVELOPED| CONTACT | VERSION 82 | ----------|---------|------- 83 | [Gohanckz](https://github.com/Gohanckz) | Gohanckz@gmail.com | 2.0 84 | [ignaciocorball](https://github.com/ignaciocorball) | ignaciocorball@gmail.com | 2.1 85 | [BSolarV](https://github.com/BSolarV) | bastian.solar.v@gmail.com | 2.2 86 | 87 | -------------------------------------------------------------------------------- /guillotine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | #_*_ coding: utf8 _*_ 3 | 4 | #------------------------------------------------------------ 5 | #----- GUILLOTINE -----| 6 | #---- FINDER HTTP SECURITY HEADERS ----| 7 | #---- Gohanckz ----| 8 | #---- Contact : gohanckz@gmail.com ----| 9 | #---- Version : 2.2 ----| 10 | #------------------------------------------------------------ 11 | try: 12 | from banner import banner 13 | from prettytable import PrettyTable 14 | import requests 15 | import argparse 16 | from urllib3.exceptions import InsecureRequestWarning 17 | from requests.auth import HTTPBasicAuth 18 | from requests_ntlm import HttpNtlmAuth 19 | except ImportError as err: 20 | print("Some libraries are missing:") 21 | print(err) 22 | 23 | security_headers = [ 24 | "Strict-Transport-Security".lower(), 25 | "X-Frame-Options".lower(), 26 | "X-Content-Type-Options".lower(), 27 | "Content-Security-Policy".lower(), 28 | "X-Permitted-Cross-Domain-Policies".lower(), 29 | "Referrer-Policy".lower(), 30 | "Clear-Site-Data".lower(), 31 | "Cross-Origin-Embedder-Policy".lower(), 32 | "Cross-Origin-Opener-Policy".lower(), 33 | "Cross-Origin-Resource-Policy".lower(), 34 | "Cache-Control".lower(), 35 | ] 36 | 37 | recommended_versions = { 38 | "Strict-Transport-Security".lower(): "max-age=31536000; includeSubDomains", 39 | "X-Frame-Options".lower(): "SAMEORIGIN", 40 | "X-Content-Type-Options".lower(): "nosniff", 41 | "Content-Security-Policy".lower(): "default-src 'self';", 42 | "X-Permitted-Cross-Domain-Policies".lower(): "none", 43 | "Referrer-Policy".lower(): "no-referrer-when-downgrade", 44 | "Clear-Site-Data".lower(): '"cache", "cookies", "storage", "executionContexts"', 45 | "Cross-Origin-Embedder-Policy".lower(): "require-corp", 46 | "Cross-Origin-Opener-Policy".lower(): "same-origin", 47 | "Cross-Origin-Resource-Policy".lower(): "same-origin", 48 | "Cache-Control".lower(): "no-cache, no-store, must-revalidate", 49 | "X-XSS-Protection".lower(): "1; mode=block", 50 | "Feature-Policy".lower(): "vibrate 'none'; geolocation 'none';", 51 | "Permissions-Policy".lower(): "geolocation=(), microphone=()", 52 | "Expect-CT".lower(): "max-age=86400, enforce" 53 | } 54 | 55 | def check_security_header_versions(headers, parser): 56 | outdated_headers = {} 57 | for header, value in headers.items(): 58 | if ( 59 | header.lower() in recommended_versions 60 | ) and ( 61 | value.lower() != recommended_versions[header.lower()].lower() 62 | ): 63 | outdated_headers[header] = (value[:38]+"..." if (len(value) > 40 and not parser.verbose) else value) 64 | return outdated_headers 65 | 66 | parser = argparse.ArgumentParser(description="Finder Security Headers") 67 | parser.add_argument("-t","--target",help="Show http security headers enabled and missing", required=True) 68 | parser.add_argument("--compare-versions",action="store_true",help="Show the recomended version for headers in use.") 69 | parser.add_argument("--ntlm", help="Use NTLM Authentication. Format: [\\\\]:") 70 | parser.add_argument("--basic", help="Use BASIC Authentication. Format: :") 71 | parser.add_argument("-v","--verbose",action="store_true",help="Show full response") 72 | parser = parser.parse_args() 73 | 74 | try: 75 | requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) 76 | 77 | auth = None 78 | if( parser.basic ): 79 | basicAuth = parser.basic.split(":") 80 | auth = HTTPBasicAuth(basicAuth[0], "".join(basicAuth[1:])) 81 | elif( parser.ntlm ): 82 | ntlmAuth = parser.ntlm.split(":") 83 | auth = HttpNtlmAuth(ntlmAuth[0], "".join(ntlmAuth[1:])) 84 | 85 | url = requests.get(url=parser.target, verify=False, auth=auth) 86 | 87 | info_headers = [] 88 | headers_site = [] 89 | security_headers_site = [] 90 | missing_headers = [] 91 | 92 | headers = dict(url.headers) 93 | 94 | for i in headers: 95 | headers_site.append(i) 96 | 97 | for i in headers: 98 | info_headers.append(headers[i]) 99 | 100 | for i in headers_site: 101 | if i.lower() in security_headers: 102 | security_headers_site.append(i) 103 | 104 | for j in security_headers: 105 | if not j.lower() in [h.lower() for h in headers_site]: 106 | missing_headers.append( "-".join( [ word.capitalize() for word in j.split("-") ] ) ) 107 | 108 | table = PrettyTable() 109 | table.add_column("Header",headers_site) 110 | table.add_column("Information",info_headers) 111 | table.align="l" 112 | 113 | while len(security_headers_site) < len(missing_headers): 114 | security_headers_site.append(" ") 115 | 116 | while len(security_headers_site) > len(missing_headers): 117 | missing_headers.append(" ") 118 | 119 | count = 0 120 | for i in security_headers_site: 121 | if i != " ": 122 | count += 1 123 | 124 | count_m = 0 125 | for j in missing_headers: 126 | if j != " ": 127 | count_m +=1 128 | 129 | s_table = PrettyTable() 130 | s_table.add_column("Enabled Security Header",security_headers_site) 131 | s_table.add_column("Missing Security Header",missing_headers) 132 | s_table.align="l" 133 | except: 134 | print("[!] time out, unable to connect to site.") 135 | 136 | def main(): 137 | banner() 138 | try: 139 | print("\n[*] Analyzing target : ",parser.target) 140 | print("[*] Security headers enabled :", count) 141 | print("[*] Missing Security Headers :",count_m) 142 | except: 143 | print("[!] Syntax Error.") 144 | print("[+] Usage: python3 guillotine.py -t http://example.site") 145 | def target(): 146 | try: 147 | print(s_table) 148 | except: 149 | pass 150 | def verbose(): 151 | try: 152 | print(table) 153 | except: 154 | pass 155 | 156 | if __name__ == '__main__': 157 | main() 158 | if( parser.compare_versions or parser.verbose ): 159 | outdated_headers = check_security_header_versions(headers, parser) 160 | if outdated_headers: 161 | print("\n[!] The following headers are outdated:") 162 | for header, value in outdated_headers.items(): 163 | CapHeader = "-".join( [ word.capitalize() for word in header.split("-") ] ) 164 | print(f" - {CapHeader}:") 165 | print(f" Current value: {value}") 166 | print(f" Recommended: {recommended_versions[header.lower()]}") 167 | if parser.verbose: 168 | verbose() 169 | elif parser.target: 170 | target() 171 | --------------------------------------------------------------------------------