Created
February 28, 2015 20:56
-
-
Save ragzilla/d9bf4320245097313564 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /home/maddison/.virtualenv/nfdump-notifier/bin/python | |
from ipwhois import IPWhois | |
from ipwhois.utils import unique_addresses | |
from pprint import pprint | |
from struct import unpack, pack | |
import socket | |
import select | |
import sys | |
import string | |
import smtplib | |
from email.mime.text import MIMEText | |
import sqlite3 | |
results = unique_addresses(file_path='./top10000.201502281040-201502281200') | |
DEBUG=False #Enables basic debug info | |
TIMEOUT=0.33 #Read timeout in seconds | |
TRIES=3 #Number of times to do the monlist request | |
FROM_ADDR='noreply@example.com' | |
SMTP_SERVER='mx.example.com' | |
EMAIL_PAYLOAD=""" | |
A public NTP server on your network, running on IP address {ip_address}, participated in a large-scale attack against a customer of ours today, generating UDP responses to spoofed "monlist" requests that claimed to be from the attack target. | |
Please consider reconfiguring this NTP server in one or more of these ways: | |
1. If you run ntpd, upgrading to the latest version, which removes the "monlist" command that is used for these attacks; alternately, disabling the monitoring function by adding "disable monitor" to your /etc/ntp.conf file. | |
2. Setting the NTP installation to act as a client only. With ntpd, that can be done with "restrict default ignore" in /etc/ntp.conf; other daemons should have a similar configuration option. More information on configuring different devices can be found here: https://www.team-cymru.org/ReadingRoom/Templates/secure-ntp-template.html. | |
3. Adjusting your firewall or NTP server configuration so that it only serves your users and does not respond to outside IP addresses. | |
If you don't mean to run a public NTP server, we recommend #1 and #2. If you do mean to run a public NTP server, we recommend #1, and also that you rate-limit responses to individual source IP addresses -- silently discarding those that exceed a low number, such as one request per IP address per second. Rate-limit functionality is built into many recently-released NTP daemons, including ntpd, but needs to be enabled; it would help with different types of attacks than this one. | |
Fixing open NTP servers is important; with the 400x+ amplification factor of NTP DRDoS attacks -- one 40-byte-long request usually generates 18252 bytes worth of response traffic -- it only takes one machine on an unfiltered 1 Gbps link to create a 450+ Gbps attack! | |
If you are an ISP, please also look at your network configuration and make sure that you do not allow spoofed traffic (that pretends to be from external IP addresses) to leave the network. Hosts that allow spoofed traffic make possible this type of attack. | |
Further reading: | |
https://cert.litnet.lt/en/docs/ntp-distributed-reflection-dos-attacks | |
https://isc.sans.org/forums/diary/NTP+reflection+attack/17300 | |
http://www.symantec.com/connect/blogs/hackers-spend-christmas-break-launching-large-scale-ntp-reflection-attacks | |
http://kb.juniper.net/InfoCenter/index?page=content&id=JSA10613&smlogin=true | |
You can find more vulnerable servers on your network through this site: http://openntpproject.org/ | |
<your signature here> | |
Thanks to NFOservers.com for the email text. | |
""" | |
def int_ip_to_str(ip_num): | |
return socket.inet_ntoa(pack('!L', ip_num)) | |
def str_ip_to_int(ip): | |
return unpack('!L',socket.inet_aton(ip)) | |
def get_payload(): | |
return """\x17\x00\x03\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""" | |
def fetch(ntp_server, timeout=5): | |
def send_payload(sock, target): | |
data = get_payload() | |
bytes_sent = sock.sendto(data, (target, 123)) | |
if bytes_sent != len(data) and DEBUG: | |
print "Failed to send payload" | |
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
sock.bind(('0.0.0.0', 0)) | |
send_payload(sock, ntp_server) | |
results = set() | |
count = 0 | |
while True: | |
rlist, wlist, xlist = select.select([sock], [], [], TIMEOUT) | |
if sock in rlist: | |
data, addr = sock.recvfrom(1024) | |
return True | |
else: | |
count += 1 | |
if count >= TRIES: | |
break | |
send_payload(sock, ntp_server) | |
return False | |
conn = sqlite3.connect('./notifier.sqlite3') | |
c = conn.cursor() | |
c.execute('''create table if not exists notifier(ip text unique)''') | |
monlist = set() | |
for result in results: | |
print result, | |
c.execute('''select count(*) from notifier where ip = ?''', (result,)) | |
d = c.fetchone()[0] | |
if d != 0: | |
print 'already notified, skipping.' | |
continue | |
conn.execute('''insert into notifier values(?)''', (result,)) | |
conn.commit() | |
if fetch(result): | |
w = IPWhois(result).lookup(get_referral=False) | |
abuse = list() | |
for net in w['nets']: | |
if 'abuse_emails' in net.keys(): | |
if net['abuse_emails'] == None: | |
continue | |
for email in net['abuse_emails'].split('\n'): | |
abuse.append(email) | |
print abuse | |
if len(abuse) == 0: | |
continue | |
msg = MIMEText(EMAIL_PAYLOAD.format(ip_address=result)) | |
msg['Subject'] = 'Exploitable NTP server used for an attack: ' + result | |
msg['To'] = ', '.join(abuse) | |
msg['From'] = FROM_ADDR | |
s = smtplib.SMTP(SMTP_SERVER) | |
s.sendmail(FROM_ADDR, abuse, msg.as_string()) | |
s.quit() | |
else: | |
print 'no monlist response' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
uses ipwhois to get a list of unique ip addresses from a text file (in our case, nfdump output of ntp ddos sources), tries to check if the IP responds to ntp monlist (lines 51-84), and if so looks up the abuse contacts via ipwhois' lookup function and sends them an email asking them to fix their open ntp amplifier.