2

I wrote a C program that constructs a ROP payload and sends it to stdout. Using Radare2's debug mode, how would I pipe this output to a binary I am trying to exploit that accepts input on stdin?

For example, if my compiled C program is exp and the binary I am exploiting is vuln, I want to execute ./exp | ./vuln in Radare2's debug mode so I can see how my payload corrupts memory.

I saw this post but, correct me if I am wrong, it doesn't seem to answer my question and just describes how to use a second terminal for input/output.

Edit: I have found a workaround for the time being, but it is rather annoying to do repeatedly when I make changes. I first redirect the output to a new a file: ./exp > exp.output and then make a rarun2 script as follows:

#!/usr/bin/rarun2
stdin=./exp.output

And then I run things via r2 -e dbg.profile=dbg.rr2 -d vuln.

4
+50

This is a great question, and lucky you - radare2 provides several ways to achieve this. Let's go over the more basic and straightforward options.

Setting up

First things first, make sure to have the latest radare2 running. At the time of writing, this is v4.3.1. The radare community recommends building radare2 from the source. On Linux systems, it's as simple as cloning the repository and executing the following commands:

$ cd radare2
$ ./sys/install.sh

Note: By installing radare2 from package-repositories you may miss crucial features due to old versions.

Now that this part is behind us, we can continue to the solutions.


Preparing test programs

For the following examples, I wrote two programs to demonstrate how the data I dynamically pass from one program to the other program is reflected in its output.

repeater is a program that receives user input and prints it to the console.
Source: repeater.c

#include <stdio.h>

int main() {

        char user_input[100];
        fgets (user_input, 100, stdin);
        printf ("[+] Received from STDIN: %s\n", user_input);

        return 0;
}

exp is a program that prints string to the console.
Source: exp.c

#include <stdio.h>

int main() {
        // print string, hex values, and the current UNIX time
        printf ("Hello, \x41\x42\xaf\xd7, %u", (unsigned)time(NULL));
        return 0;
}

To demonstrate the required behavior, we can use these programs like this:

$ ./exp | ./repeater 
[+] Received from STDIN: Hello, AB��, 1583565405

$ ./exp | ./repeater 
[+] Received from STDIN: Hello, AB��, 1583565418

Method 0: rarun2 profile

In radare2, when it gets to interact with a debuggee, rarun2 is your go-to tool.

This program is used as a launcher for running programs with different environments, arguments, permissions, directories and overridden default file descriptors.
Source: man rarun2

While very complex and rich with features, we will focus on one of the basic features, interacting with STDIO.

From your question, it is clear that you are familiar with the concept of rarun2 profiles. When I use rarun2 to pass the output of an exploit to the debuggee, I use a profile that looks somewhat like this:

$ cat profile.rr2 
#!/usr/bin/rarun2
stdin=!./exp

This rarun2 profile will execute the ./exp program and set the output of the program to the stdin of the debuggee.

Then, we can quickly execute the program in radare2 again and again without leaving the radare2 shell.

First, create a rarun profile as shown above. Then, open the debuggee in radare2 and load this profile using the -r flag:

Load the program in debug mode and use dc to execute it:

$ r2 -r profile.rr2 -d repeater 
Process with PID 86588 started...
= attach 86588 86588
[0x7f89ba9b8100]> dc
[+] Received from STDIN: Hello, AB��, 1583567900

As you can see, you entered debug mode and the program was executed successfully with the output of the exp program. You can keep executing the program as many times as you want by using doo (as well asood`) that will "Reopen in debug mode with args".

[0x7f316b76c100]> doo
Process with PID 86657 started...
= attach 86657 86657

[0x7f2aecada100]> dc
[+] Received from STDIN: Hello, AB��, 1583568042

[0x7f2aec99ace6]> doo
Process with PID 86660 started...
= attach 86660 86660

[0x7ff100166100]> dc
[+] Received from STDIN: Hello, AB��, 1583568056


[0x7f0efc9e4ce6]> doo
Process with PID 86673 started...
= attach 86673 86673

[0x7f676d6b2100]> # Define Breakpoint at main
[0x7f676d6b2100]> db main

[0x7f676d6b2100]> dc
hit breakpoint at: 5652649de159

[0x5652649de159]> dc
[+] Received from STDIN: Hello, AB��, 1583568059

Method 1: rarun2 rule

Luckily, you can skip the creation of rarun2 file and just tell radare2 from where it should grab the stdin. This can be done easily by using the -R flag followed by rarun key and value.

$ r2 -R stdin=\!./exp -d repeater
Process with PID 87508 started...
= attach 87508 87508

[0x7f87588c0100]> dc
[+] Received from STDIN: Hello, AB��, 1583568818

This is a one-time shot, and using doo again from within the radare2 session would not use the same stdin again. But, you can take advantage of the dor command and do some trick ;)

[0x7f8758780ce6]> dor?
| dor [rarun2]  Comma separated list of k=v rarun2 profile options (e dbg.profile)

[0x7f8758780ce6]> dor stdin=!./exp
[0x7f8758780ce6]> doo
Process with PID 87594 started...
= attach 87594 87594

[0x7f8758780ce6]> dc
[+] Received from STDIN: Hello, AB��, 1583568991


[0x7fdb45f3ece6]> # And this of course can be done with a single line
[0x7fdb45f3ece6]> dor stdin=!./exp; doo; dc
Process with PID 87627 started...
= attach 87627 87627
[+] Received from STDIN: Hello, AB��, 1583569028


Method ∞

There are other ways you can perform such set-up. Since it will be too long, I will quickly note them down. Here are some ideas:

  1. Use backticks: r2 -R "stdin=\"`python -c print(1234)`\"" -d repeater or even r2 -R "stdin=\"`python -c 'print(1234)'`\"" -d repeater

  2. Use other terminal's tty to redirect STDIN. You can simply pass data to the tty by executing ./exp > dev/pts/X where x is the tty number.

  3. Use popen to write to the process, or redirect output to /proc//fd/0

  4. Use an external shell script to automate the tedious tasks you encountered

| improve this answer | |
  • Thanks for this great, thorough response! – peachykeen Mar 7 at 14:34
1

As a temporary workaround, until someone gives you the right answer, you can use a bash script like this to execute what you want in one line:

#!/bin/bash 
gcc -o exp exp.c   #add another compiler options if you need
./exp > exp.output 
r2 -e dbg.profile=dbg.rr2 -d vuln

So you just run ./nameOfScript.sh to compile and debug your program.

According to comments on this issue, it seems the feature you want is not yet implemented in radare2, but I may be wrong...

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.