CTF-отчет о сетевых задачах на NahamCon 2022
NahamCon2022 закончился, и мы рады, что нам удалось финишировать в топ 5% с нашей командой Stack Labs (3049 очков, 195 среди 4034 команд). В этом году они придумали захватывающие сетевые задачи, которые создал @Kkevsterrr#7469. Хотя нам не удалось их решить, это было хорошее введение в анализ сети и манипулирование пакетами с помощью Scapy.
Вот что я узнал.
Задачи:
- 1. Contemporaneous Open – трудная – 14 решений – 500 очков – впервые решена StaticFlow
- 2. День фривольного флага – сложно – 9 решений – 500 очков – первым разгадал Maple Bacon
- 1. Contemporaneous Open
- Традиционное трехстороннее рукопожатие
- SYN-ACK отбрасывается недоверчивым брандмауэром
- TCP Simultaneous Open
- Создание скриптов с помощью Scapy
- 1. Сначала получим SYN-пакет клиента.
- 2. Теперь давайте отправим наш SYN и подтвердим SYN клиента.
- Улучшения
- 2. День причудливого флага
- Предварительные тесты
- Перехват и модификация пакетов с помощью scapy и nfqueue
- 1. Установим nfqueue
- 2. Изменение флага
- 3. Обновить контрольные суммы
- 4. Сопоставление всех флагов (окончательный код)
- Дальнейшие лекции
1. Contemporaneous Open
Автор: @Kkevsterrr#7469
Мы хотим отдать вам флаг, правда хотим. Просто дайте нам TCP HTTP сервер, на который его нужно отправить, и мы сделаем POST запрос со всеми данными, которые вам нужны! У нас просто проблема с брандмауэром на нашей стороне, и мы отбрасываем некоторые важные пакеты (в частности, любой входящий SYN+ACK пакет отбрасывается). Однако это не должно быть проблемой для сетевого профессионала вроде вас – просто сделайте TCP-сервер, которому не нужно отправлять эти пакеты!
Используемые инструменты:
- scapy для работы с пакетами
- tshark wireshark’s cli version
Традиционное трехстороннее рукопожатие
Давайте начнем с некоторых полезных напоминаний о протоколе TCP. В традиционном трехстороннем рукопожатии TCP:
- клиент посылает SYN (Synchronize Sequence Number), чтобы сообщить серверу, что он хочет начать обмен данными. SYN указывает, с каким порядковым номером он начнет сегменты.
- Сервер отвечает клиенту SYN-ACK. ACK означает ответ на SYN клиента. При этом SYN указывает, с какого порядкового номера он начнет сегменты.
- Наконец, клиент подтверждает (ACK) ответ сервера, и соединение переходит в состояние ESTABLISHED, чтобы они могли начать обмен данными.
SYN-ACK отбрасывается недоверчивым брандмауэром
В этой задаче флаг передается POST-запросом к серверу, который мы должны создать на порту 80. Дело в том, что брандмауэр клиента отбрасывает каждый входящий пакет SYN-ACK. Поэтому мы должны найти решение для установления соединения без необходимости посылать пакет с флагом SYN-ACK.
Пока я проводил часы, освежая в памяти внутреннее устройство TCP/IP, изучая основы scapy и исследуя множество способов решения проблемы (фрагментация ответа SYN/ACK, попытка установить соединение HTTP3 через QUIC), решение лежало в названии задачи.
TCP Simultaneous Open
Contemporaneous Open – это ссылка на переход состояния TCP Simultaneous Open. Также его называют “одновременное активное открытие с обеих сторон”, он относится к старой функции TCP, используемой для обработки побочных ситуаций в рукопожатиях TCP, например, когда сервер и клиент одновременно посылают друг другу SYN. Этот процесс делает возможным для двух приложений посылать SYN друг другу, чтобы начать TCP-соединение.
Когда оба конца одновременно посылают SYN, оба конца переходят в состояние SYN_SENT. Когда они получают SYN, их состояние меняется на SYN_RCVD, они повторно посылают SYN и подтверждают полученный SYN. Когда оба конца получают SYN и подтвержденный SYN, соединение переходит в состояние ESTABLISHED. В таком состоянии оба конца действуют как клиент и сервер.
Поэтому клиенту не нужен ответ SYN/ACK от сервера, чтобы завершить трехстороннее рукопожатие. Нам достаточно послать SYN и дождаться SYN/ACK клиента, чтобы установить соединение и начать обмен HTTP. Это может избавить нас от отправки пакета SYN/ACK, который будет отброшен клиентом. Мы будем использовать Scapy для имитации сервера с таким поведением.
Создание скриптов с помощью Scapy
Перед выполнением любых скриптов Scapy мы должны отключить ответ ядра Linux, чтобы избежать любых ответов с меткой RST. Scapy работает в пространстве пользователя, поэтому ядро не имеет представления о том, что делает Scapy.
Используя iptables, мы можем отбрасывать пакеты с меткой RST, которые ядро посылает с порта 80.
sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST --sport 80 -j DROP
Идея заключается в том, чтобы создать настраиваемый сервер, который делает следующее:
- принимать SYN клиента (seq=n)
- посылает SYN (seq=m)
- посылает ACK (seq=m+1, ack=n+1), чтобы подтвердить SYN клиента.
- клиент подтверждает наш SYN, получает наш ACK и устанавливает сессию
Мы будем использовать tshark в качестве дополнительного инструмента для отслеживания входящих пакетов.
1. Сначала получим SYN-пакет клиента.
Обратите внимание, что IP-адреса были изменены на “MY_IP” и “CLIENT_IP”.
# server.py
#! /usr/bin/python
from scapy import *
S_ADDR = "MY_IP"
S_PORT = 80
# 1. Listen for client's SYN and get IP and port
c_syn = sniff(
filter="tcp and port 80",
count=1,
prn=lambda x:x.sprintf("Received SYN from {IP:%IP.src%:%TCP.dport%, seq=%TCP.seq}%")
)
Мы подключаемся к вызову:
❯ nc challenge.nahamcon.com 31334
so glad you're here! i would love to give you the flag. just give me the IP address that's running an HTTP server, and I'll shoot you the flag immediately.
oh one snag, we've got some firewall issues on our side, and some important packets are getting dropped. shouldn't be a problem for you, though.
>>> MY_IP
here it comes!
hmm nope looks like you didn't get it...
# server.py output
Received SYN from CLIENT_IP:http, seq=510141201
❯ sudo tshark -f "tcp port 80"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'ens3'
1 0.000000000 CLIENT_IP → MY_IP TCP 74 58356 → 80 [SYN] Seq=0 Win=42600 Len=0 MSS=1420 SACK_PERM=1 TSval=3977333110 TSecr=0 WS=128
2 1.015633580 CLIENT_IP → MY_IP TCP 74 [TCP Retransmission] 58356 → 80 [SYN] Seq=0 Win=42600 Len=0 MSS=1420 SACK_PERM=1 TSval=3977334125 TSecr=0 WS=128
Отлично! Теперь мы можем использовать IP и порт для отправки наших пакетов.
2. Теперь давайте отправим наш SYN и подтвердим SYN клиента.
# server.py
# ...
# 2. Send Syn
C_ADDR = c_syn[0][IP].src
C_PORT = c_syn[0].sport
C_SEQ = c_syn[0].seq
S_SEQ = 1234 # random number
ip = IP(src=S_ADDR, dst=C_ADDR)
tcp_syn = TCP(
sport=S_PORT,
dport=C_PORT,
flags="S",
seq=S_SEQ,
)
s_syn = send(ip/tcp_syn)
print(f"Send SYN with seq={S_SEQ}")
# 3. Send Ack
S_SEQ+=1
C_SEQ+=1
tcp_ack = TCP(
sport=S_PORT,
dport=C_PORT,
flags="A",
seq=S_SEQ,
ack=C_SEQ,
)
s_ack = send(ip/tcp_ack)
print(f"Send ACK with seq={S_SEQ} and ack={C_SEQ}")
Received SYN from CLIENT_IP:http, seq=3800804531
Send SYN with seq=1234
Send ACK with seq=1235 and ack=3800804532
Мы будем использовать флаг -z "follow,tcp,hex,0"
для отображения содержимого первого потока TCP (CLIENT_IP → MY_IP).
❯ sudo tshark -f "tcp port 80" -z "follow,tcp,hex,0"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'ens3'
1 0.000000000 CLIENT_IP → MY_IP TCP 74 34206 → 80 [SYN] Seq=0 Win=42600 Len=0 MSS=1420 SACK_PERM=1 TSval=3979143134 TSecr=0 WS=128
2 0.009865804 MY_IP → CLIENT_IP TCP 54 80 → 34206 [SYN] Seq=0 Win=8192 Len=0
3 0.012471996 MY_IP → CLIENT_IP TCP 54 80 → 34206 [ACK] Seq=1 Ack=1 Win=8192 Len=0
4 0.114386572 CLIENT_IP → MY_IP TCP 74 [TCP Retransmission] 34206 → 80 [SYN, ACK] Seq=0 Ack=1 Win=42600 Len=0 MSS=1420 SACK_PERM=1 TSval=3979143248 TSecr=0 WS=128
5 0.115899112 CLIENT_IP → MY_IP TCP 268 POST / HTTP/1.1 [TCP segment of a reassembled PDU]
6 0.115899266 CLIENT_IP → MY_IP HTTP 104 POST / HTTP/1.1 (application/x-www-form-urlencoded)
7 0.319132750 CLIENT_IP → MY_IP TCP 318 [TCP Retransmission] 34206 → 80 [PSH, ACK] Seq=1 Ack=1 Win=333 Len=264
8 0.727116985 CLIENT_IP → MY_IP TCP 318 [TCP Retransmission] 34206 → 80 [PSH, ACK] Seq=1 Ack=1 Win=333 Len=264
9 1.551128826 CLIENT_IP → MY_IP TCP 318 [TCP Retransmission] 34206 → 80 [PSH, ACK] Seq=1 Ack=1 Win=333 Len=264
10 3.119495767 CLIENT_IP → MY_IP TCP 54 34206 → 80 [FIN, ACK] Seq=265 Ack=1 Win=333 Len=0
11 3.215170188 CLIENT_IP → MY_IP TCP 318 [TCP Retransmission] 34206 → 80 [FIN, PSH, ACK] Seq=1 Ack=1 Win=333 Len=264
^C11 packets captured
===================================================================
Follow: tcp,hex
Filter: tcp.stream eq 0
Node 0: CLIENT_IP:34206
Node 1: MY_IP:80
00000000 50 4f 53 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d POST / H TTP/1.1.
00000010 0a 48 6f 73 74 3a 20 ■ ■ ■ ■ ■ ■ ■ ■ ■ .Host: ■ ■■■■■■■■
00000020 ■ ■ ■ ■ 0d 0a 55 73 65 72 2d 41 67 65 6e 74 ■■■■..Us er-Agent
00000030 3a 20 70 79 74 68 6f 6e 2d 72 65 71 75 65 73 74 : python -request
00000040 73 2f 32 2e 32 37 2e 31 0d 0a 41 63 63 65 70 74 s/2.27.1 ..Accept
00000050 2d 45 6e 63 6f 64 69 6e 67 3a 20 67 7a 69 70 2c -Encodin g: gzip,
00000060 20 64 65 66 6c 61 74 65 0d 0a 41 63 63 65 70 74 deflate ..Accept
00000070 3a 20 2a 2f 2a 0d 0a 43 6f 6e 6e 65 63 74 69 6f : */*..C onnectio
00000080 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a 43 n: keep- alive..C
00000090 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 35 ontent-L ength: 5
000000A0 30 0d 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 0..Conte nt-Type:
000000B0 20 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 applica tion/x-w
000000C0 77 77 2d 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f 64 ww-form- urlencod
000000D0 65 64 0d 0a 0d 0a ed....
000000D6 66 6c 61 67 3d 66 6c 61 67 25 37 42 36 61 63 66 flag=fla g%7B6acf
000000E6 64 66 63 39 33 36 39 65 61 64 66 64 62 39 34 33 dfc9369e adfdb943
000000F6 39 62 30 61 63 33 39 36 39 37 31 31 25 37 44 25 9b0ac396 9711%7D%
00000106 30 41 0A
===================================================================
И вот мы получаем этот замечательный флаг:
Улучшения
Гораздо более элегантным решением было бы действовать как настоящий HTTP-сервер, принимая каждый пакет клиента, отвечая на POST-запрос и затем изящно завершая соединение, когда клиент посылает FIN.
Посмотрите решение nneonneo, в котором реализована эта возможность в дополнение к использованию асинхронного сниффера, который помещает входящие пакеты в очередь, что облегчает итерацию клиентских пакетов. Также он использует множество вспомогательных функций для отслеживания, фильтрации входящих и исходящих пакетов. Я бы определенно использовал их в качестве шаблона для следующих задач. Чисто и гладко.
В следующей части мы рассмотрим задачу Freaky Flag Day. Надеюсь, вам понравится поэзия и флаги TCP.
2. День причудливого флага
Автор: @Kkevsterrr#7469
Наши TCP-флаги решили, что сегодня они хотели бы поменяться местами; все, что вам нужно сделать, это добраться до HTTP-сервера!
Розы стали красными, а SYN-ы стали FIN-ами.
RST+ACK теперь для вас SYN+ACK.
ACK теперь Es, и что еще мы сделали?
PSH+ACKs – это FIN+SYNs, просто для развлечения.
Подсказка: Если вы хотите выполнить это задание из дома или виртуальной машины, убедитесь, что вы не находитесь за NAT, который может съесть ваши неожиданные пакеты.
Взаимодействуйте с этой задачей на: http://SERVER_IP
Использованные инструменты:
- scapy для работы с пакетами
- tshark wireshark cli версия
- curl для http-клиента
- nfqueue для перехвата пакетов, поставленных в очередь пакетным фильтром ядра.
Итак, после победы над подозрительным клиентом, который отбрасывает каждый входящий SYN/ACK пакет, наш следующий соперник – сервер, который меняет флаги каждого запроса (как входящего, так и исходящего).
Хотите установить сессию? Сервер понимает, что вы хотите его завершить.
Хотите подтвердить, что пакет успешно получен? Сервер понимает, что у вас перегрузка сети. Глупости.
Цель – правильно связаться с сервером. Поэтому мы должны говорить на его языке. Сопоставление флагов выглядит следующим образом:
S ⇔ F
RA ⇔ SA
A ⇔ E
PA ⇔ FS
Предварительные тесты
Давайте попробуем подключиться к серверу с помощью curl.
❯ curl --local-port 44444 http://SERVER_IP
❯ sudo tshark -f "tcp port 80 and host SERVER_IP"
1 0.000000000 MY_IP → SERVER_IP TCP 74 44444 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=1756025715 TSecr=0 WS=128
2 1.021102385 MY_IP → SERVER_IP TCP 74 [TCP Retransmission] 44444 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=1756026736 TSecr=0 WS=128
Как и ожидалось, ничего не происходит, так как флаг SYN интерпретируется как флаг FYN на стороне сервера.
Теперь воспользуемся scapy и отправим FIN-флаг с сегментом случайного порядкового номера.
#! /usr/bin/python
from scapy import *
C_ADDR = "MY_IP"
C_PORT = 44444
S_ADDR = "SERVER_IP"
S_PORT = 80
C_SEQ = 1234 # random
ip = IP(src=C_ADDR, dst=S_ADDR)
tcp = TCP(
sport=C_PORT,
dport=S_PORT,
flags="F",
seq=C_SEQ,
)
p = send(ip/tcp)
❯ sudo tshark -f "tcp port 80 and host SERVER_IP"
1 0.000000000 MY_IP → SERVER_IP TCP 54 44444 → 80 [FIN] Seq=1 Win=8192 Len=0
2 0.144079153 SERVER_IP → MY_IP TCP 58 80 → 44444 [RST, ACK] Seq=1 Ack=2 Win=65320 Len=0 MSS=1420
Сервер понимает “SYN” и посылает нам RST-ACK (который является SYN-ACK). Теперь он должен ждать ECN (который является ACK)… И так далее. Вы поняли.
Перехват и модификация пакетов с помощью scapy и nfqueue
Мы могли бы переписать HTTP-клиент с помощью scapy, используя модифицированные флаги. Но это долгая и утомительная задача, поскольку мы должны обрабатывать каждый запрос с помощью scapy. Менее болезненным решением было бы иметь слой, который перехватывает каждый пакет и просто обновляет флаги перед отправкой/принятием. И в этом нам может помочь nfqueue.
Используя стандартный http-клиент, например curl, перехватываем все входящие и исходящие пакеты с помощью nfqueue, передаем их нашему скрипту scapy, который соответствующим образом изменит флаги и затем отправит измененные пакеты по назначению.
Netfilter Queue – это цель iptables, которая отдает решение о пакетах в пользовательское пространство. Он является частью проекта Netfilter, который также предоставляет iptables и nftables. Она обычно используется в качестве прокси-сервера или для атак типа “человек посередине”.
Чтобы перехватывать пакеты с помощью nfqueue, необходимо соответствующим образом настроить правила цепочки брандмауэра. В данной ситуации мы хотим перехватить:
- входящие пакеты от SERVER_IP (цепочка INPUT)
- исходящие пакеты на SERVER_IP (цепочка OUTPUT).
Давайте попробуем это сделать.
1. Установим nfqueue
#!/usr/bin/python3
from netfilterqueue import NetfilterQueue
from scapy import *
import os
S_ADDR = "SERVER_IP"
# Update iptables rules
output_rule = f"iptables -A OUTPUT --destination {S_ADDR} -j NFQUEUE"
input_rule = f"iptables -A INPUT --source {S_ADDR} -j NFQUEUE"
flush_rules = "iptables -F OUTPUT && iptables -F INPUT"
os.system(input_rule)
os.system(output_rule)
def callback(raw_pkt):
# Get a scapy object from raw packet
p = IP(raw_pkt.get_payload())
print(p.show())
# Tell nfqueue to accept the packet
raw_pkt.accept()
# Init nfqueue
q = NetfilterQueue()
q.bind(0, callback)
try:
q.run()
except KeyboardInterrupt:
q.unbind()
os.system(flush_rules)
Сначала мы обновим правила iptables, чтобы перехватывать интересные пакеты для nfqueue. Затем мы инициализируем nfqueue и привязываем очередь под номером 0 к нашей функции обратного вызова. Мы используем одну и ту же очередь для входящих и исходящих пакетов.
В нашей функции обратного вызова мы получаем необработанный пакет. Для работы с ним мы создаем IP-слой scapy с полезной нагрузкой пакета в качестве аргумента. Мы печатаем его и, наконец, принимаем пакет. Если мы прерываем процесс (с помощью Ctrl+C), то мы отвязываем очередь и смываем правила iptables.
Важно: проверьте правила цепочки INPUT и OUTPUT перед прошивкой, так как она прошивает все.
Давайте посмотрим, работает ли это.
❯ curl http://SERVER_IP --local-port 44444
# Script output
###[ IP ]###
version= 4
ihl= 5
tos= 0x0
len= 60
id= 60025
flags= DF
frag= 0
ttl= 64
proto= tcp
chksum= 0xa981
src= SERVER_IP
dst= CLIENT_IP
options
###[ TCP ]###
sport= 44444
dport= http
seq= 1190907934
ack= 0
dataofs= 10
reserved= 0
flags= S
window= 64240
chksum= 0xfaf1
urgptr= 0
options= [('MSS', 1460), ('SAckOK', b''), ('Timestamp', (2004053730, 0)), ('NOP', None), ('WScale', 7)]
❯ sudo tshark -f "tcp port 80 and host SERVER_IP"
1 0.000000000 CLIENT_IP → SERVER_IP TCP 74 44444 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2004025682 TSecr=0 WS=128
Отлично! Curl пытается инициировать сессию с сервером с помощью пакета с флагом SYN. Когда пакет достигает инструкции raw_pkt.accept()
, он отправляется клиенту, и мы можем заметить это в выводе tshark.
2. Изменение флага
В нашей функции обратного вызова давайте попробуем изменить флаг пакета на ‘F’ и посмотрим, что произойдет.
def callback(raw_pkt):
# Get a scapy object from raw packet
p = IP(raw_pkt.get_payload())
print(p.show())
# Set S flag to F flag
if p.haslayer(TCP):
if p[TCP].flags = "S"
p[TCP].flags = "F"
# Update raw packet and then accept it
raw_pkt.set_payload(bytes(p))
raw_pkt.accept()
❯ sudo tshark -f "tcp port 80 and host SERVER_IP"
1 0.000000000 CLIENT_IP → SERVER_IP TCP 74 44444 → 80 [FIN] Seq=1 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2005103907 TSecr=0 WS=128
2 1.005513362 CLIENT_IP → SERVER_IP TCP 74 [TCP Retransmission] 44444 → 80 [FIN] Seq=1 Win=8222720 Len=0 MSS=1460 SACK_PERM=1 TSval=2005104912 TSecr=0 WS=128
Что-то не так, мы посылаем FIN, но не получаем ответа… Ах да! Конечно же, контрольные суммы. Давайте обновим контрольные суммы соответствующим образом.
3. Обновить контрольные суммы
# Set S flag to F flag
if p.haslayer(TCP):
if p[TCP].flags = "S"
p[TCP].flags = "F"
# Update checksums
del p[IP].chksum
del p[TCP].chksum
p.show2()
Я использую функцию show2()
, которая пересчитывает контрольные суммы, если их нет. Это функционально, но громко, так как также печатает пакет. Я не нашел другого решения, которое делает это тихо.
Давайте попробуем сейчас.
❯ sudo tshark -f "tcp port 80 and host SERVER_IP"
1 0.000000000 CLIENT_IP → SERVER_IP TCP 74 44444 → 80 [FIN] Seq=1 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=2005448037 TSecr=0 WS=128
2 0.154677103 SERVER_IP → CLIENT_IP TCP 74 80 → 44444 [RST, ACK] Seq=1 Ack=2 Win=64768 Len=0 MSS=1420 SACK_PERM=1 TSval=1006897750 TSecr=2005448037 WS=128
Да! Сервер отвечает нам RST-ACK. Но наш клиент curl все еще не понимает, что он имеет в виду. Давайте теперь сопоставим все флаги соответствующим образом.
4. Сопоставление всех флагов (окончательный код)
#!/usr/bin/python3
from netfilterqueue import NetfilterQueue
from scapy import *
import os
S_ADDR = "SERVER_IP"
# Update iptables rules to intercept incoming and outcoming packets from/to S_ADDR
output_rule = f"iptables -A OUTPUT --destination {S_ADDR} -j NFQUEUE"
input_rule = f"iptables -A INPUT --source {S_ADDR} -j NFQUEUE"
flush_rules = "iptables -F OUTPUT && iptables -F INPUT"
os.system(input_rule)
os.system(output_rule)
# Dictionary with flag mapping
fd = {
"S":"F",
"RA":"SA",
"A":"E",
"PA":"FS"
}
# Append the inverted dictionnary
fd.update(dict((v,k) for k,v in fd.items()))
# Helper function to set the flags accordingly
def set_flags(flags):
return fd[flags] if flags in fd else flags
# Callback function that modifies our packets before sending them
def callback(raw_pkt):
# Get a scapy raw packet that we can modify
p = IP(raw_pkt.get_payload())
# Set flags accordingly
if p.haslayer(TCP):
p[TCP].flags = set_flags(str(p[TCP].flags))
# Calculate new chksums
del p[IP].chksum
del p[TCP].chksum
p.show2()
# Update
raw_pkt.set_payload(bytes(p))
# Now we tell nfqueue to accept the modified packet
raw_pkt.accept()
# Init nfqueue
q = NetfilterQueue()
q.bind(0, callback)
try:
q.run()
except KeyboardInterrupt:
q.unbind()
os.system(flush_rules)
Мы используем словарь для отображения всех флагов. Затем мы используем метод Dict.update()
, чтобы добавить те же кортежи, но поменяв местами ключи и значения. (например, "S": "F"
добавляет "F": "S"
).
Давайте теперь попробуем наш новый код.
❯ curl http://SERVER_IP --local-port 44444
<!doctype html>
<html>
<head>
<title>FreakyFlagday</title>
</head>
<body>
<p>It's freakyflagday and you've made it so far! Well done. All you've gotta do is download the text file from '/gimmedafile.txt' and it'll send you a big important file with the flag at the very end.</p>
</body>
</html>
❯ sudo tshark -f "tcp port 80 and host SERVER_IP"
1 0.000000000 MY_IP → SERVER_IP TCP 74 44444 → 80 [FIN] Seq=1 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=1784676225 TSecr=0 WS=128
2 0.145262609 SERVER_IP → MY_IP TCP 74 80 → 44444 [RST, ACK] Seq=1 Ack=2 Win=64768 Len=0 MSS=1420 SACK_PERM=1 TSval=786145437 TSecr=1784676225 WS=128
3 0.160854317 MY_IP → SERVER_IP TCP 66 44444 → 80 [ECN] Seq=2 Win=64256 Len=0 TSval=1784676385 TSecr=786145437
4 0.170014574 MY_IP → SERVER_IP HTTP 144 [TCP Port numbers reused] GET / HTTP/1.1
5 0.287621647 SERVER_IP → MY_IP TCP 66 80 → 44444 [ECN] Seq=1 Win=506 Len=0 TSval=786145607 TSecr=1784676385
6 0.290354873 SERVER_IP → MY_IP HTTP 220 [TCP Port numbers reused] HTTP/1.1 200 OK
7 0.311377282 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44444 → 80 [ECN] Seq=78 Win=501 Len=0 TSval=1784676535 TSecr=786145609
8 0.426628596 SERVER_IP → MY_IP TCP 376 [TCP Port numbers reused] 80 → 44444 [FIN, SYN] Seq=0 Win=506 Len=310 TSval=786145748 TSecr=1784676535
9 0.444996568 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44444 → 80 [ECN] Seq=78 Win=501 Len=0 TSval=1784676668 TSecr=786145748
10 0.449897000 MY_IP → SERVER_IP TCP 66 [TCP ACKed unseen segment] [TCP Retransmission] 44444 → 80 [FIN, ACK] Seq=78 Ack=465 Win=501 Len=0 TSval=1784676669 TSecr=786145748
11 0.562439239 SERVER_IP → MY_IP TCP 66 [TCP Retransmission] 80 → 44444 [FIN, ACK] Seq=310 Ack=1 Win=506 Len=0 TSval=786145885 TSecr=1784676669
12 0.583268378 MY_IP → SERVER_IP TCP 66 44444 → 80 [ECN] Seq=79 Win=501 Len=0 TSval=1784676807 TSecr=786145885
Похоже, что это сработало довольно хорошо. Похоже, что флаг находится в этом файле gimmedafile.txt
. Давайте попробуем получить этот файл.
❯ curl http://SERVER_IP/gimmedafile.txt --local-port 44445
Voluptatem ipsum [...39404 characters...] dolor ut.
flag{e2960da061a85fbcabb0670e4ddb9e93}
❯ sudo tshark -f "tcp port 80 and host SERVER_IP"
1 0.000000000 MY_IP → SERVER_IP TCP 74 44445 → 80 [FIN] Seq=1 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=1784824675 TSecr=0 WS=128
2 0.142799620 SERVER_IP → MY_IP TCP 74 80 → 44445 [RST, ACK] Seq=1 Ack=2 Win=64768 Len=0 MSS=1420 SACK_PERM=1 TSval=786293873 TSecr=1784824675 WS=128
3 0.159637964 MY_IP → SERVER_IP TCP 66 44445 → 80 [ECN] Seq=2 Win=64256 Len=0 TSval=1784824834 TSecr=786293873
4 0.165712720 MY_IP → SERVER_IP HTTP 159 [TCP Port numbers reused] GET /gimmedafile.txt HTTP/1.1
5 0.278158205 SERVER_IP → MY_IP TCP 66 80 → 44445 [ECN] Seq=1 Win=506 Len=0 TSval=786294039 TSecr=1784824834
6 0.281436371 SERVER_IP → MY_IP HTTP 227 [TCP Port numbers reused] HTTP/1.1 200 OK
7 0.286101671 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=161 Win=506 Len=1408 TSval=786294048 TSecr=1784824834
8 0.290101742 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=1569 Win=506 Len=1408 TSval=786294052 TSecr=1784824834
9 0.293428900 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=2977 Win=506 Len=1408 TSval=786294056 TSecr=1784824834
10 0.300100620 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=4385 Win=506 Len=1408 TSval=786294060 TSecr=1784824834
11 0.303058929 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=5793 Win=506 Len=1408 TSval=786294060 TSecr=1784824834
12 0.305803295 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=7201 Win=506 Len=1408 TSval=786294061 TSecr=1784824834
13 0.308359956 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=8609 Win=506 Len=1408 TSval=786294061 TSecr=1784824834
14 0.311371415 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=10017 Win=506 Len=1408 TSval=786294067 TSecr=1784824834
15 0.314342631 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=11425 Win=506 Len=1408 TSval=786294070 TSecr=1784824834
16 0.317448713 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=501 Len=0 TSval=1784824974 TSecr=786294043
17 0.322497523 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784824980 TSecr=786294048
18 0.345296346 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784824986 TSecr=786294052
19 0.363030999 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784824992 TSecr=786294056
20 0.374327694 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825009 TSecr=786294060
21 0.379388653 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825014 TSecr=786294060
22 0.384474051 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825020 TSecr=786294061
23 0.389563243 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825031 TSecr=786294061
24 0.395089952 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825038 TSecr=786294067
25 0.400325803 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825049 TSecr=786294070
26 0.429292547 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=12833 Win=506 Len=1408 TSval=786294190 TSecr=1784824974
27 0.432396491 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=14241 Win=506 Len=1408 TSval=786294190 TSecr=1784824974
28 0.435011224 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=15649 Win=506 Len=1408 TSval=786294195 TSecr=1784824980
29 0.437501469 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=17057 Win=506 Len=1408 TSval=786294195 TSecr=1784824980
30 0.455718674 SERVER_IP → MY_IP TCP 1276 [TCP Port numbers reused] 80 → 44445 [FIN, SYN] Seq=0 Win=506 Len=1210 TSval=786294218 TSecr=1784824986
31 0.459562289 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=1210 Win=506 Len=1408 TSval=786294222 TSecr=1784824986
32 0.473141161 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825125 TSecr=786294190
33 0.473160024 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=2618 Win=506 Len=1408 TSval=786294235 TSecr=1784824992
34 0.476471278 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=4026 Win=506 Len=1408 TSval=786294235 TSecr=1784824992
35 0.478176107 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825133 TSecr=786294190
36 0.483052912 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=5434 Win=506 Len=1408 TSval=786294246 TSecr=1784825009
37 0.489734170 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825140 TSecr=786294195
38 0.490446940 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=6842 Win=506 Len=1408 TSval=786294246 TSecr=1784825009
39 0.495628297 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=8250 Win=506 Len=1408 TSval=786294252 TSecr=1784825014
40 0.498457646 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=9658 Win=506 Len=1408 TSval=786294252 TSecr=1784825014
41 0.502477260 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825148 TSecr=786294195
42 0.503250048 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=11066 Win=506 Len=1408 TSval=786294256 TSecr=1784825020
43 0.508097290 SERVER_IP → MY_IP TCP 1474 80 → 44445 [ECN] Seq=12474 Win=506 Len=1408 TSval=786294261 TSecr=1784825031
44 0.529693000 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825165 TSecr=786294218
45 0.547629433 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825177 TSecr=786294222
46 0.569959311 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825198 TSecr=786294235
47 0.574834351 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825216 TSecr=786294246
48 0.582698364 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825234 TSecr=786294252
49 0.590039492 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=497 Len=0 TSval=1784825245 TSecr=786294256
50 0.640226149 SERVER_IP → MY_IP TCP 651 [TCP Port numbers reused] 80 → 44445 [FIN, SYN] Seq=0 Win=506 Len=585 TSval=786294402 TSecr=1784825165
51 0.663333652 MY_IP → SERVER_IP TCP 66 [TCP Keep-Alive] 44445 → 80 [ECN] Seq=93 Win=501 Len=0 TSval=1784825334 TSecr=786294402
52 0.669714648 MY_IP → SERVER_IP TCP 66 [TCP ACKed unseen segment] [TCP Retransmission] 44445 → 80 [FIN, ACK] Seq=93 Ack=32933 Win=501 Len=0 TSval=1784825335 TSecr=786294402
53 0.776541498 SERVER_IP → MY_IP TCP 66 [TCP Retransmission] 80 → 44445 [FIN, ACK] Seq=585 Ack=1 Win=506 Len=0 TSval=786294541 TSecr=1784825335
54 0.793113070 MY_IP → SERVER_IP TCP 66 44445 → 80 [ECN] Seq=94 Win=501 Len=0 TSval=1784825466 TSecr=786294541
И вуаля! Вот второй флаг: flag{e2960da061a85fbcabb0670e4ddb9e93}
Надеюсь, вам понравилось это введение в манипулирование пакетами. Я хотел бы поблагодарить nneonneo и Kkevsterrr за их объяснения. Присоединяйтесь к Nahamsec на Discord, чтобы связаться с ними.
До следующего раза!
Дальнейшие лекции
- Переходы состояний TCP – T/TCP (Transaction TCP) для Linux
- Диаграмма переходов состояний TCP/IP (RFC793)
- RFC-793 на ietf.org
- Руководство по TCP/IP
- Использование nfqueue в python
- Документация по Scapy
- О вычислении контрольных сумм с помощью Scapy на Stack Overflow
- python-netfilterqueue на Github
- Проект Netfilter
- Флаги TCP на KeyCDN