avatar

目录
One gadget限制条件剖析

One gadget 限制条件剖析

one gadget最终是执行的execve("/bin/sh",argv,envp),所以先来耐心地看下execve文档

SYNOPSIS

c
1
2
3
4
#include <unistd.h>

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

DESCRIPTION

概述

execve() executes the program referred to by pathname.

This causes the program that is currently being run by the calling process to be replaced with a new program, with newly initialized stack, heap, and (initialized and uninitialized) data segments.

函数功能:执行pathname指定的程序(创建新进程)

参数描述

pathname

pathname must be either a binary executable, or a script starting with a line of the form:

shell
1
#!interpreter [optional-arg]

pathname 是二进制程序或是可执行脚本

数据类型:const char *pathname 字符串常量

argv & envp

argv is an array of argument strings passed to the new program.

By convention, the first of these strings (i.e., argv[0]) should contain the filename associated with the file being executed.

argv 是传给新程序的参数列表

数据类型:char *const argv[] 字符串数组常量

envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program.

envp 是新程序的环境变量列表,通常key=value的形式

数据类型:char *const envp[] 字符串数组常量

注意

The argv and envp arrays must each include a null pointer at the end of the array.

argvenvp 数组的末尾必须是一个空指针(NULL pointer)

Analysis

以上的是execve函数的官方manual (通过man execve查看)

execve的三个参数中,首个参数pathname显然是最重要的,这点可以从官方手册的描述中看出来。说到pathname时用的词是must,而到了argvenvp则说By convention或是conventionally。

也就是说argvenvp这两个参数的限制没那么严格,按照惯例argv[0]是被执行程序的程序名,但也可以不是;按照惯例环境变量应该是key=value的格式,但也可以不是。

何以见得?口说无凭,上代码:

c
1
2
3
4
5
6
7
8
9
int main(){
char *p[2] = {0x0};
char *q[2] = {0x0};
size_t a=0xc0decafe;
size_t b=0xdeadbeef;
p[0] = &a;
q[0] = &b;
execve("/bin/sh",p,q);
}

在这段程序中,字符串数组p充当argv,字符串数组q充当envp,这两个数组全部初始化为空指针

c语言的字符串变量其实就是一个指针(指针执行字符串),所以字符串数组其实就是指针数组。

  • argv[0]中的指针指向我自定义的整型数0xc0decafe

  • envp[0]中的指针指向我自定义的整型数0xdeadbeef

最终去执行execve("/bin/sh",p,q)看看能不能成功的拿到shell。

编译运行程序的结果如下:

shell
1
2
3
4
5
6
7
% ./a.out 
$ env
PWD=/home/taqini/xxxx
$ echo $0 > argv
$ od -tx4 argv
0000000 c0decafe 0000000a
0000005

结果显示成功的执行了新程序(/bin/sh),而且argv[0]0xc0decafe,而非/bin/sh,环境变量也清空了。

也就是说不按照惯例去设置argvenvp也是可以的。

背景知识就先说这么多,现在分析pwn中常用的一把梭——one gadget

One gadget

Gadget这个词最初应该是从rop攻击中来的,指的是程序代码或是libc代码中的一小段一小段的代码片段。每个gadget完成不同的任务后会跳转到下一个gadget,最终多个gadget配合完成rop攻击。

在rop攻击中,讲究的是团队合作,每个人(gadget)的能力有限,只能完成部分工作,因此需要大家互相配合一起完成任务。而one gadget是个人能力超群,以一人之力便可完成全部任务的地表最强gadget。

one gadget虽然强大,但是却存在限制条件,以libc2.30为例:

可以通过one_gadget工具进行查看libc中存在的one gadget

这里虽然只显示了4个one gadget,但实际上libc并不是只有这4个one gadget。

one_gadget命令默认显示的是限制条件比较宽泛的one gadget,通过-l参数指定搜索级别,显示更多的one gadget

可以去看看前不久虎符ctf的 MarksMan,切身体会一下~

对程序进行分析是要动静结合的,而one_gadget命令归根结底只是个静态分析工具,他提供的信息可能并不全面。在实际分析程序的过程中,你可能会发现有些不满足限制条件的one gadget居然也能成功getshell。这并不是玄学,请不要为此困惑,只不过是one_gadget命令还不够强大罢了2333

总而言之,静态分析出的限制条件只是提供了一个参考,并不意味着,不满足条件就攻击不成。

栗子

execve("/bin/sh",rsp+0x70,environ)这个one gadget为例,它对应的指令如下:

Code
1
2
3
4
5
► 0x7ffff7ec7fa9 <exec_comm+2521>    mov    rax, qword ptr [rip + 0xdef00]
0x7ffff7ec7fb0 <exec_comm+2528> lea rsi, [rsp + 0x70]
0x7ffff7ec7fb5 <exec_comm+2533> lea rdi, [rip + 0xab657]
0x7ffff7ec7fbc <exec_comm+2540> mov rdx, qword ptr [rax]
0x7ffff7ec7fbf <exec_comm+2543> call execve <0x7ffff7ea3010>

这是一个质量很高的gadget,execve的首个参数pathname(rdi)固定是字符串"/bin/sh"、第三个参数envp(rdx)固定是环境变量数组,这两个参数都正确,因此限制条件只有第二个参数,即argv(rsi)

静态分析得到的限制条件中说到的[rsp+0x70]==NULL只是一种情况,即argv数组为空,但是按照execve文档中的定义,只要argv指针数组且末尾为NULL即可,因此在实际的one gadget利用中,要具体情况具体分析,在one gadget执行时,找到限制条件对应的内存的布局,凡是满足指针数组格式的都应该被考虑在内。

就这个one gadget而言,限制条件是[rsp+0x70]==NULL,但是只要rsp+0x70对应的内存区域满足如下结构就可以:

c
1
2
3
4
5
rsp+0x70 - ptr0 -> argv[0]
rsp+0x78 - ptr1 -> argv[1]
rsp+0x80 - ptr2 -> argv[2]
......
rsp+0x?? - NULL -> ^

下面这个是成功getshell的一种情况

再看一个getshell失败的情况

失败的原因是argv数组不合法。

其他的one gadget和这个栗子类似,就不多说啦~

小结

最后下个结论好啦~

只要argvenvp最终是合法指针数组结构,那么one gadget就能成功。

文章作者: TaQini
文章链接: http://taqini.space/2020/04/29/about-execve/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 TaQini
打赏
  • Wechat
    Wechat
  • Alipay
    Alipay

评论