Try Attack/Symbolic Execution

[논문요약] 소프트웨어의 크래시를 분석 후 자동 공격코드를 생성

D4tai1 2019. 8. 21.

 

제목

설명

주제

소프트웨어의 크래시를 분석해서 자동으로 공격코드를 생성하는 프로그램

설명 정보

자동 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 executionEIPEBPsymbolic한지를 찾습니다.

2) 메모리 내 접근 가능한 곳 중 symbolic한 곳이 있는지 찾습니다.

3) 페이로드(shell_code)를 삽입하고 exploit 제약조건을 build 합니다.

4) solver가 제약조건을 해결할 수 있는지 검증합니다.

5) 해()가 없다면 2)부터 다시 진행합니다.

6) 해가 있다면 exploit을 생성합니다.

 

1) symbol 읽기

[1] 세그먼트 폴트는 소프트웨어 크래시의 일반적인 문제이며 종종 기호 포인터를 읽거나 쓰면서 발생합니다.

[2] symbol 포인터를 올바르게 처리하면 더 많은 유형의 소프트웨어 충돌을 처리하고 exploit 생성 기회를 늘릴 수 있습니다.

[3] 손상된 포인터의 액세스로 인해 소프트웨어 충돌이 발생하는 경우 구체적인 경로는 일반적으로 세그먼트 오류로 끝나고 exploit 생성 기회는 없습니다.

[4] 따라서 부정확한 주소를 가진 symbol 읽기가 감지되면 CRAXsymbol 모드로 전환하고 실행을 계속 시도하며 향후 악용 생성 기회를 기다립니다.

 

2) symbol 쓰기 : exploit 생성을 트리거하기 위해 필요한 조건

[1] Symbolic Program Counter (x86 시스템의 Symbolic EIP) : EIP 레지스터에는 다음에 실행될 명령의 주소가 포함되므로 레지스터를 제어하는 것이 모든 제어 하이재킹 공격의 최종 대상입니다.

[2] 따라서 EIP 레지스터 상태를 모니터링하는 것은 다양한 종류의 제어 흐름 하이재킹 취약점을 해결할 수 있는 포괄적이고 쉬운 방법입니다.

[3] symbolic EIP 레지스터의 검출 프로세스는 그림 2에 나와 있습니다.

[4] 특히, 기호 포인터에 할당된 기호 데이터는 임의의 데이터가 임의의 주소에 기록 될 수 있음을 의미합니다.

[5] 기호 쓰기가 감지되면 쓰기 작업의 대상이 반환 주소, .dtors 섹션 및 GOT와 같은 민감한 데이터로 리디렉션 되어 EIP 레지스터를 간접적으로 업데이트합니다.

[6] 이 취약점으로 인해 리턴주소가 직접 손상되지 않더라도 기호 포인터는 EIP 레지스터를 간접적으로 손상시키고 프로그램 제어를 가로 챌 수 있습니다.

 

31) exploit 생성 Shellcode 삽입

실패 모델이 주어지면 exploit 생성 프로세스는 경로 제약 조건을 만족하는 symbol변수의 변수 할당을 검색하고 제어 흐름을 제공된 쉘 코드로 경로 재 지정할 수 있습니다.

[1] 쉘 코드를 주입하려면, 페이로드를 보유할 만큼 충분히 크고 상징적인 모든 메모리 블록을 찾아야합니다. symbol 블록이 여러 변수로 구성되어 있어도 블록이 연속적이라면 쉘 코드를 주입하는 데 여전히 사용될 수 있습니다.

[2] 그러나 사용자 입력에 의해 오염되고 변수와 결합된 연속 메모리 영역을 찾기 위해 소스 코드를 수동으로 분석하는 것은 어렵습니다.

[3] 또한 컴파일러는 최적화를 위해 순서 또는 할당된 변수 크기를 변경하기 때문에 쉘 코드 버퍼를 수동으로 찾기가 어렵습니다.

[4] 최대 연속 symbol 메모리를 체계적으로 검색하여 이 프로세스를 자동화합니다.

 

32) 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) EIPEBPsymbolic한 지점의 조건을 수집합니다.

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 SledALSR을 무시할 수 있지만 항상 실행 가능한 것은 아닙니다.

Jump-to-Register 공격은 ASLR을 우회하는 기술입니다.

 

4-3) 다른유형의 exploit Jump-to-ESP

[1] NOP Sled를 삽입하고 함수가 결과를 반환하면 반환 주소가 pop되고 ESP 레지스터는 반환 주소를 저장하는 항목 옆의 스택 항목을 가리킵니다.

[2] 코드 세그먼트에서 "jmp % esp"명령을 찾을 수 있다면, 리턴 주소 뒤에 쉘 코드를 삽입하고 ESP 레지스터를 사용할 수 있습니다.

 

4-4) 다른유형의 exploit Jump-to-Registerexploit

[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 

 

댓글