sh711 님의 블로그

House of Force - 1 본문

Study/Linux

House of Force - 1

sh711 2025. 3. 11. 01:43

1. 개념

House of Force는 glibc의 malloc을 이용한 힙 오버플로우 공격 기법으로, top chunk(최상단 청크)를 제어하여 공격자가 원하는 메모리 주소를 할당할 수 있도록 조작하는 기법이다

tcache(Thread Cache) 도입

  • glibc 2.26부터 tcache가 도입되어 작은 크기의 할당 요청은 tcache에서 처리됨.
  • 즉, 일반적인 할당 요청이 top chunk를 직접 사용하지 않게 됨 → House of Force를 수행하기 어려움
  • top chunk 확장 방식 개선
    • malloc()을 호출할 때 top chunk를 확장하는 과정에서, 보안 체크가 추가됨.
    • 예전 버전(glibc 2.23)에서는 top chunk의 크기를 과하게 키워도 제어가 가능했으나,
      glibc 2.27에서는 heap 확장 요청이 일정 크기를 초과하면 mmap()을 사용하여 할당됨.
    • 즉, sbrk()가 아닌 mmap()을 통해 메모리를 할당하면 top chunk 조작이 어려워짐.
     malloc_consolidate() 보안 강화
    • malloc_consolidate() 함수에서 top chunk의 크기가 비정상적으로 크면 오류 처리됨.
    • top chunk의 크기를 0xFFFFFFFFFFFFFFF처럼 큰 값으로 조작하는 것이 더 어려워짐.

 

2. 환경 세팅

Docker에서 ubuntu:18.04 버전 설치
 
근데 해당 버전에서 glibc가 2.27인데 이미 tcache가 도입된 후여서 glibc 버전을 낮춰야 했다
glibc-2.23 버전 소스코드 다운

wget https://ftp.gnu.org/gnu/libc/glibc-2.23
tar -xzvf glibc-2.23
cd glibc-2.23
mkdir build
cd build
../configure --prefix=/opt/glibc-2.23
make
make install

 
1. setenv.c [__unsetenv] 에러

setenv.c: In function ‘__unsetenv’:
setenv.c:279:6: error: suggest explicit braces to avoid ambiguous ‘else’ [-Werror=dangling-else]
   if (ep != NULL)
      ^
cc1: all warnings being treated as errors
// /root/glibc-2.23/stdlib/setenv.c		line 278

ep = __environ;
  if (ep != NULL) 
    while (*ep != NULL)
      if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
        {
          /* Found it.  Remove this pointer by moving later ones back.  */
          char **dp = ep;

          do
            dp[0] = dp[1];
          while (*dp++);
          /* Continue the loop in case NAME appears again.  */
        
      else
        ++ep;
  }

수정

ep = __environ;
  if (ep != NULL) {		// 수정
    while (*ep != NULL)
      if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
        {
          /* Found it.  Remove this pointer by moving later ones back.  */
          char **dp = ep;

          do
            dp[0] = dp[1];
          while (*dp++);
          /* Continue the loop in case NAME appears again.  */
        }		// 수정
      else
        ++ep;
  }

 
 
 
2. regexp.c [loc] 에러

// /root/glibc-2.23/misc/regexp.c	line 33

char *loc1;
char *loc2;
compat_symbol (libc, loc1, loc1, GLIBC_2_0);
compat_symbol (libc, loc2, loc2, GLIBC_2_0);

/* Although we do not support the use we define this variable as well.  */
char *locs;

수정

char *loc1 __attribute__ ((nocommon));		// 수정
char *loc2 __attribute__ ((nocommon));		// 수정
compat_symbol (libc, loc1, loc1, GLIBC_2_0);
compat_symbol (libc, loc2, loc2, GLIBC_2_0);

/* Although we do not support the use we define this variable as well.  */
char *locs __attribute__ ((nocommon));		// 수정

 
 
3. e_pow.c [checkint] 에러

//  /root/glibc-2.23/sysdeps/ieee754/dbl-64/e_pow.c		line 467

if (k > 20)
    {
      if (n << (k - 20))
        return 0;               /* if not integer */
      return (n << (k - 21)) ? -1 : 1;
    }
  if (n)
    return 0;                   /*if  not integer */
  if (k == 20)
    return (m & 1) ? -1 : 1;
  if (m << (k + 12))
    return 0;
  return (m << (k + 11)) ? -1 : 1;

수정

  if (k > 20)
    {
      if (n << (k - 20) != 0)	// 수정
        return 0;               /* if not integer */
      return (n << (k - 21) != 0) ? -1 : 1;	// 수정
    }
  if (n)
    return 0;                   /*if  not integer */
  if (k == 20)
    return (m & 1) ? -1 : 1;
  if (m << (k + 12) != 0)	// 수정
    return 0;
  return (m << (k + 11) != 0) ? -1 : 1;	// 수정

 
4. rpc_parse.c [get _prog_declaration] 에러

//	/root/glibc-2.23/sunrpc/rpc_parse.c		line 520

static void
get_prog_declaration (declaration * dec, defkind dkind, int num /* arg number */ )
{
  token tok;
  char name[10];                /* argument name */

수정

static void
get_prog_declaration (declaration * dec, defkind dkind, int num /* arg number */ )
{
  token tok;
  char name[MAXLINESIZE];                /* argument name */	//수정

 
5. nis_call.c [nis_server_cache_add] 에러

//	/root/glibc-2.23/nis/nis_call.c		line 681
 
 loc = &nis_server_cache[0];
  if (*loc != NULL) 
    for (i = 1; i < 16; ++i)
      if (nis_server_cache[i] == NULL)
        {
          loc = &nis_server_cache[i];
          break;
        }
      else if ((*loc)->uses > nis_server_cache[i]->uses
               || ((*loc)->uses == nis_server_cache[i]->uses
                   && (*loc)->expires > nis_server_cache[i]->expires))
        loc = &nis_server_cache[i];
  
  old = *loc;
  *loc = new;

수정

 if (*loc != NULL) {	// 수정
    for (i = 1; i < 16; ++i)
      if (nis_server_cache[i] == NULL)
        {
          loc = &nis_server_cache[i];
          break;
        }
      else if ((*loc)->uses > nis_server_cache[i]->uses
               || ((*loc)->uses == nis_server_cache[i]->uses
                   && (*loc)->expires > nis_server_cache[i]->expires))
        loc = &nis_server_cache[i];
  }	// 수정

 
6. nisplus-alias.c [_nss_nisplus_getaliasbyname_r] 에러

// /root/glibc-2.23/nis/nss_nisplus/nisplus-alias.c	line 300

char buf[strlen (name) + 9 + tablename_len];
  int olderr = errno;

  snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);

수정

 char buf[tablename_len + 9];		// 수정
  int olderr = errno;

  snprintf (buf, sizeof (buf), "[name=],%s", tablename_val);		// 수정

 
7. make install
드디어 libc-2.23 파일을 얻게 되었다..

 
8. 테스트
기존 libc-2.27에서 free 시 tcache로 들어간다

 
glibc-2.23 라이브러리 및 링커 컴파일

gcc test.c -o test -Wl,--rpath=/opt/glibc-2.23/lib/libc-2.23.so -Wl,--dynamic-lin
ker=/opt/glibc-2.23/lib/ld-2.23.so

glibc-2.23에서 free 된 청크는 fastbin에 들어간다

'Study > Linux' 카테고리의 다른 글

Linux Kernel - 1  (0) 2025.03.26
srandom_r & random_r  (1) 2025.03.20
ELF 헤더 분석  (1) 2025.03.06
Return to Library  (0) 2025.03.01
Stack Buffer Overflow & Stack Canary  (0) 2025.02.26