🐶dogcat
I made a website where you can look at pictures of dogs and/or cats! Exploit a PHP application via LFI and break out of a docker container.
Enumeration
I first started with an Nmap service and script scan to identify the running services.
Taking a look at the website, the first thing that was apparent to me was the URL, which had a query parameter of ?view=dog
or ?view=cat
depending on what was clicked. Considering the challenge description, I assumed the parameter was susceptible to directory traversal and thus local file inclusion (LFI).
I also ran a Feroxbuster scan on the web application to determine if there were any other directories or files that might be useful, such as a /upload
endpoint.
This unfortunately did not yield any results, so the only thing that seems to be exploitable is the URL query paramater.
Web Exploitation
Hence the challenge description, I began to try different payloads for LFI. The first thing I noticed was that if ?view
did not contain dog or cat, then an error would appear.
I then began to test the logic of determining whether dog
or cat
was included. For example, is the application just checking for "dog" substring? Or is it a direct comparison of $_GET["view"] == "dog"
. It turned out to be the former, luckily.
If something like dogs/../test
is passed, we can see the application attempts to call include()
with the file being passed. Further, it automatically appends ".php" to the extension. Thus:
But, as we saw, it only checks for substrings. So, we can trick it into including a file that is not cat.php or dog.php.
I found this StackOverFlow post about vulnerabilities with including
aribtrary data. One of the things I discovered was the use of PHP Wrappers, like php://filter
. We can call these wrappers to do certain things, such as read a file and base64 encode it.
For example, if we set ?view
to the following payload:
The page will output the contents of index.php in base64, which we can decode to figure out the inner-workings of the application.
This results in the following index.php
file:
It pretty much does exactly what I thought it did. Checks for the substring, appends ".php", and so on. However, one key note is that there is a check for ?ext
query parameter. This is what determines the extension. If it is not set, then ".php" is appended by default, however, we can set ?ext=
to have no extension.
Knowing this, I now wanted to test for LFI.
And what do you know! We were able to see the base64 encoded contents of /etc/passwd
. Unfortunately, there were no interesting entries. So, we need to continue to exploit and chain LFI into Remote Code Execution (RCE).
Exploitation
Knowing that we do indeed have LFI, we can utilize Apache Log Poisoning. Essentially, we inject PHP into the Apache server logs, and then we use LFI to load the logs, which will in turn execute our injected PHP.
There are two ways of doing this:
User Agent We can modify our user-agent to
<?php system($_GET['c']); ?>
and then access the web application. This will then store that code in the Apache logs, since Apache stores users agents.Netcat We can also just send
<?php system($_GET['c']); ?>
to the web server using netcat. Simply connect withnc 10.10.10.10 80
and then send the payload. We will get an error about the server being unable to handle the request, but that's fine.
Now we've injected the code into the Apache logs, and can pass a new query paramater, ?c
which will be our code we wish to be executed.
Here we can see we have successfully chained LFI into RCE. I ran some additional commands such as "hostname" and "ls", and one of the interesting entries I found in the directory was flag.php
. Using the same method to get the contents of index.php
, I got the contents of flag.php
and retrieved flag 1/4.
I started a Netcat listener and generated a PHP reverse shell.
Once I had the initial shell, I wanted to upgrade it to an interactive TTY shell so I can get tab-completion and other quality of life features. This is relatively simple:
Some basic enumeration cd ../
got me flag 2/4.
Privilege Escalation
Now it is time to escalate our privileges. Let's start with a simple sudo -l
to determine what our user has permission to sudo.
GTFOBINs have a nifty env GTFOBIN that we can use to escalate to root.
And that is flag 3/4!
Docker Escape
We know that this box consists of a Docker escape because the description told us so. Looking at /root
, we can notice a .Dockerenv
file, so we know this web-server is a docker container. I began to check through directories like /bin
, /var
, etc, until I found /opt/backups
. In this directory were two files: backup.sh
and backup.tar
.
It looks like /root/container
is being archived as a tar and being stored in /root/container/backup
. We can untar the backup to get more of an understanding how this works.
This will deflate backup.tar
and reveal the /root
directory of the actual machine (not the Docker container). This lets us see how exactly this container is being ran. We can look at .Dockerfile
, the source to the web app, and an interesting launch.sh
file.
The -v /root/container/backup:/opt/backups
is a bind mount. This means that the volume /opt/backups
(Container) is bound to /root/container/backup
(Host). Any changes in one directory will be reflected to the other directory.
Essentially, a backup is made on the host machine (backup.tar
) and that file is reflected to the Docker container because of the bind mount. We can also take a look at ls and see that the backup is created every minute, so it's probably some sort of cronjob on the host. So, we should be able to modify backup.sh
and the file should be reflected on the Host and executed by the cronjob.
Let's try creating another reverse shell:
And then append a reverse shell payload to /opt/backups/backup.sh
:
We should see a connection to our listener, and we now have a shell as root on the host machine, thus escaping the Docker container. And we also now have flag 4/4.
One last note: We can verify my theory about the cronjob by checking
Last updated