Post

bpf stuffs

bpf 관련 내용들

bpf stuffs

BPF

# TODO

Exploit Scenario

BPF의 verifier 내부 취약점을 이용해서 실제 값과 평가된 값의 불일치를 만들경우 exploit이 가능하다.

1
6: (b7) r8 = 0                        ; R8_w=P0

예를 들어 내가 풀던 문제에서는 freezed map의 조작할 수 있는 취약점을 제공했는데, 이를 이용해서 freezed 된 상태의 값인 0이 R8에 저장된 것을 확인할 수 있다. P0는 Precise value 0을 의미하는데, verifier는 R8이 0이 될 수 있다고 판단하지만 실제로는 freezed map의 조작으로 인해 R8이 1이 되는 상황을 만들 수 있었다.

Going further

다른 분들이 작성한 이전 writeup 중에선 Arithmetic bug를 이용해서 OOB를 일으키는 방식이 많았다. 그러나 위 취약점으로 P0을 더해보니 최적화로 인해서 아예 값이 사라지는 문제를 직면했다. 이러한 경우 helper함수의 인자로 넣음으로써 최적화를 방지할 수 있다.

https://pentera.io/blog/the-good-bad-and-compromisable-aspects-of-linux-ebpf/
https://www.zerodayinitiative.com/blog/2020/4/8/cve-2020-8835-linux-kernel-privilege-escalation-via-improper-ebpf-program-verification
블로그를 참고하였다.

bpf_skb_load_bytes는 skb의 데이터를 읽어서 레지스터에 저장하는 helper 함수인데, 이를 이용해서 OOB write를 시도해볼 수 있다.
bpf_ringbuf_output는 ring buffer에 데이터를 쓰는 helper 함수인데, 이를 이용해서 OOB read를 시도해볼 수 있다.

1
2
static long (* const bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26;
static long (* const bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130;

내가 시도한 방식의 pseudocode는 다음과 같다.

1
2
bpf_skb_load_bytes(skb, 0, &rsp-0x20, R8);
bpf_ringbuf_output(ringbuf, &rsp-0x20, R8, 0);

이 방식을 통해서는 OOB는 실제로 트리거 할 수 있었다.

알고 보니, 더 쉬운 방법이 있었는데, 어차피 skb로 user memory를 읽어오는 것이 가능하기 때문에, skb read length에 R8을 넣어서 OOB read를 시도해볼 수 있었다. 이경우에는 컴파일러 최적화 경로를 타지 않기에 바로 OOB read/write가 가능했다.

This post is licensed under CC BY 4.0 by the author.