[논문요약] 소프트웨어의 크래시를 분석 후 자동 공격코드를 생성
|
제목 |
설명 |
|
주제 |
소프트웨어의 크래시를 분석해서 자동으로 공격코드를 생성하는 프로그램 |
|
설명 정보 |
자동 exploit 생성, 버그 포렌식. 소프트웨어 크래시 분석, 기호실행, 오염분석 |
|
제시 내용 |
소프트웨어에서 크래시가 발생한 경우 바이너리 프로그램에 대한 공격코드를 자동으로 생성할 수 있는 방법 |
|
목표 |
스택 및 힙 오버플로우, 형식 문자열 및 초기화 되지 않은 변수 사용을 포함한 취약성 유형에 대해서 공격을 시도할 수 있는 exploit 생성 |
|
exploit을 생성하는 것은 소스코드 없이 소프트웨어 장애에 대한 자동화된 프로세스 |
|
|
방법 |
공격은 concolic 실행으로 취약한 경로를 전환하고 IP를 조작하기 위한 제약조건을 이용해서 ROP 페이로드와 결합 |
|
조건 |
연속된 메모리공간이 오염되었고, symbolic하다면 exploit을 자동으로 생성 가능 |
|
용어 |
설명 |
|
CRAX 프레임워크 |
정적 백엔드 역할(크래시를 일으켜 exploit을 생성, 동적 프로그램 분석기, 버그 찾기, 퍼저의 크래시)에서 이러한 소프트웨어 오류가 발생하면 프론트 엔드와 프로그램 바이너리, CRAX는 자동으로 exploit을 생성 |
|
S²E |
구체적인 주소 매핑 된 기호 메모리를 사용하여 오류 지정 경로를 따라 실행을 수행하여 기호 오류 모델과 소프트웨어 크래시를 분석[symbol 애뮬레이터] |
|
1. 대규모 소프트웨어 시스템에 대한 exploit 생성기술 |
|
1) 소프트웨어 크래시에 대한 입력값 조작으로 소프트웨어의 exploit 생성 프로세서를 모델링합니다. |
|
2) 소프트웨어 크래시로 인해 symbol 입력, 오류 지점의 메모리 스냅 샷 (모든 메모리 셀의 구체적 및 symbol 값 포함) 및 오류에 도달하기 위한 경로 제한으로 구성된 정확한 symbol 오류 모델을 구성하여 분석합니다. |
|
3) S²E 를 기반으로 경로 선택 최적화, 선택적 기호 입력 및 의사 기호 변수에 대한 지연 평가를 사용하여 symbol 포인터를 처리하는 새로운 자동 exploit 생성 방법을 제안합니다. |
2. Fuzzer 도구와 Concolic 시뮬레이션을 결합한 exploit 생성을 위한 강력한 기술
|
역활 |
설명 |
|
concolic 시뮬레이션 |
실패경로를 직접탐색하고 복잡하고 관련이 없는 라이브러리 함수를 필터링해서 symbolic execution의 오버헤드를 줄임 |
|
테스트 중인 프로그램과 크래시가 발생하는 입력을 식별하는 퍼저 도구를 사용하여 하나의 경로에만 초점을 맞추기 때문에 경로를 이용할 수 있는지 결정 |
|
|
소프트웨어 크래시 |
일련의 상징적 입력, 장애 지점에서의 메모리 스냅 샷 및 장애 사이트에 도달하기 위한 경로 제약 조건으로 구성된 상징적 실행 추적으로 모델링 될 수 있음 |
|
실패 모델 |
입력에 따른 데이터흐름의 경로를 탐색해서 실패한 경로 (악용 가능한)모델을 작성 |
|
Solver |
제약조건에서 각 분기의 실행가능성을 결정 |
|
경로 제약조건 |
S²E 가 입력에 의해 결정된 방향을 따라가야 함 |
|
입력 제약조건 |
모든 symbol 변수를 구체적 값으로 제한하는 제약조건의 집합 |
|
모든 symbol 분기 조건에서는 하나의 가능한 값만 가짐 |
|
|
concolic 실행 |
이 기능을 구현하기 위해서는 입력 제약조건을 추가해야 함 |
|
경로 제약 조건을 입력 제약 조건으로 대체 |
|
|
즉, symbol을 구체적인 값으로 대체하여 제약조건을 해결 |
0x01. 퍼징을 통한 concolic시뮬레이션에서 exploit을 생성하는 프로세스
1) symbolic execution은 EIP와 EBP가 symbolic한지를 찾습니다.
2) 메모리 내 접근 가능한 곳 중 symbolic한 곳이 있는지 찾습니다.
3) 페이로드(shell_code)를 삽입하고 exploit 제약조건을 build 합니다.
4) solver가 제약조건을 해결할 수 있는지 검증합니다.
5) 해(근)가 없다면 2)부터 다시 진행합니다.
6) 해가 있다면 exploit을 생성합니다.
|
1) symbol 읽기 |
|
[1] 세그먼트 폴트는 소프트웨어 크래시의 일반적인 문제이며 종종 기호 포인터를 읽거나 쓰면서 발생합니다. |
|
[2] symbol 포인터를 올바르게 처리하면 더 많은 유형의 소프트웨어 충돌을 처리하고 exploit 생성 기회를 늘릴 수 있습니다. |
|
[3] 손상된 포인터의 액세스로 인해 소프트웨어 충돌이 발생하는 경우 구체적인 경로는 일반적으로 세그먼트 오류로 끝나고 exploit 생성 기회는 없습니다. |
|
[4] 따라서 부정확한 주소를 가진 symbol 읽기가 감지되면 CRAX는 symbol 모드로 전환하고 실행을 계속 시도하며 향후 악용 생성 기회를 기다립니다. |
|
2) symbol 쓰기 : exploit 생성을 트리거하기 위해 필요한 조건 |
|
[1] Symbolic Program Counter (x86 시스템의 Symbolic EIP) : EIP 레지스터에는 다음에 실행될 명령의 주소가 포함되므로 레지스터를 제어하는 것이 모든 제어 하이재킹 공격의 최종 대상입니다. |
|
[2] 따라서 EIP 레지스터 상태를 모니터링하는 것은 다양한 종류의 제어 흐름 하이재킹 취약점을 해결할 수 있는 포괄적이고 쉬운 방법입니다. |
|
[3] symbolic EIP 레지스터의 검출 프로세스는 그림 2에 나와 있습니다. |
|
[4] 특히, 기호 포인터에 할당된 기호 데이터는 임의의 데이터가 임의의 주소에 기록 될 수 있음을 의미합니다. |
|
[5] 기호 쓰기가 감지되면 쓰기 작업의 대상이 반환 주소, .dtors 섹션 및 GOT와 같은 민감한 데이터로 리디렉션 되어 EIP 레지스터를 간접적으로 업데이트합니다. |
|
[6] 이 취약점으로 인해 리턴주소가 직접 손상되지 않더라도 기호 포인터는 EIP 레지스터를 간접적으로 손상시키고 프로그램 제어를 가로 챌 수 있습니다. |
|
3–1) exploit 생성 – Shellcode 삽입 |
|
실패 모델이 주어지면 exploit 생성 프로세스는 경로 제약 조건을 만족하는 symbol변수의 변수 할당을 검색하고 제어 흐름을 제공된 쉘 코드로 경로 재 지정할 수 있습니다. |
|
[1] 쉘 코드를 주입하려면, 페이로드를 보유할 만큼 충분히 크고 상징적인 모든 메모리 블록을 찾아야합니다. symbol 블록이 여러 변수로 구성되어 있어도 블록이 연속적이라면 쉘 코드를 주입하는 데 여전히 사용될 수 있습니다. |
|
[2] 그러나 사용자 입력에 의해 오염되고 변수와 결합된 연속 메모리 영역을 찾기 위해 소스 코드를 수동으로 분석하는 것은 어렵습니다. |
|
[3] 또한 컴파일러는 최적화를 위해 순서 또는 할당된 변수 크기를 변경하기 때문에 쉘 코드 버퍼를 수동으로 찾기가 어렵습니다. |
|
[4] 최대 연속 symbol 메모리를 체계적으로 검색하여 이 프로세스를 자동화합니다. |
|
3–2) exploit 생성 – NOP Sled 및 악용 생성 |
|
쉘 코드의 위치가 결정되면 NOP Sled는 쉘 코드 앞에 NOP 명령어를 삽입하려고 시도합니다. |
|
[1] 패딩은 서로 다른 시스템에서 쉘 코드의 부정확한 위치에 대비하거나 쉘 코드의 진입점을 확장하는 데 도움이 됩니다. 마지막으로 기호 데이터로 손상된 EIP 레지스터는 NOP 패딩의 중간을 가리킵니다. |
|
[2] 쉘 코드, NOP Sled 및 EIP 레지스터 제약 조건을 포함한 모든 exploit 제약 조건은 경로 조건과 함께 SMT 솔버로 전달되어 exploit의 실행 가능 여부를 결정합니다. |
|
[3] 그것이 가능하지 않은 경우, exploit 생성은 exploit이 생성되거나 더 이상 사용 가능한 symbol 버퍼가 없을 때까지 쉘 코드의 위치를 변경하기 위해 쉘 코드 주입 단계로 돌아갑니다. |
|
[4] 최대 연속 기호 메모리를 체계적으로 검색하여 이 프로세스를 자동화합니다. |
|
4) 최적화 |
|
[1] S²E 는 전체 운영 체제에서 symbol 실행을 수행하므로 symbol 데이터가 라이브러리 또는 커널로 전달 될 때 많은 경로 제약 조건이 문제가 됩니다. |
|
[2] 라이브러리나 커널에 의해 유발된 제약은 일반적으로 복잡하고 거대하며 제약은 솔버는 종종 해결하려고 노력합니다. |
|
[3] 예를 들어, 열려는 파일의 경로 인 fopen () 함수의 첫 번째 인수가 기호이면 제한 조건 솔버에서 시간 종료 오류가 발생하거나 S²E 에서 정지됩니다. |
|
[4] 관련 없는 경로를 탐색하지 않으려면 해당 라이브러리 기능을 구체적으로 실행해야합니다. |
|
[5] S²E 의 필수 기능 중 하나는 선택적 기호 실행으로, 구체적으로 실행해야 할 영역을 지정할 수 있습니다. |
|
[6] 라이브러리와 커널 함수를 구체적으로 실행할 수 있으며, 구체적인 실행 중에는 경로 제약 조건이 추가되지 않습니다. |
|
[7] 이 관련 없는 기능이 완료되면 symbolic 실행으로 다시 전환합니다. |
0x02. Exploit을 생성하는 프로세스
1) 퍼저로부터 crash가 발생하는 입력 값을 얻어냅니다.
2) 입력 값에 대한 제약조건을 설계합니다.
3) EIP와 EBP가 symbolic한 지점의 조건을 수집합니다.
4) 페이로드를 삽입합니다.
5) 공격코드를 생성합니다.
|
3. 기본 구현 |
|
exploit 생성 단계는 [1] 필요한 런타임 정보 수집, [2] exploit 제약 조건 빌드, [3] exploit 제약 조건 [4] exploit 생성 |
|
S²E의 메모리 모델은 제안된 방법을 구현하는 데 중요한 열쇠입니다. |
|
메모리로의 exploit 외에도 libc 로의 리커버리와 레지스터로의 exploit 두 가지 다른 exploit을 구현하여 일부 보호 기능을 우회하여 exploit 생성이 실제 시스템에서 유용 할 수 있도록 합니다. |
|
1) symbolic 환경 및 구체적인 주소로 매핑 된 symbolic 메모리 |
|
[1] 바이너리 프로그램에서 exploit 생성의 핵심은 구체적인 주소로 매핑 된 symbol 메모리입니다. |
|
[2] S²E 는 전체 시스템 기호 에뮬레이터이며 운영 체제의 모든 종류의 환경 입력은 장치 입력, 네트워크 패킷, 소켓, 파일 (stdin 포함), 환경 변수 및 명령 인수를 포함하여 symbol로 선언 될 수 있습니다. |
|
[3] 파이프를 사용하여 symbol 표준입력을 에뮬레이트하고 mmap을 사용하여 기호 파일을 에뮬레이트합니다. 다른 모든 환경은 쉽게 에뮬레이션 할 수 있습니다. |
|
[4] 구체적인 주소로 매핑 된 symbol 메모리를 사용하여 구체적인 주소별로 symbol 메모리를 색인 할 수 있습니다. |
|
[5] 구체적인 주소 매핑 된 symbol 메모리가 없으면 이진 프로그램을 분석 할 수 없습니다. |
|
2) Shellcode 삽입 |
|
[1] 쉘 코드를 이전 단계에서 찾은 잠재적 버퍼에 저장할 수 있는지 여부를 결정하려면 symbol 블록의 각 symbol을 확인해야 합니다. |
|
[2] EIP 레지스터가 쉘 코드의 시작 위치를 정확하게 가리킬 수 없더라도 NOP Sled가 나중에 진입 점을 확장하므로 실행 가능할 수 있습니다. |
|
[3] 이러한 모든 제약 조건을 실행할 수 없는 경우 쉘 코드 삽입 위치는 새로운 바이트를 반복적으로 시도하기 위해 1 바이트 앞으로 이동합니다. |
|
3) NOP Sled |
|
[1] NOP Sled는 성공 가능성을 높이는 것보다 안정적인 exploit을 생성하는 것을 목표로 합니다. |
|
[2] CRAX는 가능한 많은 쉘 코드 앞에 NOP 명령어를 삽입하고 범위 내에서 EIP 레지스터를 조정합니다. |
|
[3] 마지막으로, 쉘 코드의 시작 주소, NOPSled의 크기 및 위치 EIP 레지스터 포인트는 가능한 경우 결정됩니다. |
|
[4] 제약 조건 솔버는 최종 경로 조건을 해결하여 셸 코드에서 악의적인 작업을 수행하는 exploit을 생성합니다. |
|
4-1) 다른유형의 exploit - Return-to-libc |
|
[1] 라이브러리로 리턴 공격은 보호와 같은 실행 불가능한 메모리 영역을 우회하는 기술입니다. |
|
[2] 제어 흐름을 system ()과 같은 C 런타임 라이브러리의 함수로 재지 정하고 함수 호출자의 동작을 위조하기 위해 함수 인수를 스택에 수동으로 삽입합니다. |
|
[3] 런타임 라이브러리는 항상 운영 체제에서 실행 가능하고 로드되기 때문에 라이브러리로 리턴 공격은 라이브러리 코드를 실행하여 악성 작업을 수행하고 실행 가능 공간 보호를 우회할 수 있습니다. |
|
[4] 명령 문자열을 포함하는 인수가 스택으로 푸시됩니다. libc 함수 호출이 어디에서 반환하는지는 중요하지 않지만 인수는 우리가 관심있는 작업을 수행하는 열쇠입니다. |
|
[5] shell을 여는 시스템 (“/bin/sh”)을 예로 들면 그림 5와 같이 문자열 “/bin/sh”를 가리키는 포인터가 유일한 인수입니다. |
|
4-2) 다른유형의 exploit – Jump-to-Register |
|
[1] 스택은 쉘 코드 주입에 가장 일반적인 메모리 영역이지만 ASLR은 스택의 기본 주소를 무작위 화하여 제어 흐름이 쉘 코드로 정확하게 점프하지 않습니다. |
|
[2] Jump-to-Register 공격은 ASLR을 우회하는 기술입니다. |
|
[3] "call % eax"명령을 찾을 수 있는 경우 코드 세그먼트 및 쉘 코드는 EAX 레지스터가 가리키는 버퍼에 주입 될 수 있으며, 흐름제어는 이 명령을 실행하고 쉘 코드로 점프하도록 재지정됩니다. |
|
[4] NOP Sled는 ALSR을 무시할 수 있지만 항상 실행 가능한 것은 아닙니다. Jump-to-Register 공격은 ASLR을 우회하는 기술입니다. |
|
4-3) 다른유형의 exploit – Jump-to-ESP |
|
[1] NOP Sled를 삽입하고 함수가 결과를 반환하면 반환 주소가 pop되고 ESP 레지스터는 반환 주소를 저장하는 항목 옆의 스택 항목을 가리킵니다. |
|
[2] 코드 세그먼트에서 "jmp % esp"명령을 찾을 수 있다면, 리턴 주소 뒤에 쉘 코드를 삽입하고 ESP 레지스터를 사용할 수 있습니다. |
|
4-4) 다른유형의 exploit – Jump-to-Register의 exploit |
|
[1] exploit을 생성하려면 "call % eax"및 "jmp % esp"와 같은 관련 명령어를 찾기 위해 코드 세그먼트를 검색해야합니다. |
|
[2] 관련 명령어가 발견되고 레지스터가 가리키는 메모리 영역이 symbol인 경우 셸 코드가 해당 위치에 삽입되고 EIP 레지스터가 리디렉션 되어 관련 명령어가 실행됩니다. |
|
[3] 데이터 세그먼트는 ASLR의 영향을 받지 않는다는 점을 이용합니다. |
|
[4] 예를 들어 "jmp % esp"명령어는 0xffe4이고 "call % eax"명령어는 0xffd0입니다. |
|
5) Concolic-Mode Simulation |
|
[1] 인수 및 환경 변수와 같은 입력 데이터를 사용하여 테스트중인 프로그램을 실행하고 주요 작업은 입력 제약 조건을 작성하고 분기 조건을 수집합니다. |
|
[2] 메모리 모델에 따르면, 구체적인 값은 기호 데이터와 별도로 저장되지만 변수는 기호로 표시되므로 구체적인 값은 무시됩니다. |
|
[3] 벡터 컨테이너는 불필요 할 때 일부 제약 조건을 쉽게 삭제하고 필요할 때 모든 제약 조건을 완전한 입력 제약 조건으로 결합하기 때문에 모든 입력 제약 조건을 저장하는 데 사용됩니다. |
|
[4] 분기 외에도 기호 주소는 기호 실행시 상태 포크를 유발합니다. |
|
[5] 값이 기호 인 메모리 주소에 액세스 할 때 는 액세스 할 위치를 결정할 수 없습니다. |
|
[6] S²E 는 실행을 수행하여 symbol 주소가 참조 할 수 있는 모든 주소에 액세스하려고합니다. |
|
6) 포인터 오염 감지 |
|
[1] 메모리 주소에 액세스 할 때 는 주소가 기호인지 여부를 확인합니다. |
|
[2] symbolic 인 경우 는 프로그램 실행을 유지하기 전에 명시 적 위치를 결정해야합니다. |
|
[3] S²E 는 이진 검색을 사용하여 기호 주소가 가리키는 모든 위치를 찾고 실행을 분기하여 각 주소를 탐색합니다. |
|
7) 코드 선택 |
|
[1] S²E 에는 내장 된 선택적 기호 실행이 있기 때문에 이 기능을 사용하여 구체적인 실행 또는 기호 실행에서 실행할 코드를 쉽게 선택할 수 있습니다. |
|
[2] Linux에서 LD_PRELOAD 환경 변수는 라이브러리 함수를 가로 채 함수로 이동할 수 있습니다. |
|
[3] 이 환경 변수의 도움으로 관련없는 라이브러리 함수를 가로 채어 구체적으로 실행할 수 있습니다. |
Linux는 plt, got가 있지만 커널과 생각보다 가까우며 내부구조가 복잡하지 않아서 Concolic 실행이 가능하지만,
이 부분을 Windows에서 해보려고 하니 라이브러리 내부에 들어가서 나오지 못하는 경우 등 문제가 있어서 리눅스에서는 어떤식으로 해결하게 된 것인지 확인하기 위해 논문을 보게 되었습니다.
또한 Path Explosion과 메모리부족, 소요시간 등의 문제가 있어서 Concolic 실행을 하고 싶었습니다.
그러려면 crash발생장소를 찾아야하고 Fuzzer를 결합하고 싶은데 Fuzzer만 책 한권은 나옵니다.
국내에는 Fuzzer에 관한 책도 많이 없구요.
Fuzzer만 해도 하나의 프로젝트는 되는 것 같은데 아직 갈 길이 머네요..
[참고논문]
1. Software Crash Analysis for Automatic Exploit Generation on Binary Programs
'Try Attack > Symbolic Execution' 카테고리의 다른 글
| [연구] Windows binary vulnerability analysis[1] (0) | 2019.08.22 |
|---|---|
| [연구] 바이너리 취약점 탐지 및 공격코드 생성[1] (0) | 2019.08.18 |
| AFL_fuzzer 설치 및 사용방법[영상포함] (0) | 2019.06.24 |
| Peach Fuzzer install (0) | 2019.06.03 |
| python3에서 angr 설치 (0) | 2019.05.29 |
댓글