2009. 8. 5. 11:03
WinDbg에서 저와 동일하게
c++ eh exception - code e06d7363 가 발생한 사람이 있어서 글을 읽다가 가져와서 여기에 올렸습니다.
저의 경우는 Application Verifier 와 WinDbg를 같이 돌렸을 때 이 문제가 나왔는데요.
여하튼 크게 신경 쓰지 않아도 될거라고 하네요. ( 맞게 내용 파악 한건가 ^.^ )
원본 주소는 다음과 같습니다.
하지만 MSDN에서 계속 찾아본 결과 다른 문제 있을 수도 있다는 생각이 듭니다.
아래의 다음 내용은 MSDN 에서 발췌한 내용입니다.
Exceptions are not a Control Mechanism
This is a partial debugger trace of an extremely popular and widespread application, used in commercial software by thousands of developers at hundreds of companies including Microsoft. This was happening when the program was functioning normally.
01.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
02.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
03.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
04.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
05.
ModLoad:
5c060000
5c072000
C:\WINDOWS\system32\SRCLIENT.DLL
06.
ModLoad:
692c0000
692ee000
C:\WINDOWS\System32\Wbem\framedyn.dll
07.
(1230.1234): Unknown exception - code
80010105
(first chance)
08.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
09.
(1230.1234): Unknown exception - code
80010105
(first chance)
10.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
11.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
12.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
13.
(1230.1254): Unknown exception - code
80010108
(first chance)
14.
ModLoad:
76bb0000
76bb4000
C:\WINDOWS\System32\SFC.DLL
15.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
16.
(1230.1234): Unknown exception - code
8001010e
(first chance)
17.
ModLoad:
76bb0000
76bb4000
C:\WINDOWS\System32\SFC.DLL
18.
ModLoad:
5c060000
5c072000
C:\WINDOWS\system32\SRCLIENT.DLL
19.
ModLoad:
692c0000
692ee000
C:\WINDOWS\System32\Wbem\framedyn.dll
20.
(1230.1234): C++ EH exception - code
e06d7363
(first chance)
21.
(1230.1234): Unknown exception - code
80010012
(first chance)
I had an A/V problem with this utility that I was trying to debug - this was made basically impossible by the fact that the program was continuing after dozens of other access violations. People, you really are not supposed to do this. I won't say what application this is, but I will say that I am not going to be using it in the future.
정확한 주소는 아래와 같습니다.
CRASH COURSE
충돌 분석을 통한 응용 프로그램 보안 취약점 찾기
Adel Abouchaev and Damian Hasse and Scott Lambert and Greg Wroblewski
이 기사에서 다루는 내용:
| 이 기사에서 사용하는 기술: Windows 디버깅, C/C++ |
프로그램 충돌을 악용하지 못하게 하려면 어떻게 해야 할까요? 답은 간단합니다. 모든 충돌이 악용 가능하다고 가정하고 모두 수정하면 됩니다! 적어도 이는 제품의 품질에 대한 문제이며, 제품을 고객에게 제공하기 전에 문제를 해결하는 편이 비용도 적게 들고 더 실용적입니다. 나중에 악용 가능성을 확인하는 데 필요한 분석을 수행하면 더 많은 비용이 들 수 있습니다.
보안 문제를 파악하기 위해 메모리 손상과 관련된 프로그램 실패를 분석하는 작업은 복잡한데다 도중에 오류가 발생하기 쉽습니다. 메모리 내의 버퍼 위치, 덮어쓸 수 있는 대상, 덮어쓸 크기, 덮어쓰는 동안 사용할 수 있는 데이터의 제한 사항, 런타임 실행 환경 상태, 적용된 완화 메커니즘을 생략할 수 있는 기능을 비롯한 여러 가지 요소를 고려해야 합니다. 간단히 말해서 이러한 모든 질문에 답하려면 근본적인 오류 원인을 파악해야 합니다.
오류 중에서는 명확히 드러나지 않는 오류도 있습니다. Microsoft 보안 공지 MS07-017에 설명된 GDI 원격 코드 실행 문제를 예로 들 수 있습니다. 취약한 구문 분석 코드를 호출하는 소프트웨어는 생성 가능한 대부분의 예외를 예외 처리기를 사용하여 복구하고 아무 일도 없었다는 듯 계속 작동합니다. 이에 대한 자세한 내용은blogs.msdn.com/sdl/archive/2007/04/26/lessons-learned-from-the-animated-cursor-security-bug.aspx를 참조하십시오. 오류 확인이 불분명한 또 다른 예는 오류는 발생했지만 현재 프로그램 상태 및 실행 환경에는 뚜렷한 징후가 나타나지 않는 특정 유형의 스택 및 힙 메모리 손상입니다.
이 기사에서는 임의 코드 실행, 또는 최소한 서비스 거부를 가능하게 하는 메모리 손상과 같은 가능한 보안 문제와 관련된 프로그램 충돌을 분석하는 방법에 대한 지침을 제공하고 이러한 유형의 문제를 검토할 때 발생할 수 있는 일반적인 하드웨어 및 소프트웨어 예외를 살펴보겠습니다. 또한 조사 과정에서 사용할 수 있는 몇 가지 일반적인 지침도 제공합니다. 예를 들어 그림 1은 특정 충돌의 악용 가능성을 확인하는 데 도움이 되는 조사 과정을 그래픽 경로로 보여 줍니다. 이러한 내용은 지침일 뿐이며, 악용 불가능한 충돌임을 정확하게 진단하는 방법은 포괄적인 근본 원인 분석 외에는 없다는 사실을 유념하십시오. 새로운 공격 기법이나 기존 공격 기법의 변종이 끊임없이 발견되고 있기 때문입니다.
.gif%22&type=m10000_10000)
그림 1 액세스 위반 분석 경로 (더 크게 보려면 이미지를 클릭하십시오.)
가장 일반적인 충돌 원인은 하드웨어 또는 소프트웨어 예외입니다. 현재 사용되는 일반적인 프로세서는 다양한 유형의 하드웨어 예외를 생성할 수 있지만 Windows®환경에서 소프트웨어 보안과 관련된 문제를 일으키는 예외는 이 중 일부입니다. 가장 일반적인 하드웨어 예외는 액세스 위반입니다. 먼저 하드웨어 예외를 분석하는 방법을 설명한 다음 소프트웨어로 넘어가겠습니다.
액세스 위반
최신 프로세서에서 액세스 위반 예외(0xc0000005= STATUS_ACCESS_VIOLATION)는 명령이나 프로그램 실행에 의해 수행되는 메모리 액세스가 프로세서 아키텍처 또는 메모리 관리 장치 구조에 정의된 특정 조건을 만족하지 못하는 경우 발생합니다.
순수한 충돌 자체는 서비스 거부 조건으로만 이어지지만 코드 실행과 같은 더 위험한 결과를 초래하는 데 사용되지 않으리라고 가정하는 것은 위험한 생각입니다. 충돌을 분석할 때에는 메모리에 몇 가지 사소한 예외가 있는 경우 공격자가 이를 통해 메모리 전체를 제어할 수 있고, 따라서 대부분의 경우 액세스 위반이 공격자의 데이터 제어로 이어질 수 있다는 점을 가정해야 합니다. 데이터를 읽거나 쓰는 명령을 실행하는 중에 발생한 예외가 이러한 상황에 해당합니다.
액세스 위반이 공격자의 데이터 제어로 이어질 수 있다면 메모리 읽기 작업에 의해 발생하는 각 액세스 위반은 공격자가 제어하는 데이터를 불러오는 작업으로 변합니다. 이러한 작업이 보안에 미치는 영향을 항상 쉽게 확인할 수 있는 것은 아닙니다. 이진 또는 소스 코드에서 전체 데이터 흐름을 분석하면 소스 주소 제어 범위 및 특정 실행 지점에서 프로그램에 임의 데이터를 제공하는 데 따르는 결과를 파악할 수 있습니다. 이는 시간도 많이 걸리고 어려운 작업입니다. 이러한 상황에 대응하기 위해 Microsoft는 액세스 위반 충돌의 잠재적인 코드 실행을 신속히 분석하는 간단한 방법을 개발했습니다.
다음 예에서 볼 수 있듯이 레지스터 eax의 잘못된 메모리 포인터가 충돌을 일으킵니다. 이 경우 메모리 내용 제어는 공격자에게 프로그램 흐름에 대한 완전한 제어를 넘겨줍니다.
Application!Function+0x133: 3036a384 eb32 call [eax] ds:0023:6c7d890d=?? 0:000> ub mov eax, [ebx] -> eax = invalid memory pointer ... (instructions not affecting register eax) call [eax] -> crash
읽기 작업이 수행되는 주소를 공격자가 충분히 제어할 수 없는 상황은 서비스 거부 조건으로 처리할 수 있습니다. 예를 들어 일반적인 Windows 사용자 모드 환경에서는 초기화되어 공격자의 영향을 받지 않는 NULL 포인터에서 발생한 충돌은 그 자체로는 코드 실행으로 이어질 수 없습니다.
다음 예에서는 레지스터 eax 값을 통한 주소 0 참조에 의해 충돌이 발생했음을 볼 수 있습니다.
Application!Function+0x133: 3036a384 8b14 mov ecx, [eax] ds:0023:00000000=?? 0:000> ub xor eax, eax ... (instructions not affecting register eax) cmp ebx, 2 jne label123 mov ecx, [eax] -> crash, eax = 0 (NULL)
Visual Studio® 명령줄 디버거에서 ub 명령을 사용하여 디스어셈블하면 악의적인 입력에 의해 레지스터 값이 영향을 받지 않는지 확인할 때까지 해당 레지스터의 데이터 흐름을 추적할 수 있습니다. 실제로 이 예에서 레지스터는 자체적으로 XORing에 의해 0이 되었으며 충돌이 발생한 명령에 도달할 때까지 사용되지 않았습니다.
충돌이 발생한 명령에서 결함의 악용 가능성이 바로 드러나지 않는 경우가 간혹 있습니다. 예를 들어 다음 명령을 디스어셈블하면 이전 단락에서 설명한 실패한 명령 다음에 중요한 명령이 나오는 경우 이는 제어 흐름의 결과임을 확인할 수 있습니다.
(1258.1638): Access violation - code c0000005 (second chance) Application!Function+0x123: 3036a384 8b12 mov eax, [ebx] ds:0023:6c7d890d=?? 0:000> u mov eax, [ebx] -> crash, ebx points to invalid memory ... (instructions not affecting regist an example er eax) call [eax] -> possibility of code execution
이 예는 첫 번째 예와 비슷하지만 이번에는 레지스터 eax로 데이터를 불러오는 명령에서 충돌이 발생합니다. 이 작업 자체는 보안 문제를 의미하지 않지만 디스어셈블리를 통해 레지스터 ebx 값에 대한 제어가 레지스터 eax 값에 대한 제어를 암시하며 코드 실행으로 이어질 수 있다는 점을 명확히 볼 수 있습니다.
나머지 경우는 프로그램 런타임 동안 악성 데이터 주입을 시뮬레이션하여 디버깅 도구를 통해 자동으로 또는 디버거에서 수동으로 분석할 수 있습니다. 액세스 위반을 일으키는 명령에 도달하면 올바른 메모리 주소를 가리키도록 소스 주소를 변경한 후 명령을 다시 실행하거나 대상 레지스터에 임의의 데이터를 넣은 후 오류가 있는 명령을 건너뛰고 계속 실행할 수 있습니다. 악용 가능한 조건에 도달할 때까지 액세스 위반이 발생할 때마다 이 프로세스를 반복합니다.
두 가지 예를 분석해보겠습니다. 먼저 소스 주소를 변경하는 경우의 데이터 흐름 추적을 살펴보겠습니다.
Application!Function+0xa70: 3036a37e 8b4708 mov eax,dword ptr [edi+8] ds:0023:040fd004=????????
여기서 충돌은 잘못된 레지스터 edi 값에 의해 발생합니다. 이 값을 올바른 메모리 영역을 가리키도록 변경합니다. 여러 가지 방법이 있지만 레지스터 eip의 현재 값을 사용하는 경우가 많습니다. 이렇게 하면 새 edi 값 주변의 비교적 넓은 영역의 메모리가 올바른지 확인할 수 있으며 항상 읽기 전용으로 표시되므로 다음 번에 코드를 포함하는 메모리 블록에 대한 쓰기를 탐지할 수 있습니다.
0:000> r edi=eip 0:000> g
레지스터 edi를 레지스터 eip의 현재 값으로 설정한 후 계속 실행하면 레지스터 esi에서 또 다른 예외가 발생하며 여기에서 프로세스를 반복합니다.
(1258.1638): Access violation - code c0000005 (second chance) Application!Function+0xa76: 3036a384 f60601 test byte ptr [esi],1 ds:0023:6c7d890d=?? 0:000> r esi=eip 0:000> g
계속 실행하면 코드 세그먼트에 데이터를 쓰려는 명령에서 쓰기 액세스 위반 예외가 발생합니다. 이는 공격자도 원하는 메모리 주소에 데이터를 쓸 수 있음을 의미합니다.
(1258.1638): Access violation - code c0000005 (second chance) Application!Function+0xbef: 3036a4fd 894710 mov dword ptr [edi+10h],eax ds:0023:3036a38e=0c46f60d
여기서 볼 수 있듯이 원래의 읽기 액세스 위반이 코드 실행 가능성이 있는 실제 보안 문제로 판명되었습니다.
이제 대상 데이터를 설정하고 명령을 건너뛰어 데이터 흐름을 추적하겠습니다. 이전과 동일한 충돌을 분석하면서 대상 데이터를 변경합니다. 첫 번째 예외가 발생한 후 레지스터 eax를 추적하기 쉬운 값으로 설정하고 레지스터 eip를 현재 명령 크기만큼 늘려 건너뛰게 합니다.
Application!Function+0xa70: 3036a37e 8b4708 mov eax,dword ptr [edi+8] ds:0023:03f93004=???????? 0:000> r eax=deadbeef 0:000> r eip=eip+3 0:000> g
계속 실행하면 다음에는 대상이 없는 예외가 발생하므로 명령을 그냥 건너뛸 수 있습니다.
(1258.1638): Access violation - code c0000005 (second chance) Application!Function+0xa76: 3036a384 f60601 test byte ptr [esi],1 ds:0023:deadbefb=?? 0:000> r eip=eip+3 0:000> g
더 실행하면 악용 문제의 가능성을 암시하는 동일한 쓰기 액세스 위반 예외에 도달합니다.
(1258.1638): Access violation - code c0000005 (second chance) Application!Function+0xbef: 3036a4fd 894710 mov dword ptr [edi+10h],eax ds:0023:03f9300c=0c46f60d
두 가지 방법의 악성 데이터 주입 시뮬레이션에서 동일한 결과가 나오는 경우가 많지만 경험을 토대로 말하자면 각 방법은 서로 다른 코드 경로를 취하며 이 중 한 가지 방법으로만 문제의 악용 가능성을 확인할 수 있는 경우가 많습니다. 지금까지 설명한 방법을 사용하면 잠재적으로 악용 가능한 읽기 액세스 위반 충돌을 매우 신속하게 찾을 수 있습니다. 그러나 이러한 방법으로 특정 충돌이 악용 문제가 아니라는 사실을 최종 검증할 수는 없습니다.
데이터를 쓸 때의 액세스 위반
데이터를 쓰는 도중 발생한 액세스 위반은 메모리를 손상시킬 위험이 있으며, 대부분의 경우 이는 코드 실행 가능성이 있는 악용 가능한 상태를 초래합니다. 이러한 쓰기 작업은 충돌이 발생한 프로그램에서 버퍼 오버플로 상태를 나타내는 경우가 상당히 많습니다. 드물기는 하지만 실제로 쓰기 액세스 위반 충돌이 악용할 수 없는 것으로 나타날 수도 있습니다. 그러나 이러한 상황이 발생하면 전체 데이터 흐름 분석을 수행하여 문제의 근본적인 원인을 파악해야 합니다. 손상으로 인해 공격자가 데이터를 덮어써서 실행 흐름에 임의로 영향을 줄 수 있게 되는 상황이 발생하지 않도록 해야 합니다.
대부분의 데이터 쓰기 액세스 위반은 악용 가능한 경우로 이어져 악성 코드의 실행을 유발할 수 있습니다. 일반적으로 코드 실행은 스택 또는 힙에서 임의의 메모리 부분을 덮어쓰는 방식으로 수행됩니다.
다음 예에서는 대상 레지스터가 스택의 상한에 도달하여 할당되지 않은 메모리 영역에 이를 때 메모리 복사 명령에서 충돌이 발생합니다. 복사 작업 크기는 주소 [ebp-8]의 변수에서 가져오며 충돌은 이 영역이 공격자의 제어하에 있음을 나타냅니다.
Application!Function+0x143: 3036a384 f3a4 rep movsb es:0023:00140000=?? ds:0023:0125432c=41414141 0:000> ub mov esi, [ebp-4] mov edi, [ebp+4] mov ecx, [ebp-8] rep movsb -> write access violation if value in ecx is big enough
쓰기 액세스 위반이 코드 실행으로 이어질 수 없는 경우는 공격자가 제어하는 대상 주소가 유효하지 않은 메모리를 가리키거나 프로그램 실행에 영향을 주지 않는 데이터를 가리키는 경우 외에는 없다고 단언할 수 있습니다. 실제로 이러한 조건을 만족하는 쓰기 액세스 위반은 소수입니다. Windows 사용자 모드 환경에서 NULL 포인터에 대한 쓰기 또는 시스템 주소 공간(보통 2GB 이상의 주소)에 대한 쓰기 작업은 악용할 수 없는 문제의 예입니다. 단, 이 경우 0x00000000 주소의 페이지는 할당 불가능하다는 가정이 전제되어야 합니다. 또한 서버 시나리오에서는 명령이 서비스 거부로 이어질 수 있으며, 이러한 상황이 관리자 이외의 사용자에 의해 트리거된 경우에는 보안 취약점으로 간주해야 합니다.
이 예는 읽기 액세스 위반과 관련하여 앞서 설명한 경우와 비슷합니다. 디스어셈블하면 레지스터 eax 값이 0으로 초기화되고 충돌이 발생한 명령에 도달할 때까지 변경되지 않았음을 볼 수 있습니다.
Application!Function+0x133: 3036a384 8d14 mov [eax], ecx ds:0023:00000000=?? 0:000> ub xor eax, eax ... (instructions not affecting register eax) cmp ebx, 2 jne label123 mov [eax], ecx -> crash, eax = 0 (NULL)
악용 불가능한 예외
커널 모드가 아닌 사용자 모드에서는 이러한 부류의 예외가 단일 단계 악용에 사용될 가능성은 낮습니다. 이러한 예외는 일반적으로 서비스 거부로 이어집니다. 이러한 예외는 다른 취약성으로 인해 예외 처리기가 변경되는 일부 경우에만 악성 코드 실행으로 이어질 수 있습니다. 서비스 거부는 서버 플랫폼에서는 우선 순위가 높은 버그이며 워크스테이션에서는 우선 순위가 중간 정도인 버그입니다.
이러한 예외에 대한 예로는 정적/전역 참조 취소(읽기 및 쓰기)가 있습니다. 이 예의 프로세스는 읽기 액세스 위반이 발생하는 0x310000 페이지에 대한 읽기 액세스 권한, 또는 쓰기 액세스 위반을 트리거하는 동일한 페이지에 대한 쓰기 액세스 권한이 없습니다.
mov ebp, 310046h mov eax, [ebp+4h] inc eax mov [ebp+8h], eax
처음 두 예외는 신중하게 분석해야 합니다. 정적/전역 값이 악성 코드가 길이에 제한 없이 쓰기 작업을 할 수 있고 복합적인 악용을 전개하기 위해 다른 구조를 덮어쓸 수 있는 메모리 주소를 가리킨다면 이는 악용 가능한 조건으로 간주해야 합니다. 제어 구조에 속하지 않는 주소에 단일 DWORD만 저장하도록 허용하는 경우에는 악용이 거의 불가능합니다. 그러나 프로그램을 실행하고 값을 확인하는 작업(런타임 분석이라고도 함)을 통해 이를 확인해야 합니다. 저장된 값이 나중에 코드에서 사용되지 않으면 다른 악용 가능한 조건으로 이어지지 않으며 무시해도 됩니다. 값이 메모리 주소로 사용되거나 메모리 복사 작업에 사용될 가능성이 있으면 악용될 가능성이 높습니다.
제어할 수 없는 주소 공간(페이지 0 및 이와 유사한 경우)의 정적/전역 주소에서 코드를 실행하는 경우는 쓰기 작업에 대해 피연산자에서 가져온 주소의 메모리 페이지를 제어할 수 있는지 여부에 따라 악용 가능성이 결정되는 또 다른 예입니다.
0040137F B8 DE C0 AD DE mov eax, 0DEADC0DEh 00401384 FF D0 call eax
정적/전역 주소에서의 코드 실행 분석은 또 다른 의미가 있습니다. 주소가 정상 프로세스 실행과는 전혀 관련이 없는 페이지(페이지 0 또는 주소가 0x80000000을 초과하는 페이지)에 속한 경우에는 악용이 불가능합니다.
0으로 나누는 작업도 예외를 트리거하지만 이 시점에서 바로 악용 가능하지는 않습니다. 이 경우 다음과 같이 이 예외로 인해 CPU가 악용으로 이어질 수 있는 코드를 실행하도록 설정되는지 확인하는 추가 분석이 필요합니다.
004013D6 33 C9 xor ecx, ecx 004013D8 8B C1 mov eax, ecx 004013DA 40 inc eax 004013DB F7 F1 div ecx
처리되지 않은 C++ 예외는 프로세스 실행을 중단시킬 수 있으며 런타임 중에는 응용 프로그램 종료를 야기할 수 있습니다. 디버거에서는 처리되지 않은 예외 이후에도 계속 진행하는 것이 가능합니다. C++ 예외는 런타임 라이브러리의 예외 발생 함수가 호출될 때 발생합니다.
00401902 mov [ebp+var_4], 1 00401909 push offset __TI1H 0040190E lea eax, [ebp+var_4] 00401911 push eax 00401912 call __CxxThrowException@8 ; _CxxThrowException(x,x)
이 조건은 예외 처리기 메커니즘을 이미 덮어쓴 경우에만 악용 가능합니다. 그렇지 않으면 처리되지 않은 C++ 예외는 악용할 수 없습니다. 이 예외에 대한 스택 추적은 그림 2에 나와 있습니다.

CommandLine: test.exe Symbol search path is: srv*c:\Symbols*\\symbols\symbols Executable search path is: ModLoad: 00400000 00405000 test.exe ModLoad: 7c900000 7c9b0000 ntdll.dll ModLoad: 7c800000 7c8f5000 C:\WINDOWS\system32\kernel32.dll ModLoad: 78130000 781cb000 C:\WINDOWS\WinSxS\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.762_x-ww_6b128700\MSVCR 80.dll ModLoad: 77c10000 77c68000 C:\WINDOWS\system32\msvcrt.dll (1494.14c4): Break instruction exception - code 80000003 (first chance) eax=00251eb4 ebx=7ffda000 ecx=00000004 edx=00000010 esi=00251f48 edi=00251eb4 eip=7c901230 esp=0012fb20 ebp=0012fc94 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 ntdll!DbgBreakPoint: 7c901230 cc int 3 0:000> g 1 (1494.14c4): C++ EH exception - code e06d7363 (first chance) (1494.14c4): C++ EH exception - code e06d7363 (!!! second chance !!!) eax=0012fee0 ebx=00000000 ecx=00000000 edx=781c3c58 esi=0012ff68 edi=004033a4 eip=7c812a5b esp=0012fedc ebp=0012ff30 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 kernel32!RaiseException+0x53: 7c812a5b 5e pop esi 0:000> kb ChildEBP RetAddr Args to Child 0012ff30 78158e69 e06d7363 00000001 00000003 kernel32!RaiseException+0x53 0012ff68 00401917 0012ff78 004022b8 00000001 MSVCR80!_CxxThrowException+0x46 0012ff7c 004011b2 00000001 00353098 003530d0 test!wmain+0x27 0012ffc0 7c816fd7 00011970 7c9118f1 7ffda000 test!__tmainCRTStartup+0x10f 0012fff0 00000000 004012fb 00000000 78746341 kernel32!BaseProcessStart+0x23
스택 오버플로 예외는 런타임 시 다른 문제를 숨기고 코드 흐름을 다른 경로로 바꾸고 /GS와 같은 컴파일러에서 생성된 보호 메커니즘을 해제하거나 현재 사용 중인 메모리를 해제하여 응용 프로그램의 일관성을 해칠 수 있습니다. 스택 공간을 거의 모두 사용한 경우 스택 오버플로 예외가 발생하여 C++ 메서드 내에서 응용 프로그램이 오류가 발생하고 중요한 코드 부분이 제대로 실행되지 않을 수 있습니다. 이러한 조건에서는 추가 분류 작업이 필요합니다.
/GS 예외
/GS(0xc0000409=STATUS_STACK_BUFFER_OVERRUN) 예외는 Windows에서 반환 주소를 보호하는 보안 쿠키가 변조되었음을 감지한 경우 발생합니다. /GS의 목적은 코드 실행으로 이어질 수 있는 버퍼 오버런을 서비스 거부 공격으로 바꾸는 것이기 때문에 이러한 충돌이 감지되는 경우 확실히 보안 버그가 있는 것입니다. 그러나 버그가 있는 메모리, 오버클럭된 마더보드, 결함이 있는 하드웨어를 비롯한 다른 문제로 인해 쿠키 유효성을 검사하는 코드가 실제 버퍼 오버런 없이 전달되는 경우가 종종 있습니다.
Windows Vista®의 경우 디버거가 있다고 가정할 때 STATUS_STACK_BUFFER_OVERRUN이 감지되면 운영 체제에서 int 3 예외를 발생시킵니다. 이전 버전의 Windows에서는 보안 쿠기의 변조 여부를 감지하려면 kernel32!UnhandledExceptionFilter에 중단점을 배치해야 했습니다. 그렇지 않으면 프로세스가 종료되고 사용자에게 이 사실이 전해지지도 않습니다.
그림 3에서 함수 foo는 스택 버퍼에 너무 많은 데이터를 복사하여 버퍼 오버런을 유발하고 이로 인해 /GS 쿠키를 덮어쓰는 상황이 발생합니다.

<the /GS cookie is being setup in the function prolog> 0:000:x86> u gs!foo gs!foo: 010011b9 8bff mov edi,edi 010011bb 55 push ebp 010011bc 8bec mov ebp,esp 010011be 83ec18 sub esp,18h <global cookie will be moved to eax register> 010011c1 a100200001 mov eax,dword ptr [gs!__security_cookie (01002000)] 010011c6 53 push ebx 010011c7 56 push esi 010011c8 57 push edi 010011c9 8b7d08 mov edi,dword ptr [ebp+8] <cookie will be placed on the stack (ebp-4)> 010011cc 8945fc mov dword ptr [ebp-4],eax <content of the source (src) and destination buffers (dst) – before the overrun> 0:000:x86> dv /V 000bfefc @ebp+0x08 src = 0x009d16cb "123456789012345678901234567890" 000bfedc @ebp-0x18 dst = char [20] "" <value of the security cookie on the stack; note that it is located right after the buffer, before saved ebp (0x000bff24) and the return address (0x0100124a)> 0:000:x86> dd 000bfedc+0n20 l3 000bfef0 0000b96f 000bff24 0100124a <value of global cookie> 0:000:x86> dd gs!__security_cookie l1 01002000 0000b96f ... code runs .. <after the overrun has happened, the cookie got overwritten (with 0x34333231) as well as the last two bytes of the return address (with (0x3039)> 0:000:x86> dd 000bfedc+0n20 l3 000bfef0 34333231 38373635 01003039 <in the function epilog before it returns the /GS cookie gets checked (i.e. the return address has not been used yet)> 0:000:x86> u gs!foo+0x54 gs!foo+0x54: 0100120d 59 pop ecx <cookie is placed in the ecx register> 0100120e 8b4dfc mov ecx,dword ptr [ebp-4] 01001211 5f pop edi 01001212 5e pop esi 01001213 5b pop ebx 01001214 e882020000 call gs!__security_check_cookie (01002000) 01001219 c9 leave 0100121a c20400 ret 4 <the below functions compares the global cookie with the one that was stored in the stack (currently in ecx register)> 0:000:x86> r eax=0000000c ebx=7efde000 ecx=34333231 edx=00000000 esi=00000000 edi=00000000 eip=0100149b esp=000bfed8 ebp=000bfef4 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 gs!__security_check_cookie: 0100149b 3b0d00200001 cmp ecx,dword ptr [gs!__security_cookie (01002000)] ds:002b:01002000=0000b96f <if a debugger was attached to the process, then an (int 3) will be issued (in Windows Vista) before the process gets terminated> 0:000:x86> STATUS_STACK_BUFFER_OVERRUN encountered (15d0.1708): Break instruction exception - code 80000003 (first chance) ntdll!DbgBreakPoint: 773e0004 cc int 3
NX 예외
NX(0xc0000005=STATUS_ACCESS_VIOLATION) 예외는 Windows에서 실행 가능하다고 표시되지 않은 코드 실행이 페이지에서 감지된 경우, 즉 페이지에 PAGE_EXECUTE, PAGE_EXECUTE_READ 또는 다른 관련 플래그가 없는 경우 발생합니다. NX는 64비트 운영 체제에 적용됩니다. 압축 해제 프로그램 및 DRM(디지털 권한 관리)과 같은 일부 응용 프로그램은 힙의 실행 코드에 의존하므로 모든 NX 예외를 보안 취약성으로 간주해서는 안 됩니다. 이 경우에도 근본적인 버그 원인을 파악하여 보안 문제가 없는지 확인해야 합니다.
이러한 유형의 예외는 오류 코드 0xc0000005를 사용하는데, 이는 NX에 국한되지 않습니다. 이 오류는 잘못 작동하는 모든 응용 프로그램(예: 할당되지 않은 메모리를 읽는 경우)에서 발생할 수 있습니다. 예외/오류 코드가 정말 NX와 관련된 것인지 확인하려면 페이지의 보호 집합을 검토해야 합니다. 페이지가 실행 가능한 것으로 표시되지 않으면 NX 예외가 있거나 그렇지 않을 경우 다른 유형의 문제가 발생한 것입니다. 예를 들어 그림 4에서는 첫 번째 예외가 발생했지만 명령은 올바른 것처럼 나타나고 유효한 데이터를 참조합니다.

(1424.a78): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. 00000000`003b0020 2801 sub byte ptr [rcx],al ds:00000000`7702e6aa=c3 0:000> r rax=0000000000000001 rbx=000000000021fd10 rcx=000000007702e6aa rdx=0000000000000000 rsi=000000000000000a rdi=0000000000000000 rip=00000000003b0020 rsp=000000000021fca0 rbp=00000000ff130000 r8=000000000021fc98 r9=00000000ff130000 r10=0000000000000000 r11=0000000000000244 r12=00000000ff131728 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl zr na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 00000000`003b0020 2801 sub byte ptr [rcx],al ds:00000000`7702e6aa=c3 <when we execute the actual instruction again, we hit a second chance access violation> 0:000> t (1424.a78): Access violation - code c0000005 (!!! second chance !!!) 00000000`003b0020 2801 sub byte ptr [rcx],al ds:00000000`7702e6aa=c3 <a closer look at the protection of the page, shows that it does not have PAGE_EXECUTE set, this explains why the code cannot be executed> 0:000> !address 00000000`003b0020 ProcessParametrs 00000000003b25c0 in range 00000000003b0000 00000000003e0000 Environment 00000000003b1370 in range 00000000003b0000 00000000003e0000 00000000003b0000 : 00000000003b0000 - 0000000000030000 Type 00020000 MEM_PRIVATE Protect 00000004 PAGE_READWRITE State 00001000 MEM_COMMIT Usage RegionUsageHeap Handle 00000000003b0000
실제 사례에 적용
여기서 볼 수 있듯이 프로그램 오류에서 보안 문제를 분석하는 작업은 복잡하고 오류 발생 가능성이 높은 작업입니다. 지금까지 읽기/쓰기 액세스 위반, 0으로 나누기, C++ 예외, /GS 예외, NX 관련 문제 등 충돌을 분석하면서 발견할 가능성이 높은 가장 일반적인 예외에 대해 설명했습니다. 명심할 사항은 특정 충돌의 악용 가능성을 정확히 진단하는 방법은 포괄적인 근본 원인 분석 외에는 없다는 점입니다.
여러분에게 응용 프로그램 분석을 위한 유용한 지침을 제공하기 위한 노력으로 시스템 옆에 두고 손쉽게 참조할 수 있는 요약 정보를 작성했습니다. 그림 5는 이번 기사에서 설명한 지침을 표 형식으로 정리한 것입니다. 예를 들어 CPU가 쓰기 액세스 권한이 없는 메모리 페이지에 데이터를 쓰려고 시도함에 따라 그 결과로 쓰기 액세스 위반이 발생하는 경우 이 문제를 해결해야 합니다.

예외 | 설명 |
해결 필요 | |
쓰기 액세스 위반 | 이 액세스 위반은 CPU가 쓰기 액세스 권한이 없는 메모리 페이지에 데이터를 쓰려고 시도하는 경우 발생합니다. |
명령 포인터의 읽기 액세스 위반(EIP 액세스 위반) | 이 액세스 위반은 CPU가 읽기 액세스 권한이 없는 메모리 페이지에서 명령을 실행하려는 경우 발생합니다. |
읽기 액세스 위반 | 다음 중 하나가 발생할 수 있습니다. •카운트 레지스터(ecx)가 큰 rep 어셈블리 명령에서(Intel 프로세서) 액세스 위반이 발생합니다. •mov 명령 결과가 바로 다음에 나오는 명령에서 호출 대상으로 사용되는 경우 mov 명령에서 액세스 위반이 발생합니다. •mov 명령 결과가 나중에 rep 명령에서 원본(esi), 대상(edi) 또는 카운트(ecx)로 사용되는 경우 mov 명령에서 액세스 위반이 발생합니다. |
분류 필요 | |
읽기 액세스 위반 | NULL(주소 0x00000000)에서 읽는 도중 액세스 위반이 발생한 경우, 또는 입력에 의해 제어되지 않는 메모리 주소에서 데이터를 읽는 도중에 액세스 위반이 발생하고 공격자가 값을 관리할 수 없는 경우 |
일반적으로 악용 불가능함 | |
0으로 나누기 | 액세스 위반이 단독 문제로 발생하고 이 액세스 위반 전에 다른 구조(예: 예외 처리기)가 손상되지 않은 경우 |
C++ 예외 | 위와 같습니다. |
마찬가지로 기사 앞부분에 나오는 그림 1은 특정 충돌의 악용 가능성 여부를 확인하는 데 도움이 되도록 그래프 구조로 프로세스에 대한 다른 시각을 제공합니다. 최상위 노드부터 시작하여 그 다음 이동할 노드를 직접 연구해 보십시오. 악용 불가능한 노드 또는 악용 가능한 노드에 도달할 때까지 이 프로세스를 계속 진행합니다.
이 지침은 프로그램 오류를 분류하는 데 유용할 것입니다. 자세한 내용은 "충돌 분석 리소스" 보충 기사의 링크를 방문하십시오. 아울러 Microsoft 제품에서 악용 가능한 충돌이 진단된 경우 secure@microsoft.com으로 해당 정보를 보내주시기 바랍니다.
Adel Abouchaev(CCIE#12037, MCSE, CISSP)는 Microsoft SWI(Secure Windows Initiative) 팀의 보안 소프트웨어 엔지니어입니다. 그는 퍼징(fuzzing) 및 런타임 분석 도구를 담당하며 Microsoft 제품 팀에 자동화된 보안 분석 및 보안 평가 방법을 제공하는 업무를 수행합니다.
Damian Hasse는 MSRC(Microsoft 보안 대응 센터)에 속해 취약성 및 보안 위협을 조사하는 보안 연구진을 이끄는 수석 보안 소프트웨어 엔지니어입니다.
Scott Lambert는 Microsoft SWI(Secure Windows Initiative) 팀의 보안 프로그램 관리자로 다양한 퍼징 도구를 비롯한 내부 보안 도구의 개선을 담당하고 있으며 현업에 종사해 온 경험을 바탕으로 주요한 취약점을 대부분 탐지하는 SWI 도구를 개발하고 있습니다.
Greg Wroblewski는 Microsoft 보안 대응 팀 소속의 보안 소프트웨어 엔지니어입니다.
댓글 없음:
댓글 쓰기