在一些64位的glibc的payload调用system函数失败问题

原先在做题的时候就发现了,一些新的64位的glibc在控制了程序流后,调用system函数的时候,会直接crash,经过调试之后,终于发现了问题所在。

问题

做64位pwn题的时候,特别是栈溢出,在控制程序流后,本以为直接调用system函数就能拿到shell,但是我们往往看到的却是crash:

Thread 2.1 "speedrun-002" received signal SIGSEGV, Segmentation fault.
[Switching to process 25578]
0x00007f134a9fc2f6 in do_system (line=0x7f134ab60e9a "/bin/sh") at ../sysdeps/posix/system.c:125
125 ../sysdeps/posix/system.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
 RAX  0x7f134ab60e97 ◂— sub    eax, 0x622f0063 /* '-c' */
 RBX  0x0
 RCX  0x7f134ab60e9f ◂— jae    0x7f134ab60f09 /* 'sh' */
 RDX  0x0
 RDI  0x2
 RSI  0x7f134ad9a6a0 (intr) ◂— 0x0
 R8   0x7f134ad9a600 (quit) ◂— 0x0
 R9   0x10
 R10  0x8
 R11  0x246
 R12  0x7f134ab60e9a ◂— 0x68732f6e69622f /* '/bin/sh' */
 R13  0x7ffe3e3eb140 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x601ce1 ◂— 0x0
 RSP  0x601c81 ◂— 0x0
 RIP  0x7f134a9fc2f6 (do_system+1094) ◂— movaps xmmword ptr [rsp + 0x40], xmm0
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
  0x7f134a9fc2f6 <do_system+1094>    movaps xmmword ptr [rsp + 0x40], xmm0
   0x7f134a9fc2fb <do_system+1099>    call   sigaction <0x7f134a9ec110>

   0x7f134a9fc300 <do_system+1104>    lea    rsi, [rip + 0x39e2f9] <0x7f134ad9a600>
   0x7f134a9fc307 <do_system+1111>    xor    edx, edx
   0x7f134a9fc309 <do_system+1113>    mov    edi, 3
   0x7f134a9fc30e <do_system+1118>    call   sigaction <0x7f134a9ec110>

   0x7f134a9fc313 <do_system+1123>    xor    edx, edx
   0x7f134a9fc315 <do_system+1125>    mov    rsi, rbp
   0x7f134a9fc318 <do_system+1128>    mov    edi, 2
   0x7f134a9fc31d <do_system+1133>    call   sigprocmask <0x7f134a9ec140>

   0x7f134a9fc322 <do_system+1138>    mov    rax, qword ptr [rip + 0x39bb7f]
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ rsp  0x601c81 ◂— 0x0
01:0008│      0x601c89 —▸ 0x7f134ab60e97 ◂— sub    eax, 0x622f0063 /* '-c' */
02:0010│      0x601c91 ◂— 0x0
... 
04:0020│      0x601ca1 —▸ 0x7f134a9fc360 (cancel_handler) ◂— push   rbx
05:0028│      0x601ca9 —▸ 0x601c9d ◂— 0x4a9fc36000000000
06:0030│      0x601cb1 ◂— 0x0
... 
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
  f 0     7f134a9fc2f6 do_system+1094
   f 1                a
   f 2                0
Program received signal SIGSEGV (fault address 0x0)

程序直接crash了。

原因

主要原因是0x4F2F6处的movaps [rsp+198h+var_158], xmm0指令要求rsp+198h+var_158的值是对齐16byte(0x10),否则会直接触发中断从而crash。

.text:000000000004F2F1 movhps  xmm0, [rsp+198h+var_190]
.text:000000000004F2F6 movaps  [rsp+198h+var_158], xmm0 ; here
.text:000000000004F2FB call    sigaction

演示

这里我用一个简单的程序来演示这个问题。

shell.c

// compiled: gcc -g -no-pie -Og shell.c -o shell
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    system("/bin/sh");
    return 0;
}

程序编译之后是能正常运行的,但是我们要的是让程序不正常运行。

接下来直接我们就在.text:000000000004F2F6 movaps [rsp+198h+var_158], xmm0 ; here这里下断点,也就是do_system+1094处,然后运行到此。

LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
 RAX  0x7ffff7b97e97 ◂— sub    eax, 0x622f0063 /* '-c' */
 RBX  0x0
 RCX  0x7ffff7b97e9f ◂— jae    0x7ffff7b97f09 /* 'sh' */
 RDX  0x0
 RDI  0x2
 RSI  0x7ffff7dd16a0 (intr) ◂— 0x0
 R8   0x7ffff7dd1600 (quit) ◂— 0x0
 R9   0x7ffff7dd0d80 (initial) ◂— 0x0
 R10  0x8
 R11  0x246
 R12  0x400594 ◂— 0x68732f6e69622f /* '/bin/sh' */
 R13  0x7fffffffdc60 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffda40 ◂— 0x0
 RSP  0x7fffffffd9e0 ◂— 0x0
 RIP  0x7ffff7a332f6 (do_system+1094) ◂— movaps xmmword ptr [rsp + 0x40], xmm0
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
  0x7ffff7a332f6 <do_system+1094>    movaps xmmword ptr [rsp + 0x40], xmm0
   0x7ffff7a332fb <do_system+1099>    call   sigaction <0x7ffff7a23110>

   0x7ffff7a33300 <do_system+1104>    lea    rsi, [rip + 0x39e2f9] <0x7ffff7dd1600>
   0x7ffff7a33307 <do_system+1111>    xor    edx, edx
   0x7ffff7a33309 <do_system+1113>    mov    edi, 3
   0x7ffff7a3330e <do_system+1118>    call   sigaction <0x7ffff7a23110>

   0x7ffff7a33313 <do_system+1123>    xor    edx, edx
   0x7ffff7a33315 <do_system+1125>    mov    rsi, rbp
   0x7ffff7a33318 <do_system+1128>    mov    edi, 2
   0x7ffff7a3331d <do_system+1133>    call   sigprocmask <0x7ffff7a23140>

   0x7ffff7a33322 <do_system+1138>    mov    rax, qword ptr [rip + 0x39bb7f]
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffd9e0 ◂— 0x0
01:0008│      0x7fffffffd9e8 —▸ 0x7ffff7b97e97 ◂— sub    eax, 0x622f0063 /* '-c' */
02:0010│      0x7fffffffd9f0 ◂— 0x0
... 
04:0020│      0x7fffffffda00 —▸ 0x7ffff7a33360 (cancel_handler) ◂— push   rbx
05:0028│      0x7fffffffda08 —▸ 0x7fffffffd9fc ◂— 0xf7a3336000000000
06:0030│      0x7fffffffda10 —▸ 0x7ffff7ffe738 —▸ 0x7ffff7ffe710 —▸ 0x7ffff7ffa000 ◂— jg     0x7ffff7ffa047
07:0038│      0x7fffffffda18 ◂— 0x0
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
  f 0     7ffff7a332f6 do_system+1094
   f 1     7ffff7a3344a system+10
   f 2           4004f7 main+16
   f 3     7ffff7a05b97 __libc_start_main+231
Breakpoint *(0x7ffff7a32eb0+1094)

然后在来看看rsp + 0x40的值。

pwndbg> p/x $rsp+0x40
$1 = 0x7fffffffda20

可以看到此时的值是对齐的,所以可以正常运行,我们尝试对rsp加1,看看会不会crash。

pwndbg> set $rsp=$rsp+1
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
 RAX  0x7ffff7b97e97 ◂— sub    eax, 0x622f0063 /* '-c' */
 RBX  0x0
 RCX  0x7ffff7b97e9f ◂— jae    0x7ffff7b97f09 /* 'sh' */
 RDX  0x0
 RDI  0x2
 RSI  0x7ffff7dd16a0 (intr) ◂— 0x0
 R8   0x7ffff7dd1600 (quit) ◂— 0x0
 R9   0x7ffff7dd0d80 (initial) ◂— 0x0
 R10  0x8
 R11  0x246
 R12  0x400594 ◂— 0x68732f6e69622f /* '/bin/sh' */
 R13  0x7fffffffdc60 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffda40 ◂— 0x0
*RSP  0x7fffffffd9e1 ◂— 0x9700000000000000
 RIP  0x7ffff7a332f6 (do_system+1094) ◂— movaps xmmword ptr [rsp + 0x40], xmm0
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
  0x7ffff7a332f6 <do_system+1094>    movaps xmmword ptr [rsp + 0x40], xmm0
   0x7ffff7a332fb <do_system+1099>    call   sigaction <0x7ffff7a23110>

   0x7ffff7a33300 <do_system+1104>    lea    rsi, [rip + 0x39e2f9] <0x7ffff7dd1600>
   0x7ffff7a33307 <do_system+1111>    xor    edx, edx
   0x7ffff7a33309 <do_system+1113>    mov    edi, 3
   0x7ffff7a3330e <do_system+1118>    call   sigaction <0x7ffff7a23110>

   0x7ffff7a33313 <do_system+1123>    xor    edx, edx
   0x7ffff7a33315 <do_system+1125>    mov    rsi, rbp
   0x7ffff7a33318 <do_system+1128>    mov    edi, 2
   0x7ffff7a3331d <do_system+1133>    call   sigprocmask <0x7ffff7a23140>

   0x7ffff7a33322 <do_system+1138>    mov    rax, qword ptr [rip + 0x39bb7f]
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffd9e1 ◂— 0x9700000000000000
01:0008│      0x7fffffffd9e9 ◂— 0x7ffff7b97e
02:0010│      0x7fffffffd9f1 ◂— 0x0
03:0018│      0x7fffffffd9f9 ◂— 0x6000000000000000
04:0020│      0x7fffffffda01 ◂— 0xfc00007ffff7a333
05:0028│      0x7fffffffda09 ◂— 0x3800007fffffffd9
06:0030│      0x7fffffffda11 ◂— 0x7ffff7ffe7
07:0038│      0x7fffffffda19 ◂— 0x100000000000000
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
  f 0     7ffff7a332f6 do_system+1094
   f 1             4004
   f 2 9700000000000000
   f 3  100007ffff7a05b
   f 4 6800000000000000
   f 5       7fffffffdc
   f 6 e700000001000080
   f 7             4004
   f 8 a400000000000000
   f 9   6b6cdf11464cf7
   f 10 6000000000004004
Breakpoint *(0x7ffff7a32eb0+1094)

继续来查看rsp + 0x40的值。

pwndbg> p/x $rsp+0x40
$2 = 0x7fffffffda21

可以看到此时已经不对齐了,那么继续运行。

pwndbg> c
Continuing.

Thread 2.1 "shell" received signal SIGSEGV, Segmentation fault.
0x00007ffff7a332f6 in do_system (line=0x400594 "/bin/sh") at ../sysdeps/posix/system.c:125
125 in ../sysdeps/posix/system.c
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
 RAX  0x7ffff7b97e97 ◂— sub    eax, 0x622f0063 /* '-c' */
 RBX  0x0
 RCX  0x7ffff7b97e9f ◂— jae    0x7ffff7b97f09 /* 'sh' */
 RDX  0x0
 RDI  0x2
 RSI  0x7ffff7dd16a0 (intr) ◂— 0x0
 R8   0x7ffff7dd1600 (quit) ◂— 0x0
 R9   0x7ffff7dd0d80 (initial) ◂— 0x0
 R10  0x8
 R11  0x246
 R12  0x400594 ◂— 0x68732f6e69622f /* '/bin/sh' */
 R13  0x7fffffffdc60 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffda40 ◂— 0x0
 RSP  0x7fffffffd9e1 ◂— 0x9700000000000000
 RIP  0x7ffff7a332f6 (do_system+1094) ◂— movaps xmmword ptr [rsp + 0x40], xmm0
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
  0x7ffff7a332f6 <do_system+1094>    movaps xmmword ptr [rsp + 0x40], xmm0
   0x7ffff7a332fb <do_system+1099>    call   sigaction <0x7ffff7a23110>

   0x7ffff7a33300 <do_system+1104>    lea    rsi, [rip + 0x39e2f9] <0x7ffff7dd1600>
   0x7ffff7a33307 <do_system+1111>    xor    edx, edx
   0x7ffff7a33309 <do_system+1113>    mov    edi, 3
   0x7ffff7a3330e <do_system+1118>    call   sigaction <0x7ffff7a23110>

   0x7ffff7a33313 <do_system+1123>    xor    edx, edx
   0x7ffff7a33315 <do_system+1125>    mov    rsi, rbp
   0x7ffff7a33318 <do_system+1128>    mov    edi, 2
   0x7ffff7a3331d <do_system+1133>    call   sigprocmask <0x7ffff7a23140>

   0x7ffff7a33322 <do_system+1138>    mov    rax, qword ptr [rip + 0x39bb7f]
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffd9e1 ◂— 0x9700000000000000
01:0008│      0x7fffffffd9e9 ◂— 0x7ffff7b97e
02:0010│      0x7fffffffd9f1 ◂— 0x0
03:0018│      0x7fffffffd9f9 ◂— 0x6000000000000000
04:0020│      0x7fffffffda01 ◂— 0xfc00007ffff7a333
05:0028│      0x7fffffffda09 ◂— 0x3800007fffffffd9
06:0030│      0x7fffffffda11 ◂— 0x7ffff7ffe7
07:0038│      0x7fffffffda19 ◂— 0x100000000000000
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
  f 0     7ffff7a332f6 do_system+1094
   f 1             4004
   f 2 9700000000000000
   f 3  100007ffff7a05b
   f 4 6800000000000000
   f 5       7fffffffdc
   f 6 e700000001000080
   f 7             4004
   f 8 a400000000000000
   f 9   6b6cdf11464cf7
   f 10 6000000000004004
Program received signal SIGSEGV (fault address 0x0)

从上面可以看到,程序就直接crash了。

解决办法

核心思想就是改变栈的地址。

  1. 改变payload长度
  2. 栈转移

改变payload长度

直接更改我们的payload长度,在栈溢出的时候栈的地址自然不同,然后将栈地址+1,如果不行的话,就继续增加,最多也就改16次就一定会遇到栈对齐的情况。

栈转移

当payload有长度限制的时候,我们可以尝试进行栈转移来进行栈地址的改变,如果遇到了没有对齐的情况就继续将栈地址+1,直到遇到栈对齐的情况。

调用execve

用execve函数来替换system函数,这个要求就更高,应为他需要三个参数才能正常调用。也就是说我们需要构造rdirsirdx这三个参数。

int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

总结

遇到问题就多上手调试一下。