2015-10-23 90 views
49

在一些無機可乘獲得root的shell,我經常看到這樣的指針:在指針的解釋漏洞代碼

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++; 
} 
+3

以二進制的值'8191'是'1111111111111',並且'long'類型是32位。我想給你一個確定的答案,我們需要看看如何使用'* p'指針。 '&'操作符可能是某種掩碼。 –

+0

@TimBiegeleisen感謝您的回覆。我編輯過它。 – HuangJie

回答

54

代碼獲取局部變量i的地址,以獲得一個指針到當前堆棧幀。然後,對齊地址8K頁面(也就是你x & ~8191做什麼:8191爲2^13 - 1,這意味着~8191,除了低13位的所有的人,所以取與它的值將清除低13位,即將數字與最接近的2^13的較低倍數對齊,換句話說,對齊到8K邊界)。

然後,它需要該地址和其解釋爲指向指針的指針,並從它加載尖地址。有關更多信息,請參見Understanding the getting of task_struct pointer from process kernel stack

之後,它會嘗試找到地址後,保存在某個地方的具體構成:它看上去通過以下1024-13unsigned S,試圖在內存中找到一個地方,當前的進程信息(可能)的存儲位置:當發現一塊內存中持有當前UID和GID的多個副本,它假定它已經找到它。在這種情況下,它會修改它,以便當前進程獲取UID和GID 0,從而使進程在根目錄下運行(並將所有進程存儲到以下功能標誌中)。

參考struct cred

8

我要去發佈另一個答案,因爲真的有東西在這裏補充。

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); 

有必要轉換成integer和回指針的代碼工作;但是要保證int大小的指針需要使用ptrdiff_t(我們記得signed和unsigned對於按位操作的行爲完全相同)。至於他們爲什麼不用十六進制常量編寫它們,誰在乎。做這些事的人知道他們的權力2。讀取8191然後讀取0x1FFF可能會更快。

+1

爲了嚴格的ISO正確性,在這兩個地方都使用'uintptr_t'而不是'ptrdiff_t'。但是,所有的Linux ABIs都保證所有T的'sizeof(unsigned long)== sizeof(T *)'。因此,在Linux內核漏洞的情況下,使代碼正確的最小*改變就是'&8191UL '而不是'〜〜8191'。 – zwol

+0

當'〜'表達式的'〜8191'在左邊的'&'-expression'中使用'unsigned long'時,將應用符號擴展。因此,高位不會從面具上剪下來。這就是爲什麼'uint64_t x = -1;'將所有64位設置爲1的原因。 – mortehu

+1

@mortehu:我在別處使用該片段燒燬了。如果int的位數比unsigned long少,那麼轉換就會做錯誤的事情。 – Joshua