choppers

DEFCON Quals 2015: catwestern

2015-05-17 00:00:04

Description

Coding Challenge: 1 point
catwestern

meow
catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me 9999

Analysis

$ nc catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me 9999
****Initial Register State****
rax=0xa0504ecce73afa76
rbx=0x5a13d4eedde89dd6
rcx=0x57133954268c4e0a
rdx=0x26ff55a0946d596f
rsi=0x88083114e942934
rdi=0xf44d57a3d3d198
r8=0xb28f1b7728f2f12a
r9=0xb5ad8bf05cf8c443
r10=0x7d1447be2beb4f34
r11=0xf0cc61bb5cf39c82
r12=0x9667b7e6afbc0605
r13=0x5aea9b213e8d6054
r14=0x7f5cf31d13c648a8
r15=0x14e7334ea89a915a
****Send Solution In The Same Format****
About to send 80 bytes:
......................................................................................
$

On connecting to the server, you get sent a list of register values for a AMD64 cpu, then some number of bytes of binary data. It appears that the binary data is AMD64 instructions.

$ nc catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me 9999 | awk 'NR > 17' | ndisasm -b 64 -
00000000  4C19C2            sbb rdx,r8
00000003  49FFCB            dec r11
00000006  49F7DA            neg r10
00000009  480FC8            bswap rax
0000000C  48FFCF            dec rdi
0000000F  4881EB012ACF3A    sub rbx,0x3acf2a01
00000016  4819F1            sbb rcx,rsi
00000019  49F7E0            mul r8
0000001C  4921CF            and r15,rcx
0000001F  49F7D7            not r15
00000022  4C09DA            or rdx,r11
00000025  4D29C2            sub r10,r8
00000028  6863A55C16        push qword 0x165ca563
0000002D  48F7E7            mul rdi
00000030  4881D32371CF27    adc rbx,0x27cf7123
00000037  49F7DC            neg r12
0000003A  4809D0            or rax,rdx
0000003D  480FCF            bswap rdi
00000040  49FFCC            dec r12
00000043  4881F35B491135    xor rbx,0x3511495b
0000004A  415D              pop r13
0000004C  C3                ret
0000004D  0A                db 0x0a
$

I assumed the challenge is to give the final register state after execution.

Solution

catwestern.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>

#include <sys/mman.h>

struct registers {
    uint64_t rax;
    uint64_t rbx;
    uint64_t rcx;
    uint64_t rdx;
    uint64_t rsi;
    uint64_t rdi;
    uint64_t r8;
    uint64_t r9;
    uint64_t r10;
    uint64_t r11;
    uint64_t r12;
    uint64_t r13;
    uint64_t r14;
    uint64_t r15;
};

int read_input(struct registers *regs, int *len, uint8_t *instructions){
    char buf[1024];
    char *c = &buf[4];

    fgets(buf, sizeof(buf), stdin);
    fgets(buf, sizeof(buf), stdin); regs->rax = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->rbx = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->rcx = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->rdx = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->rsi = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->rdi = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r8 = strtoull(c-1, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r9 = strtoull(c-1, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r10 = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r11 = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r12 = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r13 = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r14 = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin); regs->r15 = strtoull(c, NULL, 16);
    fgets(buf, sizeof(buf), stdin);
    fgets(buf, sizeof(buf), stdin); *len = strtol(&buf[14], NULL, 10);

    if(*len > 1024){
        fprintf(stderr, "len too big: %d\n", *len);
        exit(1);
    }

    fread(instructions, sizeof(uint8_t), *len, stdin);

    return 0;
}

int run_program(struct registers *regs, int len, uint8_t *instructions, struct registers *regs_out){
    static uint64_t fake_stack[64];
    static uint64_t *fake_stack_pointer;
    static uint64_t *real_stack_pointer;
    uint8_t *text;
    uint8_t *myrsp = 0;

    text = mmap(NULL, 1024*4, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0);
    if(text == MAP_FAILED){
        fprintf(stderr, "mmap() error: %d\n", errno);
        exit(1);
    }

    memcpy(text, instructions, len);

    fake_stack[63] = (uint64_t) text;
    fake_stack[62] = regs->r15;
    fake_stack[61] = regs->r14;
    fake_stack[60] = regs->r13;
    fake_stack[59] = regs->r12;
    fake_stack[58] = regs->r11;
    fake_stack[57] = regs->r10;
    fake_stack[56] = regs->r9;
    fake_stack[55] = regs->r8;
    fake_stack[54] = regs->rdi;
    fake_stack[53] = regs->rsi;
    fake_stack[52] = regs->rdx;
    fake_stack[51] = regs->rcx;
    fake_stack[50] = regs->rbx;
    fake_stack[49] = regs->rax;
    fake_stack_pointer = &fake_stack[49];


    asm(
        "movq %%rsp,%0\n"
        "movq %1,%%rsp\n"
        "popq %%rax\n"
        "popq %%rbx\n"
        "popq %%rcx\n"
        "popq %%rdx\n"
        "popq %%rsi\n"
        "popq %%rdi\n"
        "popq %%r8\n"
        "popq %%r9\n"
        "popq %%r10\n"
        "popq %%r11\n"
        "popq %%r12\n"
        "popq %%r13\n"
        "popq %%r14\n"
        "popq %%r15\n"
        "callq *(%%rsp)\n"
        "push %%r15\n"
        "push %%r14\n"
        "push %%r13\n"
        "push %%r12\n"
        "push %%r11\n"
        "push %%r10\n"
        "push %%r9\n"
        "push %%r8\n"
        "push %%rdi\n"
        "push %%rsi\n"
        "push %%rdx\n"
        "push %%rcx\n"
        "push %%rbx\n"
        "push %%rax\n"
        "movq %0,%%rsp\n"
        : "=m" (real_stack_pointer)  /* output */
        : "m" (fake_stack_pointer)   /* input */
        : "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "%rsp"
    );
    memcpy(regs_out, fake_stack_pointer, sizeof(*regs));

    return 0;
}

int main(int argc, char *argv[]){
    struct registers regs;
    struct registers regs_out;
    int len;
    uint8_t instructions[1024];

    memset(&regs, 0, sizeof(regs));
    memset(&regs_out, 0, sizeof(regs_out));
    read_input(&regs, &len, instructions);

    run_program(&regs, len, instructions, &regs_out);

    printf("rax=0x%016llx\n", regs_out.rax);
    printf("rbx=0x%016llx\n", regs_out.rbx);
    printf("rcx=0x%016llx\n", regs_out.rcx);
    printf("rdx=0x%016llx\n", regs_out.rdx);
    printf("rsi=0x%016llx\n", regs_out.rsi);
    printf("rdi=0x%016llx\n", regs_out.rdi);
    printf("r8=0x%016llx\n", regs_out.r8);
    printf("r9=0x%016llx\n", regs_out.r9);
    printf("r10=0x%016llx\n", regs_out.r10);
    printf("r11=0x%016llx\n", regs_out.r11);
    printf("r12=0x%016llx\n", regs_out.r12);
    printf("r13=0x%016llx\n", regs_out.r13);
    printf("r14=0x%016llx\n", regs_out.r14);
    printf("r15=0x%016llx\n", regs_out.r15);
    return 0;
}

Output

$ mkfifo f; cat f | ./catwestern | nc catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me 9999 | tee f
****Initial Register State****
rax=0xa4fe3da0bf290370
rbx=0xdaefffdc0caf0516
rcx=0xc1b387cbd582dce2
rdx=0xa4736131e5709b8f
rsi=0xa2c134694541df80
rdi=0x21bdeef6e0da34da
r8=0xda738fde001026a6
r9=0x97e2df6bdacad7d2
r10=0x176246f48f74cfc6
r11=0xa3bc0728d6799d08
r12=0x2372c1bbc0baf8a9
r13=0x4047f8d298fcb13c
r14=0xbcdc58738df27530
r15=0x4ff91aac5a0321c4
****Send Solution In The Same Format****
About to send 80 bytes:
...............................................................................................................................The flag is: Cats with frickin lazer beamz on top of their heads!