CIDY
[System_Hacking] AD: stage4__rtld_global 본문
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를 주면 된다.
'Hack > DreamHack(로드맵)' 카테고리의 다른 글
[System_Hacking] AD: stage4_문제풀이(__environ) (0) | 2022.07.14 |
---|---|
[System_Hacking] AD: stage4_문제풀이(Overwrite _rtld_global) (0) | 2022.07.14 |
[System_Hacking] AD: stage3_문제풀이(Master Canary) (0) | 2022.07.11 |
[System_Hacking] AD: stage3_Master Canary2 (0) | 2022.07.11 |
[System_Hacking] AD: stage3_문제풀이(master canary) (0) | 2022.07.11 |