2019年12月6日 星期五

Defense hacker using overflow attack

#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/socket.h>

int s_serverSocket;

void hacker_shell() {
    printf("The system has been taken over by hackers /bin/sh...\n");
}

void safe_handler() {
    printf("Safely end.\n");
}

struct NAS_Memory_Block {
    char* buffer;
    void (*error_handler)();
};

void InitServerSocket(int port)
{
    int                 ret;
    int                    sockFd;
    int                 optEnable;
    struct sockaddr_in    serverAddr = { 0 };

    sockFd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockFd < 0)
        printf("Could not create server socket: %s\n", strerror(errno));

    optEnable = 1;
    setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &optEnable, sizeof(optEnable));

    serverAddr.sin_family       = AF_INET;
    serverAddr.sin_addr.s_addr  = htonl(INADDR_ANY);        //remote connect
    //serverAddr.sin_addr.s_addr= inet_addr("127.0.0.1");   //local connect
    serverAddr.sin_port         = htons(port);

    ret = bind(sockFd, (struct sockaddr*) &serverAddr, sizeof(serverAddr));
    if (ret < 0)
        printf("Could not bind serverAddr socket: %s\n", strerror(errno));

    ret = listen(sockFd, SOMAXCONN);
    if (ret < 0)
        printf("Could not listen: %s\n", strerror(errno));

    s_serverSocket = sockFd;
}

//After attacker run into AcceptData heap_block->error_handler can go to hacker_shell
void AcceptData() {
    uint16_t            packet_len;
    uint16_t            alloc_size;
    int                 maxFd;
    int                 acceptFd;
    int                    len;
    fd_set                 fdSvSet;
    fd_set                 fdRdSet;
    struct sockaddr_in     clientAddr;
    struct timeval         timeoutMonitor;
    socklen_t             addrLen;

    FD_ZERO(&fdSvSet);
    FD_SET(s_serverSocket, &fdSvSet);

    fdRdSet = fdSvSet;
    //need do every time, after select will auto reset
    timeoutMonitor.tv_sec   = 60;
    timeoutMonitor.tv_usec  = 0;

    maxFd = s_serverSocket;
    //use select monitor all sockets of list
    if (select(maxFd+1, &fdRdSet, NULL, NULL, &timeoutMonitor) == -1)
    {
        printf("Server select failure \n");
    }

    addrLen = sizeof(clientAddr);
    memset(&clientAddr, 0, sizeof(clientAddr));
    acceptFd = accept(s_serverSocket, (struct sockaddr*)&clientAddr, &addrLen);
    if (acceptFd < 0)
        return;

    printf("DEBUG: hacker_shell = %p\n", &hacker_shell);
    printf("DEBUG: safe_handler = %p\n", &safe_handler);

    struct NAS_Memory_Block* heap_block = (struct NAS_Memory_Block*)malloc(sizeof(struct NAS_Memory_Block));

    recv(acceptFd, &packet_len, 2, 0);
    alloc_size = packet_len + 2;
    if( alloc_size < 8192 )
    {
        printf("Integer overflow is triggered when alloc_size becomes 1\n");
        alloc_size = packet_len + 2;
        heap_block->buffer = (char*)malloc(alloc_size);
    }
    heap_block->error_handler = safe_handler;

    printf("DEBUG: heap_block->error_handler = %p\n", heap_block->error_handler);
    recv(acceptFd, heap_block->buffer, packet_len, 0);
    printf("DEBUG: heap_block->error_handler = %p\n", heap_block->error_handler);

    heap_block->error_handler();
}

/* Due to cybersecurity concerns, the attack code is not publicly available */
int ClientSend(char* szip, int port)
{
    ...
    return true;
}

int main(int argc, char** argv)
{
    uint16_t a = 5;
    uint16_t b = 10;

    // Implicit conversion show you
    if ((a - b) > 0) {
        printf("The answer is greater than 0!!\n");
    } else {
        printf("Answer is less than or equal to 0 !!\n");
    }

#if 1
    InitServerSocket(9006);
    AcceptData();
#else
    ClientSend("192.168.1.100", 9006);
#endif

    return 0;
}

********* Defense for hacker attack *********
#define MAX_ALLOWED_PACKET 8190 // 8192 - 2, keeping the total size strictly within safe bounds
void AcceptData() {
    //int64_t overflow is undefined behavior
    uint64_t            packet_len = 0;
    uint64_t            alloc_size = 0;
    int                 maxFd;
    int                 acceptFd;
    fd_set              fdSvSet;
    fd_set              fdRdSet;
    struct sockaddr_in  clientAddr;
    struct timeval      timeoutMonitor;
    socklen_t           addrLen;

    // Initialize socket sets for select() multiplexing
    FD_ZERO(&fdSvSet);
    FD_SET(s_serverSocket, &fdSvSet);

    fdRdSet = fdSvSet;
    
    // Set explicit monitoring timeout to prevent connection hanging (DoS mitigation)
    timeoutMonitor.tv_sec   = 60;
    timeoutMonitor.tv_usec  = 0;

    maxFd = s_serverSocket;
    if (select(maxFd+1, &fdRdSet, NULL, NULL, &timeoutMonitor) == -1)
    {
        printf("Server select failure \n");
    }

    addrLen = sizeof(clientAddr);
    memset(&clientAddr, 0, sizeof(clientAddr));
    
    // Accept incoming client connection
    acceptFd = accept(s_serverSocket, (struct sockaddr*)&clientAddr, &addrLen);
    if (acceptFd < 0) {
        return;
    }

    printf("DEBUG: hacker_shell = %p\n", &hacker_shell);
    printf("DEBUG: safe_handler = %p\n", &safe_handler);

    // SECURE STEP 1: Allocate the core structure block and immediately validate against NULL
    struct NAS_Memory_Block* heap_block = (struct NAS_Memory_Block*)malloc(sizeof(struct NAS_Memory_Block));
    if (heap_block == NULL) {
        close(acceptFd);
        return;
    }

    // DEFENSIVE: Initialize pointer to NULL to prevent dangling pointer bugs
    heap_block->buffer = NULL;

    // SECURE STEP 2: Read exactly 2 bytes for the length header and strictly verify the return value
    // This prevents partial read exploits or blocked streams from introducing garbage data
    if (recv(acceptFd, &packet_len, 2, 0) != 2) {
        printf("DEBUG: Failed to read full packet length header\n");
        free(heap_block);
        close(acceptFd);
        return;
    }

    // SECURE STEP 3: Input Validation and Business Boundary Definition
    // Define a strict maximum threshold to prevent resource exhaustion and integer overflow
    // DEFENSIVE BITWISE RULE: Always validate inputs using subtraction BEFORE executing addition.
    // If packet_len is larger than MAX_ALLOWED_PACKET, reject immediately.
    if (packet_len > MAX_ALLOWED_PACKET) {
        free(heap_block);
        close(acceptFd);
        return;
    }

    // SECURE STEP 4: Prevent Integer Overflow
    // Since packet_len is proven to be <= 8190, this addition is mathematically guaranteed never to overflow
    alloc_size = (size_t)packet_len + 2;

    // Allocate heap memory based on the verified safe size and perform NULL verification
    heap_block->buffer = (char*)malloc(alloc_size);
    if (heap_block->buffer == NULL) {
        free(heap_block);
        close(acceptFd);
        return;
    }

    // Assign the trusted safe handler callback function pointer
    heap_block->error_handler = safe_handler;

    // SECURE STEP 5: Safe Payload Ingestion
    // Since packet_len is strictly smaller than alloc_size, a heap buffer overflow is physically impossible
    int received = recv(acceptFd, heap_block->buffer, packet_len, 0);
    if (received < 0) {
        printf("DEBUG: Error occurred while receiving packet payload\n");
    }

    // Verify if the function pointer address remains pristine and uncorrupted after packet read
    printf("DEBUG: After recv -> heap_block->error_handler = %p\n", heap_block->error_handler);

    // Execute the verified function pointer securely
    heap_block->error_handler();

    // SECURE STEP 6: Mandatory Resource Cleanup
    // Deallocate memory and close descriptors to prevent memory leaks and file descriptor exhaustion (DoS)
    free(heap_block->buffer);
    free(heap_block);

    close(acceptFd);
}

********* Another example explain *********
Binary Exploitation Analysis: From Memory Layout to Vulnerability Mechanics
This document compiles the architectural memory layouts, assembly code paths,
and logical vulnerabilities associated with low-level stack buffer overflows and
integer overflow conditions.

### 【 Text Segment (Executable Code Space) 】
#text
Memory Address        Instructions
0x1000                <filter_and_spawn>: Perform security validation...
...
0x2005                ret   <-- [Current RIP points here]
...
0x3000                <execv>: Execute external binary execution... (Hacker's target!)
...
0x4000                <main>: Normal execution return target (Abandoned)

Memory Address        Data Content inside Slot
0x9010                [ 0x41414141] (Overwhelmed by hacker's junk payload string "AAAA")
0x9008                [ 0x3000    ] <-- [Current RSP points here] (Critical! Return address overwritten to 0x3000)
0x9000                [ 0x41414141] (Buffer capacity filled with injected junk "AAAA")

High Memory Address
+-----------------------------------------------------+
| 0x9008 | [ 0x4000 ] -> Normal return address (Intended return path to main) |
+-----------------------------------------------------+
| 0x9000 | buffer[60-63]                              | \
+--------+--------------------------------------------+  \
| 0x8FFC | buffer[56-59]                              |   \
| ...    | ...                                        |    > Total 64-byte continuous buffer allocation
| 0x8FC4 | buffer[4-7]                                |   /  (RSP currently references 0x8FBC)
+--------+--------------------------------------------+  /
| 0x8FBC | buffer[0-3]  <-- Buffer starting base boundary | /
+-----------------------------------------------------+

High Memory Address
+-----------------------------------------------------+
| 0x9008 | [ 0x3000 ] <-- Overwritten with malicious execv address via network stream!
+-----------------------------------------------------+
| 0x9000 | [ AAAA ]                                   |
| ...    | ... Completely flooded with 'A' padding ... |
| 0x8FBC | [ AAAA ] <-- Buffer starting base boundary  |
+-----------------------------------------------------+

; --- Vulnerable Assembly Sequence ---
sub  rsp, 64          ; Allocate 64 bytes on Stack (RSP = 0x8FBC, target ret slot resides at 0x9008)
mov  rdi, [socket]    ; Argument 1: Network socket file descriptor
lea  rsi, [rsp]       ; Argument 2: Memory address of buffer destination (0x8FBC)
mov  rdx, 1024        ; Argument 3: Ingestion size limit (Fatal Error! Grants write capacity up to 0x93BC)
call recv             ; Fire recv routine; memory boundaries broken beyond 0x9008

process_nas_packet:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp           ; Allocate 16 bytes for local structural variables on the Stack

    ; ----------------------------------------------------
    ; Step 1: Read size header via recv(socket, &packet_len, 2, 0);
    ; ----------------------------------------------------
    ; RDI register is pre-populated with active socket descriptor by calling context
    leaq    -2(%rbp), %rsi      ; Argument 2: Memory pointer reference to &packet_len
    movq    $2, %rdx            ; Argument 3: Extract exactly 2 bytes from stream
    movl    $0, %rcx            ; Argument 4: network options flag = 0
    call    recv                ; Invoke standard library execution for recv

    ; ----------------------------------------------------
    ; Step 2: Validate boundary - Bypassed via type confusion / signedness optimization flaws
    ; ----------------------------------------------------
    movzwl  -2(%rbp), %eax      ; EAX = packet_len (Hacker transmits malicious value 0xFFFF)
    ; If compiler optimization rules process packet_len as an explicitly signed int16_t element:
    ; 0xFFFF maps identically to a numerical representation of "-1"
    cmpw    $100, %ax           ; Execute comparison: -1 against 100
    jg      .L_RETURN           ; jg (Jump if Greater) evaluated conditionally: -1 is not greater than 100!
                                ; Security validation boundary successfully bypassed! (Pass)

    ; ----------------------------------------------------
    ; Step 3: Compute allocation parameter: alloc_size = packet_len + 2; (Core Exploit Vulnerability)
    ; ----------------------------------------------------
    movzwl  -2(%rbp), %eax      ; AX = 0xFFFF (65535)
    addw    $2, %ax             ; Compute addition: AX = AX + 2
                                ; Mathematical limit: 0xFFFF + 2 = 0x10001
                                ; Because AX register constraints restrict width to 16 bits, the carry bit '1' is dropped
                                ; The AX register truncates to 0x0001 (1) !!
    movw    %ax, -4(%rbp)       ; System writes out alloc_size = 1

    ; ----------------------------------------------------
    ; Step 4: Instantiation of tracking buffer: char *buffer = malloc(alloc_size);
    ; ----------------------------------------------------
    movzwl  -4(%rbp), %eax      ; EAX = 1 (Current alloc_size)
    movq    %rax, %rdi          ; Argument 1: rdi = 1 (Request malloc engine to instantiate 1 byte allocation)
    call    malloc
    movq    %rax, -16(%rbp)     ; buffer = Destination pointer returned by allocator (1-byte tracking footprint)

    ; ----------------------------------------------------
    ; Step 5: Payload ingestion: recv(socket, buffer, packet_len, 0); (Physical Memory Destruction)
    ; ----------------------------------------------------
    movq    -16(%rbp), %rsi     ; Argument 2: Destination target pointer (points to the single byte space)
    movzwl  -2(%rbp), %edx      ; Argument 3: Target stream processing packet_len = 0xFFFF (65535)
    movl    $0, %rcx            ; Argument 4: network options flag = 0
    call    recv                ; Fire payload ingestion!

    ; The recv routine obeys the OS kernel command, fetching 65535 bytes from the active network stream.
    ; It continuously pushes data into the memory pool starting at rsi.
    ; Because only 1 byte was allocated, the subsequent 65534 bytes overwrite sequential structures
    ; (Heap headers or Stack return slots), completely destroying tracking metadata and hijacking execution targets to execv!

.L_RETURN:
    leave
    ret

沒有留言:

張貼留言