드림핵의 Overwrite _rtld_global 를 좀 더 이해하기 위해 정리하고자 작성한 글입니다.
(틀린 내용이 있다면 댓글로 지적해주시면 감사드립니다.)
_rtld_global 개념
_rtld_global 이 무엇인지 부터 알아보겠습니다.
pwn이나 여러 바이너리 파일을 분석해보면 개발자가 main이라는 함수를 작성해서 실행했을때
main함수가 바로 실행되지 않는 다는 점은 디버깅을 하면 가장먼저 알게되는 구조입니다.
그러면 이후에 종료되는 과정이 정확하게 어떻게 되는가?
gdb-peda$ disas main
Dump of assembler code for function main:
0x00000000000005fa <+0>: push rbp
0x00000000000005fb <+1>: mov rbp,rsp
0x00000000000005fe <+4>: mov eax,0x0
0x0000000000000603 <+9>: pop rbp
0x0000000000000604 <+10>: ret
gdb-peda$ b *main+10
Breakpoint 1 at 0x604
gdb-peda$ r
gdb-peda$ si
...
=> 0x7ffff7a03bf7 <__libc_start_main+231>: mov edi,eax
0x7ffff7a03bf9 <__libc_start_main+233>: call 0x7ffff7a25240 <__GI_exit>
...
이는 gdb를 통해 보면 main을 호출한 libc 함수로 돌아가게 되어 있으며 _GI_exit라는 것을 콜하게 되어있습니다.
이 _GI_exit 함수로 들어가보면
0x7ffff7a25240 <__GI_exit>: lea rsi,[rip+0x3a84d1] # 0x7ffff7dcd718 <__exit_funcs>
0x7ffff7a25247 <__GI_exit+7>: sub rsp,0x8
0x7ffff7a2524b <__GI_exit+11>: mov ecx,0x1
0x7ffff7a25250 <__GI_exit+16>: mov edx,0x1
0x7ffff7a25255 <__GI_exit+21>: call 0x7ffff7a24ff0 <__run_exit_handlers>
다시 _run_exit_handlers를 호출하고 _di_fini 를 통해 종료됩니다.
즉 main함수가 종료되어 main을 호출한 부분을 돌아가게 되면
_GI_exit => _run_exit_handlers => _dl_fini 순서로 함수를 호출하는 과정이 있다는 건데
여기서 취약점이 존재하게 됩니다.
_rtld_global 구조
_dl_fini의 함수안에는 _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>,
...
}
드림핵 자료와 관련 자료들의 설명을 보면 해당영역이
FULL RELRO 기법이 적용되어있더라도 쓰기가 가능하다고 합니다.
그중에서도 _dl_load_lock 와 _dl_rtld_lock_recursive 가 실행흐름을 조작할 수 있는데
흐름을 보면 _dl_load_lock를 인자로 _dl_rtld_lock_recursive 포인터에 저장된 함수 (_rtld_global)를
호출하고 있으므로 Overwrite를 하면 공격자의 마음대로 실행흐름 조작이 가능하다는 겁니다.
시나리오
그러면 위 부분을 어떻게 조작할 수 있는가?
1. 위 구조체는 ld에 존재하므로 임의 주소 읽기, 쓰기 취약점이 존재해야합니다.
2. ld까지 접근해야 하기에 libc~ld까지의 오프셋을 알아야 합니다.
(이는 gdb의 vmmap을 통해 수월하게 확인이 가능합니다)
3. 임의 주소읽기로 해당 포인터에 공격코드 혹은 system("/bin/sh")의 overwrite
_rtld_global의 overwrite는 FULL RELRO 기법을 우회할 수 있다는 점과
gdb가 가능하다면 심볼을 이용해 이를 확인가능하다는 점이 상당히 새롭습니다.
Reference
1. https://dreamhack.io/lecture/courses/268
'Reference > Pwnable_Study' 카테고리의 다른 글
[Pwn] SROP(Sig Return Oriented Programming) (0) | 2023.05.02 |
---|---|
[Pwn] gdb offset Tip (0) | 2023.04.01 |
tcache double free bypass (0) | 2023.02.12 |
DFB(Double Free Bug) 취약점 (0) | 2023.01.19 |
(Linux) Heap 구조 이해하기 1 (0) | 2023.01.17 |