Главная

Tuesday, 1 September 2020

Мониторинг поля TTL с помощью Scapy.

Всем привет.

Сетевая библиотека Scapy имеет много возможностей. Давайте разберем пару реальных кейсов из книги Violent Python Book.

Мониторинг значения поля TTL.

Давайте создадим скрипт для вывода исходного IP-адреса и TTL из входящих пакетов. Будет легче написать этот код с помощью dpkt. Мы создаем функцию для прослушивания и передачи каждого отдельного пакета функции testTTL(), которая проверяет каждый пакет на уровне IP, извлекая IP-адрес источника и значение TTL поля и выводит эти данные на экран.

from scapy.all import *
def testTTL(pkt):
try:
if pkt.haslayer(IP):
ipsrc = pkt.getlayer(IP).src
ttl = str(pkt.ttl)
print '[+] Pkt Received From: '+ipsrc+' with TTL: ' \
+ ttl
except:
pass
def main():
sniff(prn=testTTL, store=0)
if __name__ == '__main__':
main()

Запускаем:

analyst# python printTTL.py
[+] Pkt Received From: 192.168.1.7 with TTL: 64
[+] Pkt Received From: 173.255.226.98 with TTL: 52
[+] Pkt Received From: 8.8.8.8 with TTL: 13
[+] Pkt Received From: 8.8.8.8 with TTL: 13
[+] Pkt Received From: 192.168.1.7 with TTL: 64
[+] Pkt Received From: 173.255.226.98 with TTL: 52
[+] Pkt Received From: 8.8.8.8 with TTL: 13

Запустив наш код, мы получим довольно много пакетов от разных адресов с разными TTL. Эти результаты также включают заготовленную ловушку сканирования с адресом 8.8.8.8 и TTL равным 13. Как мы знаем, TTL должен быть 64 минус 11 = 53 прыжка, т.е. мы можем утверждать, что кто-то их подделал. Это важно отметить, что системы Linux обычно запускаются с начальным TTL равным 64, системы на базе Windows начинают с TTL равным 128. В целях эскперимента мы предположим, что анализируем только IP-пакеты с рабочих станций Linux, сканирующих нашу сеть, поэтому давайте добавим функцию для проверки полученного TTL против фактического TTL.

Наша функция checkTTL() принимает исходный IP-адрес с соответствующим TTL в качестве входных данных и выводит сообщение о недопустимых значениях TTL. Мы будем использовать условное выражение для удаления пакетов с частных IP-адресов (10.0.0.0–10.255.255.255, 172.16.0.0–172.31.255.255 и 192.168.0.0–192.168.255.255). Для этого импортируем библиотеку IPy. Чтобы избежать IP конфликтующих с IP-адресом класса Scapy, мы переклассифицируем его как IPTEST. Если файл IPTEST(ipsrc). iptype () возвращает "PRIVATE", мы возвращаемся из нашей функции checkTTL, игнорируя пакет для дальнейшей проверки.


Мы получим довольно много уникальных пакетов с одного и того же адреса источника. На самом деле мы хотим только один раз проверить исходный адрес. Если мы не видели исходный адрес ранее то давайте создадим IP-пакет с адресом назначения, равным адресу источника. Кроме того, мы создадим пакет с эхо-запросом ICMP, чтобы хост назначения смог ответить. Как только адрес назначения ответит, мы запомним значение TTL себе в словарь, проиндексированный по IP-адресу источника. Затем мы проверим, есть ли разница между фактическим полученным TTL и TTL исходного пакета и превышает ли она пороговое значение. Пакеты могут идти по разным маршрутам к хосту назначения, и поэтому могут иметь разные TTL. Однако, если расстояние между мапшрутами отличается на пять прыжков, мы можем предположить, что это может быть поддельный TTL, и вывести предупреждающее сообщение на экран.

from scapy.all import *
from IPy import IP as IPTEST
ttlValues = {}
THRESH = 5
def checkTTL(ipsrc, ttl):
if IPTEST(ipsrc).iptype() == 'PRIVATE':
return
if not ttlValues.has_key(ipsrc):
pkt = sr1(IP(dst=ipsrc) / ICMP(), \
retry=0, timeout=1, verbose=0)
ttlValues[ipsrc] = pkt.ttl
if abs(int(ttl) - int(ttlValues[ipsrc])) > THRESH:
print '\n[!] Detected Possible Spoofed Packet From: '\
+ ipsrc
print '[!] TTL: ' + ttl + ', Actual TTL: ' \
+ str(ttlValues[ipsrc])

Добавим некоторые опции синтаксического анализа для конкретного адреса для прослушивания, а затем возможность установить порог для создания окончательного варианта скрипта. Вот что мы имеем в итоге:

import time
import optparse
from scapy.all import *
from IPy import IP as IPTEST
ttlValues = {}
THRESH = 5
def checkTTL(ipsrc, ttl):
if IPTEST(ipsrc).iptype() == 'PRIVATE':
return
if not ttlValues.has_key(ipsrc):
pkt = sr1(IP(dst=ipsrc) / ICMP(), \
retry=0, timeout=1, verbose=0)
ttlValues[ipsrc] = pkt.ttl
if abs(int(ttl) - int(ttlValues[ipsrc])) > THRESH:
print '\n[!] Detected Possible Spoofed Packet From: '\
+ ipsrc
print '[!] TTL: ' + ttl + ', Actual TTL: ' \
+ str(ttlValues[ipsrc])
def testTTL(pkt):
try:
if pkt.haslayer(IP):
ipsrc = pkt.getlayer(IP).src
ttl = str(pkt.ttl)
checkTTL(ipsrc, ttl)
except:
pass
def main():
parser = optparse.OptionParser("usage%prog "+\
"-i<interface> -t <thresh>")
parser.add_option('-i', dest='iface', type='string',\
help='specify network interface')
parser.add_option('-t', dest='thresh', type='int',
help='specify threshold count ')
(options, args) = parser.parse_args()
if options.iface == None:
conf.iface = 'eth0'
else:
conf.iface = options.iface
if options.thresh != None:
THRESH = options.thresh
else:
THRESH = 5
sniff(prn=testTTL, store=0)
if __name__ == '__main__':
main()

Запустив наш код, мы видим, что он правильно определяет сканирование Nmap-приманки с 8.8.8.8 c TTL 13 по сравнению с фактическим TTL 53. Важно отметить, что это значение создается на основе начального TTL по умолчанию для Linux 64. Хотя RFC 1700 рекомендует TTL по умолчанию как 64, Windows использовала начальный TTL 128.  Кроме того, другие варианты Unix имеют разные TTL, например Solaris 2.x со значением TTL по умолчанию 255. На данный момент мы оставим наш сценарий как есть и предположим что поддельные пакеты исходят от машины на базе Linux. Имеем:

analyst# python spoofDetect.py –i eth0 –t 5

[!] Detected Possible Spoofed Packet From: 8.8.8.8
[!] TTL: 13, Actual TTL: 53
[!] Detected Possible Spoofed Packet From: 8.8.8.8
[!] TTL: 13, Actual TTL: 53
[!] Detected Possible Spoofed Packet From: 8.8.8.8
[!] TTL: 13, Actual TTL: 53
[!] Detected Possible Spoofed Packet From: 8.8.8.8
[!] TTL: 13, Actual TTL: 53
<..SNIPPED..>

Продолжение следует.

No comments:

Post a Comment

А что вы думаете по этому поводу?