MotoMAGX weakness of kernel modules hashes

From OpenEZX

Jump to: navigation, search

Contents

Security of kernel modules

Motorola uses SHA1 hash of kernel module matching to prevent "untrusted" modules to be loaded. Using the GPL'ed source code of linux kernel (actually kernel/module.c) we can study how this hash is generated. Generally, all sections of ELF file which has ALLOC flag will be used to produce a hash of module. But there are some exceptions - symbol tables are treated as ALLOCed (for kallsyms feature), .modinfo treated as non-ALLOCed, and .gnu.linkonce.this_module section is not used in hash computation. After studying this, we can speak of a weakness of this system.

Weakness

If we'll see, which sections of ELF file doesn't affect module hash - we can find that very important section - a section with relocations - doesn't have ALLOC flag, so we can easily edit it, not affecting the hash. This actually leads us to editing the module text or data in some very limited way. But that is enough - since we have .gnu.linkonce.this_module section which is ALLOCed, we can relocate some branches in kernel text to this section, where we can place our code. That's it - we can finally modify module's logic for our own needs. Actually, it is not guaranteed that we'll find suitable branch instruction in kernel text, but it seems that is not a problem in existing MAGX phones (tested on Z6, U9, E8).

Example of usage

I had chosen motsecurity_sysfs.ko module, which contains suitable branch instruction (funny thing - module with motsecurity in his name helps to break security of the phone), and I didn't find better code, than a code that disables module's hash checking in runtime.

 % arm-linux-gnueabi-objdump -rd motsecurity_sysfs.ko
 ...
 140:   e2855001        add     r5, r5, #1      ; 0x1
 144:   ea000008        b       16c <init_module-0xf0>
 148:   e5113030        ldr     r3, [r1, #-48]
 ...

As we can see, on 0x144 there is a unconditional branch to +0x28. If we'll analyze logic module's text, we can find out that this branch happens when module finds '\n' in text written to /sys/security/allowed_mounts. Let's adjust the code with relocation

 % arm-linux-gnueabi-objdump -rd motsecurity_sysfs_new.ko
 ...
 140:   e2855001        add     r5, r5, #1      ; 0x1
 144:   ea000008        b       28 <init_module-0x234>
                        144: R_ARM_JUMP24       __this_module
 148:   e5113030        ldr     r3, [r1, #-48]
 ...

__this_module is a symbol that can mean "start of .gnu.linkonce.this_module section". So, now this instruction can be interpreted as "unconditionally branch to code, which is placed in .gnu.linkonce.this_module+0x28". 0x28 is a great choice, because it points to space which is reserved to null-terminated kernel name string. We have enough space to execute following code

 ptr:    .word 0xc005f914
 old:    .word 0xebfffdb1
 new:    .word 0xe3a00000
         sub     r7, pc, #0x14
         ldmia   r7, {r4, r5, r6}
         ldr     r7, [r4]
         cmp     r5, r7
         streq   r6, [r4]
         mvn     r0, #0x1
         sub     sp, fp, #0x20
         ldmia   sp, {r4, r5, r6, r7, r8, fp, sp, pc}

which safely changes a word at 0xc005f914 from 0xebfffdb1 (call hash cheking subroutine) to 0xe3a00000 (put 0 to r0, successful "checking") and returns from orignal sysfs write handler. Let's put it in ELF section

 % arm-linux-gnueabi-objdump -rD motsecurity_sysfs_new.ko
 ...
 1c:   c005def4        strgtd  sp, [r5], -r4
 20:   ebfffdb1        bl      fffff6ec <cleanup_module+0xfffff3ec>
 24:   e3a00000        mov     r0, #0  ; 0x0
 28:   e24f7014        sub     r7, pc, #20     ; 0x14
 2c:   e8970070        ldmia   r7, {r4, r5, r6}
 30:   e5947000        ldr     r7, [r4]
 34:   e1550007        cmp     r5, r7
 38:   05846000        streq   r6, [r4]
 3c:   e3e00001        mvn     r0, #1  ; 0x1
 40:   e24bd020        sub     sp, fp, #32     ; 0x20
 44:   e89da9f0        ldmia   sp, {r4, r5, r6, r7, r8, fp, sp, pc}
 ...

First three instruction is actually our data, our entry point of this code is 0x28. In current example the string with a name should be shortened - to "motsecurity_sys" for example.

You can get working example for 44R Z6 firmware here. Please ensure that you have 6ccb945a8bd6726df807cdba7992c5c63eb4d246 listed in trusted hashes (/etc/modules.hash), and that your kernel's md5 is 34517c12b2f6c9cb7a8898345ddc1b1f (you can calculate it on /dev/mtdblock/kern).

Better way to patch a kernel

As we can obtain an addresses of kernel symbols using kallsyms, we can can make this exploit to be more convinient. It would be hard (or even impossible) to use kallsyms lookup subroutines from our kernelspace code, so it is possible to write userspace helper, that looks address of module_hash_verify in /proc/kallsyms, modifies module's code to use that address and inserts module. It should be fairly simple. The kernelmode code itself should replace first to words of module_hash_verify with

 e3a00000  mov     r0, #0
 e12fff1e  bx      lr

If we'd assume that module_hash_verify's code consists of at least two instructions, it would be fairly safe to do such substitution. Thanks for WyrM for idea. Simple and dirty implementation is here.

Personal tools