Как найти приложения/порты, которые выполняют привязку (), но не прослушивают ()?

Вы немного запутались в том, какие IP-адреса использовать. При переадресации портов есть две пары IP/портов:

<from IP>:<from PORT>:<to IP>:<to PORT>

Если вы устанавливаете только один IP-адрес, это читается как

<from PORT>:<to IP>:<to PORT>

Это сбивает с толку, потому что «от ip от порта» к клиенту ssh и «от ip к порту» исходят от сервера.

Попробуйте это с сервера А:

ssh -N -f -L 2222:localhost:22 admin@10.10.10.1

Затем вы можете

ssh  43.24.24.29:2222

Это работает, потому что SSH-соединение осуществляется от сервера A к серверу B. Таким образом, в приведенной выше команде «localhost» относится к серверу B.

3
20.04.2021, 14:31
1 ответ

Пока не будет доступно что-то более подходящее, вот ответ, который пытается абсолютно не -промышленным способом найти процессы, которые использовали bind(2)на сокете TCP, но затем не сделали ни listen(2), ни connect(2), а также может отображать привязанный TCP-адрес.

Требуется getfattr, найденный в пакете с именем attrв большинстве дистрибутивов, плюс ядро ​​>= 3.7 для фильтрации сокетов TCP, отличных от -, и минимальная установка gdb(, например, в Debian:gdb-minimal). Не требует среды разработки. Следует запускать от имени пользователя root (, иначе будет найдена информация только об одних и тех же пользователях, но это даже не будет работать в контейнерах ). См. Предостережения в конце.


Компоненты:

  • Первый сценарий оболочки имитирует часть того, что должно lsofделать, но только для этого конкретного случая. Ищет все процессы для сокета FD. Для сокетов со свойством TCPили TCPv6(, которое доступно как мета-атрибут -файла system.sockprotonameс использованием getfattr, как найдено с lsof, который будет использоватьgetxattr(2)таким образом, чтобы по крайней мере отображать это сокет TCP ), проверьте, можно ли найти(sockfs псевдо -индекс )файловой системы в соответствующем сетевом пространстве имен tcpилиtcp6proc , и если нет отображает pid, fd и inode как кандидат 3 -uple.Только этот сценарий найдет и перечислит «дефектные» процессы.

    findbadtcpprocs.sh:

    #!/bin/sh
    
    find /proc -mindepth 1 -maxdepth 1 -name '[1-9]*' |
        xargs -I{} find {}/fd -follow -type s 2>/dev/null |
            while read procfd; do
                type=$(getfattr --absolute --only-values -L -n system.sockprotoname $procfd | tr '\0' '\n')
                if [ "$type" = "TCP" -o "$type" = "TCPv6" ]; then
                    inode=$(stat -L -c %i $procfd)
                    pid=$(echo $procfd | cut -d/ -f3)
                    if awk '$10 == inode { exit 1 }' inode=$inode /proc/$pid/net/tcp /proc/$pid/net/tcp6; then
                        fd=$(echo $procfd | cut -d/ -f5)
                        echo $pid $fd $inode
                    fi
                fi
            done 
    

    Этот скрипт можно использовать отдельно, чтобы просто найти процессы-кандидаты без дополнительной информации.

  • Затем следует gdbсценарий, которому необходимо предоставить правильную fd информацию. Он подключается к процессу-кандидату и (сначала выделяет немного памяти, чтобы )запустить getsockname(2), отобразить связанный сокет (и освободить выделенные ресурсы )и освободить процесс.

    getsockname.gdb:

    set $malloc=(void *(*)(long long)) malloc
    set $ntohs=(unsigned short(*)(unsigned short)) ntohs
    p $malloc(64)
    p $malloc(4)
    set *(long *)$2=64
    p (int) getsockname($fd,$1,$2)
    set logging file /dev/stdout
    set logging on
    if *((short *) $1) == 2
        set $ip=(unsigned char *) ($1+4)
        printf "%hu.%hu.%hu.%hu",$ip[0],$ip[1],$ip[2],$ip[3]
    else
        if *((short *) $1) == 10
            set $ip6=(unsigned short *) ($1+8)
            printf "[%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx]",$ntohs($ip6[0]),$ntohs($ip6[1]),$ntohs($ip6[2]),$ntohs($ip6[3]),$ntohs($ip6[4]),$ntohs($ip6[5]),$ntohs($ip6[6]),$ntohs($ip6[7])
        end
    end
    printf ":%hu\n",$ntohs(*(unsigned short *)($1+2))
    set logging off
    call (void) free($2)
    call (void) free($1)
    quit
    
  • Наконец, связующий сценарий использует оба предыдущих сценария для упрощения работы. Это позволит избежать бесполезного подключения к нескольким процессам (или потокам ), использующим один и тот же сокет.

    result.sh:

    #!/bin/sh
    
    oldinode=-1
    ./findbadtcpprocs.sh | sort -s -n -k 3 | while read pid fd inode; do
        printf '%d\t%d\t%d\t' $pid $fd $inode
        if [ $inode -ne $oldinode ]; then
            socketname=$(gdb -batch-silent -p $pid -ex 'set $fd'=$fd -x./getsockname.gdb 2>/dev/null) || socketname=FAIL
            oldinode=$inode
        fi
        printf '%s\n' "$socketname"
    done
    

    Просто запустив это, вы получите все:

    chmod a+rx findbadtcpprocs.sh result.sh
    ./result.sh
    
  • В качестве бонуса — простой воспроизводящий код на языке C, который создаст два процесса, использующих один и тот же сокет TCP, без использования listen(2)для него. Использование:gcc -o badtcpbind badtcpbind.cи./badtcpbind 5555

    badtcpbind.c:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #include <strings.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        int sockfd;
        struct sockaddr_in myaddr;
        if (argc < 2) {
            exit(2);
        }
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket");
            exit(1);
        }
        bzero(&myaddr, sizeof myaddr);
        myaddr.sin_family = AF_INET;
        myaddr.sin_addr.s_addr = INADDR_ANY;
        myaddr.sin_port = htons(atoi(argv[1]));
        if (bind(sockfd, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) {
            perror("bind");
            exit(1);
        }
    
    #if 0
         listen(sockfd,5);
    #endif
    
        fork();
        sleep(9999);
    }
    

пример:

#./badtcpbind 5555 &
[1] 330845
#./result.sh 
108762  20  303507  0.0.0.0:0
330845  3   586443  0.0.0.0:5555
330846  3   586443  0.0.0.0:5555

(Да, по какой-то неизвестной причине здесь появляется процесс libvirtdдля создания сокета TCP, который не используется и попадает в первую строку результатов ).


Предостережения:

  • Вероятно, следует использовать язык лучше оболочки, чтобы обеспечить большую читабельность и эффективность.

  • определенно даже более колоритный, чем lsof.

  • подключение к запущенному процессу, как здесь, имеет проблемы:

    • не работает со статически связанной двоичной функцией(malloc()или некоторые определения символов недоступны, тогда ).
    • поскольку информация об отладке недоступна, большинство функций имеют явную область действия, и это может не работать во всех средах без изменений (протестировано на архитектуре amd64 с ядром 5.10.x, в Debian Bullseye, Debian 10 и пользовательские пространства CentOS 7 ).
    • также не может быть, так как -работает над двоичным файлом, связанным с другой libc, чем обычная glibc.
    • является навязчивым и может привести к сбою хрупких (особенно многопоточных -приложений ). Проверки не выполняются (например:malloc(3)или getsockname(2)сбой ).
  • последний скрипт считает sockfs иноды глобальными (, а не -сетевыми -пространством имен )уникальными, что я не пытался доказать, но скрипт упрощается.

2
28.04.2021, 22:51

Теги

Похожие вопросы