【问题标题】:Explanation of a pointer in exploit code漏洞利用代码中的指针说明
【发布时间】:2015-10-23 06:18:41
【问题描述】:

在一些获取root shell的exploit中,经常会看到这样一个指针:

int i;
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

谁能稍微解释一下这个指针?我认为 8191 是内核堆栈的大小。 p 指向内核栈的底部? 以下是指针p 的使用方式:

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 
for (i = 0; i < 1024-13; i++) { 
    if (p[0] == uid && p[1] == uid && 
        p[2] == uid && p[3] == uid && 
        p[4] == gid && p[5] == gid && 
        p[6] == gid && p[7] == gid) { 
            p[0] = p[1] = p[2] = p[3] = 0; 
            p[4] = p[5] = p[6] = p[7] = 0; 
            p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); 
            p[0] = p[1] = p[2] = ~0; 
            break; 
        } 
    p++; 
} 

【问题讨论】:

  • 二进制中8191的值为1111111111111long类型为32位。我想给你一个肯定的答案,我们需要看看*p指针是如何被使用的。 &amp; 运算符可能是某种位掩码。
  • @TimBiegeleisen 感谢您的回复。我已经编辑过了。

标签: c linux pointers exploit


【解决方案1】:

代码获取局部变量i 的地址以获取指向当前堆栈帧的指针。然后,它将地址与 8K 页面对齐(这就是您对 x &amp; ~8191 所做的操作:8191 是 2^13 - 1 这意味着 ~8191 是除低 13 位之外的所有 1,因此用数字与它进行与将清除低 13 位,即将数字对齐到最近的 2^13 的低倍数,也就是对齐到 8K 边界)。

然后它获取该地址并将其解释为指向指针的指针并从中加载指向的地址。请参阅Understanding the getting of task_struct pointer from process kernel stack 了解更多信息。

之后,它尝试定位存储在该地址之后某处的特定结构:它查看以下1024-13unsigneds,尝试在内存中找到存储当前进程信息(可能)的位置:当它发现一块内存包含当前 UID 和 GID 的多个副本时,它假定它已经找到它。在这种情况下,它会对其进行修改,以便当前进程获得 UID 和 GID 0,从而使进程在 root 下运行(另外它将全 1 存储到以下功能标志中)。

参照。 struct cred.

【讨论】:

    【解决方案2】:

    我将发布另一个答案,因为这里确实有一些东西要添加。

    unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 
    

    导致 p 是指向 8192 字节大小的内存块开始的指针。但是,代码是错误的。如果 p 高于 INT_MAX(它可以是或将被强制转换为无符号,而不是无符号长),则高位被掩码剪掉。正确代码如下:

    unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191);
    

    或使用 uintptr_t:

    unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U);
    

    必须转换为整数并返回指针才能使代码正常工作;但是,要保证 int 大小的指针需要使用 ptrdiff_t(我们记得有符号和无符号的按位运算的行为完全相同)。至于他们为什么不用十六进制常量来写,谁在乎。做这些事情的人都知道他们的力量 2。读取 8191 可能比读取 0x1FFF 更快。

    【讨论】:

    • 对于严格的 ISO 正确性,uintptr_t 在这两个地方都不是 ptrdiff_t。然而,所有 Linux ABI 都保证所有 T 都为 sizeof(unsigned long) == sizeof(T*)。因此,在 Linux 内核漏洞利用的上下文中,使代码正确的最小更改只是 &amp; ~8191UL 而不是 &amp; ~8191
    • ~8191(类型为int)在&amp;-表达式中使用且左侧为unsigned long 时,将应用符号扩展。因此,高位不会从掩码中剪掉。这与uint64_t x = -1; 将所有 64 位设置为 1 的原因相同。
    • @mortehu:我在别处被那个片段烫伤了。如果 int 的位数比 unsigned long 少,则转换会出错。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    • 1970-01-01
    • 2014-03-03
    • 1970-01-01
    • 2017-04-28
    相关资源
    最近更新 更多