Hua*_*Jie 49 c linux pointers exploit
在获取root shell的一些漏洞中,我经常看到这样一个指针:
int i;
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191);
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释一下这个指针吗?我认为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++;
}
Run Code Online (Sandbox Code Playgroud)
Mor*_*gil 54
代码获取局部变量的地址i
以获取指向当前堆栈帧的指针.然后,它将地址对齐到8K页面(这就是你要做的事情x & ~8191
:8191是2 ^ 13 - 1,这意味着~8191
除了低13位之外都是1,所以用数字对它进行AND运算将清除低13位,即对齐数字到最接近的2 ^ 13的较低倍数,换句话说,对齐到8K边界).
然后它接受该地址并将其解释为指向指针的指针并从中加载指向的地址.有关详细信息,请参阅了解从进程内核堆栈获取task_struct指针.
之后,它试图找到存储在该地址之后某处的特定结构:它查看以下内容1024-13
unsigned
,尝试在内存中找到存储当前进程信息(可能)的位置:当它找到一块存储多个内存时当前UID和GID的副本,它假定它已找到它.在这种情况下,它会修改它,以便当前进程获得UID和GID 0,使进程在root下运行(加上它将all-ones存储到以下功能标志中).
参看 struct cred
.
我将发布另一个答案,因为这里确实有一些东西需要补充.
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191);
Run Code Online (Sandbox Code Playgroud)
导致p成为指向8192字节大小的内存块的开始的指针.但是,代码是错误的.如果p高于INT_MAX(它可以是或者它将被转换为无符号,而不是无符号长),则高位会被掩码剪掉.正确的代码如下:
unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191);
Run Code Online (Sandbox Code Playgroud)
或使用uintptr_t:
unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U);
Run Code Online (Sandbox Code Playgroud)
有必要转换为整数并返回指针以使代码工作; 但是要保证一个int大小的指针需要使用ptrdiff_t(我们记得有符号和无符号对于按位运算的行为完全相同).至于为什么他们不用十六进制常量来编写它们,谁在乎呢.做这些事情的人都知道他们的力量.读取8191然后读取0x1FFF可能会更快.