Comments 28
Ну, месяц прошёл. Приём ответов этим комментарием закрывается.
И ответ для желающих, а также явно тегну @whocoulditbe и @DimaFromMai, как людей, которые показали свой интерес, но не дошли до решения.
Задача этого дампа была использовать описанные в статье приёмы, но не сделать решение невозможным.
Шаг 1
Большая часть статьи посвящена тому, что символы кодируются как разница между временными метками в теле ICMP-пакета. Автоматизируем разбор дампа, в моем случае -- через scapy.
from scapy.layers.inet import ICMP
from scapy.utils import rdpcap
scapy_cap = rdpcap('contest.pcap')
data = []
for i in range(2, len(scapy_cap), 2):
prev = scapy_cap[i - 2]
curr = scapy_cap[i]
assert prev[ICMP].type == 8
assert curr[ICMP].type == 8
p_s = int.from_bytes(prev[ICMP].load[0:8], "little")
p_ms = int.from_bytes(prev[ICMP].load[8:16], "little")
c_s = int.from_bytes(curr[ICMP].load[0:8], "little")
c_ms = int.from_bytes(curr[ICMP].load[8:16], "little")
diff = (c_s - p_s - 1) * 1_000_000 + (c_ms - p_ms)
data.append(diff)
print(data)
Если считать с ограничением в миллисекунды, как указано в статье, то разница -- идеально точная секунда. Так не бывает :) Считаем в микросекундах и получается что-то такое:
[9, 0, 30, 6, 1, 12, 0, 13, 3, 12, 14, 2, 0, 5, 2, 6, 0, 7, 1, 9, 2, 8, 26, 2, 5, 5, 1, 0, 5, 4, 11, 3, 11, 1, 19, 1, 0, 7, 1, 1, 21, 29, 2, 5, 4, 16, 0, 6, 22, 11, 0, 7, 1, 9, 7, 2, 12, 0, 6, 22, 11, 0, 6, 1, 22, 5, 1, 0, 6, 22, 11, 0, 30, 6, 1, 0, 9, 7, 33, 0, 1, 21, 12, 3, 5]
Что делать с этим набором данным -- под следующим спойлером.
Шаг 2
Это, конечно, ещё не ответ, но раз нам обещали загадку, значит ответ где-то рядом. В статье можно найти такой абзац:
Если передавать необходимо текст, то можно использовать компактную кодировку. Например, только заглавные буквы алфавита и пробел: 34 символа для русского алфавита. А если алфавит отсортировать с учетом частотности символов, то отклонение множества пакетов будет в рамках погрешности и заметить передачу будет сложнее.
33 буквы алфавита + пробел = 34 символа. В нашем массиве данных минимальное значение 0, максимальное -- 33, отлично подходит. Переходим по ссылке и честно забираем оттуда буквы в порядке убывания частотности символа. При это заглавные буквы будут или строчные -- неважно, на ответ не влияет. Получается что-то такое:
voc = ["о", "е", "а", "и", "н", "т", "с", "р", "в", "л", "к", "м", "д", "п", "у", "я", "ы", "ь", "г", "з", "б", "ч", "й", "х", "ж", "ш", "ю", "ц", "щ", "э", "ф", "ъ", "ё"]
Остается вопрос: где должен быть пробел. Хотя пробел можно расположить куда угодно, это всего 33 строки на проверку, что относительно быстро перебирается. Но интуитивно хочется поместить в начало (код 0) или в конец (код 33).
dec = [voc[i] for i in data]
print("".join(dec))
Ответ
Пробел прячется на нулевой позиции. Ответ:
в этом дампе нет совершенно никакого сообщения тчк совсем тчк точно тчк это всё обман
Самые «неприметные» — это эхо-запросы.
Один ICMP-пакет может содержать до 65535 байт информации за вычетом заголовков IP и ICMP (ограничение IP-пакетов).
Ага, неприметные. Прям никто не заметит неприметный поток пингов на 65 килобайт каждый
Но вроде бы автор эти и отметил парой строк ниже?
Пакеты легко обнаружить из-за большого размера.
В этом и дело. Получается, что с одной стороны они неприметные, но тут же говорится о том, что они приметные.
Эхо-запросы по умолчанию (с их 32 или 56 байтами данных) весьма неприметные. И к пакетам с малым объемом минус "легко обнаружить из-за размера" действительно неприменим. Мало ли кто что пингует.
В тексте неприметность заключается в том, что эхо-запрос может быть создан кем угодно с какой угодно мотивацией. А вот если вы начнете рассылать эхо-ответы без запроса или пакеты про TTL без входящего трафика, то вопросов будет даже больше.
Меня больше интересует как в этом случае обойти ограничение MTU на оборудовании, которое в среднем 1500 Б?
У ping есть параметр "-p pattern".
Этот паттерн передаётся в "данных"?
Я недавно тоже писал прототип key-value хранилища в пакетах пинга. В комментариях умные люди упомянули https://github.com/yarrick/pingfs. Пусть это будет тут тоже для будущих изобретателей.
А то гуглится информация не очень просто.
Любой NGFW ловит это на раз (про отечественные от PT например, другие сомневаюсь), если конечно включена функциональность
Хм. А интересно, можно ли передавать данные через "ШИМ"? Ну то есть кодировать данные временем между пакетами, да хоть бы той же морзянкой (понятно, что надёжность и скорость невысоки).
Ничто не мешает выставить задержку между пакетами, которая будет однозначно выше допустимого порога, скажем в 99%. Например при среднем пинге в 40мс, устанавливать дополнительную задержку в 200мс между пакетами на "1" и без задержки на "0", то какая будет вероятность, что пакет без задержки придет позже пакета с задержкой в 200мс?
Да и как бы это давно известные приемы стеганографии, про которые в статье просто не акцентируют внимание, но есть что-то похожее: "Теперь байт данных — это разница временных меток между пакетами."
ну если совсем упороться, то можно в значении миллисекунд кодировать биты, например четная цифра это ноль, нечетная это один, за один пакет получится 6, но лучше 4 бита оставим два старших бита не изменными чтоб совсем не привлекать внимания, наверное самое палевное что останется это source и target таких пакетов
Нашёл такие символы
!"#$%&'()*+,-/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU
Не смог осилить дамп, подписался на комментарии в ожидании ответа.
Как я понимаю, в дампе достаточно данных, чтобы расшифровать его, не прибегая к дополнительным шагам, типа rot13 поверх текста? Нет никаких волшебных констант, участвующих в шифровании?
IP-чексуммы верные, к идентификаторам пакетов не прицепиться. Единственное, что смущает, - Метка пакета в будущем!
, как было упомянуто в статье, но там можно шифроваться как угодно, например, делать xor между первым байтом чексуммы icmp-пакета + sequence number и данными. А можно и со вторым делать xor, почему бы и нет? Как насчёт "вычесть из первого байта ts третий байт и помножить его на второй байт чексуммы"? А что если данные зашифрованы алгоритмом, использующим identifier в качестве ключа? Количество возможных операций только с sequence number и временной меткой достаточно велико (а жизнь слишком коротка), чтобы перестать гадать и начинать разогревать паяльник.
А ещё у вас по адресу из дампа гитлаб по хттп смотрит наружу, это часть загадки?
А ещё у вас по адресу из дампа гитлаб по хттп смотрит наружу, это часть загадки?
В загадке участвует только дамп. Так что гитлаб по хттп не является частью загадки.
Ну, месяц прошёл. Приём ответов этим комментарием закрывается.
И ответ для желающих, а также явно тегну @whocoulditbe и @DimaFromMai, как людей, которые показали свой интерес, но не дошли до решения.
Задача этого дампа была использовать описанные в статье приёмы, но не сделать решение невозможным.
Шаг 1
Большая часть статьи посвящена тому, что символы кодируются как разница между временными метками в теле ICMP-пакета. Автоматизируем разбор дампа, в моем случае -- через scapy.
from scapy.layers.inet import ICMP
from scapy.utils import rdpcap
scapy_cap = rdpcap('contest.pcap')
data = []
for i in range(2, len(scapy_cap), 2):
prev = scapy_cap[i - 2]
curr = scapy_cap[i]
assert prev[ICMP].type == 8
assert curr[ICMP].type == 8
p_s = int.from_bytes(prev[ICMP].load[0:8], "little")
p_ms = int.from_bytes(prev[ICMP].load[8:16], "little")
c_s = int.from_bytes(curr[ICMP].load[0:8], "little")
c_ms = int.from_bytes(curr[ICMP].load[8:16], "little")
diff = (c_s - p_s - 1) * 1_000_000 + (c_ms - p_ms)
data.append(diff)
print(data)
Если считать с ограничением в миллисекунды, как указано в статье, то разница -- идеально точная секунда. Так не бывает :) Считаем в микросекундах и получается что-то такое:
[9, 0, 30, 6, 1, 12, 0, 13, 3, 12, 14, 2, 0, 5, 2, 6, 0, 7, 1, 9, 2, 8, 26, 2, 5, 5, 1, 0, 5, 4, 11, 3, 11, 1, 19, 1, 0, 7, 1, 1, 21, 29, 2, 5, 4, 16, 0, 6, 22, 11, 0, 7, 1, 9, 7, 2, 12, 0, 6, 22, 11, 0, 6, 1, 22, 5, 1, 0, 6, 22, 11, 0, 30, 6, 1, 0, 9, 7, 33, 0, 1, 21, 12, 3, 5]
Что делать с этим набором данным -- под следующим спойлером.
Шаг 2
Это, конечно, ещё не ответ, но раз нам обещали загадку, значит ответ где-то рядом. В статье можно найти такой абзац:
Если передавать необходимо текст, то можно использовать компактную кодировку. Например, только заглавные буквы алфавита и пробел: 34 символа для русского алфавита. А если алфавит отсортировать с учетом частотности символов, то отклонение множества пакетов будет в рамках погрешности и заметить передачу будет сложнее.
33 буквы алфавита + пробел = 34 символа. В нашем массиве данных минимальное значение 0, максимальное -- 33, отлично подходит. Переходим по ссылке и честно забираем оттуда буквы в порядке убывания частотности символа. При это заглавные буквы будут или строчные -- неважно, на ответ не влияет. Получается что-то такое:
voc = ["о", "е", "а", "и", "н", "т", "с", "р", "в", "л", "к", "м", "д", "п", "у", "я", "ы", "ь", "г", "з", "б", "ч", "й", "х", "ж", "ш", "ю", "ц", "щ", "э", "ф", "ъ", "ё"]
Остается вопрос: где должен быть пробел. Хотя пробел можно расположить куда угодно, это всего 33 строки на проверку, что относительно быстро перебирается. Но интуитивно хочется поместить в начало (код 0) или в конец (код 33).
dec = [voc[i] for i in data]
print("".join(dec))
Ответ
Пробел прячется на нулевой позиции. Ответ:
в этом дампе нет совершенно никакого сообщения тчк совсем тчк точно тчк это всё обман
Если TCP и UDP вам не доступны, то уезжайте из этой страны.
О, я такое в армии делала )) тоже скорее в качестве proof of concept чем реального применения - задача была выбрать протокол и незаметно передать через него данные. Мы правда пошли глубже, выбрали ARP, но другие команды выбирали и ICMP.
Как передать информацию в ICMP-пакетах и не привлечь внимания санитаров