My Personal CTF Notes
Introduction
These are my private notes that I used to keep for myself but I figured that there is some in my opinion cool stuff worth sharing there. I use them all the time myself!
Running non-native binaries with Qemu
Run:
qemu-aarch64-static -g 2345 -L optional_library_path ./a.out
…and after this we can connect with gdb-multiarch through:
pwndbg> target remote 0.0.0.0:2345
However it can be super wonky. Ctrl-c
to stop execution doesn’t work, but we can instead send the sigint signal to the qemu process and then it stops execution after a continue
. It’s still buggy though, for example when we disconnect and connect GDB again after a continue, then sending sigint instead of stopping the execution, terminates the process. It’s a common problem there’s a 12 years old bug report: https://gdb.sourceware.narkive.com/Z1LCkr20/remote-qemu-ctrl-c-does-not-work .
Also plugins like pwndbg and gef might appear buggy cuz qemu might not provide vmmap info to GDB: https://lore.kernel.org/all/20220221030910.3203063-1-dominik.b.czarnota@gmail.com/ .
Copying libc and ld from a Docker Container
docker run --rm -ti -v "$PWD":/host ubuntu bash # to get an ubuntu container with a mounted path to copy things out
ldd /bin/ls # to see where the libc is or whatever (in this case libc is in /lib/x86_64-linux-gnu)
cp /lib/x86_64-linux-gnu/libc.so.6 /host
Papers
- AVX Timing Side-Channel Attacks against Address Space Layout Randomization
- Prefetch Side-Channel Attacks: Bypassing SMAP and Kernel ASLR
- Blind Format Strings Attacks
Linux kernel exploitation
In case a challenge doesn’t provide helper scripts there are some templates based on https://lkmidas.github.io/posts/20210123-linux-kernel-pwn-part-1/ . For compressing the initramfs directory:
#!/bin/sh
# try to move the exploit automatically to initramfs
gcc -o exploit -static $1
mv ./exploit ./initramfs
# compress the initramfs dir
cd initramfs
find . \
| cpio -ov --format=newc --owner=root \
| gzip -v1 > initramfs.cpio.gz
mv ./initramfs.cpio.gz ../
For decompressing initramfs.cpio.gz
:
#!/bin/sh
mkdir initramfs
cd initramfs
cp ../initramfs.cpio.gz .
gunzip ./initramfs.cpio.gz
cpio -idm < ./initramfs.cpio
rm initramfs.cpio
For decompressing the compressed kernel image (usually called vmlinuz) use: this.
For running the kernel vm with an exposed GDB port:
#!/bin/sh
qemu-system-x86_64 \
-m 128M \
-cpu kvm64,+smep,+smap \
-kernel vmlinuz \
-initrd initramfs.cpio.gz \
-hdb flag.txt \
-snapshot \
-nographic \
-monitor /dev/null \
-gdb tcp::1234 \
-no-reboot \
-append "console=ttyS0 kaslr pti=on quiet panic=1"
Links
- Best resource for kernel exploitation introduction
- Structs useful for heap exploitation
- https://github.com/ocastejon/linux-kernel-learning/blob/main/notes/slab-allocator.md
- Linux mm notes
- Understanding paging
More or less obscure tricks
- Code execution with a write primitive on last libc
- There’s the
_dl_make_stack_executable
function in glibc’s ld. I’ve never seen this used in an actual exploit but seems cool to know about nonetheless. mmap
returns addresses placed close to libc (check outhouse of muney
for more info).- The read and write syscalls do not complain about being fed an invalid memory address and simply return an error. Might be useful for finding writeable memory without having a leak.
- Allegedly theres a magic race condition in one
one_gadget
(the gadget is actually in exec_comm and to find it you have to use one_gadget with-l 1
argument). It can return a shell even though the constraints aren’t met. I saw it a long ago in a ctf being used but when I tried to recreate it recently it didn’t seem to work, so take this with a grain of salt and do your own research.0x10dbca posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x40], 0, rsp+0x70, r9) constraints: [rsp+0x70] == NULL [r9] == NULL || r9 == NULL [rsp+0x40] == NULL || (s32)[[rsp+0x40]+0x4] <= 0
Important versions of things
- glibc-2.24 - Added check for
FILE
s vtable address. - glibc-2.26 - Moved the
FILE
s vtable to a non-writable memory. - glibc-2.28 - Added tcache double free check.
- glibc-2.29 - Moved the
FILE
s vtable back to a writable area. - glibc-2.34 - Removed the ret2csu gadget.
- glibc-2.34 -
malloc
hooks removed from the API. - Linux 2.6.23 - NULL ptr dereference mitigation
- Linux 6.2 -
commit_creds(prepare_kernel_cred(NULL));
no longer works. Now we need to pass&init_cred
as the arg.
Pyjails
Example payloads to reuse:
(__builtins__:=__import__('code'))==(lambda:interact())()
# from https://hackmd.io/@crazyman/H1s0b1Hii
__import__('antigravity',setattr(__import__('os'),'environ',dict(BROWSER='/bin/sh -c "/readflag giveflag" #%s')))
Tricks that might be helpful:
- Non-ascii identifiers get translated to ascii ones.
- You can change the encoding of the file with a comment. Changing to utf-7 is especially useful.
- Symbolic Python.
LaTeX
For simple RCE’s I like the hacktricks article. If it’s not enough there’s this very cool paper: Are Text-Only Data Formats Safe? Or, Use This LATEX Class File to Pwn Your Computer. Also reading binary files might be useful.