Как узнать PID пакетов отправки процесса (генерирующий сетевой трафик)?

Несколько недель назад у меня была проблема, где я изменил адреса DNS в большой сети приблизительно 300 узлов. После этого некоторые узлы все еще продолжали спрашивать старые серверы DNS, хотя resolv.conf был в порядке, и host/nslookup запрашивали новые серверы DNS.

Смотря tcpdump и пытающийся записать запросы с входом iptables, я подтвердил, что действительно некоторые хосты все еще отправляли запросы на старые серверы имен.

Я вынул один из хостов из производства и начал закрывать сервисы / stracing процессы в попытке узнать преступника.

В конце - это был lldpd демон, который, очевидно, кэшировал серверы имен при запуске и даже не заметил изменений в resolv.conf.

Так, мой вопрос - там более интеллектуальный способ узнать, какой PId генерирует определенный вид трафика? Я попробовал auditctl, но без большого успеха. CentOS 6 рассматриваем, но если бы существует решение для какого-либо дистрибутива Linux, я ценил бы его.

4
задан 10 February 2015 в 17:10
6 ответов

В netstat есть много опций, которые показывают комбинации прослушивающих / открытых сокетов через tcp / udp / both. Что-то вроде:

$> sudo netstat -pan
Active Internet connections (servers and established)
Proto  Recv-Q Send-Q Local Addr            Foreign Addr           State       PID/Program name
...
tcp    0      1      192.168.66.1:39219    192.168.66.139:2003    SYN_SENT    2045/logstash-forwa

... дало бы вам много выходных данных, но включало источник, место назначения, номера портов и PID процесса, владеющего этими портами.

1
ответ дан 3 December 2019 в 02:50

Что не так с auditctl?

Вы бы сделали это так

1) Определите правило аудита для аудита системных вызовов sendmsg и sendto. Эти системные вызовы используются при разрешении имен.

auditctl -a exit,always -F arch=b64 -S sendmsg -S sendto -k send

2) Теперь найдите свои записи аудита. Вы можете использовать команду grep на основе IP-адреса удаленного DNS здесь

ausearch -k send -i|grep -A2 "serv:53"

. В приведенном ниже примере вы можете увидеть, что приложение, которое отвечало за системный вызов, называется dig

ausearch -k send -i|grep -A2 "serv:53"
type=SOCKADDR msg=audit(10/31/2016 15:24:56.264:176998) : saddr=inet host:172.16.0.23 serv:53 
type=SYSCALL msg=audit(10/31/2016 15:24:56.264:176998) : arch=x86_64 syscall=sendmsg success=yes exit=29 a0=14 a1=7fa1919f9ac0 a2=0 a3=7fa1919f9780 items=0 ppid=31729 pid=32047 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts5 ses=52 comm=dig exe=/usr/bin/dig subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=send


comm=dig exe=/usr/bin/dig

И способ определить, на какой удаленный DNS-запрос отправлен, находится здесь. Так что вам просто нужно будет найти конкретный хост DNS с помощью grep.

saddr=inet host:172.16.0.23 serv:53 

Или даже лучше - посмотрите, какие хосты DNS используются (в этом примере у меня только один)

ausearch -k send -i|grep "serv:53"|awk '{print $6}'|sort|uniq -c
      3 host:172.16.0.23

А затем сузьте, какие приложения используют эти конкретные хосты.

Правка 1: На самом деле я просто выполнил несколько простых запросов на хост. Похоже, sendmsg не всегда используется. Вот что я вижу

socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, 16) = 0
gettimeofday({1477929832, 712018}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
sendto(4, "\3\326\1\0\0\1\0\0\0\0\0\0\tvkontakte\2ru\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
poll([{fd=4, events=POLLIN}], 1, 5000)  = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [62])                = 0
recvfrom(4, "\3\326\201\200\0\1\0\2\0\0\0\0\tvkontakte\2ru\0\0\1\0\1\300\f"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, [16]) = 62
close(4)                                = 0

Мой предыдущий пример был основан на приложении dig, которое использует несколько иной маршрут с точки зрения системных вызовов.
Таким образом, похоже, что в большинстве случаев это правило

auditctl -a exit,always -F arch=b64 -S connect -k connect

, за которым следует ausearch

ausearch -k connect -i|grep saddr|grep "serv:53"|awk '{print $6}'|sort|uniq -c
2
ответ дан 3 December 2019 в 02:50

+1 за ответ Дмитрия выше; это прекрасно сработало для меня:

auditctl -a exit,always -F arch=b64 -F a0=2 -S socket -k SOCKET

Чтобы увидеть результирующие записи, я grep файл журнала для этой строки "-k"

grep SOCKET /var/log/audit/audit.log

Чтобы получить только интересные поля,

grep SOCKET /var/log/audit/audit.log | \
  cut -d' ' -f 4- | \
  sed "s|^|@\n|g;s| |\n|g" | \
  grep -E "^((exe|uid|comm)=|@)" | \
  tr '\n@' ' \n' |\
  sort -u

(объяснение: cut -d ' '-f 4- -> разделить строку на поля, используя пробел (-d' ') в качестве разделителя, показать поля с четвертого до последнего (4-))

(объяснение: sed "s | ^ | @ \ n | g; s | | \ n | g " -> отредактируйте строку, добавьте '@' char-plus-newline к началу строки, замените пробелы на новые строки)

(объяснение: grep -E "^ ((uid | comm | exe) = | @)" -> поскольку каждое поле исходной строки теперь находится на отдельной строке, выберите интересующие поля: идентификатор пользователя, команда, исполняемый файл - и начало строки '@' char.)

(объяснение: tr '\ n @' '\ n' -> теперь имея только нужные поля, превратите символы новой строки обратно в пробелы , а добавленный "@" обратно в новую строку (который объединяет поля в одну строку)

(объяснение: sort -u -> сортировать строки, показывать только уникальные строки)

дает мне:

uid=0 comm="atop" exe="/usr/bin/atop" 
uid=0 comm="http" exe="/usr/lib/apt/methods/http" 
uid=0 comm="links" exe="/usr/bin/links" 
uid=0 comm="ntpdate" exe="/usr/sbin/ntpdate" 
uid=0 comm="ufdbguardd" exe="/usr/local/ufdbguard/bin/ufdbguardd" 
uid=1000 comm=536F636B657420546872656164 exe="/usr/lib/firefox/firefox" 
uid=1000 comm="clock-applet" exe="/usr/lib/mate-panel/clock-applet" 
uid=1000 comm="pool" exe="/usr/lib/mate-panel/clock-applet" 
uid=105 comm="http" exe="/usr/lib/apt/methods/http" 
uid=105 comm="https" exe="/usr/lib/apt/methods/https" 
uid=135 comm="unbound" exe="/usr/sbin/unbound" 
uid=13 comm="squid" exe="/usr/src/squid-4-master/src/squid" 
uid=1 comm="debsecan" exe="/usr/bin/python2.7" 

Команды, содержащие пробелы, кодируются в простом метод ascii в шестнадцатеричный (см. audit_logging.c ). Для декодирования замените «FF» на «ÿ» и перекодируйте это из html в ascii:

grep SOCKET /var/log/audit/audit.log | \
  cut -d' ' -f 4- | sed "s|^|@\n|g;s| |\n|g" | \
  grep -E "^((exe|uid|comm)=|@)" | tr '\n@' ' \n' | \
  sort -u  | sed "s|^[^=]*=||g;s| [^ ]*=| |g" | \
  while read U C E ; do \
    echo "$C" | grep -q '"' || \
      { C=\"`echo $C | sed "s|\(..\)|\&#x\1;|g" | recode h4..u8`\" ; } ; \
    echo "uid=$U comm=$C exe=$E" ; 
  done

(объяснение: sed "s | ^ [^ =] = || g; s | [^] = | | g " -> удалите часть строки 'xxx =' - сначала: начало строки (^), за которым следует any-char-except - '=' заменяется на пробел, затем пробел, за которым следует any-char-except- '' заменен пробелом)

(объяснение: при чтении UCE; do ... done -> цикл по каждой строке, считывание каждого из трех битов данных в U, C, E (идентификатор пользователя, команда, исполняемый файл))

(объяснение: echo "$ C" | grep -q '"' || -> проверьте поле команды, чтобы увидеть если он содержит двойные кавычки - если нет ('||'), выполните следующие действия:)

(объяснение: {C = \ " echo $ C | sed" s | \ (.. \ ) | \ & # x \ 1; | g "| recode h4..ascii \";} -> распечатайте командную строку, измените каждую пару символов 'FF' на ',', затем пройти через gnu 'recode', чтобы преобразовать их из HTML-сущностей в символы ascii.)

(объяснение: echo "uid = $ U comm = $ C exe = $ E " -> распечатать измененную строку)

Это дает мне вывод (просто показывая декодированную строку):

uid=1000 comm="Socket Thread" exe="/usr/lib/firefox/firefox

/ j

1
ответ дан 3 December 2019 в 02:50

Несколько дней назад я боролся с той же проблемой и придумал очень простой метод. Это основано на том факте, что отправляющий процесс будет ждать ответа DNS, на том же порту, на котором он отправил запрос от :

  1. Узнайте порт источника исходящего запроса DNS, с iptables -j LOG
  2. Используйте lsof -i UDP: <порт_источника> , чтобы узнать, какой процесс ожидает ответа на этом порту.

Конечно, когда ответ приходит в миллисекунды, вы не можете сделать это вручную; более того, даже в автоматическом режиме нет гарантии, что вы сможете запросить систему до того, как придет ответ DNS и процесс отправки завершится. Вот почему, прежде чем даже выполнить вышеупомянутые шаги, я также настраиваю Контроллер трафика ядра на задержку исходящих пакетов, направленных на определенный IP / порт (с помощью модуля tc netem ). Это позволяет мне контролировать временное окно, в котором я должен запрашивать систему о том, какой PID ожидает ответа DNS, на исходном UDP-порту, полученном на шаге 1.

Я автоматизировал вышеуказанные шаги, включая tc задержка в небольшом скрипте, называемом ptrap (который является более общим решением, не ограниченным запросами DNS, таким образом, подходящим для обнаружения процессов, использующих любой протокол на основе TCP / UDP). С его помощью я узнал, что в моем случае служба, связывающаяся со старым DNS-сервером, была sendmail.

3
ответ дан 3 December 2019 в 02:50

На вершине находится . Существует модуль ядра ( netatop ) и демон, который заставит поверх отслеживать использование сети процессом.

Сначала вы должны установить поверх

Вот как вы установить модуль ядра. Это действительно, когда сообщение было написано, но оно может устареть:

sudo apt install linux-headers-$(uname -r) make zlib1g-dev
wget https://www.atoptool.nl/download/netatop-2.0.tar.gz
tar xvf netatop-2.0.tar.gz
cd netatop-2.0
make
sudo make install
sudo modprobe -v netatop

Если у вас есть systemd, создайте служебный файл netatopd.service файл в / etc / systemd / system / . Он будет содержать:

[Unit]
Description=NetAtop Daemon

[Service]
Type=forking
ExecStart=/usr/sbin/netatopd

[Install]
WantedBy=multi-user.target

Теперь вы можете включить демон:

sudo systemctl enable netatopd

Чтобы увидеть использование сети в реальном времени для каждого процесса:

sudo atop -n

Чтобы увидеть 3 самых интенсивных в сети за день:

atopsar -N

man atopsar для получения дополнительной информации варианты.

1
ответ дан 3 December 2019 в 02:50

lsof будет подходящим инструментом для мониторинга определенного порта и определения PID, который генерирует на нем трафик. Например, здесь я отслеживаю на сервере TCP-порт 53 DNS / домена, чтобы определить, какой PID вызывает поиск DNS:

$ lsof -PniTCP:53 -r 1 | grep :53

Теперь, если бы я отправил некоторый трафик curl на DNS-сервер:

$ curl -v telnet://192.168.3.182:53
* About to connect() to 192.168.3.182 port 53 (#0)
*   Trying 192.168.3.182...
* Connected to 192.168.3.182 (192.168.3.182) port 53 (#0)

мы увидим следующий тип вывода из приведенной выше команды:

curl    4953 root    3u  IPv4 197807920      0t0  TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)
curl    4953 root    3u  IPv4 197807920      0t0  TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)
curl    4953 root    3u  IPv4 197807920      0t0  TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)

Как это работает

Вышеупомянутая команда, которая отслеживает трафик порта 53, работает, помещая lsof в повторяющийся цикл, который выполняется каждую 1 секунду, -r 1 . Затем мы говорим lsof сообщать только о трафике, который использует порт 53, iTCP: 53 . -Pn инструктирует lsof отображать имена хостов и порты в виде чисел, а не фактических имен.

Затем мы используем grep для чтения вывода, поступающего из lsof и отфильтруйте вывод так, чтобы мы видели только трафик порта : 53 .

PID процесса, отправляющего трафик, находится в выводе, отображаемом lsof тоже. Во 2-м столбце показан PID, 4953.

0
ответ дан 3 December 2019 в 02:50

Теги

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