정리겸 작성합니다.
이전에 csu_init 관련해서 작성했던적이 있다.
2020.04.26 - [SYSTEM HACKING/기법] - Return to Csu
Return to Csu
Return to csu -특별하게 사용할 수 있는 Gadget이 없는 경우 __libc_csu_init()이라는 함수를 이용하면 함수를 호출 할 수 있게 인자를 설정을 할 수 있다. [__libc_csu_init()] 이 사진을 보면 두가지 파트로..
hoho9.tistory.com
바이너리를 처음 실행하게 되면 start함수를 실행하게 된다.
해당 실행과정에서 __libc_csu_init()함수를 호출하는데 이때 .init_array 섹션을 참조하게된다.
만약 Full Relro가 아닌 경우 해당 섹션을 덮어 쓸 수가 있게된다.
동일하게 바이너리를 종료할때도 exit함수를 실행하게된다.
이때는 .fini_array 섹션을 참조하게 되는데, 동일하게 해당 섹션도 조건이 맞춰지면 overwrite 할 수있다.
정확하게 .fini_array를 호출하는 과정은
처음에는 exit함수를 호출하게된다.
해당 함수에서 __run_exit_handlers를 통해서 _dl_fini함수를 호출한다.
_dl_fini함수 호출은 exit_function구조체의 변수인 flavor값에 따라서 달라지게된다.
void exit (int status) {
__run_exit_handlers (status, &__exit_funcs, true, true);
}
/*__exit_funcs = exit.h에 exit_function구조체로 선언되어있음*/
extern struct exit_function_list *__exit_funcs attribute_hidden;
/*exit.h; extern을 통해서 cxa_atexit.c을 외부변수로 사용 */
static struct exit_function_list initial;struct exit_function_list *__exit_funcs = &initial;
/*cxa_atexit.c*/
struct exit_function_list{
struct exit_function_list *next;
size_t idx;
struct exit_function fns[32];
};
struct exit_function{
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;
};
void attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
bool run_list_atexit, bool run_dtors) {
#ifndef SHARED
if (&__call_tls_dtors != NULL)
#endif
if (run_dtors)
__call_tls_dtors ();
while (true) {
struct exit_function_list *cur;
__libc_lock_lock (__exit_funcs_lock);
restart:
cur = *listp;
if (cur == NULL) {
__exit_funcs_done = true;
__libc_lock_unlock (__exit_funcs_lock);
break;
}
while (cur->idx > 0) {
struct exit_function *const f = &cur->fns[--cur->idx];
const uint64_t new_exitfn_called = __new_exitfn_called;
__libc_lock_unlock (__exit_funcs_lock);
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:
f->flavor = ef_free;
cxafct = f->func.cxa.fn;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (cxafct);
#endif
cxafct (f->func.cxa.arg, status);
break;
}
__libc_lock_lock (__exit_funcs_lock);
if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
goto restart;
}
*listp = cur->next;
if (*listp != NULL)
free (cur);
__libc_lock_unlock (__exit_funcs_lock);
}
if (run_list_atexit)
RUN_HOOK (__libc_atexit, ());
_exit (status);
}
이와 같이 _dl_fini 함수를 호출하게 되면, 해당 함수 내부에서 .fini_array를 호출하게된다.
.fini_array를 원하는 값으로 overwrite하면 exit함수 실행시 원하는 ret주소로 변경이 가능하다.
.fini_array를 덮는경우는 relro가 꺼져있고,
함수 주소값을 변경한 이후 해당 함수를 호출하는 포인터가 없는 경우에 사용하게 되면 좋을 것 같다.
하지만 Full RELRO가 되어있는 경우에는 이와 같은 방법을 이용하지 못한다.
그래서 두가지 방법을 이용한 Exploit이 있는데,
첫번째로는 _rtld_global Overwrite
두번째로는 _hook Overwrite가 있다.
첫번째
_dl_fini부분을 확인하면 __rtld_lock_unlock_recursive함수가 있고 인자로 dl_load_lock을 받고있다.
해당 함수와 인자는 _rtld_global구조체를 사용한다.
해당 구조체는 쓰기 권한이 존재하는 위치에 있기 때문에 해당 함수와 인자를 덮어주게 되면
원하는 함수와 인자 값으로 변경이 가능하다.
void
_dl_fini (void)
{
#ifdef SHARED
int do_audit = 0;
again:
#endif
for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
{
if (nloaded == 0
#ifdef SHARED
|| GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
#endif
)
__rtld_lock_unlock_recursive (GL(dl_load_lock));
else
{
/* Now we can allocate an array to hold all the pointers and
copy the pointers in. */
struct link_map *maps[nloaded];
unsigned int i;
struct link_map *l;
.
.
.
.
두번째
__run_exit_handler를 보면 해당 함수의 마지막 부분에
free를 해주는 부분을 볼 수가 있다.
이를 통해서 free의 _hook을 덮어주게된다면 원하는 함수를 실행할 수 있다.
하지만 해당 free를 호출하기 위해서는 __run_exit_handler함수에서
while(cur->idx > 0)
if(*listp != NULL)
이 두가지가 성립이 되어야지 free를 실행시킬 수 있다.
cur->idx 부분은 구조체의 8번째 부분이니
*initial+8 부분을 0보다 크지 않게 만들어주고,
listp는 initial이므로 아무 값이나 넣어서 조건을 만족시켜주면 된다.
그렇게되면 free호출이 가능해진다.
.
.
.
'SYSTEM HACKING > 기법' 카테고리의 다른 글
_IO_FILE vtable overwrite & _IO_FILE Structure (0) | 2022.06.04 |
---|---|
House of Force? (0) | 2022.01.25 |
Unsafe unlink in glibc 2.23, 2.27 and over 2.27 (0) | 2022.01.22 |
Poison Null Byte in glibc 2.27 (0) | 2022.01.16 |
Fastbin double free in Glibc 2.3.x (0) | 2022.01.01 |