Avoiding NULL

The presence of NULL characters in payloads can be detrimental as they can prematurely terminate string-based operations, leading to unintended consequences or truncation of data.

Analysing Shellcode

Using the workflow we developed we can compile our shellcode into a .bin /raw file. We can take this raw file and analyse with a tool in my github repository here. You will need to install the keystone and capstone engines and the rich framework using pip:

pip.exe install keystone-engine
pip.exe install capstone
pip.exe install rich

We can compile our MessageBox shellcode from the previous section:

nasm -f bin -o messagebox.bin messagebox.asm

We can then use the Bad Character tool to check for null bytes:

python.exe .\bad-char-check.py --raw ..\x64\messagebox.bin 
    --badchars "0x00" --scroll 10 --platform x64

We can scroll through the shellcode until we find that there is two NULL bytes at 0x10d8:

We can see the offending shellcode in our assembly:

Shellcode containing NULLs
call_loadlibrarya_user32.dll:
    mov rax, 0x6c6c                 ; PUSH user32.dll

Removing NULL Bytes

Various techniques exist to eliminate NULL bytes from shellcode, often requiring imaginative approaches to ensure that the payload remains free of these characters.

The shellcode below presents a way in which we can acheive the same objective but avoid NULL bytes:

Alternative shellcode
call_loadlibrarya_user32.dll:
    mov rax, 0x4141adad                 ; PUSH user32.dll
    mov rcx, 0x41414141                 ; RCX = 0x41414141
    sub rax, rcx                        ; RAX = 0x6c6c

After testing the .bin file again, we see that the NULL character has been eliminated:

As we work through eliminating the null bytes it would be prudent to insert a breakpoint and test the shellcode to ensure that we acheive the same outcome:

Inserted breakpoint
call_loadlibrarya_user32.dll:
    mov rax, 0x4141adad             ; PUSH user32.dll
    mov rcx, 0x41414141             ; RCX = 0x41414141
    sub rax, rcx                    ; RAX = 0x6c6c
    push rax                        ;
    mov rax, 0x642e323372657375     ;
    push rax                        ;
    mov rcx, rsp                    ; RCX = lpLibFileName = user32.dll
    sub rsp, 0x2c                   ; Allocate stack space for the function call 
                                    ; (+ allignment)
    int3                            ; our breakpoint
    call [rbp-0x30]                 ; CALL LoadLibraryA
    add rsp, 0x2c                   ; Clean up allocated space
    add rsp, 0x10                   ; Clean up user32.dll on stack

The breakpoint has been entered at line 11. If we recompile this and debug it in Windbg Preview, we can hit the breakpoint. We can use the da @rcx command to show that the lpLibFileName is still correct:

0:000> g
(a24.1618): Break instruction exception - code 80000003 (first chance)
x64+0x10f8:
00007ff6`46c810f8 cc              int     3
0:000> da @rcx
00000038`cd6ffc3c  "user32.dll"

Don't forget to remove any int3 instructions when you want to use your shellcode for real.

Bad Characters

This technique can be used to eliminate bad characters too, but sometimes instruction mnemonics include bad characters.

Let us imagine that 0x31 is a bad character:

python.exe .\bad-char-check.py --raw ..\x64\x64.bin --badchars "0x00 0x31"
        --scroll 10 --platform x64

We can replace the xor rcx, rcx with:

Replace bad character
find_kernel32:
    ;xor rcx, rcx                   ; removed
    mov rcx, -0x01                  ;
    inc rcx                         ; RCX = 0

We can debug in Windbg Preview and we find that rcx is zero:

0:000> g
(2134.b6c): Break instruction exception - code 80000003 (first chance)
x64+0x1016:
00007ff6`f5f51016 cc              int     3
0:000> r rcx
rcx=0000000000000000

There are many different ways to replace bad characters in your shellcode and sometimes it might be more beneficial to change your shellcode rather than rely upon decoding shellcode in memory. This is particularly true if the memory in which your shellcode has been copied does not have write permissions.

Exercises

  1. Work through the MessageBox shellcode and eliminate NULL bytes.

  2. Give yourself a handful of bad characters and eliminate them from your shellcode.

Last updated