역방향 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.

이는 다음을 가정합니다.

  • /proc리눅스 는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 대신 이를 사용합니다. 150/sshd:.이를 수행하는 방법은 여러 가지가 있지만 이 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를 한 번만 호출하고 모든 데이터를 얻기 위해 계속해서 출력을 처리하도록 약간 조정하는 것을 고려할 수 있습니다.

관련 정보