Explain pointer in exploit code

In some exploits for getting the root shell, I often see this pointer:

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

Can someone explain this pointer a bit? I think 8191 is the size of the kernel stack. p points to the bottom of the kernel stack ? Here's how the p pointer 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++; } 
+49
c pointers linux exploit
Oct 23 '15 at 6:18
source share
2 answers

The code takes the address of local variable i to get a pointer to the current stack stack. Then it aligns the address on the 8K page (this is what you do with x & ~8191 : 8191 is 2 ^ 13 - 1, which means that ~8191 is everything except low 13 bits, so ANDing with the number will clear the low 13 bits , i.e. align the number to the nearest lower multiple of 2 ^ 13, in other words, align the border of 8K).

Then he takes this address and interprets it as a pointer to a pointer and loads the highlighted address from it. See Understanding how to get the task_struct pointer from the process kernel stack for more information.

After that, he tries to find a specific structure stored somewhere after this address: he looks at the following 1024-13 unsigned s, trying to find a place in the memory where the current information about the process is stored (possibly): When he finds a piece of memory that contains several copies current UID and GID, he assumes that he found it. In this case, it changes it so that the current process receives UID and GID 0, forcing the process to work as root (plus it saves all-ones to the following capability flags).

Cf. struct cred .

+54
Oct 23 '15 at 10:20
source share

I am going to post another answer because there is something to add.

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

leads to the fact that p is a pointer to the beginning of a memory block of size 8192 bytes. However, the code is incorrect. If p exceeds INT_MAX (whatever it may be or it will be distinguished without an unsigned sign, and not an unsigned long sign), the high bits are trimmed with a mask. The 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 send an integer and return to the pointer for the code to work; however, to ensure that the int-size pointer requires the use of ptrdiff_t (recall that signed and unsigned behave the same for bitwise operations). As for why they don't write them with hexadecimal constants, who cares. The guys who do this know their abilities 2 by heart. Faster reading 8191, then 0x1FFF.

+8
Oct 23 '15 at 19:43
source share



All Articles