완료됨
막힌 부분에 대한 질문 드립니다!

.............................................................................................................................................................................................................................................................미리보기 스포 방지.....................................................................................................................................................
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
release 부분에서 occupied에 대한 검사가 따로 없어서
UAF와 엮어서 double free가 가능하다는 것 까지는 파악하였고, safe link 가 돼있다는 것 까지는 알아냈습니다.

payload 구성을 cat의 포인터 배열을 통해 free의 got 를 system으로 덮어 system(/bin/sh)를 실행하는것이 답이라 생각하고 디버깅 중입니다.
다만 double free 이후에 이후에 할당된 청크를 조작하기 위해 처음 free 이후 see를 통해 xor 할 alue 를 알아넀다고 생각하고

1 0 (cat[0]에 처음 할당)
4 0 (cat[0] free)
2 0 (save link 의 xor 값 읽어서 변수 xorV로 저장)
1 1 (cat[1] 에 cat[0] 로 할당되었던 청크 할당)
4 0 (cat[1] 과 같은 청크이므로 free 가능)
3 1 'a'* 9 (cat[1] 을 통해 fd bk 값 오염)
4 0 (cat[1] 의 청크 double free)
1 2 (cat[2] 에 double free 된 청크 1번 할당)
3 2 p64(cats주소 혹은 GOT 값과 xorV가 xor 된 값)(safe link의 양식에 맞는 할당을 원하는 주소)
1 3 (cat[3] 에 double free 된 청크 2번째 할당. 이후 청크는 제가 조작한 cats 로 들어갈 것으로 생각)
1 4
마지막 1 4 과정에서 제가 원한 주소가 할당이 되어 cat[4] 가 제가 임의로 쓰고 읽을 수 있는 주소가 될 것이라고 기대했습니다. 하지만 새롭게 heap chunk 가 할당되었습니다.
제가 어디서 잘못 접근한 것인지 혹은 해결 방법에 대한 힌트를 조금 알려주실 수 있으실까요??

#pwnable
작성자 정보
더 깊이 있는 답변이 필요할 때
드림핵 팀과 멘토에게 직접 문의해 보세요!
답변 1
PUPU
시스템 해킹 초심자

우리가 유심히 봐야 할 부분은 __libc_malloc()함수 부분입니다.

void *
__libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim;

  _Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2,
                  "PTRDIFF_MAX is not more than half of SIZE_MAX");

  if (!__malloc_initialized)
    ptmalloc_init ();
#if USE_TCACHE
  /* int_free also calls request2size, be careful to not pad twice.  */
  size_t tbytes;
  if (!checked_request2size (bytes, &tbytes))
    {
      __set_errno (ENOMEM);
      return NULL;
    }
  size_t tc_idx = csize2tidx (tbytes);

  MAYBE_INIT_TCACHE ();

  DIAG_PUSH_NEEDS_COMMENT;
  if (tc_idx < mp_.tcache_bins
      && tcache
      && tcache->counts[tc_idx] > 0)
    {
      victim = tcache_get (tc_idx);
      return tag_new_usable (victim);
    }
  DIAG_POP_NEEDS_COMMENT;

__libc_malloc함수는 초기에 tcache_perthread_struct head chunk를 구성합니다. (MAYBE_INIT_TCACHE) 이후 새로운 chunk에 대해서 할당이 요청되면, 아래부분

  DIAG_PUSH_NEEDS_COMMENT;
  if (tc_idx < mp_.tcache_bins
      && tcache
      && tcache->counts[tc_idx] > 0)
    {
      victim = tcache_get (tc_idx);
      return tag_new_usable (victim);
    }
  DIAG_POP_NEEDS_COMMENT;

여기에서 기존에 들어있는 tcachebin이 있는지 확인하고 전역변수 victim을 tcache_get으로 가져온 chunk 주소로 설정하고 끝납니다.
중요한것은 tchache_get함수의 구조입니다.

static __always_inline void *
tcache_get (size_t tc_idx)
{
  tcache_entry *e = tcache->entries[tc_idx];
  if (__glibc_unlikely (!aligned_OK (e)))
    malloc_printerr ("malloc(): unaligned tcache chunk detected");
  tcache->entries[tc_idx] = REVEAL_PTR (e->next);
  --(tcache->counts[tc_idx]);
  e->key = 0;
  return (void *) e;
}

tcache_get()함수는 tcachebin에 있는 chunk를 가져올때 tcache->counts를 -1합니다. 현재 질문자님 환경은 하나의 chunk를 가지고 DFB를 체인하여 fd를 조작하려는 것 같습니다. 하지만 하나의 chunk를 여러번 free하는 것은 가능하겠으나, tcache->counts는 계속 0에 머물러 있기 때문에 .. 아마 tcachebin의 fd를 조작해도 그쪽으로는 malloc하지않고 top chunk에서 split 하여 chunk를 할당하는 것으로 보입니다.
감이 좀 잡히시는지요..? chunk를 한번 여러개 만들어 보시면 될겁니다.

2025.06.13. 00:21