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 as
ood`) 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:
Use backticks: r2 -R "stdin=\"`python -c print(1234)`\"" -d repeater
or even r2 -R "stdin=\"`python -c 'print(1234)'`\"" -d repeater
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.
Use popen
to write to the process, or redirect output to /proc//fd/0
Use an external shell script to automate the tedious tasks you encountered