Hello hello!

Everyone knows: This years r2con, the conference about radare2 , has a very special challenge – PwnDebian:

The almighty blenk92 and me decided to assist the radare2 project in finding such an exploit an we think we were quite successful :)

But first some basics for r2 .

Shelling Out Via The r2 Shell

While running r2 , it’s possible to shell out and execute shell commands without leaving the r2 console:

[0x00000000]> ! echo YOLO YOLO [...] [0x5645965b5060]> dr `cat /tmp/reg` 0x5645965b5060

As can be seen, the ! and backtick characters are treated in quite a special way in the r2 shell. In fact, they cause the arguments to be executed by sh -c via r_sys_cmd_str() and similar functions from libr/util/sys.c .

The . command

Another important aspect is that the . command also has a special function in r2 . Let’s consult the help function in the radare2 shell:

[0x00000000]> .? .r2cmd: interpret the output of the command as r2 commands

This is similar to an eval() function that takes the raw input, in this case the output from an arbitrary r2 command, and executes it as if it was typed directly into the r2 shell.

Grep It Like It’s Hot

While searching through the git log with git log --grep , we’ve found an interesting commit that seems to fix code injection issues in the ir* and is* commands. In particular, the fixes in libr/core/cbin.c seem to fix this by wrapping the output of these commands in quotes. This seems strange, since these changes happen in r_cons_printf() calls that only print text to the terminal.

This situation has been cleared up for us after we’ve stumbled across usages of .<command>* in the radare2 code after grepping with ag "\"\." | ag cmd .

An interesting example of this is present in the r_core_file_reopen_debug() function in libr/core/cmd_open.c :

[...] r_core_cmd0 (core, ".is*"); r_core_cmd0 (core, ".ir*"); r_core_cmd0 (core, ".iz*"); r_core_cmd0 (core, ".iM*"); [...]

The first line calls is* internally and evaluates the output by executing the commands printed by is* that look as follows:

[...] f sym.imp.getxattr 16 0x00000000 f sym.imp.gethostname 16 0x00000000 f sym.imp.sigismember 16 0x00000000 [...]

This seems like a good attack vector, since the symbol names after sym.imp are user controlled and taken directly from the analyzed binary.

Crafting A Payload

After some tests, it turned out that no sanitation of the symbol names is taking place. Therefore the first idea was to force a new line and append a command prefixed with ! . As this didn’t seem to work properly, another approach was used.

It turns out that commands can be injected by wrapping them in:

`! <cmd>`

and inserting them into an existing command. A simple example of this approach is:

f sym.imp.`! sleep 999` 16 0x0

Manipulating /bin/ls

Our exploit uses our all-time favorite target: /bin/ls .

By replacing a symbol name like gethostname with

`! <cmd>`

using r2 or a hex editor, it becomes possible to provide an arbitrary shell command withing a binary. Since it’s quite common to analyse and debug untrusted and malicious binaries, this seems like a great attack scenario since this is largely invisible for potential victims. Also, the shell command doesn’t even get printed into the console after it has been executed:

|ERROR| Invalid command 'f sym.imp.p 99`AAAAAAAAAAA 16 0x557aa086c000' (0x66)

No sign of the sleep command: The victim doesn’t even realize it has been pwned in case a more APT-like payload is being used <:

Executing The Payload

As already mentioned, the function that’s executing r_core_cmd0 (core, ".is*") internally is called r_core_file_reopen_debug() . This smells like the ood command!

r2 -c "ood" -d /tmp/hax --> PWNED

Therefore you can get pwned by executing ood after opening the binary, which is quite a common thing to do.

Demo Time

Let’s see it in action:

PoCs

The proof of concept exploits shown above can be found here.

Please note that the calculator exploit calls gnome-calculator , so you need that installed. If you dont’ have it, try the sleep exploit.

The Hotfix

We’ve thought really hard about this.

We decided to quote the entire command:

f sym.imp.__sprintf_chk 16 0x00000000

becomes

"f sym.imp.__sprintf_chk 16 0x5562473eb000"

And

f sym.A`!sleep 99`AAAAAAAAAAA 8 0x000222c0

becomes

"f sym.A`!sleep 99`AAAAAAAAAAA 8 0x000222c0"

and therefore doesn’t get executed*. Therefore, the old Debian 3.2.1 release is affected and the current master isn’t :)

* Well at least not this time, however the fix is not complete yet :) Pull request coming soon™

The PR for the hotfix can be found here.

And now update r2 for Debian pls and don’t forget to git pull .

CVE-2019-14745 has been assigned to this.

Update: We’ve presented this at r2con 2019 and you can find the recorded talk here. While preparing the talk shortly before the presentation, we’ve discovered CVE-2019-16718 which bypasses the implemented fixes for the original exploit.

Thanks for reading, I love you all, see you at r2con!