Post

Heap Exploit - House of Lore (a.k.a. Smallbin attack)

House of Lore

malloc 함수의 smallbin에 해당하는 chunk의 재할당과 smallbin에 배치하는 과정을 이용한 공격 기법이다.
[1] [2] [3]

제약 조건

1. Smallbin에 들어간 chunk의 bk를 조작할 수 있음

2. 다음과 같은 구성을 만들어야 함(= Heap leak이 선행됨)

diagram

소스 코드와 함께 파악하는 원리

glibc 2.23 / malloc.c [4]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
      if ((victim = last (bin)) != bin)
        {
          if (victim == 0) /* initialization check */
            malloc_consolidate (av);
          else
            {
              bck = victim->bk;
	if (__glibc_unlikely (bck->fd != victim))
                {
                  errstr = "malloc(): smallbin double linked list corrupted";
                  goto errout;
                }
              set_inuse_bit_at_offset (victim, nb);
              bin->bk = bck;
              bck->fd = bin;

              if (av != &main_arena)
                victim->size |= NON_MAIN_ARENA;
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }
        }

last(bin)은 bin->bk를 의미한다. 즉, 가장 위 조건문은 bin->bk != bin를 검사하는 구문이고, bin->bk == bin은 해당 smallbin에 free’d chunk가 없다는 것을 의미한다.

그 외에 추가적인 조건문은 victim->bk->fd == victim을 맞춰주기만 하면 된다. bypass_chunk가 존재하는 이유가 바로 여기서 드러난다. smallbin에 들어간 fake_chunk를 다시 할당하기 위해서는 fake_chunk->bk->fd == fake_chunk가 되어야 하기 때문이다.

이 조건만 만족해준다면, 공격자는 임의의 주소에 위치한 청크인 fake_chunk를 smallbin에서 재할당하여 사용할 수 있게 된다.

glibc 2.26~ / malloc.c [5]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
	if ((victim = last (bin)) != bin)
        {
          bck = victim->bk;
	  if (__glibc_unlikely (bck->fd != victim))
	    malloc_printerr ("malloc(): smallbin double linked list corrupted");
          set_inuse_bit_at_offset (victim, nb);
          bin->bk = bck;
          bck->fd = bin;

          if (av != &main_arena)
	    set_non_main_arena (victim);
          check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
	  /* While we're here, if we see other chunks of the same size,
	     stash them in the tcache.  */
	  size_t tc_idx = csize2tidx (nb);
	  if (tcache && tc_idx < mp_.tcache_bins)
	    {
	      mchunkptr tc_victim;

	      /* While bin not empty and tcache not full, copy chunks over.  */
	      while (tcache->counts[tc_idx] < mp_.tcache_count
		     && (tc_victim = last (bin)) != bin)
		{
		  if (tc_victim != 0)
		    {
		      bck = tc_victim->bk;
		      set_inuse_bit_at_offset (tc_victim, nb);
		      if (av != &main_arena)
			set_non_main_arena (tc_victim);
		      bin->bk = bck;
		      bck->fd = bin;

		      tcache_put (tc_victim, tc_idx);
	            }
		}
	    }
#endif
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }

glibc 2.26 이상부터는 tcache가 생겼기 때문에, smallbin을 할당하는 루틴 중에 smallbin에 위치한 free’d chunk들을 같은 size의 tcache_entry로 이동시키는 루틴이 생겼다.

while 반복문을 돌면서, tcache entry가 모두 채워지거나, smallbin에 아무런 free’d chunk가 없을 때까지 tcache_put 함수를 실행하려고 한다. 따라서, glibc 2.23 때와 같이, fake_chunk와 bypass_chunk 딱 2개만 만들면, tcache로 옮기는 과정에서 segmentation fault가 반드시 발생하게 된다.

1. tcache->counts[tc_idx] < mp_.tcache_count

이를 우회하기 위해서는 tcache를 모두 채울 만큼 충분한 개수(7개)의 fake_chunk를 만들면 된다.

전자를 만족시켰다면(tcache entry가 모두 채워짐), 공격자는 가장 나중에 tcache에 배치됐으면서, 임의의 주소에 위치한 청크인 fake_chunk를 재할당할 수 있게 된다.

2. (tc_victim = last (bin)) != bin

그런데, 후자를 만족시키면서(smallbin에 아무런 free’d chunk가 없음), 비슷한 결과를 내는 방법이 있다.

libc leak을 이미 성공한 이후라면, bypass_chunk->bk를 smallbin으로 만들면 되지만, 그렇지 않다면 다음의 방법을 이용하면 된다.

세 개의 chunk를 smallbin에 둔 상태이고, 가운데 위치한 chunk->bk를 fake_chunk로 만들고, fake_chunk->bk에는 원래 chunk->bk를 넣는다. 이는 smallbin에 위치한 chunk를 tcache에 넣을 때, 검사가 부재하다는 것을 이용한 방법으로, 결국 마지막 smallbin에 위치한 chunk의 bk 값은 smallbin이기 때문에 while문을 탈출할 수 있게 된다. [6]

더욱 자세한 설명은 이 블로그에서 보면 되겠다.

전자의 방법은 smallbin이 망가진다는 단점이 있으나, 후자의 방법은 그러한 단점이 없다.

Reference

[1] shellphish, “how2heap”, https://github.com/shellphish/how2heap/blob/master/glibc_2.27/house_of_lore.c

[2] Lazenca, “The House of Lore[Korean]”, https://www.lazenca.net/pages/viewpage.action?pageId=1148020

[3] dokydoky, “[Heap Exploitation] Smallbin attack”, https://dokydoky.tistory.com/461

[4] bootlin, “glibc-2.23 malloc.c”, https://elixir.bootlin.com/glibc/glibc-2.23/source/malloc/malloc.c

[5] bootlin, “glibc-2.27 malloc.c”, https://elixir.bootlin.com/glibc/glibc-2.27/source/malloc/malloc.c

[6] OSORI, “tcache smallbin 취약점”, https://osoriselfmanage.tistory.com/96?category=916275

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