Lazy Binding 구조

Download Report

Transcript Lazy Binding 구조

Return to Dynamic Linker
리눅스 ASLR/DEP 환경에서 동적 링커를 이용한 원하는 함수 호출
진 용휘
목차
•
기존 리눅스의 공격기법 및 방어기법 소개
•
Return To Dynamic Linker
•
시연 ( PoC 코드 설명 )
기존 리눅스 공격방법
메모리의 모든 영역이 실행 가능하다.
스택 오버플로우
지역변수가 넘친다면?
main 함수의 스택 구조
::
buf[256]
main()
SFP RET
SFP RET
………………………………… ……. …….
buf[256]
SFP: 함수 다음 명령의 스택 위치
RET: 함수 다음 명령의 실행 위치
공격해보기
[ ~ ] : 바이
트
char buf[256]
1. RET을 버퍼의 위치로 조작한다!
2. 버퍼에 내가 원하는 쉘코드를 넣는다.
SFP
[4]
RET
[4]
RET, SFP의 저장 방식
•
•
•
스택, 코드의 메모리 주소를 담고 있어야 한다.
인텔 32비트 x86 컴퓨터 기준으로 32비트의 주소를 지원
한다.
그래서 주소는 0~2^32-1까지이다.
32비트 = 4바이트
RET, SFP의 저장 방식 2
•
32비트 주소 = 4바이트
•
Little-Endian 저장방식
•
주소가 0x0123ABCD라면?
0xCD
0xAB
0x23
0x01
4바이
트
참고 : 16진수는 0~9, a~f까지 있는 진법, 2자리로 0~255까지 표
현 가능
리눅스 방어기법의 등장
- DEP, ASLR -
DEP 란 ?
Data Execution Prevention
•
•
“데이터 영역”을 “실행 불가능한 영역”으로 지정 가능
최신 리눅스에서 코드를 제외한 거의 모든 영역에 적용되어
있다.
DEP가 적용된 메모리 구조
r: 읽기, w: 쓰기, x: 실행, p: 프로세스
전용
-가 있으면 “불가능”
DEP가 적용된 메모리 구조
r: 읽기, w: 쓰기, x: 실행, p: 프로세스
전용
스택: 읽기/쓰기 전용!
DEP FOR HACKERS
DEP가 적용되있는 경우
•
스택 영역에 코드를 올리고 점프시킬 수 없다.
•
다른 방법을 써보자!
DEP FOR HACKERS
공격 방법: Return To Library
외부 라이브러리의 함수를 이용하는 방법
이미 있는 함수들(gets, system 등등)을 이용한다.
DEP FOR HACKERS
공격 방법: Return To Library
[ ~ ] : 바이
트
libc.so.6 (C 표준 함수 라이브러
리)
~
printf
system
gets ~
…
SFP RET RET2 인자1 인자2
[4]
[4]
[4]
[4]
[4]
function
RET를 실제 외부 함수 주소로 이동시킨다.
주소는 디버거 등으로 얻을 수 있다.
…
DEP FOR HACKERS
공격 방법: Return To Library
실행파일에서 그 함수를 호출하는 부분을 써도
된다.
ASLR
라이브러리, 실행 파일이 랜덤한 위치에 로딩된다
ASLR FOR HACKERS
라이브러리 주소가 계속 바뀌어서
원하는 함수를 쓰기 어렵다.
해결법은 없을까?
ASLR FOR HACKERS
기존의 해결법
한번 나온 라이브러리 주소를 기준으로 계속 대입한다
.
→ 시간이 오래 걸린다.
실행 파일에 있는 외부함수 주소를 얻어올 방법을 찾
는다.
→ !!
ASLR FOR HACKERS
기존의 해결법: 공격 절차
1. 한 함수의 실제 주소를 얻어온다.
2. 그 부분의 상대적인 위치를 찾는다.
3. 라이브러리 베이스를 계산한다.
4. 목표 함수의 상대주소를 더하면 끝!
ASLR FOR HACKERS
기존 방법들의 한계
•
•
해당 라이브러리의 버전을 알아야한다.
해당 라이브러리의 바이너리를 가지고 있어야
한다.
ASLR + DEP
최신 리눅스에는
1. 외부 라이브러리, 스택의 주소는 랜덤이다.
2. 코드 영역 외의 실행은 불가능하다.
코드 영역은 기본적으로는 덮어쓸 수 없다.
Return to Dynamic Linker
발표할 기법
ELF 실행파일은 실행할 때
모든 외부함수의 주소를 로딩하지 않는다?
“Lazy Binding”
Lazy Binding 이란 ?
“뒤늦게 고정시킨다”
- 실행할
때 “함수의 위치를 얻는 함수”만 알아온다.
Lazy Binding 장점
실행 속도를 높여준다
-> 거의 모든 프로그램에 쓰인
다
Lazy Binding 인가 ?
lazy binding 방식 사용 여부는
file 명령어로 확인할 수 있다.
Lazy Binding 구조
•
PLT
•
GOT
•
_dl_runtime_resolve: 로딩 과정을 수행한다.
PLT
•
PLT는 외부 라이브러리를 “연결” 해주는 함수
•
코드 내에서도 실제 주소 대신 이 함수를 호출
이부분은 DL에 쓰인
다.
PLT
PLT 함수는 이렇게 생겼다.
1
2
PLT
다시보자. 실행 중에는 이런 식으로 변한다.
GOT
•
•
PLT 함수들은 GOT라는 테이블의 주소로 점프한다.
GOT는 외부 라이브러리의 함수/변수의 주소를 저장
한다.
Lazy Binding 과정 1
•
GOT+4 주소에 로딩 정보를 놓는다.
•
모든 함수는 맨 처음 공통된 과정을 실행한다.
과정:
•
1. 개별 함수 번호를 스택에 넣는다 : push 0x0
2. 공통적인 주소로 점프한다.
: jmp _loader
_loader은 가상의 함수이다.
Lazy Binding 과정 2
… 3. 프로그램 파일의 정보를 스택에 넣는다 : push
(GOT+4)
4. _dl_runtime_resolve 함수로 점프한다.
5. 함수 정보, 프로그램의 정보로 목표 함수를 불러온
다.
•
•
_dl_runtime_resolve 함수의 주소는 GOT+8에 위치한다.
로딩 후에도 함수는 유지된다.
로딩 후 GOT에는 실제 함수 주소가 들어간다.
Return to Dynamic Linker
공격하기
•
Dynamic Linker 에는 두 가지 인자가 들어감
•
두 인자 조작이 가능하다면 원하는 함수 획득 가능
공격하기
1. 첫번째 인자만 조작
•
함수별로 다른 첫번째 인자만 조작한다.
•
조작해야할 범위가 줄어들어서 편하다.
•
함수 정보가 담긴 위치에서 벗어나는 번호를 넣는다.
공격하기
2. 두 인자 모두 조작
•
입맛대로 정보를 구성 가능하다.
•
비교적 많은 공간이 필요하다. (약 500바이트)
•
비효율적이다.
공격하기 - 최적의 방법
첫번째 인자로 공격
PoC 코드
•
•
•
3가지 퍼즐(구조체)이 서로 엉켜있는 형태
퍼즐끼리 서로 연결되어있고, 마지막으로 함수이름에 연결
된다.
로더 함수에서 주소 획득, 실행까지 해준다.
조금 자세히 들어가보자
•
_dl_runtime_resolve 함수 정보 (인자) :
1. 함수번호 를 조작한다
2. 로딩정보 (.dynamic section 재구성 정보)
우리는 첫번째 인자인 함수번호를 조작한다.
readelf -a 공격할실행파
일
조금 자세히 들어가보자 2
•
•
_dl_runtime_resolve 함수:
1. JMPREL[함수번호] 에는 Elf32_Rel 구조체가 있다.
Elf32_Rel
— r_offset : 덮어쓸 GOT 주소
— r_info : 불러올 함수의 정보
계산 1
함수 번호 = “페이로드 주소 - JMPREL”
조금 자세히 들어가보자 3
…
JMPREL[함수번호]
Elf32_Rel
— r_offset : 덮어쓸 GOT 주소
— r_info : 불러올 함수의 정보 - 제 2의 함수번호
• r_info:
0xABCDEF07
마지막 바이트가 0x07 : “불러올 함수”
0xABCDEF를 계산해서 넣어줘야함.
조금 자세히 들어가보자 4
•
•
2. SYMTAB[r_info * 16] 에는 Elf32_Sym 구조체가 있다.
Elf32_Sym
— st_name [4] (함수 이름의 상대적 위치)
— st_value [4] (상관없음)
— st_size [4] (상관없음)
— st_info [1] (상관없음)
— st_other [1] (최하위 2비트가 0이어야됨)
— st_index [2] (상관없음)
계산 2
SYMTAB[r_info * 16] -> 페이로드 안에 위치
조금 자세히 들어가보자 5
•
계산 3
Elf32_Sym
— st_name [4] (함수 이름의 상대적 위치)
…
st_name = “system\x00” 위치 - STRTAB
조금 자세히 들어가보자 6
•
결과적으로
1. JMPREL[함수번호] : Elf32_Rel 구조체 “Rel”
2. SYMTAB[Rel.r_info * 16] : Elf32_Sym 구조체 “Sym”
3. Sym.st_name = “system\x00” 위치 - STRTAB
•
이대로 호출하면 system함수가 바로 호출된다.
공격!!
지역변수 사용 : 128-16=112 바이트 스택 오버플로우
입력된 크기만큼 고정된 주소의 전역변수에 복사
char buf[256]
SFP
[4]
AAAAAAAAAAAAAAAAAAAAAAAAA…
~~~~
_loader
[4]
RET
after
“system”
조작된
함수 번호
1. 스택 오버플로우
문자 256개 + SFP + Dynamic Linker + RET(after library function) + “함수 고유 번호”
2. 구조체 구성
전역 변수에 구조체들을 담는다.
Rel + Dummy + Sym + “함수이름\x00” + “명령어 #”
Dummy = SYMTAB[16 * r_info] 에서 16의 배수 맞춰주기 ex) 20글자를 A로 채움
이 방법의 장점
•
실행파일에서 참조되지 않은 함수를 바로 호출할 수 있다
.
•
보편적으로 적용되는 컴파일방식이므로 활용도가 큼
•
로딩된 라이브러리 전체에 적용 가능
이 방법의 단점
•
페이로드가 조금 복잡하다
•
페이로드가 있는 곳의 주소를 알아야 한다.
•
Full-RELRO일 경우 불가능 ( 다음 장에 설명 )
•
PIE 문제를 해결해주진 않는다. ( 다음 장에 설명 )
단점: Full RELRO
•
Partial RELRO: PLT를 못 덮어쓴다.
•
Full RELRO : GOT(외부 함수 테이블)을 못쓰게 막는다.
•
실행할 때 불러오기 때문에 로더 함수를 안쓴다.
•
함수를 다시 이용할 수가 없다.
단점: PIE
•
Position Independent Executable
•
실행 파일의 주소도 모두 랜덤이다.
•
먼저 실행파일의 주소를 알아내는 과정이 필요하다.
•
그 다음에는 동일
시연
Demo
예시 프로그램 익스플로잇
exploit using Ret2DL
Q&A
무엇이든지 질문하세요!
공격코드 : http://blog.jinmo123.pe.kr
페이스북 : http://fb.me/jinyonghwi