논문 준비하면서 번역했던 문서.
급하게 번역하느라 대충했지만 뭐 그런데로 알아들을만 하다.....는것....
윈도우상의 힙 메모리 영역 보호우회를 위한 새로운 방법을 제시하는 논문.
뭐...이게 정말 논문에 필요했는지는 모르겠지만 비슷한거 같아서 하긴했는데;;;
ㄷㄷㄷ
Nicolas Falliere
nicolas.falliere@gmail.com
History
Windows heap-based buffer overflows can be summarized in two categories.
윈도우즈 힙 베이스기반의 버퍼오버플로우는 두가지로 요약할수있다.
The first one covers overflows for Windows 2000, Windows XP and Windows XP SP1
platforms. 하나는 윈도우즈 2000,xp,sp1의 오버플로우를 포함한다.
The heap management code for these systems, located in ntdll.dll, do not
perform any sanity check on heap chunks.
힙 메모리 관리코드는 3가지 시스템을 위하여 ntdll.dll 상의 많은 양의 힘상에서 정상적인 검사를 수행하지 않는다.
When an overflow occurs, the next adjacent chunk can be overwritten, and if good values are forged, a subsequent heap operation (alloc, free……) can result in an arbitrary four-byte overwrite in memory.
오버플로우가 발생했을때, 다음 근접한 힙의 메모리에 덮어쓸수가 있고, 만약 좋은 코드라면 다음 발생하는 힙의 동작은 임의의 4바이트를 메모리에 덮어쓰는 결과를 초래할수 있다.
New techniques emerged recently, but the principle remains the same: overwriting a
specific portion of memory with specific values, to gain control and execute a payload
later on.
최근 새로운 기술이 나왔다. 그러나 근본적인 원리는 아직도 유지되고있다. : 메모리에 특수한 코드를 값을 덮어쓰고, 원하는 문자열을 실행시키고 제어할수있다.
The second category includes Windows XP SP2 and Windows 2003 operating
systems.
두 번째는 윈도우 xp(sp2)와 2003기반이다.
Microsoft modified heap structures and heap manipulation functions; two checks on the chunks were added.
MS는 힙 구조와 조작 함수를 만들었다. : 더해진 블록위에 두가지를 검사한다.
The first check is to verify the integrity of a security cookie in the chunk header, to ensure no overflow has occurred when this same chunk is allocated; the second check, extremely efficient, verifies the forward and backward link pointers of a free chunk being unlinked, for any reason (allocation, coalescence).
첫 번째 검사는 블록해더안의 보안 쿠키의 무결성을 검증하고 같은 블록 안에서 배정할 때 오버플로우가 발생하지 않도록 한다.
The same check is performed for virtually allocated blocks.
같은 검사는 실질적으로 허용된 블록을 위하여 수행된다.
Others protections have been introduced as well, mainly PEB randomization, and
exception pointers encoding.
다른 보호법들은 잘 알려져 있다. 보통 PEB 난수화나 실행 포인터 암호화이다.
These protections are there to minimize the amount of fixed and well-known function pointers, used globally by the process.
이런 보호들은 프로세서에 의해 전역으로 사용되고 잘 알려진 함수포인터와 고쳐지기 까지의 최소한의 보호이다.
These locations were priviledged targets to exploit a heap overflow the old way.
이 위치들은 특권화된 타겟이 오래된 방법으로 힙 오버플로우를 이용하기위해 있다.
The first public paper detailing a method to bypass the new heap protections was
published at the beginning of year 2005 by Alexander Anisimov.
첫 번째 공개 페이퍼의 세부 사항은 2005년 초에 Alexander Anisimov가 새로운 힙 보호 우회에 대해 발표하였다.
It consists of exploiting the inexistent checks on the lookaside list.
이것은 lookaside list에 존재하지 않는 검사를 이용하는 것으로 이루어져있다.
The first dword of a lookaside entry is the start of a simply-linked list of chunks, marked as busy, but ready for allocations.
첫 번째 lookaside entry의 dword는 간단한 chunk의 연결 리스트로 시작한고, 사용중으로 표시되지만 허용을 위해 준비한다.
When an allocation occurs, the first block of a matching lookaside list may be returned: It is simply removed from the list by replacing the forward link pointer (FLink) in the lookaside entry by the FLink pointer of the newly allocated block.
할당될때, 첫 번째 매칭되는 블록 lookaside list는 반환될수있다.: 이것은 링크 포인터를 제자리로 돌리는 과정에 의해 리스트로부터 간단히 삭제된다.
This process is explained in Figure 1.
이과정은 예이다.
This new technique is good in theory, but seems pretty hard in practice.
이 신기술은 좋은 이론이다. 하지만 연습에선 어려워 보인다.
The following heap operations must occur, by forging good input values, if we want the N-byte
overwrite to happen:
힙 작동의 흐름은 위조된 입력값에 의해 분명히 일어나게 된다. 만약 n-바이트의 오버플로우를 원한다면:
1 –- Allocation of a block of size N (<0x3F8 bytes)
n 크기의 블록을 배치한다.
2
2 –- Freeing of this block: the block gets referenced in the lookaside table
블록은 lookaside table의 레퍼런스를 얻어온다.
3 –- The overflow occurs in a previous adjacent block: we can manipulate the FLink
pointer of the previously freed block
오버플로우가 앞쪽의 인접한 블록에서 발생한다. 전에 자유로웠던 블록 포인터를 조작할수있다.
4 –- A block of size N is allocated: our fake pointer is written in the lookaside table
n사이즈의 블록을 배치한다. 가짜 포인터는 lookaside table에 쓸수있다.
5 –- A second block of size N is allocated: our fake pointer is returned
n사이즈의 두 번째 블록은 허용된다. 가짜 포인터는 리턴당한다.
6 –- A copy operation from a controlled input to this buffer occurs: these bytes are
written to our chosen location
연산을 제어된 입력으로부터 버퍼 발생을 위해 복사한다. : 이 바이트들은 선택된 위치에 쓸수있다.
As you can see, these conditions can be hard to produce in practice, especially in
complex programs. The heap must also have an active and unlocked lookaside table
for the operation to succeed.
볼 수 있는 동안 , 특히 복잡한 문제에서 이 상태들은 실행 도중 어려움을 발생시킬수 있다. 힙 또한 연산결과를 얻기위해 lookaside table을 활성화 하고 열수있다.
A new way to bypass heap protections
힙 보호 우회를 위한 새로운 방법
The method I introduce here does not use the overwriting of heap-management
structures to produce a four-byte overwrite.
내가 소개하는 이 방법은 힙 관리의 덮어쓰기를 사용하지 않는다. 4바이트를 덮어쓰는 것을 만들어 내기위한 구조이다.
Lookaside entry Chunk B
Chunk A
A (FLink)
B (FLink)
(FLink)
Lookaside entry
Chunk B
B (FLink)
(FLink)
Fig. 1: Allocation of a block A from the lookaside table
Before allocation:
After allocation:
3
The process default heap, as well as others system-created heaps, is used by many
APIs to store information concerning the process and its environment.
많은 api들은 관련있는 정보와 프로세스 그리고 환경을 저장하기 위해 프로세스 기본 힙과 다른 system-created힙을 사용한다.
When a DLL is loaded, its main function is executed (DllMain, or similar) and often, data can get
stored on the process heap.
DLL이 적재될때, 가끔 매인 함수가 실행되고, 데이터는 프로세스 힙상에 저장할수있다.
What if these pieces of data are overwritten?
만약 이 자료의 조각이 덮어써진다면 어떻게 하겠냐?
Let’'s take a basic program, such as Windows notepad.
윈도우즈 노트페드와 같은 기본 프로그램을 실행할수있다.
We can notice that even this program needs a lot of dynamic libraries to run.
이 프로그램 조차 많은 동적 라이브러리가 실행되기 위해 필요하다는 것을 알아차릴수있다.
If we examine the default heap, before the main thread starts to execute, we’'ll notice that a fair amount of heap chunks have been allocated by these DLLs.
만약 메인 쓰레드가 실행되기전 기본 힙을 검사하면 ,DLL에 의해 힙 chunks 가 정당하게 할당된것을 알수있다.
Many of these chunks have a length of 40 bytes (including 8 bytes for the header) and have the structure described in Figure 2:
chunks중 많은 부분이 40바이트의 길이를 가지고있고 다음과같이 구조를 묘사할수있다.
A: Address of the next “"40-byte long structure”"
B: Address of the previous “"40-byte long structure”"
It happens that the structure pointed by X is in fact a critical section. When a critical
section is initialized, an associated “"40-byte long structure”" –- we will call it a linking
structure, is also created to keep track of the critical section.
이것은 구조포인터가 실질적으로 치명적인 부분에 의해 일어난다. critical section이 초기화 되었을때, 40바이트의 긴 구조는 조합된다. 우린 이것을 링킹구조라고 부르겠다. 또한 critical section 트렉의 유지를 위해 만들어진다.
A few of these structures are located in the data section of ntdll.dll; when all of them are used, the linking structures are created in the default heap.
이 구조중에 몇몇은 ntdll.dll의 데이터 섹션에 위치한다. 모든 것이 사용될때 링킹구조는 기본 힙 안에 만들어진다.
Figure 3 shows the relation between linking structures and critical sections.
그림 3은 링킹 구조와 중요한 부분사이의 관계를 보여준다.
This doubly-linked list reminds us the way free chunks are handled by heap management routines.
이 doubly-linked 리스트는 우리에게 힙 관리 루틴에 의해 free chunk가 다뤄지는것을 생각나게 한다.
During the destruction of a critical section, the associated linking structure will be removed from its list.
크리티컬 섹션이 파괴 되는 동안 연합된 연결 구조는 이 리스트로부터 제거될것이다.
If we replace A and B, we should then be able to overwrite a 4-byte portion of memory:
만약 A가 B로 대채되면, 메모리의 4바이트 영역에 덮어쓰는게 가능해진다.
From RtlDeleteCriticalSection (ntdll.dll version 5.1.2600.2180):
……
mov [eax], ecx ; eax=B
mov [ecx+4], eax ; ecx=A
……
0 X
0 0
? ?
Chunk header
Fig.2: A 40-byte long heap chunk, found in the process default heap
A B
4
The technique works because:
- No sanity checks are performed on these particular backward and forward
pointers.
어떤 온전한 check도 이 부분의 앞과 뒤에서 실행되어진다.
- The critical sections are destroyed during process termination; this ensures
the overwriting will occur.
이 critical section은 프로세스가 종료되는동안 파괴되어진다. 덮어쓰기가 발생할 책임.
- The linking structures can easily be found in the default heap; if we control the
size of the chunk in which we overflow, we can adjust it in such a way that a
linking structure lies a few bytes after.
이 연결 구조는 이 기본 힙에서 쉽게 찾을수 있다. 만약 우리가 덮어쓰는 chunk의 사이즈를 제어할수 있다. 우리는 연결구조가 소량의 바이트가 잘못된 그런 방법으로 이것들을 조정할수있다.
Of course, a major drawback is the limitation to this process heap.
물론, 이 프로세스 힙에게는 큰 결점은 제한적이다.
You will find a proof-of-concept code demonstrating the technique in Annex 1.
annex 1에서 그 기술을 보여준ㄴ proof-of-concept 코드를 찾을 것이다.
Conclusion
결론
The technique was used to successfully exploit an unpatched heap-overflow located
in a standard Windows utility of Windows XP SP2.
이 기술은 표준 윈도우의 윈도우즈 xp sp2유틸에서 unpatched된 입 오버플로우위치에 성공적으로 이용되었다.
However, several problems remain partially solved:
그러나 몇몇의 문제는 부분적인 해결로써 남아 있다.
Though we have the possibility to overwrite at least 4 bytes of memory, we have to choose good values for the backward and forward pointers.
메모리의 4바이트에 덮어쓰기를 위해 가능한것을 가진다. 그리고 최고의 값을 포인터 앞과 뒤쪽에 선택해야만한다.
The classic use of pointers to exception handlers is compromised, as well as global function pointers located in the PEB.
기본적인 포인터의 사용은 예외 해들러를 제외한다. 그리고 전역 함수로써 PEB에 위치한다.
Thus, exploiting heap overflows on the newest Windows systems is possible, but the
issues now are to increase their portability and their reliability.
그래서, 새 윈도우즈 시스템은 힙 오버플로우를 개선하는것이 가능하다. 그러나 지금 주목해야할것은 이것들의 신뢰성과 휴대성을 증가시키는것에 있다.
0 X
Fig.3: Critical sections and linking structures
A B
Critical Section
0 X A B
0 X A B
Critical Section
Critical Section
5
Annex 1: Proof-of-Concept code
//---------------------------------------------------------------------
// This code demonstrates how an overflow in critical section related
// structures stored in heap chunks can be used to produce an
// arbitrary memory overwrite
// (c) 2005 Nicolas Falliere
// nicolas.falliere@gmail.com
//---------------------------------------------------------------------
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
VOID GetChunkList(DWORD *pChunks, INT *nbChunks)
{
DWORD pid;
HANDLE snapshot;
HEAPLIST32 list;
HEAPENTRY32 entry;
BOOLEAN bNext;
INT cnt = 0;
pid = GetCurrentProcessId();
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, pid);
if(snapshot == INVALID_HANDLE_VALUE)
{
printf("[Error] Cannot take a heap snapshot\n");
}
else
{
ZeroMemory(&list, sizeof(list));
list.dwSize = sizeof(HEAPLIST32);
bNext = Heap32ListFirst(snapshot, &list);
while(bNext)
{
ZeroMemory(&entry, sizeof(entry));
entry.dwSize = sizeof(HEAPENTRY32);
bNext = Heap32First(&entry, list.th32ProcessID,
list.th32HeapID);
while(bNext)
{
pChunks[cnt] = entry.dwAddress;
cnt++;
ZeroMemory(&entry, sizeof(entry));
entry.dwSize = sizeof(HEAPENTRY32);
bNext = Heap32Next(&entry);
}
ZeroMemory(&list, sizeof(list));
list.dwSize = sizeof(HEAPLIST32);
bNext = Heap32ListNext(snapshot, &list);
}
CloseHandle(snapshot);
6
*nbChunks = cnt;
}
}
int main(void)
{
HANDLE hHeap;
DWORD pChunks[500];
INT nbChunks;
INT i;
HMODULE hLib;
DWORD *p;
hHeap = GetProcessHeap();
printf("Default heap: %X\n", hHeap);
hLib = LoadLibrary("oleaut32.dll");
printf("LoadLibrary : oleaut32.dll\n");
GetChunkList(pChunks, &nbChunks);
for(i = 0; i < nbChunks; i++)
{
// Chunk size is 40 bytes
if(*(WORD *)(pChunks[i] - 8) == 5)
{
p = (DWORD *)(pChunks[i]);
// Check if FLink and BLink are there
if(p[2] && p[3])
{
printf("Structure found at address: %8X\n", p);
printf("Before modification : A=%8X B=%8X\n", p[2], p[3]);
memcpy(p + 2, "AAAABBBB", 8);
printf("After modification : A=%8X B=%8X\n", p[2], p[3]);
break;
}
}
}
printf("Press Enter to terminate the program and trigger the access
violation\n");
getchar();
return 0;
}
=================================================================================================