(Я работаю над Mac Sierra, Python 2.7.12 и Pycharm IDE).
Я занимаюсь созданием приложения для сниффинга, чтобы извлекать избыточные копии форм отправки или другого трафика Ethernet. Это часть Unhackable Server Project.
Я уже начал проект с pyshark, но я мог получать только заголовки http, а не полезные данные, поэтому мне пришлось сменить тактику.
Одна из моих предыдущих публикаций была посвящена выполнению команд терминала через python. Причина, по которой мне это было нужно, заключалась в том, что я переключился с использования Pyshark для прослушивания моей сети на прямой переход к источнику; tshark. Я просто хотел сделать его более элегантным и при этом получить как можно больше информации.
import os os.system("tshark -T fields -e data.data -e frame.time -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -F pcap -c 1000")
Мне нужно захватить пакеты, отправляемые на сервер.
Примечание: у вас могут возникнуть проблемы, если вы воспользуетесь им для сбора информации, которая вам не принадлежит.
Когда вы запускаете это, он сохраняет два файла в каталоге, файл Pcap и текстовый файл после захвата 1000 пакетов. Результатом является отметка времени и все данные, которые были захвачены.
Моя цель сейчас - следить за потоком TCP и извлекать файлы из пакетов.
Чтобы проверить пакеты, которые вы сохранили при написании этого кода, я рекомендую вам загрузить Wireshark и предоставить ему root-доступ, запустив это: /Applications/Wireshark.app/Contents/MacOS/Wireshark в вашем терминале.
Я использую эту статью из Codingsec.net в качестве руководства, но мои потребности немного проще и понятнее. Я также должен быть осторожен, потому что код, на который я ссылаюсь, написан на Python 3, а я использую Python 2.
Скачал scapy через терминал. Мне пришлось перезапустить PyCharm, чтобы он перезагрузил интерпретатор и заставил его импортировать в код.
Я начал просматривать справочный код и кое-что перенять.
import os import pcapy as p from scapy.all import * a = " " os.system("tshark -T fields -e frame.time -e data.data -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -F pcap -c 1000") data = "Eavesdrop_Data.pcap" a = rdpcap(data)
Итак, это почерпнуто из вышеупомянутого кода. Это было просто, чтобы опустить палец на ногу и посмотреть, что именно делают разные модули.
Во-первых, мне пришлось повторно загрузить scapy, по какой-то причине он не загружался должным образом раньше, поэтому я удалил его и переустановил с помощью терминала. Затем мне пришлось загрузить Pcapy, который не мог работать без Dumbnet. Фу! Я целый день занимался поиском и установкой пакетов. Но вскоре у меня это заработало. Результат выглядел так:
Woot! Ошибок нет!
N.B. Я установил dumbnet на свой терминал из git.
Теперь я перешел к следующим нескольким строкам кода ссылки:
from scapy.all import * a = " " os.system("tshark -T fields -e frame.time -e data.data -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -F pcap -c 1000") data = "Eavesdrop_Data.pcap" a = rdpcap(data) sessions = a.sessions() print sessions
и вывод:
Woot! По-прежнему нет ошибки. Мне нравится использовать команду печати, чтобы проверить, что происходит, и что данные остаются нетронутыми.
Код:
from scapy.all import * a = " " os.system("tshark -T fields -e frame.time -e data.data -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -F pcap -c 1000") data = "Eavesdrop_Data.pcap" a = rdpcap(data) sessions = a.sessions() for session in sessions: print sessions
выход:
Вход:
from scapy.all import * a = " " #os.system("tshark -T fields -e frame.time -e data.data -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -F pcap -c 1000") #I commented out the t-shark so i could just reuse the same data data = "Eavesdrop_Data.pcap" a = rdpcap(data) sessions = a.sessions() for session in sessions: http_payload = "" for packet in sessions[session]: print packet
Теперь, на этом этапе я думаю, как мне убедиться, что я получаю полезную нагрузку от всех HTTP-запросов (не https), которые я получаю? Поэтому я изменил файл командной строки и добавил еще один под ним, чтобы проанализировать http-пакеты из файла, захваченного в первой командной строке. Затем я распечатал результат.
Код:
from scapy.all import * data = "Eavesdrop_Data.pcap" a = rdpcap(data) os.system("tshark -T fields -e _ws.col.Info -e http -e frame.time -e " "data.data -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -c 1000") os.system("tshark -r Eavesdrop_Data.pcap -Y http -w Eavesdrop_Data_http.pcap") sessions = a.sessions() i = 1 for session in sessions: http_payload = "" for packet in sessions[session]: print packet
На данный момент мы все еще используем только scapy.
Выход:
Я получил простой текст, который мне и нужен!
Итак, теперь я должен выяснить, как изолировать типы текстового / простого содержимого. Сначала я использовал функцию try and accept внутри цикла for, чтобы проверить, есть ли у http-пакетов TCP. Затем я сопоставил исходные порты (спорт) и порты назначения (dports) с портом 80. Порт 80 является портом прослушивания по умолчанию для http.
Теперь мой код выглядит так:
from scapy.all import * data = "Eavesdrop_Data.pcap" a = rdpcap(data) #os.system("tshark -T fields -e _ws.col.Info -e http -e frame.time -e " # "data.data -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -c 1000") os.system("tshark -r Eavesdrop_Data.pcap -Y http -w Eavesdrop_Data_http.pcap") sessions = a.sessions() i = 1 for session in sessions: http_payload = "" for packet in sessions[session]: try: if packet[TCP].dport == 80 or packet[TCP].sport == 80: print packet[TCP].payload except: pass
и я получаю это в своем терминале:
Это выглядит намного менее пугающим, чем раньше, и, как вы можете видеть, простой текст все еще там.
Мне нужно выяснить, как получить заголовок http, чтобы я мог просеивать типы контента. Если вы прокрутите вниз пример кода, вы увидите, что автор создал класс для обработки этого. Его класс ориентирован на образы.
Я начал с копирования и вставки его функции и адаптации ее для работы с текстом (везде, где написано изображение, измените его на текст). Вот как это выглядит:
import re def HTTPHeaders(http_payload): try: #isolate headers headers_raw = http_payload[:http_payload.index("\r\n\r\n") + 2] regex = r"(?P<'name>.*?): (?P<value>.*?)\r\n'" headers = dict(re.findall(regex, headers_raw)) except: return None if 'Content-Type' not in headers: return None return headers def extractText(headers, http_payload): text = None text_type = None if 'text' in headers['Content-Type']: text_type = headers['Content-Type'].split("/")[1] text = http_payload[http_payload.index("\r\n\r\n")+4:] return text,text_type
Это мой результат:
Нет
Нет
Нет
Нет
Нет
Нет
Нет
Нет
Нет
Нет
…
…
всю дорогу вниз.
Как мы видели ранее, на выходе должны быть заголовки. Я изолировал проблему от регулярного выражения в функции try. Пора узнать что-то новое… снова. Как лучше всего?
Покопавшись в Интернете, я нашел самый потрясающий api! Я смог протестировать регулярное выражение, приведенное в примере кода, с помощью headers_raw. А пока я вытащил код и просто сохранил весь необходимый мне код (избавился от функций text_type).
После этого я решил выяснить, что происходит. Я тестировал с операторами печати. Я проследил за данными, распечатал их на каждом шаге, каждом цикле, каждой функции и обнаружил, что проблема заключалась в строке headers_raw. Ошибка заключалась в том, что не удалось найти подстроку. При этом я удалил его и использовал часть кода, сгенерированного из https://regex101.com/ (переведенного на python 2.7).
Теперь это выглядит так:
import re import zlib def HTTPHeaders(http_payload): try: # isolate headers matches = re.finditer(r'\r\n\r\n', http_payload) for matchNum, match in enumerate(matches): matchNum = matchNum + 1 headers_raw = http_payload[:match.end()] print headers_raw regex = ur"/?P<'name>.*?/: /?P<value>.*?/\n" headers = dict(re.findall(regex, headers_raw, re.UNICODE)) return headers except: return None if 'Content-Type' not in headers: return None return headers def extractText(headers, http_payload): text = None try: if 'text/html' in headers['Content-Type']: text = http_payload[http_payload.index("\n\n")+4:] try: if "Content-Encoding" in headers.keys(): if headers['Content-Encoding'] == "gzip": text = zlib.decompress(text) elif headers['Content-Encoding'] == "deflate": text = zlib.decompress(text) except: pass except: return None return text
Результат выглядит так:
Совпадение сработало отлично, но вскоре я обнаружил, что, хотя я решил свою проблему header_raw, что-то не так с регулярным выражением, которое анализирует заголовки в словарях.
Опять же, я прослезился, потому что понятия не имею о регулярных выражениях. Я закатал рукава и принялся за работу.
Это будет намного сложнее, чем просто найти заголовки. Я провел больше исследований, искал в Google и искал Stackoverflow, а также Regex101 в поисках решения. Regex101 имеет библиотеку регулярных выражений, созданных пользователями. В поисках json я нашел это: (?: [\ N] {0,1}) (\ w +) (?: \ * = \ *) ([^ \ N] *) (?: [\ N] { 0,1})
Когда я вставил его в свой код как есть, он дал мне следующее:
Ой! Это должна быть словарная форма:
Если вы внимательно посмотрите на вывод, вы увидите, что помимо этого есть некоторые мошеннические значения \ r, регулярное выражение вырезало много информации. Если мы добавим \ r к нашему \ n в регулярном выражении следующим образом: «(?: [\ R \ n] {0,1}) (\ w +) (?: \ * = \ *) ([^ \ r \ n] *) (?: [\ r \ n] {0,1}) ”
и наш результат выглядит так:
Это приближается….
Еще одна корректировка, которую мне нужно было сделать, заключалась в замене знака равенства на двоеточие, чтобы адаптировать регулярное выражение к формату моего ввода (в оригинале было = для обозначения пар ключ-значение).
Этот:
(?:[\r\n]{0,1})(\w+)(?:\ *:\ *)([^\r\n]*)(?:[\r\n]{0,1})
Дает мне это:
К этому времени я немного понимаю, как работает регулярное выражение, поэтому, когда я понял, что код отбрасывает первую часть всего, что переносится через дефис, я знал, что мне нужно избежать дефиса и добавить \ w +, чтобы добавить слово перед дефисом.
regex = ur"(?:[\r\n]{0,1})(\w+\-\w+)(?:\ *:\ *)([^\r\n]*)(?:[\r\n]{0,1})"
что дало мне это:
Если вы посмотрите на вывод, то заметите, что теперь он игнорирует все, что не содержит дефисов. Поэтому мне нужно было добавить «или», чтобы захватить отсутствующие комбинации ключ / значение (или обозначены знаком |).
regex = ur"(?:[\r\n]{0,1})(\w+\-\w+|\w+)(?:\ *:\ *)([^\r\n]*)(?:[\r\n]{0,1})"
Woooot! Это не в порядке, но для меня это не имеет значения. На этом этапе я вернулся, чтобы посмотреть, будет ли работать исходный синтаксический анализ headers_raw, потому что цикл for для сопоставления казался немного громоздким. Это сработало!
В конце концов, мой код состоял из двух файлов Python. Они здесь:
Eavesdrop.py
import os from scapy.all import * from getHTTPHeaders import HTTPHeaders, extractText data = "Eavesdrop_Data.pcap" a = rdpcap(data) os.system("tshark -T fields -e _ws.col.Info -e http -e frame.time -e " "data.data -w Eavesdrop_Data.pcap > Eavesdrop_Data.txt -c 1000") sessions = a.sessions() carved_texts = 1 for session in sessions: http_payload = "" for packet in sessions[session]: try: if packet[TCP].dport == 80 or packet[TCP].sport == 80: http_payload += str(packet[TCP].payload) except: pass headers = HTTPHeaders(http_payload) if headers is None: continue text = extractText(headers,http_payload) if text is not None: print (text)
getHTTPHeaders.py
import re import zlib def HTTPHeaders(http_payload): try: # isolate headers headers_raw = http_payload[:http_payload.index("\r\n\r\n") + 2] regex = ur"(?:[\r\n]{0,1})(\w+\-\w+|\w+)(?:\ *:\ *)([^\r\n]*)(?:[\r\n]{0,1})" headers = dict(re.findall(regex, headers_raw, re.UNICODE)) print headers return headers except: return None if 'Content-Type' not in headers: return None return headers def extractText(headers, http_payload): text = None try: if 'text/plain' in headers['Content-Type']: text = http_payload[http_payload.index("\r\n\r\n")+4:] try: if "Accept-Encoding" in headers.keys(): if headers['Accept-Encoding'] == "gzip": text = zlib.decompress(text, 16+zlib.MAX_WBITS) elif headers['Content-Encoding'] == "deflate": text = zlib.decompress(text) except: pass except: return None return text
В данных есть только одна полезная нагрузка в виде простого текста…. и код его нашел!
Это было испытанием терпения и любовью. Уже несколько месяцев пытаюсь решить эту проблему. Имейте в виду, что когда я начинал, у меня не было опыта сетевого программирования. Надеюсь, это кому-то поможет. Если у вас есть предложения, прокомментируйте или найдите меня на моем Github