Advanced HeartBleed Compartmentalisation
Server V2
Now considered this updated version of our vulnerable monolithic server:
// includes omitted
void privileged_function(char *heartbeat) {
printf("Privileged code running!\n");
// Set the heartbeat word
printf("Enter the new value for the heartbeat word to send to the clients: ");
fgets(heartbeat, 32, stdin);
// remove carriage return
heartbeat[strlen(heartbeat)-1] = '\0';
// more privilege operations...
return;
}
int main(int argc, char **argv) {
char admin_pw[64] = "supersecret";
unsigned char buf[32];
char heartbeat[32];
int opt = 1;
// initialise the heartbeat word to a default value
strcpy(heartbeat, "heartbeat");
if(argc == 2 && !strcmp(argv[1], "login")) {
char attempt[128];
printf("Please enter the admin password: ");
fgets(attempt, 128, stdin);
// remove carriage return
attempt[strlen(attempt)-1] = '\0';
if(!strcmp(attempt, admin_pw))
privileged_function(heartbeat);
else {
printf("Admin authentication failed!\n");
return -1;
}
}
int server = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(12345),
.sin_addr.s_addr = INADDR_ANY
};
bind(server, (struct sockaddr*)&addr, sizeof(addr));
listen(server, 1);
int client = accept(server, NULL, NULL);
recv(client, buf, sizeof(buf), 0);
// Heartbleed-style vulnerability:
// client sends: [type][len][data] -> respond with `len` bytes
int len = buf[1]; // vulnerable: no bounds check
// Send back the heartbeat word
send(client, heartbeat, len, 0);
close(client);
close(server);
return 0;
}
You can download this source file here.
In this version, the content of heartbeat response sent back to the client can be defined by a successfully authenticated administrator: it is taken from the command line in privileged_function after authentication:
gcc server-monolithic-v2.c -o server
./server login 130 ↵
Please enter the admin password: supersecret
Privileged code running!
Enter the new value for the heartbeat word to send to the clients: hello
In a separate terminal:
printf '\x01\x05hi' | nc localhost 12345
hello
The server is still vulnerable to leaking the password: you can try it out yourself by sending a request with a large size as we saw previously.
Note that here the way the heartbeat system works is quite silly: the content of response is now defined by the server, with its size defined by the client. This is once again done for the sake of the example, but note that this kind of bugs may happen when you have many developers working on a rather complex code base.
Compartmentalising the Server V2
The goal here is to compartmentalise the server with the same policy as for the previous part of the exercise: the authentication and privileged code in compartment 1, and the networking code in compartment 2. Split the code into comp1.c and comp2.c as done previously. You will notice that more work is needed: indeed there is the need for communication between compartment 1 and 2: compartment 1 is where the heartbeat word is set, and its value must be transmitted to compartment 2, so it can send it to the client through the network.
Cross-Compartment Communication: Command Line Arguments
The easiest way to pass the heartbeat word from compartment 1 to compartment 2 is through a command line argument. Modify compartment 2 to accept that string as first command line parameter. Compartment 1 must also be modified to invoke compartment 2 with the heartbeat word as first command line parameter, that is quite simple to achieve with execve().
Validate that your solution behave similarly vs. the monolithic version of the server V2 under normal operation. Then, try to trigger the exploit and see what is sent back to the client by the server: the leaked bytes should not contain the password value.
Cross-Compartment Communication: Pipe
The command line argument is good to pass a small amount of string-like data between compartments, but if one wants to pass more complex data structure, or a larger amount of content, a proper IPC mechanism needs to be used. Implement a second version of the compartmentalised server, this time using a named pipe (FIFO) to send the heartbeat word between compartment 1 and compartment 2.
Validate the behaviour of your solution under normal operation, and when an attacker triggers the overflow.
Submission Instructions
For both alternatives of the compartmentalised server (command line arguments and pipe-based communication) submit the source code of each compartment as two separate files grouped in a folder in the git repository:
heartbleed-advanced/comp1-cmdline.cheartbleed-advanced/comp2-cmdline.cheartbleed-advanced/comp1-pipe.cheartbleed-advanced/comp2-pipe.c