从反向 SSH 隧道获取源 IP

从反向 SSH 隧道获取源 IP

如果我使用客户端通过 SSH 创建反向隧道,-R如果运行 netstat,我将在服务器上得到以下输出:

default@debian:~$ sudo netstat -pln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      801/sshd
tcp        0      0 127.0.0.1:39963         0.0.0.0:*               LISTEN      1074/sshd: anonymou
tcp6       0      0 :::22                   :::*                    LISTEN      801/sshd
udp        0      0 0.0.0.0:68              0.0.0.0:*                           451/dhclient

我想知道在 port 上创建侦听套接字的客户端的 IP 是什么39963

我可以运行什么命令,以便我不仅可以获得绑定的 IP:PORT,还可以获得创建隧道的客户端的 IP?

答案1

我不能给你一个命令,但有一些命令可以让你找到客户端。问题在于 OpenSSH 创建进程的方式。拥有侦听套接字的进程39963是拥有 SSH 连接的进程的子进程。所以他们会有不同的PID。在您的示例中,您需要找到 的父进程1074。以下适用于 Ubuntu 上的 OpenSSH 服务器。 我不能保证每个发行版都会以相同的方式运行。

首先查找客户端连接的 PID。用于grep过滤 的输出netstat

$ sudo netstat -ptln | grep 39963
tcp        0      0 127.0.0.1:39963         0.0.0.0:*               LISTEN      21921/sshd: philip@
tcp6       0      0 ::1:39963               :::*                    LISTEN      21921/sshd: philip@

这里的监听过程是21921。你需要它的父级:

$ ps -ef | grep 21921
philip   21921 21919  0 11:01 ?        00:00:00 sshd: philip@pts/1
philip   21924 21921  0 11:01 pts/1    00:00:00 -bash
philip   22844 20309  0 11:15 pts/0    00:00:00 grep --color=auto 21921

# Or try this:

$ ps -ef | awk '$2 == 21921 { print $3 }'
21919

这里的父级2192121919.现在我们回头看netstat来找到客户端:

$ sudo netstat -ptn | grep 21919
tcp        0      0 192.168.1.36:22          192.168.1.10:54425   ESTABLISHED 21919/sshd: philip

这表明远程IP是192.168.1.10

答案2

SSH 客户端和服务器交换消息以设置转发,但内核网络表只关心/了解其本地 IP 堆栈,因此它只看到sshd.

但是,OpenSSH 还共享环境中的一些详细信息(这也是通过消息交换传递的)。

 sshcpid=$(pgrep -P 1074)               # fetch child PID
 xargs -0 -L1 -a /proc/$sshcpid/environ # show environment strings

变量SSH_CLIENTSSH_CONNECTION都应该包含您需要的内容。

这里xargs有一个处理 NUL 分隔字符串的便捷方法environ

这假设:

  • linux,对于/proc一个environ
  • OpenSSH 的进程链如下:

    守护进程 sshd → 分叉连接 sshd → privsep sshd → 用户 shell

例如(来自pstree -lp):

 init(1)-+
         .
         |-sshd(1015) +-sshd(1072)---sshd(1074)---bash(1075)

隐私塞普 sshdprocess 是您在 中看到的netstat,您读取的环境来自其直接后代,即 shell(本例中为 bash)。

答案3

Linux 中的 lsof 命令在其输出中显示有关进程打开的文件的信息

从netstat中获取ssh的进程ID,

lsof -p $pid -a -d 3

将显示连接另一端的 IP。由于 ssh 可以跳跃,因此这可能不是最终位置。

$pid 是你的 ssh 服务的进程 ID

如果你没有,lsof你可以安装它apt-get install lsof

答案4

基于最佳答案想法,我编写了一个简单的脚本来自动化该过程。单个参数是监听端口,它返回 ip 和外向的客户端的端口。

#!/bin/bash
PORT_TUNEL=$1
PID=`fuser $PORT_TUNEL/tcp 2>/dev/null`
# xargs is just used for trimming
PARENT_PID=`ps -p ${PID:-$$} -o ppid= | xargs`
netstat -ptn |  awk -v ppid="$PARENT_PID/sshd:"  '{if($7==ppid) print $5}'

用法:

rsship 39963

示例输出:

67.214.221.43:26412

解释:

该脚本假设您已经知道要获取客户端 IP 的侦听端口。正如其他答案中所解释的,直接不可能从连接本身知道这一点。相反,我们需要获取持有监听端口的进程 ID (pid)。我们使用 fusion 来做到这一点并将其存储在变量中。由于 fusionr 在 stderr 中有一些输出会扰乱我们的代码,因此我们将其重定向到 /dev/null。现在我们有了在 $PID 变量中保存监听端口的 PID。

下一步是获取父进程,该进程维护与客户端的 SSH 连接。它使用 ps 来做到这一点。 Xargs 用于修剪输出,因为它可能带有一些空格,这些空格稍后会扰乱我们的 awk。

最后,它使用 netstat 获取所有连接的列表,并使用 awk 在第 7 列与字符串“$PARENT_PID/sshd:”匹配时显示第 5 列。请注意,-v 选项用于以正确的方式将 bash 变量传递给 awk。我不会在这个问题上扩展自己,搜索“将 bash 变量传递给 awk 脚本“如果您需要对此有更多见解。我正在声明 ppid 变量,其中包含“$PARENT_PID/sshd:”,这是我想要与第 7 列匹配的文本。我们使用它而不是 grep 因为 1150/sshd : 将匹配 150/sshd:。有多种方法可以做到这一点,但是这个 awk 命令是我能想到的最简单且最具可读性的命令。这是在经历了一些困难之后发生的,因为 grep 只是 $PARENT_PID 经常匹配不同连接上的一些随机端口,从而给出意想不到的结果。

我知道这可以用更紧凑的方式编写,但我喜欢在有意义的情况下保持脚本的可读性和不言自明

监听端口枚举器

我有一个脚本来列出我的机器上的所有侦听反向隧道并获取其客户端的 IP 和传出端口:

在我的设置中,我有一个特殊的 SSH 服务器用于此目的,因此所有连接都通过“sshtunnel”用户进行。如果您使用普通的 ssh 服务器,则必须修改脚本并想出另一种方法来获取第一个侦听端口列表。使用“grep sshd”几乎可以解决问题,尽管它也会列出您的主 shh 服务器侦听端口,尽管在我的特定情况下它可以工作,因为 ssh 服务器绑定到 *,因此 grep 127.0.0.1 也会将其过滤掉:

#!/bin/bash

IP_SCRIPT="/etc/openvpn/rsship"

PORTS=`lsof -i -P -n | grep LISTEN | grep sshtunnel | grep 127.0.0.1 | awk -F" " '{print $9}' | awk -F":" '{print $2}' | awk '{if($1==$1+0 && $1>16000 && $1<16255) print $1}' | sort -n`


while IFS= read -r line; do
    REAL_IP=$($IP_SCRIPT $line)
    echo $line$'\t'$REAL_IP
done <<< "$PORTS"

注意:$IP_SCRIPT 引用了发布的第一个脚本。

grep 127.0.0.1 用于一次性删除 ipv6 行以及绑定到所有接口(在输出中用星号 * 标记)的行(在我们的设置中,所有 IPv6 都有 IPv4 对应项)。

该脚本仅列出 16000 到 16255 之间的端口(因为这是我的特定用例)。您可以删除整个 awk 部分以使其接受任何端口。像这样:

#!/bin/bash

IP_SCRIPT="/etc/openvpn/rsship"

PORTS=`lsof -i -P -n | grep LISTEN | grep sshtunnel | grep 127.0.0.1 | awk -F" " '{print $9}' | awk -F":" '{print $2}' | sort -n`


while IFS= read -r line; do
    REAL_IP=$($IP_SCRIPT $line)
    echo $line$'\t'$REAL_IP
done <<< "$PORTS"

不过,lsof 部分可以做得更好,因为它已经制作好了,而且由于我很着急,所以我决定重用该代码,而不用考虑它。它基本上是使用 awk 来分割和打印列的特定文本(特别是端口)。正如我们通常所说:在我的机器上运行。如果您想改进它,请使用“lsof -i -P -n | grep LISTEN”并看看如何以更好的方式进一步缩小范围,因为我的线路可能不适合您的特定情况。我不是 100% 确定,但我认为仅使用 netstat 就可以达到相同的结果。

如果您非常频繁地运行此脚本并且拥有多个端口,您可以考虑对其进行一些调整,以便它仅调用一次 netstat 并一遍又一遍地处理该输出以获取所有数据。

相关内容