This repository is an addition to the article published in MISC Magazine #96.
We achieved to elevate our privileges in a reliable way, on our virtual machine with SMEP / SMAP and KASLR enabled. It should however be noted that the system if left in an unstable state and that a oops is very likely to occur.
In the folder configs/
, you will find configuration files for the Linux kernel
and Busybox, each one being sightly different of the default ones.
The folder binaries/
contains all the pre-built binaries that you will need
to reproduce our test environment, such as a whole rootfs ready to be used with
QEMU.
linux-stable
is a git submodule pointing to a vulnerable revision of the Linux
kernel. Use git submodule update --recursive
to fetch it (if you a least
1GB free on your system...). Same thing goes for busybox
.
Our environment is based on a QEMU x86 virtual machine with a folder shared
with the host via 9P. A vulnerable version of the kernel (ff33952e4d23
) is
compiled along with a statically-linked build of Busybox.
Our configuration is pretty simple, since we do not need to support any kind
of exotic architecture or hardware. Is has been generated by running make defconfig
and some features were enabled, to support QEMU's networking and
folder sharing:
CONFIG_BLK_MQ_VIRTIO=y
CONFIG_MEMORY_BALLOON=y
CONFIG_BALLOON_COMPACTION=y
CONFIG_NET_9P=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_NET_9P_DEBUG=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_BLK_SCSI=y
CONFIG_VIRTIO_NET=y
CONFIG_HVC_DRIVER=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
CONFIG_9P_FS=y
CONFIG_9P_FS_POSIX_ACL=y
CONFIG_9P_FS_SECURITY=y
GDB scripts and debugging symbols were also added to ease the developpement of the first PoCs:
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_GDB_SCRIPTS=y
Please note that in this version, KASLR is enabled by default on x86. Since the
kernel mapped at a different addresses after each reboot, GDB will not be able
to match the symbol file with it. You have then two options: performing tests
without KASLR or passing the base address of the kernel to symbol-file
when
loading the symbols in GDB.
After compiling it, you will find the bzImage in linux-stable/arch/x86/boot/bzImage
.
This file is present in binaries/bzImage
and our configuration file in
confifs/kernel.config
. To use it, you only have to copy it as .config
in linux-stable
.
We used the last stable version of Busybox, 1.28. The only setting to change is
CONFIG_STATIC
, set it to y
. Compiling it should not cause any issue.
Our configuration file is available into configs/busybox.config
and a
statically-compiled binary in binaries/busybox
. As for Linux, just copy it
as .config
in Busybox's sources folder.
Busybox already implements an init process that will try to execute /etc/init.d/rcS
.
Usually, it is provided by your distribution (potentially with a different name)
but we will have to do it by ourselves here! This script will create and mount
several mandatory system folders (proc
, sys
, dev
), our folder shared with
the host and drop us into an unprivilegied shell:
for i in $(seq 1 9); do mknod /dev/tty$i c 4 1; done
mknod -m 0666 /dev/null c 1 3
mknod -m 0660 /dev/ttyS0 c 4 64
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
mkdir -p /mnt/share
mount -t 9p -o trans=virtio share /mnt/share/ -oversion=9p2000.L,posixacl,sync
chmod 777 /mnt/share/
export ENV=/etc/profile
setsid cttyhack setuidgid 1000 sh
umount /proc
umount /sys
umount /dev
poweroff -f
The file /etc/profile
is not mandatory but kind of useful during our tests,
more especially when the exploit was not reliable and it took several tries to
obtain root privileges.
The process behing exploit's writing is fairly documented in MISC 96: we use
unsafe_put_user
to probe memory until we find the base address of the heap.
Then, thousands of calls to clone
let us spray numerous cred
structures
in memory. During our tests, their position in memory was much more "constant"
than when using fork
, since it allocates less structures for the new task.
Exiting the child will cause a kernel oops, due to a faulty paging request, this needs to be correctly handled.
If you want to improve the reliability of this exploit or add documentation, contributions are welcome! We may also have also made mistakes or imprecisions on some concepts, do not hesitate to open an issue if you think something is wrong.
Linux kernel
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=96ca579a1ecc943b75beba58bebb0356f6cc4b51
- https://01.org/linuxgraphics/gfx-docs/drm/dev-tools/gdb-kernel-debugging.html
Exploitation