what is heap spray

우리는 UAF와 같은 공격을 할때 kfree()를 하고 다시 kmalloc을 할텐데

유저랜드 처럼 깔끔하게 LIFO 형태로 malloc 되면 좋겠지만, 커널에서는 이를 보장하지 않는다.

물론, 기본적으로 freelist는 LIFO 형태를 뛰고 있기는 하다. 즉, 가장 최근에 free 된것이 head에 들어가고 뽑은때로 head에서 pop 한다.

근데 왜 보장이 안되냐면,

일단 첫번째로, 같은 cpu에서 free 된다는 보장이 없다.

해당 글을 보면 알겠지만, context switch가 일어났거나, 중간에 담당 cpu가 바뀔수도 있다.

이러면 알겠지만, 해당 cpu에 freelist에 들어가지 못하고 patiallist로 들어가게 된다. 이러면, cpu는 freelist에서부터 찾기 때문에 최근에 내가 free한게 반환 안될 수도 있다.

CONFIG_SLAB_FREELIST_RANDOM: 새 slab을 만들 때 freelist 순서를 랜덤화해서 “다음에 어느 object가 나올지” 예측을 어렵게 함. 또한 다음과 같은 보안기법이 껴져있으면 더욱더 확률이 낮아진다.

심지어 공유자원을 위해서 설정한 RCU 때문에 원하는 타이밍에 free가 안될수도 있다.

이러면 확률이 상당히 낮으므로, 우리는 heap spray라는 기술을 사용해서 확률을 올린다. 어떻게 보면 쉬운기술이다. 스프레이처럼 여러개를 할당하겠다는 거다.

그러다 보면 원하는 위치를 할당 받을 것이다.

커널 힙(slab/slub)에 “내가 원하는 형태의 객체”를 대량으로 뿌려서(spray) 특정 타이밍에 생기는 빈 슬롯(특히 UAF/free 이후 재할당 자리)에 내 객체가 “높은 확률로” 들어가게 만드는 기법

근데 조금더 깊숙이 들어가보자면,

주로 spray에 사용되는 객체들은 msg_msg , pipe_buffer , simple_xattr 들이 있는데

이 커널 객체에는 내부에 자제적인 포인터도 있고, 사용자의 입력을 담는 버퍼도 존재한다.

따라서 크게 두가지 이점이 있음,

  1. spray를 하면서 바로 원하는 정보들을 커널 객체에 넣음

이는 UAF에서 유용하게 사용 될 수 있음

  1. 커널 내부에 포인터를 통해 UAF로 leak이 가능

객체에서 ops 같은 포인터가 존재하면 UAF로 읽으면 커널 주소 leak이 가능하다.