суббота, 9 июля 2022 г.

PoC to blind pamspy

Lets disasm jit code from this spyware:

 [8] prog 0xffffb02dc0133000 id 160 len 46 jited_len 215 aux 0xffff8ccb58fab400 used_maps 1 used_btf 0 func_cnt 0
     tag: 0F 86 19 76 BC 37 68 B3
  stack_depth: 16
  num_exentries: 0
  type: 2 BPF_PROG_TYPE_KPROBE
  expected_attach_type: 0 BPF_CGROUP_INET_INGRESS
  used maps:
   [0] 0xffff8ccbc1b1c600 - rb
...
ffffffffc07bc801 e80a38e6f1  call 0xffffffffb2620010 ; bpf_ringbuf_submit
ffffffffc07bc806 31c0        xor eax, eax
ffffffffc07bc808 415e        pop r14
ffffffffc07bc80a 415d        pop r13
ffffffffc07bc80c 5b          pop rbx
ffffffffc07bc80d c9          leave
ffffffffc07bc80e c3          ret

and in ebpf opcodes:
43 85 00 00 00 C0 CF 02 00 call 0x2CFC0 ; bpf_ringbuf_submit
44 B7 00 00 00 00 00 00 00 mov r0, 0
45 95 00 00 00 00 00 00 00 ret

Here 0x2CFC0 is offset to bpf_ringbuf_submit from __bpf_call_base
The last call submit some data to bpf map rb with type BPF_MAP_TYPE_RINGBUF. If we could patch this function no data will be passed to usermode. How are these native function addresses filled in at all?
Long story short - they are filled by bpf verifiers. It`s really madness how complex this logic is: they have array of bpf_verifier_ops for each type of bpf programs where function pointer get_func_proto returns structure bpf_func_proto which contains address of some function for binding. 

I write simple PoC to patch address of function inside bpf_ringbuf_submit_proto to some retn instruction. Lets look again at jit code (sure you first need to load my driver and then run pamspy):

ffffffffc087ff99 e84704daf1  call 0xffffffffb26203e5 ; ptr to c3 byte
ffffffffc087ff9e 31c0        xor eax, eax
ffffffffc087ffa0 415e        pop r14
ffffffffc087ffa2 415d        pop r13
ffffffffc087ffa4 5b          pop rbx
ffffffffc087ffa5 c9          leave
ffffffffc087ffa6 c3          ret

ebpf opcodes:
43 85 00 00 00 95 D3 02 00 call 0x2D395 ; FFFFFFFF812203E5
44 B7 00 00 00 00 00 00 00 mov r0, 0
45 95 00 00 00 00 00 00 00 ret

Especially wonderful that all bpfs loaded before this patch continue to work as usually and moreover - if you unload my driver patch will be reverted but all bpfs loaded with patched bpf_func_proto will still contain patched addresses!

Lets see which artifacts from my driver we can find:

./lkmem -r -d -c -B -t -k kernel5.13 System.map
mem at 0xffffffffb36387a0 (bpf_ringbuf_submit_proto) patched to 0xffffffffb26203e5

Perhaps to minimize lifetime for this patch I could employ kprobe for example on bpf_check and restore all patches in kretprobe handler. Can your ebpf based EDR detect this?

Комментариев нет:

Отправить комментарий