リバースSSHトンネルからソースIPを取得する

リバースSSHトンネルからソースIPを取得する

クライアントを使用してSSHを介してリバーストンネルを作成し、-Rnetstatを実行すると、サーバーに次の出力が表示されます。

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

ポートでリスニングソケットを作成したクライアントのIPが何であるかを知りたいです39963

バインディングのIP:PORTだけでなく、トンネルを生成したクライアントのIPも取得するには、どのコマンドを実行できますか?

答え1

コマンドを発行することはできませんが、クライアントを見つけるためのいくつかのコマンドがあります。問題は、OpenSSHがプロセスを生成する方法にあります。リスニングソケットを所有するプロセスは、39963SSH接続を所有するプロセスの子です。したがって、異なる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

ここの親21921はです21919。それでは、クライアントを見つけるために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

これは次のことを前提としています。

  • /procLinuxはenviron
  • OpenSSHのプロセスチェーンは次のとおりです。

    デーモン sshd → ブランチ接続 sshd → privsep sshd → ユーザーシェル

たとえば(からpstree -lp):

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

これプライバシー Sepp sshdnetstatプロセスは、シェル(この場合は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と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スクリプトに渡す「これについてより多くの洞察が必要な場合は、列7に一致させるテキスト「$PARENT_PID/sshd:」を使用してppid変数を宣言します。1150 / sshd:が一致するので、grepの代わりにそれを使用します。 。これを行う方法はいくつかありますが、このawkコマンドは私が考えることができる最も簡単で読みやすいです。。 $ PARENT_PIDをgrepingすると、しばしば他の接続の任意のポートと一致し、予期しない結果が発生するため、少し難しい後にこれが発生しました。

私はこれがより簡潔な方法で書くことができることを知っていますが、理解するときは読みやすく、説明が不要なスクリプトを維持するのが好きです。

受信ポート列挙子

マイコンピュータからすべてのリスニングリバーストンネルを一覧表示し、そのクライアントの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を一度だけ呼び出して、すべてのデータを取得するために出力を処理するように少し調整することを検討してください。

関連情報