This level introduces partial hash collisions (hashcash) and more stack corruption
Option | Setting |
---|---|
Vulnerability Type | Stack |
Position Independent Executable | No |
Read only relocations | No |
Non-Executable stack | Yes |
Non-Executable heap | Yes |
Address Space Layout Randomisation | Yes |
Source Fortification | No |
#include "../common/common.c"
#include "json/json.h"
unsigned char *gRequest; // request buffer
int gRequestMax = 4096; // maximum buffer size
int gRequestSize; // current buffer size
char *token;
char *gServerIP;
unsigned char *gContents;
int gContents_len;
unsigned char *gTitle;
int gTitle_len;
json_object *gObj;
void generate_token()
{
struct sockaddr_in sin;
int len;
len = sizeof(struct sockaddr_in);
if(getpeername(0, (void *)&sin, &len) == -1)
err(EXIT_FAILURE, "Unable to getpeername(0, ...): ");
srand((getpid() << 16) ^ (getppid() + (time(NULL) ^
sin.sin_addr.s_addr) + sin.sin_port));
asprintf(&token, "// %s:%d-%d-%d-%d-%d", inet_ntoa(sin.sin_addr),
ntohs(sin.sin_port), (int)time(NULL), rand(), rand(),
rand());
}
void send_token()
{
generate_token();
printf("\"%s\"\n", token);
fflush(stdout);
}
void read_request()
{
int ret;
gRequest = malloc(gRequestMax);
if(!gRequest) errx(EXIT_FAILURE, "Failed to allocate %d bytes",
gRequestMax);
while(1) {
ret = read(0, gRequest + gRequestSize, gRequestMax - gRequestSize);
if(ret == -1) err(EXIT_FAILURE, "Failed to read %d bytes ... ",
gRequestMax - gRequestSize);
if(ret == 0) break;
gRequestSize += ret;
if(gRequestSize == gRequestMax) {
gRequest = realloc(gRequest, gRequestMax * 2);
if(gRequest == NULL) {
errx(EXIT_FAILURE, "Failed to realloc from %d bytes "
"to %d bytes ", gRequestMax, gRequestMax * 2);
}
gRequestMax *= 2;
}
}
close(0); close(1); close(2);
}
#include <openssl/hmac.h>
void validate_request()
{
unsigned char result[20];
unsigned char invalid;
int len;
if(strncmp(gRequest, token, strlen(token)) != 0)
errx(EXIT_FAILURE, "Token not found!");
// XXX won't be seen by user
len = sizeof(result);
HMAC(EVP_sha1(), token, strlen(token), gRequest, gRequestSize, result,
&len); // hashcash with added hmac goodness
invalid = result[0] | result[1]; // Not too bad :>
if(invalid)
errx(EXIT_FAILURE, "Checksum failed! (got %02x%02x%02x%02x...)",
result[0], result[1], result[2], result[3]);
// XXX won't be seen by user.
}
void parse_request()
{
json_object *new_obj;
new_obj = json_tokener_parse(gRequest);
if(is_error(new_obj)) errx(EXIT_FAILURE, "Unable to parse request");
gObj = new_obj;
}
void decode_string(const char *src, unsigned char *dest, int *dest_len)
{
char swap[5], *p;
int what;
unsigned char *start, *end;
swap[4] = 0;
start = dest;
// make sure we don't over the end of the allocated space.
end = dest + *dest_len;
while(*src && dest != end) {
// printf("*src = %02x, dest = %p, end = %p\n", (unsigned char)
// *src, dest, end);
if(*src == '\\') {
*src++;
// printf("-> in src == '\\', next byte is %02x\n", *src);
switch(*src) {
case '"':
case '\\':
case '/':
*dest++ = *src++;
break;
case 'b': *dest++ = '\b'; src++; break;
case 'f': *dest++ = '\f'; src++; break;
case 'n': *dest++ = '\n'; src++; break;
case 'r': *dest++ = '\r'; src++; break;
case 't': *dest++ = '\t'; src++; break;
case 'u':
src++;
// printf("--> in \\u handling. got %.4s\n",
// src);
memcpy(swap, src, 4);
p = NULL;
what = strtol(swap, &p, 16);
// printf("--> and in hex, %08x\n", what);
*dest++ = (what >> 8) & 0xff;
*dest++ = (what & 0xff);
src += 4;
break;
default:
errx(EXIT_FAILURE, "Unhandled encoding found");
break;
}
} else {
*dest++ = *src++;
}
}
// and record the actual space taken up
*dest_len = (unsigned int)(dest) - (unsigned int)(start);
// printf("and the length of the function is ... %d bytes", *dest_len);
}
void handle_request()
{
unsigned char title[128];
char *tags[16];
unsigned char contents[1024];
int tag_cnt = 0;
int i;
int len;
memset(title, 0, sizeof(title));
memset(contents, 0, sizeof(contents));
json_object_object_foreach(gObj, key, val) {
if(strcmp(key, "tags") == 0) {
for(i=0; i < json_object_array_length(val); i++) {
json_object *obj = json_object_array_get_idx(val, i);
tags[tag_cnt + i] = json_object_get_string(obj);
}
tag_cnt += i;
} else if(strcmp(key, "title") == 0) {
len = sizeof(title);
decode_string(json_object_get_string(val), title, &len);
gTitle = calloc(len+1, 1);
gTitle_len = len;
memcpy(gTitle, title, len);
} else if(strcmp(key, "contents") == 0) {
len = sizeof(contents);
decode_string(json_object_get_string(val), contents, &len);
gContents = calloc(len+1, 1);
gContents_len = len;
memcpy(gContents, contents, len);
} else if(strcmp(key, "serverip") == 0) {
gServerIP = json_object_get_string(val);
}
}
printf("and done!\n");
}
void post_blog_article()
{
char *port = "80", *p;
struct sockaddr_in sin;
int fd;
int len, cl;
unsigned char *post, *data;
// We can't post if there is no information available
if(! gServerIP || !gContents || !gTitle) return;
post = calloc(128 * 1024, 1);
cl = gTitle_len + gContents_len + strlen("\r\n\r\n");
len = sprintf(post, "POST /blog/post HTTP/1.1\r\n");
len += sprintf(post + len, "Connection: close\r\n");
len += sprintf(post + len, "Host: %s\r\n", gServerIP);
len += sprintf(post + len, "Content-Length: %d\r\n", cl);
len += sprintf(post + len, "\r\n");
memcpy(post + len, gTitle, gTitle_len);
len += gTitle_len;
len += sprintf(post + len, "\r\n");
memcpy(post + len, gContents, gContents_len);
len += gContents_len;
p = strchr(gServerIP, ':');
if(p) {
*p++ = 0;
port = p;
}
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(gServerIP);
sin.sin_port = htons(atoi(port));
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1) err(EXIT_FAILURE, "socket(): ");
if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1)
err(EXIT_FAILURE, "connect(): ");
nwrite(fd, post, len);
close(fd);
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *p;
signal(SIGPIPE, SIG_IGN);
background_process(NAME, UID, GID);
fd = serve_forever(PORT);
set_io(fd);
send_token();
read_request();
validate_request();
parse_request();
handle_request();
post_blog_article();
}