A3Security Newsletter > 이달의 보안권고
* 지난호 메모리가드기법 보기 Click
1. ROP Exploit(ASLR, NX, ASCII Armor Bypass)
가. ROP(Return Oriented Programming)
ROP(Return Oriented Programming)는 취약한 프로그램 내부에 있는 기계어 코드 섹션들(Gadget)을 이용하여 BOF공격 시 특정 명령을 실행시키는 방법을 말한다. 보통 Gadget은 함수 끝에 기술되어있는 ret 명령어를 포함, 상위 몇가지 명령어들의 집합이며 이를 이용하여 단한번의 실패없이 한번에 공격을 성공할 수 있다.
[그림 1] Gadget #1
[그림 2] Gadget #2
이러한 ROP는 ASLR, DEP/NX, ASCII-Armor 메모리 보호기법들을 우회할 수 있기 때문에 Fedora core 13(kernel 2.6.33.3-85)에서 BOF공격이 가능한다.
ROP는 RTL, Chaining RTL calls, GOT overwrite의 3가지 특징적인 기술을 사용한다.
(1) RTL(Return to libc)
스택에 있는 리턴 주소를 특정 라이브러리 주소와 이에 알맞은 인자 구성을 하여 공격하는 방법을 Return-to-libc 공격이라고도 부릅니다. 특정 프로그램(/bin/sh)을 실행 하기 위하여 system(), exec*() 등과 같은 함수를 사용하여 관리자 권한의 쉘을 획득한다.
다음은 RTL 공격 전후의 스택구조를 나타낸 그림이다.
[그림 3] RTL 개념도
DEP/NX의 보호기법으로 인해 스택영역에 코드를 삽입하여 공격하는 고전적인 쉘코드 삽입방법은 무용지물이 되었다. 하지만 이러한 RTL 공격기법을 이용하여 코드 삽입 방법이 아닌 함수를 직접 호출하는 RTL 방법으로 우회할 수 있다.
이러한 방법은 DEP/NX + ASLR 보호 기법이 적용되면서 공격이 어렵게 된다. 라이브러리 로딩 순서가 바뀌거나, 부팅할 때마다 이미지 적재 위치가 바뀌게 되면 임의의 리턴 주소를 찍어 맞춰야 하기 때문에 사실상 공격이 어려워진다.
(2) Chaining RTL calls
Chaining RTL calls 기법은 RTL 공격을 연속적으로 일어나게 스택을 구성하여 공격하는 방법을 말한다. pop-pop-ret 과 같은 명령어를 이용하여 스택의 인자 참조 영역을 지나치고 다음 함수주소로 이동, 연속적으로 함수를 호출할 수 있다.
다음은 Chaining RTL calls의 구성을 나타낸 화면이다. 아래와 같이 스택이 구성된다면 연속적인 함수 호출이 가능하다.
[그림 4] Chaining RTL calls 개념도
(3) GOT(Global Offset Table) Overwrite
GOT Overwrite는 libc.so 내에 실제 함수 주소를 저장해 놓은 공간인 GOT에 특정 함수의 GOT 값을 변경하여 원하는 함수가 실행되게 하는 방법이다. 예를 들어 printf 함수의 GOT값(실제 함수 주소)을 execve 함수의 주소로 변경하게 된다면 printf 함수가 수행될 때 printf가 호출되는 것이 아닌 execve함수가 호출되게 되는 것을 말한다.
다음은 PLT에서 GOT의 흐름과 실제 함수 주소를 확인하는 화면이다.
[그림 5] PLT, GOT 흐름도
나. ROP 시나리오
ROP 시나리오는 Stage-1과 Stage-0으로 구분된다. Stage-1에서는 ROP 공격을 위한 Payload가 구성되며 Stage-0에서는 Stage-1에서 구성된 Payload를 특정 스택 영역에 복사하기 위한 strcpy 함수의 Chaining RTL calls가 구성된다.
(1) Stage-1
Stage-1은 GOT Overwrite로 실행 함수(exec* 계열)나 getreuid함수를 호출하게 한다. 하지만 ASLR 보호기법으로 인해 직접적인 함수 주소를 overwrite를 할 수 없기 때문에 add operation gadget을 이용하여 함수간 offset을 add operation으로 계산하여 원하는 함수가 호출되게 한다.
다음은 execve 함수와 printf 함수의 주소 그리고 이들간에 offset값을 나타낸 화면이다.
[그림 6] execve, printf 함수 주소
offset = execve() ? printf()
execve() = printf() + offset
execve() = 0x94ee40
printf() = 0x8fad30
offset = 0x54110
다음은 Stage-1애서 GOT Overwrite를 하기위한 스택구조를 나타낸 화면이다. '
[그림 7] GOT Overwrite가 일어나는 스택구조
ecx 레지스터에 해당 offset 값을 넣어두고 add operation gadget의 명령에 맞춰 5b042464를 빼준 값을 ebp에 넣어둔다. 해당 명령이 실행될 때에 ebp와 5b042464값이 더해지게 되어 printf함수의 주소로 바뀌게 되고 이를 ecx(offset)와 더하면서 printf 함수의 GOT 값이 execve 함수의 주소로 바뀌게 된다. 마지막으로 printf의 PLT값이 호출되면서 execve함수가 수행되는 방식으로 진행된다.
(2) Stage-0
Stage-1에서는 Stage-0에서 구성된 Payload를 실행은 되지 않지만 메모리 주소가 변하지 않는 특정 custom stack영역에 복사를 수행한다. 여기서는 앞서 설명한 Chaining RTL calls를 이용하여 반복적으로 strcpy 함수를 호출하게 되는데 이는 부분 부분 흩어져 있는 특정 문자열들을 모아서 Stage-0 Payload에 맞게 구성해야 하기 때문이다. 이와 같은 작업을 하는 이유는 Stage-1에서 존재하는 NULL byte(0x00) 이후의 값을 strcpy 함수로 복사할 수 없기 때문이며, 원하는 기능을 수행하기 위해서 ebp의 값을 원하는 위치에 놓이게 하기 위해서다.
다. ROP Exploitation
ROP Exploit은 Fedora Core 13에서 수행하였다. 일반적인 BOF는 수동으로 공격을 수행하는 것이 보편적이지만 ROP Exploit을 수동으로 구성하는 것은 효율적이지 않다. 다양한 gadget들을 모아야하며Stage-1을 구성하기 위해 사용되는 수많은 코드들 또한 모아야 하기 때문에 오랜시간이 소비된다. 이때문에 ROP는 자동화된 공격도구를 사용하게 된다. 앞으로의 예제는 공격코드가 구성되는 gadget과 Stage-1과 Stage-0를 쉽게 구성하기 위해 BlackHat 2010에서 소개되었던 ROPEME(ROP Exploit Made Easy)를 이용하였다.
다음은 BOF 취약점이 존재하는 프로그램 소스코드 이다. strcpy함수로 인해 BOF가 일어나게 된다.
[그림 8] BOF 취약점이 존재하는 프로그램
다음은 ROPEME에서 제공하는 ropshell을 이용하여 해당 gadget들을 찾는 화면이다.
[그림 9] ropshell을 이용한 gadget 검색
다음은 Stage-1이 놓이게될 custom stack 영역을 찾는 화면이다. 주소값이 변하지 않는 .bss 영역을 지정하게 된다. 정확한 위치는 사용하고 있는 8byte를 지난 bss+8 영역이 된다.
[그림 10] custom stack이 놓이게 될 위치
gadget들과 custom stack의 위치를 찾게되면 이를 이용하여 exploit한다. 다음은 exploit 코드 화면이다.
[그림 11] exploit #1
[그림 12] exploit #2
exploit은 지정된 gadget들을 참조하여 stage-1과 stage-0를 구성한다. ret의 주소를 70번 삽입하고 그 이후 stage-0를 구성하는 형태로 공격이 진행된다.
다음은 이를 이용하여 관리자 권한을 획득하는 화면이다.
[그림 13] exploit #3
exploit에서 구성된 공격코드를 추출하여 분석결과 ret 이후 strcpy 함수의 Chaining RTL calls로 구성한다. 또한 stage-0을 구성할 시에 쓰이는 문자열들은 취약한 프로그램 코드를 검색하여 수집한다.
다음은 exploit상에서 출력되는 Stage-1과 Stage-0의 구성화면이다.
[그림 14] Stage-1 Payload
[그림 15] Stage-0 Payload
다음은 공격코드를 추출하여 Stage-0의 strcpy의 호출과 복사 위치를 확인하는 화면이다.
[그림 16] Stage-0의 strcpy 복사 위치 확인 #1
위와 같이 Stage-1을 구성하기위해 custom stack위치에 특정 문자열을 반복적으로 복사하는 것을 확인 할 수 있다.
다음은 이해를 쉽게하기 위해 실제 디비거 상에서 source위치를 확인하는 화면이다.
[그림 17] Stage-0의 strcpy 복사 위치 확인 #2
다음은 Stage-1의 Payload 이다. 복사되는 문자열이 Stage-1의 값이라는 것을 확인할 수 있다.
[그림 18] Stage-0의 strcpy 복사 위치 확인 #3
Stage-1이 일어나는 액션은 다음과 같다. 취약한 프로그램의 getuid함수의 GOT를 Overwrite하여 setreuid() 함수로 root권한을 상속받고 setreuid() GOT를 다시 execvp함수로 Overwrite하여 shell을 실행하는 순서로 진행한다.
[그림 19] Stage-1의 액션 확인
위에서 설명했던 액션을 대략적으로 스택에 반영하여 확인하면 다음과 같다.
[그림 20] Stage-1 스택 구성도
다음은 추출한 공격코드들을 수동으로 수행하는 화면이다.
[그림 21] 추출한 Payload로 수동공격
다음은 trace 명령으로 해당 공격코드를 삽입하여 strcpy함수를 연속적으로 호출하고 있는것을 확인하는 화면이다. 정상함수 수행 후 BOF되어 strcpy함수를 연속적으로 호출하는 것을 확인할 수 있다.
[그림 22] trace 명령으로 함수 호출 확인#1
[그림 23] trace 명령으로 함수 호출 확인#2
II. 참고
참고 URL)
가. http://msdn.microsoft.com
나. http://x82.inetcop.org/
다. http://nchovy.kr/forum/5/article/377
라. http://en.wikipedia.org/wiki/Buffer_overflow_protection
마. http://ko.wikipedia.org/wiki/버퍼 오버플로우
바. http://lucid7.egloos.com/
참고 문헌)
가. poc09-sotirov.pdf(POC 2009 발표자료)
나. BHUS10_Paper_Payload_already_inside_data_reuse_for_ROP_expl.pdf
(BlackHat 2010 USA 발표자료)
다. Linux Memory Protectiion Mechanism
라. bh08sotirovdowd.pdf(BlackHat 2008 발표자료)
마. BOF_공격방지_매커니즘_구현의_최신_동향.pdf
바. http://www.shell-storm.org/papers/files/732.pdf
사. www.phreedom.org/presentations/reverse.../reverse-engineering-ani.pdf
아. CanSecWest2010 ? SEH Overwrite, Shuichiro Suzuki
※현재 ㈜에이쓰리시큐리티에서 테스트 및 분석 중에 있으며, 이 문서는 계속 업데이트될 것이다. 본 문서는 보안취약점으로 인한 피해를 최소화하는 데 도움이 되고자 작성되었으나, 본 문서에 포함된 대응방안의 유효성이나 기타 예상치 못한 시스템의 오작동 발생에 대하여서는 ㈜에이쓰리시큐리티에서는 일체의 책임을 지지 아니합니다
* 현종석 송종태 컨설턴트 | (주)에이쓰리시큐리티 지식사업팀
* 본 정보의 저작권은 (주)에이쓰리시큐리티에 있으며 무단 도용 및 배포를 금한다.