Explanation of a pointer in exploit code
In some exploits for getting the root shell, I often see such a pointer:
int i;
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191);
Can anyone explain this pointer a little bit? I think 8191 is the size of the kernel stack. p
points to the bottom of the kernel stack?
Here is how pointer p
is used:
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++;
}
Solution 1:
The code takes the address of the local variable i
to get a pointer into the current stack frame. Then, it aligns the address to 8K page (that is what you do with x & ~8191
: 8191 is 2^13 - 1 which means ~8191
is all ones except the low 13 bits, so ANDing it with a number will clear the low 13 bits, i.e. align the number to the nearest lower multiple of 2^13, in other words, align to 8K boundary).
It then takes this address and interprets it as a pointer to a pointer and loads the pointed address from it. See Understanding the getting of task_struct pointer from process kernel stack for further information.
After that, it tries to locate a specific structure stored somewhere after that address: It looks through the following 1024-13
unsigned
s, trying to find a place in memory where the current process information (probably) is stored: When it finds a piece of memory holding multiple copies of the current UID and GID, it presumes it has found it. In that case, it modifies it so that the current process gets UID and GID 0, making the process running under root (plus it stores all-ones into the following capability flags).
Cf. struct cred
.
Solution 2:
I'm going to post yet another answer because there really is something to add here.
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191);
results in p being the pointer to the start of the 8192 byte size block of memory. However, the code is wrong. If p is above INT_MAX (which it can be or it would be cast to unsigned, not unsigned long), the high bits get sheared off by the mask. Correct code is as follows:
unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191);
or using uintptr_t:
unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U);
It is necessary to cast to integer and back to pointer for the code to work; however to guarantee an int-sized pointer requires use of ptrdiff_t (we recall that signed and unsigned behave exactly the same for bitwise operations). As for why they don't write them with hex constants, who cares. The guys who do these kinds of things know their powers of 2 by heart. It may be faster to read 8191 then 0x1FFF.