updated:

一些Pwn操作中常见的迷思(持续更新)


system函数

我们使用system函数getshell时经常使用/bin/sh字符串,但这是必须的吗?首先我们需要了解system函数是如何工作的,通过查看glibc的源码,我们看到system函数最终使用了clone系统调用来创建一个新线程,然而该线程中执行的并非system函数的参数,而是一个以posix_spawn_args结构体为参数,名为__spawni_child的函数。接下来的一系列操作如图所示(根据glic 2.29源码绘制):

1
2
3
4
5
6
7
8
9
10
11
12
13
struct posix_spawn_args
{
sigset_t oldmask;
const char *file;
int (*exec) (const char *, char *const *, char *const *);
const posix_spawn_file_actions_t *fa;
const posix_spawnattr_t *restrict attr;
char *const *argv;
ptrdiff_t argc;
char *const *envp;
int xflags;
int err;
};

可惜在这里,我们依然无法回答上面提出的问题。要想回答这一问题并避免继续再函数实现中套娃,我们需要使用一种略微取巧的方法:我们可以使用strace -f来获得执行system函数中使用到的系统调用。写一个程序如下:

1
2
3
4
5
#include <stdlib.h>
int main(void) {
system("sh");
return 0;
}

编译后使用strace -f ./sys运行,我们看到了下面的输出:

查找资料后我们发现,linux系统调用stat可以被用来确定一个文件的状态,可以看到我们的程序搜索了一系列路径,这些路径实际上是由PATH环境变量所规定的。相应的文件查找到之后,系统使用clone创建了一个线程,并在这个线程中使用execve系统调用来运行这个文件。 回答:我们在system函数中不一定需要指定绝对路径,对于位于PATH中的可执行文件,我们可以仅仅使用文件名来运行。

pwntools的NX enabled说明了什么

首先,将NX说成是栈不可执行是一种不全面的说法。实际上NX是AMD用于称呼他们的x86-64架构下也表条目的第63位(最高位)的说法,Intel将其称为XD位。这个标识位与所有页息息相关,不仅仅是栈所在的页。 然而pwntools是怎样得知程序是否为NX的呢?最快的方法是看源码,在源码中判断ELF是否为NX的代码如下:

1
2
3
4
5
6
7
if not self.executable:
return True
for seg in self.iter_segments_by_type('GNU_STACK'):
return not bool(seg.header.p_flags & P_FLAGS.PF_X)
# If you NULL out the PT_GNU_STACK section via ELF.disable_nx(),
# everything is executable.
return False

可以看到,程序依据ELF文件Header Table中的信息来判断程序是否被NX。这里有一篇文章详细讲解了可执行栈的详细信息。 然而经过实验我们可以看到,关闭了nx的程序的bss段在程序运行期间是可执行的,具体原因为何还无从知晓。


← Prev 堆的理论基础 | 一道国赛题的多种解法 Next →