배경지식
ELF는 GOT를 활용하여 반복되는 라이브러리 함수의 호출 비용을 줄인다. 이때 GOT에 값을 쓰게 되는데, 그 중 처음 호출 될떄 함수의 주소를 구하고, 이를 got에 적는 Lazy Binding 방식이 있다.
이때 Lazy Binding의 특성 상 GOT를 실시간 업데이트(실행 중) 해야하므로, GOT에 쓰기 권한이 부여되는데, 이는 GOT를 변조 시킬 수 있는 취약점으로 연계될 수 있다.
.init_array && .fini_array
ELF의 데이터 세그먼트에는 프로세스의 초기화 및 종료와 관련된 .init_array, .fini_array가 존재하고 이 영역들은 프로세스의 시작과 종료에 실행할 함수의 주소를 저장하고 있다. 이때 이 주소값을 임의의 값을로 덮어 쓴다면 어떻게 될까?
답은 공격자가 원하는 프로세스의 실행 흐름으로 조작할 수 있다이다.
RELRO(RELocation Read-Only)
이 보호 기법은 리눅스 개발자들이 함수의 주소값 변조를 방지하기 위해 만든 보호 기법이다.
즉, RELRO는 쓰기 권한이 불필요한 데이터 세그먼트의 쓰기 권한을 제거한다.
구분
NO RELRO : 왼만한 모든것 가능
Partial RELRO : .got.plt, .data, .bss
FULL RELRO: .data, .bss
범위에 따라 위와 같이 나누어 부루고 각각의 특징들이 있으니 참고한다.
( 단, 이는 이론 상의 표준이고 실제 문제에서는 Partial 임에도 쓰기권한이 부여되어 있는 경우가 존재하니, 문제의 의도를 파악하고 gdb로 분석해보길…)
.got ? .got.plt ???
: Partial RELRO 가 적용된 got와 관련된 섹션이 .got와 .got.plt 로 두 개가 존재한다. 이는 전역 변수 중 실행되는 시점(시작 시) 바인딩되는 (NOW Binding) 변수는 .got, 전역 변수 중 실행 중 바인딩 되는(LAZY Binding)되는 변수는 .got.plt에 위치한다.
당연하게도 FULL RELRO 는 .got만 존재한다.
우회
이를 우회하기 위해서 여러 방법이 있지만 다음과 같은 방법을 사용할 수도 있다.
NO RELRO : Whatever — .init_array, .fini_array Overwrite
Partial RELRO :.got.plt, .data, .bss —> GOT over Write
FULL RELRO: .data, .bss hook Overwrite(malloc hook, free hook)
.fini_array Overwrite
.text:0000000000402960 sub_402960 proc near ; DATA XREF: start+F↑o
.text:0000000000402960 ; __unwind {
.text:0000000000402960 push rbp
.text:0000000000402961 lea rax, unk_4B4100
.text:0000000000402968 lea rbp, fini_array
.text:000000000040296F push rbx
.text:0000000000402970 sub rax, rbp
.text:0000000000402973 sub rsp, 8
.text:0000000000402977 sar rax, 3
.text:000000000040297B jz short loc_402996
.text:000000000040297D lea rbx, [rax-1]
.text:0000000000402981 nop dword ptr [rax+00000000h]
.text:0000000000402988
.text:0000000000402988 loc_402988: ; CODE XREF: sub_402960+34↓j
.text:0000000000402988 call ss:(fini_array - 4B40F0h)[rbp+rbx*8]
.text:000000000040298C sub rbx, 1
.text:0000000000402990 cmp rbx, 0FFFFFFFFFFFFFFFFh
.text:0000000000402994 jnz short loc_402988
.text:0000000000402996
.text:0000000000402996 loc_402996: ; CODE XREF: sub_402960+1B↑j
.text:0000000000402996 add rsp, 8
.text:000000000040299A pop rbx
.text:000000000040299B pop rbp
.text:000000000040299C jmp _term_proc
.text:000000000040299C ; } // starts at 402960
.text:000000000040299C sub_402960 endp
__int64 fini()
{
signed __int64 v0; // rbx
if ( (&unk_4B4100 - (_UNKNOWN *)fini_array) >> 3 )
{
v0 = ((&unk_4B4100 - (_UNKNOWN *)fini_array) >> 3) - 1;
do
fini_array[v0--]();
while ( v0 != -1 );
}
return term_proc();
}
fini_arrayv0--; 으로 보아 fini_array+8 값에 해당하는 함수를 먼저 실행하고 fini_array 값에 해당하는 함수를 실행한다.
이를 사용하면 fini_array = fini(), fini_array +8 = main() 으로 덮어 쓴다면, main 으로 무한 루프가 가능하다.
이때 leave_ret 가젯이 존재한다면, fini_array +16 부터 rop 체인을 걸수 있다.
에필로그 (leave, ret)
(lea rbp, fini_array)
1. leave:
mov esp, ebp
pop ebp # arg(fini_array+8)
2. ret:
pop eip (fini_array + 16)
jmp eip
'Pwnable > Tech' 카테고리의 다른 글
house_of_spirit (0) | 2023.02.25 |
---|---|
FSOP - glibc 2.35 에서 FSOP 하는 법(feat. House of apple ) (0) | 2023.02.25 |
Tcache dup / glibc 2.26 (0) | 2023.02.18 |
_IO_FILE AAR (0) | 2023.02.18 |
no longer existing hook (0) | 2023.02.18 |