Nosebleed
This is not real software. It is intentionally vulnerable.
It serves no real purpose and should not be used as anything other than a simple demonstration of what can happen if you trust user input.
If you want to use it, contact me (csx239@coventry.ac.uk) to get access to the source code. What is presented here is not actually what is used in the container - the "live" version will always give some leaked credentials. The code here is intended to be used for learners to read and identify potential exploitable assumptions.
Purpose
Nosebleed is a simple watchdog service.
It recieves input on a given port (usually 8259) and can respond to the folowing commands:
- pingNxxxx - sends back the string xxxx of length N (for checking liveness and turnaround times)
- time - sends back the time
Source Code
nosebleed_server.c
#include "nosebleed_utils.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#define PORT 8259
#define BUFFERSIZE 1024
int main(int argc, char *argv[])
{
//Buffer to store data recieved
char* buffer=allocateBuffer(BUFFERSIZE);
zero(buffer,BUFFERSIZE);
//file descriptors for listening and the actual connection
int listenfd = 0, connfd = 0;
//Struct used to store address/port data, etc
struct sockaddr_in serv_addr;
//Make the scket (internet, tcp)
listenfd = socket(AF_INET, SOCK_STREAM, 0);
//Blank out the struct ready for data
memset(&serv_addr, '0', sizeof(serv_addr));
//Fill in the server details struct
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
//"bind" details from struct to file descriptor
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//accept conneections
listen(listenfd, 10);
printf("Listening for connections on port %d\n.",PORT);
while(1){
zero(buffer,BUFFERSIZE);
//wait for connection
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
//read data
//Read up to BUFFERSIZE, prevent buffer overlflow
read(connfd,buffer, BUFFERSIZE);
if(strncmp(buffer,"time",4)==0){ //Check if first 4 chars are "time"
time_t now= time(NULL);
//snprintf is like sprintf, but length limited to avoid buffer overflows...
//(sprintf is like printf but writes to a buffer instead of the screen)
snprintf(buffer, BUFFERSIZE, "%.24s\r\n", ctime(&now));
//Uee strlen to find the end of the time.
//It will be null-terminated, so strlen can tell
write(connfd, buffer, strlen(buffer));
}else if(strncmp(buffer,"ping",4)==0){
//The word "ping" will be followed by an integer representing
//the length of the string being sent
//Need to work out where the digits of the number start and stop, then
//convert it to a number. This number is how many bytes/chars the word is
int start=4; //integer starts here
int end=start; //end needs to be worked out
while(end<BUFFERSIZE && buffer[end]>='0' && buffer[end]<='9')
end++;
if(end>start){
printf("Number starts at %d and ends at %d\n",start,end);
char len_s[1+end-start];
memcpy( len_s, &buffer[start], end-start );
len_s[end-start] = '\0';
int len_i=atoi(len_s);
printf("The number is [%d]\n",len_i);
//Now send it back...
//Add 4+(end-start) to skip the ping word and number
write(connfd,(buffer+4+(end-start)),len_i);
}
}
close(connfd);
}
free(buffer);
return 0;
}
nosebleed_utils.c
#include "nosebleed_utils.h"
#include <string.h>
#include <stdlib.h>
void zero(char* buff, int len){
//Zero the buffer so no left-over stuff gets sent back
//Important security stuff right there.
memset(buff, '0', len);
}
char* allocateBuffer(int len){
char*b= (char*)malloc(len*sizeof(char));
return b;
}