crashMe (pwn)

The main Python code is as follows:

import json
import ctypes

lib = './crashMe.so'
funcs = ctypes.cdll.LoadLibrary(lib)

# void init()
init = funcs.init

# int setString(char *data);
setString = funcs.setString
setString.argtypes = [ctypes.c_char_p]
setString.restype = ctypes.c_int

# int getString();

getString = funcs.getString
getString.restype = ctypes.c_int

# int delString();
delString = funcs.delString
delString.restype = ctypes.c_int

# int setNum(uint64_t data);
setNum = funcs.setNum
setNum.argtypes = [ctypes.c_uint64]
setNum.restype = ctypes.c_int

# int getNum();
getNum = funcs.getNum
getNum.restype = ctypes.c_int

# int delNum();
delNum = funcs.delNum
delNum.restype = ctypes.c_int

init()
print("Hello! CrashMe!")
while True:
    argc = 0
    args = None
    received = input()
    try:
        received = json.loads(received)
        if received['callNum'] is None:
            raise Exception("Invalid Parameter")

        callNum = received['callNum']
        if received['args'] is not None:
            argc = len(received['args'])
            args = received['args']
        
        if callNum == 1:
            if argc != 1:
                raise Exception("Invalid Parameter")
            data = ctypes.c_char_p(args[0].encode())
            setString(data)

        elif callNum == 2:
            getString()

        elif callNum == 3:
            delString()

        elif callNum == 4:
            if argc != 1:
                raise Exception("Invalid Parameter")
            setNum(ctypes.c_uint64(args[0]))

        elif callNum == 5:
            getNum()

        elif callNum == 6:
            delNum()

        else:
            raise Exception("Invalid Parameter")
        print("Done")

    except json.decoder.JSONDecodeError:
        print("The input must be in JSON format")
        exit()
        
    except Exception as e:
        print(e)
        exit()

It provides functionality for getting, setting and deleting strings, as well as getting, setting and deleting numbers. Our input must be in JSON format. These functions are implemented in the crashMe.so library. We actually only need to understand the string functions to solve the challenge. The important functions are as shown below:

undefined8 getString(void)
{
  undefined8 uVar1;
  
  if (str == 0) {
    uVar1 = 0xffffffff;
  }
  else {
    printf("string @ %s\n",*(undefined8 *)(str + 8));
    uVar1 = 0;
  }
  return uVar1;
}
undefined8 setString(void *param_1)
{
  void *pvVar1;
  void *pvVar2;
  
  if (str == (void *)0x0) {
    str = malloc(0x10);
    memset(str,0,0x10);
  }
  pvVar1 = str;
  if (*(long *)((long)str + 8) == 0) {
    pvVar2 = malloc(0x10);
    *(void **)((long)pvVar1 + 8) = pvVar2;
    memset(*(void **)((long)str + 8),0,0x10);
  }
  memcpy(*(void **)((long)str + 8),param_1,0xf);
  return 0;
}
undefined8 delString(void)
{
  undefined8 uVar1;
  
  if (str == (void *)0x0) {
    uVar1 = 0xffffffff;
  }
  else {
    free(*(void **)((long)str + 8));
    free(str);
    uVar1 = 0;
  }
  return uVar1;
}
void getflag(void)
{
  int __fd;
  __fd = open("./flag",0,0);
  read(__fd,flag,0x100);
  puts(flag);
                    /* WARNING: Subroutine does not return */
  exit(0);
}
void setSigHandler(void)
{
  signal(0xb,getflag);
  return;
}
int init(EVP_PKEY_CTX *ctx)
{
  int iVar1;
  
  setvbuf(_stdout,(char *)0x0,2,0);
  setvbuf(_stderr,(char *)0x0,2,0);
  iVar1 = setSigHandler();
  return iVar1;
}

On initialisation, the program calls setSigHandler which does signal(0xb, getflag). So on triggering a segmentation fault we can get the flag.

str is a global variable which points to the string we saved.

getString dereferences the address of the string, which is in str + 8, and prints it.

setString first does a check to see if str is NULL. If it is, it allocates 16 bytes of memory and assigns str to point to its address. Then, it checks if str + 8 is NULL. If it is, it allocates another 16 bytes of memory, then str + 8 is made to store the address of the new 16 bytes. This new 16 bytes is also initialised to 0. Finally, 15 bytes of the input string are copied into the string at the address stored in str + 8.

delString frees str and the other 16 bytes, whose address is at str + 8. However, notice that the function does not set str to NULL. In other words, its address remains as that of the memory region we have freed.

We can trigger a segmentation fault by calling setString to allocate the memory, delString to free it, then getString to access the freed memory region. This gets the flag, which is CDDC24{Y0u_L34rN3D_US3_4f73r_Fr33}

Last updated