Loading repository data…
Loading repository data…
nick0ve / repository
ASLR bypass without infoleak
In this article, I'll discuss about the application of the technique described by Samuel Groß in his Remote iPhone Exploitation Part 2: Bringing Light into the Darkness -- a Remote ASLR Bypass, to bypass ASLR on Linux x86_64.
To show this I'm gonna solve a pwnable challenge from Buckeye CTF, guess_god.
I'll try to keep the content as beginner friendly as possible, so feel free to skip any section if you feel confident enough and just want to see the exploit.
I didn't play the CTF, but I got interested in the challenge about 2hrs before the ctf end thanks to Guray00, who was asking for help in fibonhack discord about some crypto shenanigans.
I couldn't help him, but I took a look at pwnable challenges, and figured it would be good to understand the P0 blogpost and hopefully get that bounty.
Address Space Layout Randomization (ASLR) is a computer security technique which involves randomly positioning the base address of an executable and the position of libraries, heap, and stack, in a process's address space.
On linux, you can inspect the mappings of a process given its pid through procfs, by reading the file /proc/<pid>/maps.
If you are a process and you want to know your own memory mappings, you can read /proc/self/maps.
For example, you can try to read /proc/self/maps with cat:
root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps
55faeb01c000-55faeb01e000 r--p 00000000 fe:01 2497233 /usr/bin/cat
55faeb01e000-55faeb023000 r-xp 00002000 fe:01 2497233 /usr/bin/cat
55faeb023000-55faeb026000 r--p 00007000 fe:01 2497233 /usr/bin/cat
55faeb026000-55faeb027000 r--p 00009000 fe:01 2497233 /usr/bin/cat
55faeb027000-55faeb028000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat
55faeb115000-55faeb136000 rw-p 00000000 00:00 0 [heap]
7fe15dfb1000-7fe15dfd5000 rw-p 00000000 00:00 0
7fe15dfd5000-7fe15dffb000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15dffb000-7fe15e166000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e166000-7fe15e1b2000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b2000-7fe15e1b5000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b5000-7fe15e1b8000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7fe15e1b8000-7fe15e1c3000 rw-p 00000000 00:00 0
7fe15e1c7000-7fe15e1c8000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1c8000-7fe15e1ef000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1ef000-7fe15e1f9000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1f9000-7fe15e1fb000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fe15e1fb000-7fe15e1fd000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7fff4388f000-7fff438b0000 rw-p 00000000 00:00 0 [stack]
7fff43989000-7fff4398d000 r--p 00000000 00:00 0 [vvar]
7fff4398d000-7fff4398f000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
root@088ec31b2ce9:/home/ctf/challenge# cat /proc/self/maps
55ffc0b1b000-55ffc0b1d000 r--p 00000000 fe:01 2497233 /usr/bin/cat
55ffc0b1d000-55ffc0b22000 r-xp 00002000 fe:01 2497233 /usr/bin/cat
55ffc0b22000-55ffc0b25000 r--p 00007000 fe:01 2497233 /usr/bin/cat
55ffc0b25000-55ffc0b26000 r--p 00009000 fe:01 2497233 /usr/bin/cat
55ffc0b26000-55ffc0b27000 rw-p 0000a000 fe:01 2497233 /usr/bin/cat
55ffc2108000-55ffc2129000 rw-p 00000000 00:00 0 [heap]
7f1ec6e0f000-7f1ec6e33000 rw-p 00000000 00:00 0
7f1ec6e33000-7f1ec6e59000 r--p 00000000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f1ec6e59000-7f1ec6fc4000 r-xp 00026000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f1ec6fc4000-7f1ec7010000 r--p 00191000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f1ec7010000-7f1ec7013000 r--p 001dc000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f1ec7013000-7f1ec7016000 rw-p 001df000 fe:01 2761561 /usr/lib/x86_64-linux-gnu/libc-2.33.so
7f1ec7016000-7f1ec7021000 rw-p 00000000 00:00 0
7f1ec7025000-7f1ec7026000 r--p 00000000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f1ec7026000-7f1ec704d000 r-xp 00001000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f1ec704d000-7f1ec7057000 r--p 00028000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f1ec7057000-7f1ec7059000 r--p 00031000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7f1ec7059000-7f1ec705b000 rw-p 00033000 fe:01 2761539 /usr/lib/x86_64-linux-gnu/ld-2.33.so
7ffc72fa4000-7ffc72fc5000 rw-p 00000000 00:00 0 [stack]
7ffc72fe7000-7ffc72feb000 r--p 00000000 00:00 0 [vvar]
7ffc72feb000-7ffc72fed000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
If you do this a couple of times, you could deduce that:
Let's discuss what you can do to bypass ASLR when no information leak is possble.
This is my attempt to summarize what I got from reading Saelo's blogpost.
To bypass ASLR you need:
A memory spraying technique, which lets you map contiguous memory of a given size, on a given range of addresses.
As he says there are two ways of doing it:
An isAddressMapped oracle, which given an address tells you wheter or not that address is mapped.
Let's try to reproduce saelo's PoC to completely break aslr on Linux.
On Linux it's not so easy, it is possible to completely break ASLR only if you are able to allocate 16TB of memory.
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 64gb
size_t size = 0x1000000000;
// 16TB allocations
for (int i = 0; i < 256; i++) {
void *mem = malloc(size); // this ends up calling mmap
if (!mem) {
puts("Failed");
return 1;
}
printf("%p\n", mem);
}
unsigned int *mem = (void*)0x7f0000000000ULL;
*mem = 0x41414141;
printf("R/W to %p: %x\n", mem, *mem);
return 0;
}
From man malloc notes:
So void *mem = malloc(size) will end up calling mmap(size + malloc_metadata_size, ...)
Since libraries are mapped into the process through mmap by ld, those allocations will end up near the libraries.
If you look at the addresses returned by malloc you can better understand what is happening. Protip: look at the most significant bytes.
| mem | 16tb boundary cross? |
|---|---|
| 0x7fb03b55e010 | No |
| 0x7fa03b55d010 | No |
| 0x7f903b55c010 | No |
| 0x7f803b55b010 | No |
| 0x7f703b55a010 | No |
| 0x7f603b559010 | No |
| 0x7f503b558010 | No |
| 0x7f403b557010 | No |
| 0x7f303b556010 | No |
| 0x7f203b555010 | No |
| 0x7f103b554010 | No |
| 0x7f003b553010 | No |
| 0x7ef03b552010 | Yes |
| 0x7ee03b551010 | Yes |
| 0x7ed03b550010 | Yes |
| 0x7ec03b54f010 | Yes |
The poc is exploiting the fact that, at some point, the most significant byte of the address returned changes from 7F to 7E and since the allocations are contiguous there must be something inside that range. (Yeah we are applying the Bolzano-Weirstress theorem to solve this problem!)
Thankfully to the author, the zip contains binaries, source code and dockerfile to reproduce the same environment as the remote one.
It's always a good thing to grasp some knowledge about the environment, let's scroll through the files and take some notes.
jail.cfg set some restrictions, let's not forget about those limits since they might screw up the exploit:
time_limit: 300
cgroup_cpu_ms_per_sec: 100
cgroup_pids_max: 64
rlimit_fsize: 2048
rlimit_nofile: 2048
cgroup_mem_max: 1073741824 # 1GB
From the Dockerfile we can learn some interesting things:
Build and install oatpp 1.2.5, maybe there are useful bugs in this specific version?
# Install oatpp
RUN git clone https://github.com/oatpp/oatpp.git
RUN cd /oatpp && git checkout 1.2.5 && mkdir build && cd build && cmake .. && make install
It builds the challenge from scratch
WORKDIR /home/ctf/challenge/src/
RUN mkdir -p src/build && cd src/build && cmake .. && make
RUN cp src/build/flag_server-exe src/build/libkylezip.so flag.txt / home/ ctf/challenge/
This might be a problem, so let's copy the distribuited binaries instead.
COPY bins/flag_server-exe /home/ctf/challenge/
COPY bins/libkylezip.so /home/ctf/challenge/
And the last thing, check the protections of the binaries provided
docker-compose.yml file is provided so it is not