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

TOC

  1. 1. 问题
  2. 2. 原因
    1. 2.1. 演示
  3. 3. 解决办法
    1. 3.1. 改变payload长度
    2. 3.2. 栈转移
    3. 3.3. 调用execve
  4. 4. 总结

原先在做题的时候就发现了,一些新的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[ ]);

总结

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