Linux Binary Hardening with Glibc Hyeonho Seo
About Me 서현호(Hyeonho Seo) KDMHS 재학 중인 파릇한(?) 고등학 생 게임/팀플 빼고는 우분투만 사용 관심 분야는 상당히 잡식성 POSIX System Hacking Linux Kernel Programming Network Protocol C, Modern C++
왜 발표해요? 1. Linux Binary Hardening 2.Low-level Mechanism 3.For Me
어떤 순서로 발표해요? 1. ASLR (Address Space Layout Randomization) 2. PIE (Position Independent Executables) 3. SSP (Stack Smashing Protector)
Linux Binary Vulnerabilities Stack BOF(Buffer Overflow) 스택에 함수 별로 구현되어있는 스택 프레임을 공격 현재 존재하는 거의 대부분의 공격 기법의 모체 지역 변수에 할당된 공간을 초과해 값을 넣음으로 해당 함 수의 Return(Caller)주소를 변조, 원하는 코드 실행 'Non-Executable Stack'을 통해 Stack Shellcode기 반의 공격을 방어 가능 'Stack Smashing Protector'를 통해 공격 자체를 원천 예방 가능
Linux Binary Vulnerabilities RTL(Return to Libc) 동적 라이브러리 영역에 로드 된 함수를 호출 하는 공격 'Non-Executable Stack'과 같은 메모리 권한 기반의 방어 기법을 우회하는 목적 Return주소 변조 시, Shellcode를 실행시키기보다 execve()와 같은 함수를 이용해 간접적으로 셸 호출 'Address Space Layout Randomization'과 같이, 동 적 라이브러리 로드 주소를 Randomization 시키는 방 어 기법이 효율적임
Linux Binary Vulnerabilities ROP(Return Oriented Programming) RTL공격 시, 여러 Phase에 걸친 공격이 필요할 때 사용 모든 메모리 영역에서 코드 조각(Gadget, 대게 RET/RETN으로 끝남)을 모으고 조합해 원하는 행위를 연 쇄적으로 수행하는 공격 가장 Modern한 공격 기법이며 동시에 가장 Critical한 공격 기법임 'ASLR'과 'Position Independent Executables'기법 을 함께 사용함으로서 그나마 위험성을 줄일 수 있음
ASLR 메모리 상에 Mapping되는 모든 영역의 Base주소 를 Randomization Base주소는 변하나, 해당 영역에서의 주소 Offset 은 변하지 않음 공유 라이브러리의 경우에도 랜덤 Base주소를 할 당 받으나 개개의 주소에 대응하는 물리 주소는 같 음 사실상 정적 데이터인 Code영역에 대한 Randomization은 진행하지 않음
ASLR : Environment Option ASLR의 경우 활성/비활성 방법이 환경 종속적임 /proc/sys/kernel/randomize_va_space 0 = Disable ASLR 1 = Stack, VDSO, Shared Memory Space 2 = Full ASLR(Stack, VDSO, Shared Mem, Data/BSS ) uid-0 즉, root만 가능함 setarch linux32/linux64 setarch명령을 통해 현재 및 하위 프로세스의 ASLR을 일시 중단 setarch linux32/linux64 -R을 하게 되면 내부적으로 personailty(addr_no_randomize Flag)호출, ASLR 비활 성화 모든 User가능, 단 해당 User의 권한을 승계
ASLR : ASLR Example *Execution Result / Difference
ASLR : ASLR Example *Execution Result (Physical Address)
ASLR : ASLR Mechanism 바이너리 Randomization 과정 1. 셸, 또는 타 프로그램에서 execve() syscall호출 2. ELF Binary의 실행을 위해 load_elf_binary()호출 3. 해당 함수에서 randomize_va_space의 값 확인 4. ASLR 활성화 시 arch_randomize_brk(), randomize_stack_top()등을 통해 Randomization 진행 Random Value get_random_int Non-Blocking, Low Entropy Cost, Internal get_random_bytes Non-Blocking Entropy Pool, urandom
ASLR : ASLR Mechanism Stack Randomization *'load_elf_binary' : /fs/binfmt_elf.c
ASLR : ASLR Mechanism Stack Randomization *'setup_arg_pages' : /fs/exec.c *'arch_align_stack' : /arch/x86/kernel/process.c
ASLR : ASLR Mechanism Stack Randomization *'randomize_stack_top' : /fs/binfmt_elf.c
ASLR : ASLR Mechanism Data Section Randomization (Full ASLR) *'randomize_stack_top' : /fs/binfmt_elf.c *'arch_randomize_brk' : /arch/x86/kernel/process.c *'randomize_range' : /drivers/char/random.c
ASLR : ASLR Mechanism mmap(2) Allocate Randomization *'mmap_base' & 'mmap_rnd' : /arch/x86/mm/mmap.c
PIE Code영역의 Base주소를 Randomization, Entry Point 또한 상대 주소로 나타남 이 또한, Base주소는 변하나, 해당 영역에서의 주 소 Offset은 변하지 않음 파일 형식이 기존의 EXEC에서 DYN, 즉 Shared Library로 분류됨 gcc에서 '-pie (-fpic)'옵션으로 적용가능 사실 ASLR과 합치려고 고민하려다 따로 뺌
PIE : PIE Example *Objdump - File Type
PIE : PIE Example *Readelf ELF Header
PIE : PIE Example *Execution Result (PIE Disable / PIE Enabled)
PIE *Execution Result / Difference : PIE Example
PIE : PIE Mechanism(?) PIE의 경우에는 뭔가 커널 레벨에서 건드리는 부분 은 거의 없다고 볼 수 있다 컴파일 시에 EXEC가 아닌 DYN, 즉 Dynamic Link형식의 바이너리로 컴파일 하게 됨 ld.so(dynamic Linker)가 해당 바이너리의 Code영역를 Load 및, mmap으로 Mapping을 하게 됨 사실상, ASLR자체에서 구현되어 있는 mmap(2) 의 Randomization을 재사용한다고 볼 수 있음
SSP 함수 구성 단계에 추가적인 루틴을 구현함으로서 작동하게 됨 Stack Canary라는 값을 통해 Buffer Overflow Attack을 탐지하게 됨 공격에 대한 방지의 개념보다는, 비정상 행위에 대 한 탐지가 목적 Stack Frame 구조를 감시하기에, 다른 메모리 영 역에 대한 공격은 탐지 불가
SSP : Compiler Option GCC 기준으로 SSP에 관한 옵션은 총 3개 -fno-stack-protector : SSP 비활성화 -stack-protector-all : 모든 함수 대상 -fstack-protector [Default] : 특정 함수 (기본적으로 8Byte이상 char배열, --param ssp-buffer-size = N) *Test Source Code
SSP : SSP Example *Execution Result / Strace Result
SSP : Stack Canary 크게 Master Canary와 Stack Canary로 나뉨 Master Canary TLS에 저장되어 있다 (특수한 경우,.bss에 저장) 해당 스레드의 모든 SSP Routine이 참조함 ' libc_start_main'함수를 통해 설정 됨 1._dl_setup_stack_chk_guard에서 _dl_random을 참조해 랜덤 값 생 성 2.THREAD_SET_POINTER_GUARD를 통해서 TLS에 Canary 저장 Stack Canary Function Prologue이후, 할당된 지역 변수들과 SFP(Stack Frame Pointer) 사이에 Master Canary를 복사해 넣음 Function Epilogue직전에, Stack Canary와 Master Canary를 비교해, 같지 않으면 ' stack_chk_fail'호출
SSP : Stack Canary Random Canary Terminator Canary 랜덤 값을 Canary로 지정함으로서, 공격자가 Canary를 무효 화 시키는 행위를 예방 Canary값 안에 '\0', 즉 NULL Terminator를 포함 시킴으로 서, 'strcpy'와 같은 문자열 함수를 이용한 공격 예방 최신 리눅스 배포판에서의 카나리 형태 Random Canary + Terminator Canary Example(x86) : 0x4d81fe00 Random(3) + Terminator
SSP : Additional Routines -fno-stack-protector (SSP Disabled) *Function List *Disassembled Code
SSP : Additional Routines -fstack-protector (SSP Enabled) -> -> -> -> --> > *Function List *Disassembled Code
SSP : Additional Routines ' stack_chk_fail' Function Added stack_chk_fail.plt/.got stack_chk_fail.glibc Access to TLS(Thread Local Storage) %gs(x86), %fs(x64) : Thread Local Storage segment register [Disassembled] mov rax, fs:28h... xor rdx, fs:28h...
SSP : Additional Routines SSP Routine [Function Prologue]... ` mov rax, fs:28h mov [rbp+stackcanary], rax xor eax, eax... [Function Routine]... mov eax, 0 mov rdx, [rbp+stackcanary] xor rdx, fs:28h jz label_normally call stack_chk_fail label_normally:
SSP : Mechanism of SSP SSP Disabled CALLER Function Local Variables CALLER Function Modified Value SFP RET RET *Normal Input *Stack Destroyed (Works, not safe) Modified Value Cras h! NOP Sled Shell Code Tainted RET *RET Destroyed *Overflow Exploit (Raise 'SIGSEGV') (Execute Shell)
SSP : Mechanism of SSP SSP Enabled (SSP Function = stack_chk_fail) Canary [TLS] Canary [TLS] Canary [TLS] CALLER Function SSP Function SSP Function Local Variables Modified Value NOP Sled Canary SFP Canary Destroyed RET RET *Normal Input *Stack Destroyed (Raise 'SIGABRT') Canary Destroyed Shell Code Tainted RET *Overflow Exploit (Raise 'SIGABRT')
SSP : stack_chk_fail What is ' stack_chk_fail'? *' stack_chk_fail' : /debug/stack_chk_fail.c *' fortiy_fail' : /debug/fortify_fail.c
SSP : stack_chk_fail How ' stack_chk_fail' raise 'SIGABRT'?...... *' libc_message' : /sysdeps/posix/libc_fatal.c Print out SSP Message by '/dev/tty' Raise 'SIGABRT' by 'abort()' syscall
So What? 일단은, 기술적인 호기심 충족(?). 보안 기법들에 대한 이해도 증가 각 취약 코드에서 발생할 수 있는 공격에 대해, 알 맞은 보호 기법을 채택 각 보호 기법의 특징과 차이점에 대해 명확히 확인 여차하면 직접 커널 모듈을 통해 추가적인 보호 기 법을 구현할 수 있음
Vulnerability Example? 마지막으로, 실제 취약점에 대한 Hardening기법 CVE-2011-1938 : Stack-BOF Vuln PHP 5.3.3 ~ PHP 5.3.6 PHP의 socket_connect()에서 발생한 취약점 AF_UNIX Socket사용시, 'memcpy(&s_un.sun_path, addr, addr_len);' 를 통한 Unlimited copy
Yeah, That's it. 해당 취약점의 경우 ROP Exploitation이 가능 공격 대상인 s_un구조체의 경우, Stack에 할당되 어 있음, Stack-Smashing Protector를 통해 일 차적인 예방 가능 ROP에 사용된 Gadget의 경우 상당수가 Code영 역에서 추출됨, PIE를 통해 제한적으로나마 방어 가능 위 CVE의 경우 어디까지나 예시 사례, 하지만 이렇 듯이 취약점에 대한 방어 기법 고려 가능
Thank You