Recent Posts
Recent Comments
Link
«   2024/11   »
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
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] AD: stage4__rtld_global 본문

Hack/DreamHack(로드맵)

[System_Hacking] AD: stage4__rtld_global

CIDY 2022. 7. 12. 03:48
int main() {
  return 0;
}


간단한 코드를 디버깅해보자. 리턴하기전에 브레이크 걸고 ret내부로 들어가봤다.


당연하게도 거기엔 __libc_start_main이 있다.

그리고 __GI_exit가 있는데, 그 안으로 또 가 보면


__exit_func라는 함수를 호출한다.

void
attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
		     bool run_list_atexit, bool run_dtors)
{
	  const struct exit_function *const f = &cur->fns[--cur->idx];
	  switch (f->flavor)
	    {
	      void (*atfct) (void);
	      void (*onfct) (int status, void *arg);
	      void (*cxafct) (void *arg, int status);
	    case ef_free:
	    case ef_us:
	      break;
	    case ef_on:
	      onfct = f->func.on.fn;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (onfct);
#endif
	      onfct (status, f->func.on.arg);
	      break;
	    case ef_at:
	      atfct = f->func.at;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (atfct);
#endif
	      atfct ();
	      break;
	    case ef_cxa:
	      cxafct = f->func.cxa.fn;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (cxafct);
#endif
	      cxafct (f->func.cxa.arg, status);
	      break;
	    }
	}

.
이게 그 함수의 C코드이다. exit_function구조체의 멤버 변수에 따른 함수 포인터를 호출한다. 그 구조체가 어떻게 생겼냐면,

struct exit_function
{
/* `flavour' should be of type of the `enum' above but since we need
   this element in an atomic operation we have to use `long int'.  */
long int flavor;
union
  {
void (*at) (void);
struct
  {
    void (*fn) (int status, void *arg);
    void *arg;
  } on;
struct
{
    void (*fn) (void *arg, int status);
    void *arg;
    void *dso_handle;
  } cxa;
  } func;
};


이렇게 생겼다.

아무튼 이 함수가 _dl_fini함수를 호출한다.

# define __rtld_lock_lock_recursive(NAME) \
  GL(dl_rtld_lock_recursive) (&(NAME).mutex)
  
void
_dl_fini (void)
{
#ifdef SHARED
  int do_audit = 0;
 again:
#endif
  for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
    {
      /* Protect against concurrent loads and unloads.  */
      __rtld_lock_lock_recursive (GL(dl_load_lock));


이게 _dl_fini함수이다. 여기서 __rtld_lock_lock_recursive를 호출하는데, 위쪽 매크로를 보면 dl_rtld_lock_recursive라는 함수 포인터이다. 이 함수 포인터는 _rtld_global구조체의 멤버 변수이다.

이 구조체는 엄청 커서 다 살펴보기란 힘들고...

gdb-peda$ p _rtld_global
_dl_load_lock = {
    mutex = {
      __data = {
        __lock = 0x0, 
        __count = 0x0, 
        __owner = 0x0, 
        __nusers = 0x0, 
        __kind = 0x1, 
        __spins = 0x0, 
        __elision = 0x0, 
        __list = {
          __prev = 0x0, 
          __next = 0x0
        }
      }, 
      __size = '\000' <repeats 16 times>, "\001", '\000' <repeats 22 times>, 
      __align = 0x0
    }
  },
  _dl_rtld_lock_recursive = 0x7ffff7dd60e0 <rtld_lock_default_lock_recursive>, 
  ...
}
gdb-peda$ p &_rtld_global._dl_rtld_lock_recursive
$2 = (void (**)(void *)) 0x7ffff7ffdf60 <_rtld_global+3840>
gdb-peda$ vmmap 0x7ffff7ffdf60
Start              End                Perm	Name
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p	/lib/x86_64-linux-gnu/ld-2.27.so


gdb에서 그 구조체(_rtld_global)를 출력한거다. 구조체 내부에서 _dl_rtld_lock_recursive는 rtld_lock_default_lock_recursive함수의 주소를 저장해 둔 친구이다. 구조체의 함수 포인터가 저장된 영역은 읽기 & 쓰기가 가능해서 -> 덮어써서 공격 가능

(*이 구조체는 ld파일에서 찾을 수 있다.)

static void
dl_main (const ElfW(Phdr) *phdr,
	 ElfW(Word) phnum,
	 ElfW(Addr) *user_entry,
	 ElfW(auxv_t) *auxv)
{
  GL(dl_init_static_tls) = &_dl_nothread_init_static_tls;
#if defined SHARED && defined _LIBC_REENTRANT \
    && defined __rtld_lock_default_lock_recursive
  GL(dl_rtld_lock_recursive) = rtld_lock_default_lock_recursive;
  GL(dl_rtld_unlock_recursive) = rtld_lock_default_unlock_recursive;


이 코드는 프로세스 로드 시 호출되는 _dl_main 코드의 일부인데, 위 구조체(_rtld_global)의 함수 포인터인 _dl_rtld_lock_recursive가 초기화되는 것을 볼 수 있다.

 

 

요약: _dl_rtld_lock_recursive는 dl_load_lock을 인자로 받아 실행되니 (둘다 _rtld_global의 구조체 멤버로, 내부에서 오프셋을 찾을 수 있다.) _dl_rtld_lock_recursive를 system(혹은 원가젯)으로 덮고, dl_load_lock에 /bin/sh를 주면 된다.