Publisher
Test your enumeration skills on this boot-to-root machine.
Last updated
Test your enumeration skills on this boot-to-root machine.
Last updated
This work by Manav G Krishna is licensed under CC BY-NC 4.0
Machine IP
: 10.10.141.14
Hosts file entry
: echo '10.10.141.14 publisher.thm' | sudo tee -a /etc/hosts
Nmap Scan
:
The scan results shows that we have two ports open, that is port 22
& 80
. On port 80
an Apache web server
is running.
Checking out port 80
:
Directory Busting
:
Command
:
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
.
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
:
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
:
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
:
We now have successful RCE
.
Using a webshell
:
We have to firstly find the length
of the above webshell
to make use of it as a payload
. A script
to achieve this:
The length
is 39
.
The payload
:
URL encoded payload
:
Sending the request & checking out the webshell
:
We can see the output of the id
command in the response
.
Using a reverse shell in the webshell
:
URL encoded
:
Setting up a listener on port 443
:
We have got a connection as www-data
.
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
:
We are now think
.
Checking for binaries
that have the SUID
bit set:
Command
:
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
:
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
:
The output of these commands shows that we are indeed on an ash shell
.
Now let us check out the apparmor profile
for ash
:
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
:
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
:
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
:
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
:
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.
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
:
Running the run_container binary
:
The SUID
bit is now set.
Spawning a root shell
:
Command
:
The root flag
can be fetched from the /root
directory.
Room solved!!
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
:
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: