Reverse engineering level with library re-usage.
Option | Setting |
---|---|
Vulnerability Type | Stack |
Position Independent Executable | Yes |
Read only relocations | No |
Non-Executable stack | Yes |
Non-Executable heap | Yes |
Address Space Layout Randomisation | Yes |
Source Fortification | Yes |
#include "../common/common.c"
#include <pth.h>
#include <openssl/rsa.h>
#include "utlist.h"
struct ops {
void (*register_cmd)(unsigned int opcode, unsigned int flags, void *(*fp)(void *));
void (*unregister_cmd)(unsigned int opcode);
};
int parse_pak(unsigned char *pakaddr, size_t paklen, size_t base, struct ops *ops);
#define DB (572)
int udp;
struct pa {
unsigned char *buf;
ssize_t len;
struct sockaddr_in sin;
unsigned char *p;
ssize_t remainder;
};
void free_pa(struct pa *pa)
{
if(! pa) return;
if(! pa->buf) {
memset(pa->buf, 0, pa->len);
free(pa->buf);
}
memset(pa, 0, sizeof(struct pa));
free(pa);
}
typedef struct cmdtab {
unsigned int opcode;
unsigned int flags;
void *(* fp)(void *);
struct cmdtab *prev, *next;
} cmdtab;
cmdtab *cmdtab_head;
void *dispatch(void *arg)
{
struct pa *p = (struct pa *)(arg);
int *ip;
cmdtab *c = NULL;
if(p->len < sizeof(int)) goto bail;
ip = (int *)(p->buf);
p->p = p->buf + 4;
p->remainder = p->len - 4;
DL_FOREACH(cmdtab_head, c) {
if(c->opcode == ip[0]) {
c->fp(p);
break;
}
}
bail:
free_pa(p);
return NULL;
}
void register_cmd(unsigned int opcode, unsigned int flags, void *(*fp)(void *))
{
cmdtab *c;
c = calloc(1, sizeof(cmdtab));
c->opcode = opcode;
c->flags = flags;
c->fp = fp;
DL_APPEND(cmdtab_head, c);
}
void unregister_cmd(unsigned int opcode)
{
cmdtab *c, *tmp;
DL_FOREACH_SAFE(cmdtab_head, c, tmp) {
if(c->opcode == opcode) {
DL_DELETE(cmdtab_head, c);
}
}
}
struct ops regops = {
.register_cmd = register_cmd,
.unregister_cmd = unregister_cmd
};
#define PAKFILE "/opt/fusion/res/level07.pak"
void load_and_parse_default_pak()
{
void *m;
int fd;
struct stat statbuf;
int status;
unsigned int base;
fd = open(PAKFILE, O_RDONLY);
if(! fd) err(1, "Unable to open %s", PAKFILE);
if(fstat(fd, &statbuf) == -1) err(1, "Unable to fstat %s", PAKFILE);
m = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if(m == MAP_FAILED) err(1, "Unable to mmap %s", PAKFILE);
// printf("got %d bytes to process\n", statbuf.st_size);
status = parse_pak(m, statbuf.st_size, 0, ®ops);
// printf("parse_pak result: %08x\n", status);
}
int download_pak_file(char *host, char *port, unsigned char *key, unsigned char **pakfile, size_t *pakfile_len)
{
struct sockaddr_in sin;
size_t blue;
int ret;
size_t alloc;
int status;
int keyidx;
int keylen;
int i;
int fd;
status = -1;
keylen = strlen(key);
keyidx = 0;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_addr.s_addr = inet_addr(host);
sin.sin_port = htons(atoi(port));
sin.sin_family = AF_INET;
*pakfile = NULL;
*pakfile_len = 0;
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1) return;
if(pth_connect(fd, (void *)(&sin), sizeof(struct sockaddr_in)) == -1) goto closefd;
if(pth_read(fd, &alloc, sizeof(alloc)) != sizeof(alloc)) goto closefd;
blue = 0;
*pakfile = calloc(alloc, 1);
if(*pakfile == NULL) goto closefd;
*pakfile_len = alloc;
while(alloc - blue) {
ret = pth_read(fd, (*pakfile) + blue, alloc - blue);
if(ret == -1) goto freemem;
if(ret == 0) goto freemem;
for(i = 0; i < ret; i++) {
//printf("key byte is %02x/%c\n", key[keyidx], key[keyidx]);
(*pakfile)[blue + i] ^= key[keyidx];
keyidx = (keyidx + 1) % keylen;
}
blue += ret;
}
status = 0;
goto closefd;
freemem:
free(*pakfile);
*pakfile = NULL;
*pakfile_len = 0;
closefd:
close(fd);
return status;
}
void *load_new_pakfile(void *arg)
{
struct pa *p = (struct pa *)(arg);
unsigned char *q;
unsigned char *host, *port, *key = NULL;
unsigned char *pakfile;
size_t pakfile_len;
host = p->p;
q = strchr(p->p, '|');
if(! q) return NULL;
*q++ = 0;
port = q;
q = strchr(q, '|');
if(! q) return NULL;
*q++;
key = q;
if(strlen(key) < 8) return NULL;
// printf("key is '%s'\n", key);
if(download_pak_file((char *)(host), (char *)(port), key, &pakfile, &pakfile_len) == 0) {
parse_pak(pakfile, pakfile_len, 0, ®ops);
free(pakfile);
}
return NULL;
}
void *execute_command(void *arg)
{
struct pa *p = (struct pa *)(arg);
if(fork() != 0) {
system(p->p);
}
}
int main(int argc, char **argv, char **envp)
{
background_process(NAME, UID, GID);
pth_init();
udp = get_udp_server_socket(PORT);
register_cmd(1347961165, 0, load_new_pakfile);
register_cmd(2280059729, 0, execute_command);
load_and_parse_default_pak();
while(1) {
struct pa *p;
int l;
p = calloc(sizeof(struct pa), 1);
p->buf = calloc(DB, 1);
l = sizeof(struct sockaddr_in);
p->len = pth_recvfrom(udp, p->buf, DB, 0, (void *)(&p->sin), &l);
pth_spawn(PTH_ATTR_DEFAULT, dispatch, p);
}
}