This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE-885
Assignment number: 5.3
Github repo: https://github.com/abatchy17/SLAE
In the third and final part of assignment 5 in SLAE, I’ll be analyzing the linux/x86/exec
module using Ndisasm.
Ndisasm comes with Nasm assembler, unlike GDB and Libemu it doesn’t execute any code, just converts shellcode to the corresponding instructions, so we’ll be analyzing the instructions by hand.
1. Setting payload parameters
root@kali:~# msfvenom -p linux/x86/exec --payload-options
Options for payload/linux/x86/exec:
Name: Linux Execute Command
Module: payload/linux/x86/exec
Platform: Linux
Arch: x86
Needs Admin: No
Total size: 36
Rank: Normal
Provided by:
vlad902 <[email protected]>
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
CMD yes The command string to execute
Description:
Execute an arbitrary command
Payload requires a single command, so let’s use something simple like /bin/date
.
root@kali:~# msfvenom -p linux/x86/exec CMD=/bin/date -f c
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 45 bytes
Final size of c file: 213 bytes
unsigned char buf[] =
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0a\x00\x00\x00\x2f"
"\x62\x69\x6e\x2f\x64\x61\x74\x65\x00\x57\x53\x89\xe1\xcd\x80";
2. Analyzing payload with Ndisasm
root@kali:~# echo -ne "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0a\x00\x00\x00\x2f\x62\x69\x6e\x2f\x64\x61\x74\x65\x00\x57\x53\x89\xe1\xcd\x80" | ndisasm -u -
00000000 6A0B push byte +0xb
00000002 58 pop eax
00000003 99 cdq
00000004 52 push edx
00000005 66682D63 push word 0x632d
00000009 89E7 mov edi,esp
0000000B 682F736800 push dword 0x68732f
00000010 682F62696E push dword 0x6e69622f
00000015 89E3 mov ebx,esp
00000017 52 push edx
00000018 E80A000000 call dword 0x27
0000001D 2F das
0000001E 62696E bound ebp,[ecx+0x6e]
00000021 2F das
00000022 6461 fs popad
00000024 7465 jz 0x8b
00000026 005753 add [edi+0x53],dl
00000029 89E1 mov ecx,esp
0000002B CD80 int 0x80
Okay let’s go through every few commands and get an idea what’s going on.
push byte +0xb
pop eax ; EAX = 0xb
cdq ; Extend EAX -> EDX = 0x0
push edx ; Push zero byte
Nothing special here so far.
push word 0x632d ; Translates to "-c"
mov edi,esp ; Make EDI point to top of stack, which contains "-c" terminated with a null character
This part is constructing the command to be executed, -c
will execute the command.
push dword 0x68732f ; PUSH "hs/"
push dword 0x6e69622f ; PUSH "nib/"
mov ebx,esp ; Make EBX point to top of stack "/bin/sh -c"
push edx ; Push another NULL byte
Next part is interesting, code tells us to jump to instruction 27, but there isn’t one! Reason is that there is a string starting at 1D till 27.
root@kali:~# echo -ne "\x2f\x62\x69\x6e\x2f\x64\x61\x74\x65\x00"
/bin/date
Remaining instructions (after 27) are interpreted incorrectly, so let’s pipe them again to ndisasm and come up with the correct instructions.
root@kali:~# echo -ne "\x57\x53\x89\xe1\xcd\x80" | ndisasm -u -
00000000 57 push edi
00000001 53 push ebx
00000002 89E1 mov ecx,esp
00000004 CD80 int 0x80
Awesome, this makes more sense now. Let’s rewrite the assembly code in a more understandable structure.
00000000 push byte +0xb
00000002 pop eax ; EAX = 0xb
00000003 cdq ; Extend EAX -> EDX = 0x0
00000004 push edx ; Push zero byte
00000005 push word 0x632d ; Translates to "-c"
00000009 mov edi,esp ; Make EDI point to top of stack, which contains "-c" terminated with a null character
0000000B push dword 0x68732f ; PUSH "hs/"
00000010 push dword 0x6e69622f ; PUSH "nib/"
00000015 mov ebx,esp ; Make EBX point to top of stack "/bin/sh"
00000017 push edx ; Push another NULL byte
00000018 call dword 0x27 ; Address of "/bin/date" is pushed onto stack through "call"
0000001D "/bin/date" ; Not executed
00000027 push edi ; Push pointer to "-c"
00000027 push ebx ; Push pointer to "/bin/sh"
00000029 mov ecx,esp ; ECX point to top of stack
; This is the "meat" of the shellcode, ECX points to [addr1][addr2][addr3][null]
; addr1 points to /bin/sh (0B, 10 and 15)
; addr2 points to -c (05 and 09)
; addr3 points to "/bin/date" (18 and 1D)
; NULL indicates end of argv[]
0000002B int 0x80 ; EAX = 0xb, which is syscall for execve()
; EBX points to argv[0]
; ECX points to &argv[0]
TL;DR
1. EAX
is set to 0xb, which is the syscall number for execve()
2. EBX
points to /bin/sh
3. ECX
points to argv[]
which is { "/bin/sh", "-c", "/bin/date" }
That’s it!
- Abatchy