Skip to content

bypassing devmem_is_allowed with kernel probes

In this article I’m going to illustrate how to read the full content of /dev/mem on linux 3.x machines. I will bypass the function devmem_is_allowed with a kernel return probe.

The kernel probes is a kernel component designed for kernel developers to debug the system internals.It can dynamically break into any kernel routine and modify the function’s behavour. This proves had been heavily since yeah by kernel developers. RedHat has build an user interface to kprobes called SystemTap
You can find kprobes’ documentation in Documentation/kprobes.txt. You should also download the article example files kprobe.tgz

The /dev/mem restriction

As far as you know, nobody can access beyond > 1024M using /dev/mem. This restriction was imposed since kernel ~v2.6.
If you try to read /dev/mem beyond that point, the follwing occurrs:

[text]
arch ~ # dd if=/dev/mem of=yeah.img bs=4K count=4096
dd: leyendo «/dev/mem»: Operación no permitida
257+0 registros leídos
257+0 registros escritos
1052672 bytes (1,1 MB) copiados, 0,192253 s, 5,5 MB/s
arch ~ #
[/text]

And this messages is shown in /var/log/kernel.log
[text]
Sep 2 02:16:30 arch kernel: [ 6446.740508] Program dd tried to access /dev/mem between 100000->200000.
[/text]

I’m going to ilustrate how to bypass the restricction imposed by devmem_is_allowed(int) with kprobes.
The devmem_is_allowed code is located at /usr/src/linux/arch/x86/mm/init.c.

Looking for this funcion in /usr/src/linux/arch/x86/mm/init.c , we find:

[cpp]
/*
* devmem_is_allowed() checks to see if /dev/mem access to a certain address
* is valid. The argument is a physical page number.
*
*
* On x86, access has to be given to the first megabyte of ram because that area
* contains bios code and data regions used by X and dosemu and similar apps.
* Access has to be given to non-kernel-ram areas as well, these contain the PCI
* mmio resources as well as potential bios/acpi data regions.
*/
int devmem_is_allowed(unsigned long pagenr)
{
if (pagenr <= 256)
return 1;
if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
return 0;
if (!page_is_ram(pagenr))
return 1;
return 0;
}
[/cpp]

Coding a kernel module

Currently, I’m using the kernel 3.5.3-1 on x86_64:

[text]
Linux arch 3.5.3-1-ARCH #1 SMP PREEMPT Sun Aug 26 09:14:51 CEST 2012 x86_64 GNU/Linux
[/text]

It is needed to code a kernel return probe to overwrite the return value of devmem_is_allowed(int).So, the goal is to program a function which checks the return code of devmem_is_allowed (saved in the %eax register on i386/x86_64), and change It to 1 when this value equals 0.

For building a kernel module, 2 files are needed:

  1. Makefile
  2. Source.c

Here is the Makefile for building the kernel module kprobe.c
[text]
obj-m += kprobe.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
[/text]

The kernel module source kprobe.c:
[cpp]
/*
* root@libcrack.so
* kprobe to bypass > 1024M read from /dev/mem
* From /usr/src/linux-3.6-rc3/arch/x86/mm/init.c:
* devmem_is_allowed() checks to see if /dev/mem access to a certain address
* is valid. The argument is a physical page number.
* On x86, access has to be given to the first megabyte of ram because that area
* contains bios code and data regions used by X and dosemu and similar apps.
* Access has to be given to non-kernel-ram areas as well, these contain the PCI
* mmio resources as well as potential bios/acpi data regions.
*
* int devmem_is_allowed(unsigned long pagenr)
* {
* if (pagenr <= 256)
* return 1;
* if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
* return 0;
* if (!page_is_ram(pagenr))
* return 1;
* return 0;
* }
*
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/kprobes.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("root@libcrack.so");
MODULE_DESCRIPTION("kprobe to bypass >1024M read from /dev/mem");

/**************** CONSTANTS ***************/
static const char *func_name = "devmem_is_allowed";

/**************** FUNC HOOKS ***************/
static int ret_handler (struct kretprobe_instance *rp, struct pt_regs *regs)
{
/* denied access bypass */
if (regs->ax == 0) {
//printk("Intercepted %s returns (%%eax) 0 => setting %%eax to 0x01\n", func_name);
regs->ax = 0x1;
}
return 0; /* not reached */
}

/**************** KRETPROBE ***************/
static struct kretprobe krp = {
.handler = ret_handler,
.maxactive = 20 /* Probe up to 20 instances concurrently. */
};

/**************** INIT MODULE ***************/
int init_module(void)
{
int ret;
printk("Activating %s kreprobe \n", func_name);

krp.kp.symbol_name = func_name;

if ((ret=register_kretprobe(&krp)) < 0) {
printk("register_kprobe for %s failed!\n", func_name);
} else {
printk("Kprobed %s :-D \n", func_name);
}
return 0;
}

/**************** CLEANUP MODULE ***************/
void cleanup_module(void)
{
printk("Unregistering %s kprobe\n", func_name);
unregister_kretprobe(&krp);
printk("%s kprobe unregistered\n", func_name);
}
[/cpp]

Using the kretprobe module

First, It is needed to compile the kernel module with “make”
[text]
arch ~/kprobe # make
make -C /lib/modules/2.6.32-5-amd64/build M=/root/kprobe modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.32-5-amd64′
CC [M] /root/kprobe/kretprobe-bypass-devmem_is_allowed.o
Building modules, stage 2.
MODPOST 1 modules
CC /root/kprobe/kretprobe-bypass-devmem_is_allowed.mod.o
LD [M] /root/kprobe/kretprobe-bypass-devmem_is_allowed.ko
make[1]: Leaving directory `/usr/src/linux-headers-2.6.32-5-amd64′
arch ~/kprobe #
[/text]

Then, insert the kernel module with “insmod”
[text]
arch ~/kprobe # insmod kretprobe-bypass-devmem_is_allowed.ko
[/text]

You must see the following loglines in /var/log/kernel.log
[text]
Sep 2 04:03:04 arch kernel: [1365788.135295] Activating devmem_is_allowed kreprobe
Sep 2 04:03:04 arch kernel: [1365788.135730] Kprobed devmem_is_allowed :-D
[/text]

Then, try to dump /dev/mem content into the filesystem:
[text]
arch ~/kprobe # dd if=/dev/mem of=/root/mem.img &
[1] 31661
[/text]

You can check the increasing size of the dump file:
[text]
arch ~/kprobe # ls -la /root/mem.img
-rw-r–r– 1 root root 3517398528 Sep 2 04:11 /root/mem.img
arch ~/kprobe #
[/text]

To make “dd” binary print how much has been dumped, send a SIGUSR1 signal to the dd process
[text]
arch ~/kprobe # ps aux | grep if= | grep -v grep | awk ‘{print $2}’ | xargs kill -SIGUSR1
6879303+0 records in
6879302+0 records out
3522202624 bytes (3.5 GB) copied, 435.751 s, 8.1 MB/s
arch ~/kprobe #
[/text]


Conclusions

It is clear that we can hook ANY kernel function with kprobes. An evil coder can hook determined
syscalls and take complete control over the system.

This tiny source code can help forensic investigators grab an actual RAM image via /dev/mem on Linux i386/x86_64 systems.

It is interesting that Debian stable comes with KPROBES active by default:
[text]
debian:~#
debian:~# grep KPR /boot/config-2.6.32-5-amd64
CONFIG_KPROBES=y
CONFIG_HAVE_KPROBES=y
debian:~#
debian:~#
debian:~# uname -a
Linux debian 2.6.32-5-amd64 #1 SMP Sun May 6 04:00:17 UTC 2012 x86_64 GNU/Linux
debian:~#
debian:~#
debian:~# cat /etc/debian_version
6.0.5
debian:~#
[/text]

As said, kprobes gives great advantage to an evil coder. Why are they actives by default? There is really a need to have such debug feature enabled in debian stable kernels?

Android Devices

For the curious ones, android 4 looks like having KPROBES active by default
[text]
borja@arch:~/android-sdks/tools $ ./android list
Available Android targets:
.
.
.
———-
id: 2 or "android-15"
Name: Android 4.0.3
Type: Platform
API level: 15
Revision: 2
Skins: WVGA800 (default), WQVGA432, WXGA800, WVGA854, WXGA720, WQVGA400, QVGA, WSVGA, HVGA
ABIs : armeabi-v7a
Available Android Virtual Devices:
Name: android4
Path: /home/borja/.android/avd/android4.avd
Target: Android 4.0.3 (API level 15)
ABI: armeabi-v7a
Skin: WVGA800
Sdcard: 128M
borja@arch:~/android-sdks/tools $ cd ../platform-tools
borja@arch:~/android-sdks/platform-tools $ sudo ./adb root
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
adbd is already running as root
borja@arch:~/android-sdks/platform-tools $
borja@arch:~/android-sdks/platform-tools $ ./adb devices
List of devices attached
emulator-5554 device

borja@arch:~/android-sdks/platform-tools $
borja@arch:~/android-sdks/platform-tools $ ./adb shell
# cat /proc/version
Linux version 2.6.29-g46b05b2 (vchtchetkine@vc-irv.irv.corp.google.com) (gcc version 4.4.3 (GCC) ) #28 Thu Nov 17 06:39:36 PST 2011
#
# exit
borja@arch:~/android-sdks/platform-tools $
borja@arch:~/android-sdks/platform-tools $
borja@arch:~/android-sdks/platform-tools $ ./adb pull /proc/config.gz
85 KB/s (7170 bytes in 0.081s)
borja@arch:~/android-sdks/platform-tools $
borja@arch:~/android-sdks/platform-tools $
borja@arch:~/android-sdks/platform-tools $ zgrep KPR config.gz
CONFIG_HAVE_KPROBES=y
borja@arch:~/android-sdks/platform-tools $
borja@arch:~/android-sdks/platform-tools $ # LOLLLLLLLLL
[/text]


Usefull links

Happy hacking!

Published indebugginghackinglinuxprogramming

2 Comments

  1. Frank Ch. Eigler Frank Ch. Eigler

    In systemtap, this would have been done as the one-liner

    # stap -g -e ‘probe kernel.function(“devmem_is_allowed”).return { $return = 1 }’

    It is no special risk to have the kprobes facility in the kernel, since it is accessible only to kernel modules. If one can load a kernel module, the security game is over anyway, never mind minor protections against /dev/mem reading. (Userspace perf probe can also attach, but in a strictly read-only way, and only if the invoking user is already root.)

  2. Hi Frank!

    Yeah, you totally right! I know about RH’s SystemTap; indeed, I tought about writting a new post about SystemTap!
    But the point is that usually, Linux boxes do not ship SystemTap by default, but kprobes facility is active.

    So, if you manage to root remotely a box, SystemTap will not be there :-(

    An interesting technique is to patch range_is_allowed(), and then inject code in realtime via /dev/mem (think about IDT smashing, etc)
    You have a handfull of differents combinations :-)

    Cheers! ;-)

Leave a Reply

Your email address will not be published.

ERROR: si-captcha.php plugin: GD image support not detected in PHP!

Contact your web host and ask them to enable GD image support for PHP.

ERROR: si-captcha.php plugin: imagepng function not detected in PHP!

Contact your web host and ask them to enable imagepng for PHP.