C 从mmaped空指针读取数据

C 从mmaped空指针读取数据,c,linux,linux-kernel,null,mmap,C,Linux,Linux Kernel,Null,Mmap,检查此代码: #include <sys/mman.h> #include <stdio.h> #include <string.h> int main(void) { char *addr = mmap(NULL, 6, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED|MAP_ANONYMOUS, -1, 0); if (addr == MAP_FAILED) { perror("mma

检查此代码:

#include <sys/mman.h>
#include <stdio.h>
#include <string.h>

int main(void) {
    char *addr = mmap(NULL, 6, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED|MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    strcpy(addr, "abcxyz");
    printf("Mapped into addr=0x%X\n", addr);
    printf("%s\n", addr);
    printf("%s\n", NULL);
}
发生了什么事?为什么可以将可访问的数据映射到空地址?有了这种行为,我如何使用它来利用系统

注意:程序必须在root权限下运行

注2:该行为是在
Linux XXXXXX 4.4.0-174-generic\204 Ubuntu SMP周三1月29日06:41:01 UTC 2020 x86\u 64 x86\u 64 x86\u 64 GNU/Linux

发生了什么事

mmap()
按照您的要求映射了您在地址0处指定的文件

显然,您不应该这样做(在第一个参数中提供
NULL
,在第四个参数中的标志中提供
MAP\u FIXED
),因为这样会打破各种期望

MAP\u FIXED
是一个脚踏枪:如果未指定,内核将遵守地址请求,即使已经映射,也会滚动任何现有映射。只有当您使用
MAP\u FIXED
时,内核才会确保映射不会与现有映射重叠,以此类推,从而避免零地址如果可能的话。)

Linux并不试图阻止用户自食其果;它假定用户知道他们想要完成什么,并且只确保特权分离等等。这就是为什么它允许您做乍一看似乎没有任何用处的事情

(库可能会映射前几页,甚至作为PROT_NONE,并安装一个SIGBUS处理程序来捕获空指针解引用,而不会使程序崩溃。因此,即使我们可能没有想到它们,也有这类东西的使用案例。相反,某些功能可能会以危险/无用/自我破坏的方式使用;内核会这样做(不是评判。)

另外请注意,正如前面对问题的评论中所提到的,您正在LP64体系结构上运行Linux,其中
int
为32位,
long
和指针为64位。这意味着
%x
%x
仅打印值的无符号整数表示的低32位。请使用
%p
指针。对于其他类型,包括
(或者对于没有转换说明符的类型,只使用
),并使用特定宽度或最小宽度类型,如
uint32\u t
uint\u fast32\u t
。至少要记住对内存大小(如字符串长度或数组元素计数)使用
size\u t

发生了什么事

通常,在
0x0
处没有页面映射,因此取消对空指针的引用是SEGFULTS。但您在那里显式映射了页面,因此它不再SEGFULT

为什么可以将可访问的数据映射到空地址

你回答了自己的问题却没有意识到:

注意:程序必须在root权限下运行

vm.mmap\u min\u addr
sysctl通常通过设置为高于0的值来防止出现这种情况。但是,该sysctl不适用于以root用户身份运行的进程

有了这种行为,我如何使用它来利用系统

你不能,因为如果你能做到,那么


顺便说一句,您的代码在技术上是未定义的行为,因为即使存在某些内容,您仍然在取消对NULL的引用。事实上,对于大多数常见的libc,只有编译器对
printf
put
的优化才能使其工作。

“如果
addr
为NULL,那么内核选择(页面对齐)从文档创建映射的地址(例如)。尝试
printf(“映射到addr=%p\n”,(void*)addr);
而不是
printf(“映射到addr=0x%X\n”,addr)
并告诉我们显示的内容。使用
%X
显示指针值是UB。请发布所有代码,包括
fd
的分配方式。无法使用
printf(“%s\n”,NULL);
打印
abcxyz
.prints。执行相同的操作。
%X\n”,addr)
是未定义的行为,我猜它很可能只显示64位指针的32位部分(较高/较低?我认为较高,但不确定)。请发布一个@KamilCuk,代码实际上不调用
printf
。GCC优化
printf(“%s\n”,x);
put(x)
。我看到您添加了MAP_ANONYMOUS和fd=-1。这是否意味着一个文件的行为会相同?@Silver是的。您在这个问题中询问的效果与映射是否支持文件无关。
Mapped into addr=0x0
abcxyz
abcxyz