Spending spring days crafting packets at NahamCon 2022

f0rked - May 30 '22 - - Dev Community

A CTF writeup of Networking challenges at NahamCon 2022

why not both?

NahamCon2022 is over and we're glad we managed to finish in top 5% with our team at Stack Labs (3049 points, 195 on 4034 teams). This year, they came up with exciting Networking challenges made by @Kkevsterrr#7469. Although we didn't manage to solve these, it was a nice introduction to network analysis and packets manipulation using Scapy.

Here's what I learned.

Challenges:

  • 1. Contemporaneous Open - hard - 14 solves - 500 points - first blooded by StaticFlow
  • 2. Freaky Flag Day - hard - 9 solves - 500 points - first blooded by Maple Bacon

1. Contemporaneous Open

Author: @Kkevsterrr#7469
We want to give you the flag, we really do. just give us a TCP HTTP server to send it to, and we'll make a POST request with all the deets you need! we've just got a firewall issue on our side and we're dropping certain important packets (specifically, any inbound SYN+ACK packet is dropped). Shouldn't be a problem for a networking pro like you, though - just make a TCP server that doesn't need to send those!

Tools used:

  • scapy for packet manipulation
  • tshark wireshark's cli version

Traditional 3-way handshake

Let's start with some useful reminders about TCP Protocol. In a traditional TCP 3-way handshake:

  1. the client sends a SYN (Synchronize Sequence Number) to inform the server he wants to start a communication. The SYN signifies with what sequence number it will start the segments with.
  2. the server responds to the client with SYN-ACK. The ACK signifies the response of client's SYN. Meanwhile the SYN signifies with what sequence number it will start the segments with.
  3. Finally, the client acknowledges (ACK) the response of the server and the connection enters the ESTABLISHED state so they can start exchanging data.

TCP 3-Way Handshake

SYN-ACK get dropped by a distrustful firewall

In this challenge the flag is given by a POST request to a server we're supposed to create at port 80. The thing is: the client's firewall drops every inbound SYN-ACK packet. So we must find a solution to establish a connection without having to send a SYN-ACK-flagged packet.

SYN-ACKs get dropped by client's firewall

While I was spending hours refreshing my memory about TCP/IP internals, learning the basics of scapy and exploring many ways to solve the problem (fragmenting the SYN/ACK response, attempting an HTTP3 over QUIC conection), the solution was laying in the challenge's name.

TCP Simultaneous Open

Contemporaneous Open is a reference to TCP's Simultaneous Open state transition. Also called "simultaneous active open on both side", it refers to an old TCP feature used to handle edge cases in TCP handshakes, as when a server and a client send a SYN to each other at the same time. This process makes it possible for two applications to send a SYN to each other to start a TCP connection.

TCP Simultaneous Open

When both ends send a SYN at the same time, both ends enter the SYN_SENT state. When they receive the SYN, their state changes to SYN_RCVD and they resend the SYN and acknowledge the received SYN. When they both receive the SYN and the acknowledged SYN, the connection enters the ESTABLISHED state. In such a state, both ends act as a client and server.

So the client doesn't need a SYN/ACK answer from the server in order to complete the three-way handshake. All we need is to send a SYN and wait for the client's SYN/ACK to establish the connection and start HTTP exchange. This can save us from sending a SYN/ACK packet that would be dropped by the client. We'll use Scapy to mimic a server with such a behavior.

Crafting with scapy

Before executing any Scapy scripts, we must disable the Linux Kernel's response to avoid any RST-flagged answers. Scapy operates in user space so the Kernel has no idea of what Scapy is doing.

Using iptables, we can drop RST-flagged packets the kernel sends from port 80.

sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST --sport 80 -j DROP
Enter fullscreen mode Exit fullscreen mode

The idea is to create a customized server that does the following:

  • receive client's SYN (seq=n)
  • send a SYN (seq=m)
  • send a ACK (seq=m+1, ack=n+1) to acknowledge client's SYN
  • client acknowledges our SYN, receives our ACK and establish session

We'll use tshark as a complementary tool to track the incoming packets.

1. First, let's get client's SYN packet.

Note that IP addresses have been changed to "MY_IP" and "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}%")
)
Enter fullscreen mode Exit fullscreen mode

We connect to the challenge:

❯ 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...
Enter fullscreen mode Exit fullscreen mode
# server.py output
Received SYN from CLIENT_IP:http, seq=510141201
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

Great! We now can use the IP and port for sending our packets.

2. Now let's send our SYN and acknowledge client's 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}")
Enter fullscreen mode Exit fullscreen mode
Received SYN from CLIENT_IP:http, seq=3800804531
Send SYN with seq=1234
Send ACK with seq=1235 and ack=3800804532
Enter fullscreen mode Exit fullscreen mode

We'll use the flag -z "follow,tcp,hex,0" to display the contents of the first TCP Stream (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
===================================================================
Enter fullscreen mode Exit fullscreen mode

And so we get this lovely flag:
flag{6acfdfc9369eadfdb9439b0ac3969711}

Yaaay!

Improvements

A much more elegant solution would be to act like a real HTTP server by acquitting each client's packet, answering the POST request and then gracefully ending the connection when the client sends a FIN.

Check out nneonneo's solution that implements this feature in addition to using an asynchronous sniffer that pushes incoming packets to a queue, making client's packets easier to iterate on. Also he uses many helper functions to track, filter and incoming and outcoming packets. I would definitely use these as a template for next challenges. Clean and smooth.

In the next part, we'll cover the Freaky Flag Day challenge. I hope you enjoy poetry and TCP flags.

2. Freaky Flag Day

Is this a FIN?

Author: @Kkevsterrr#7469

Our TCP flags have decided that they'd like to change places today; all you need to do is reach the HTTP server!

Roses are red, and SYNs are FINs too.

RST+ACKs are now SYN+ACKs for you.

ACKs are now Es, and what else have we done?

PSH+ACKs are FIN+SYNs just for the fun.

Hint: If you want to run this challenge from your home or a VM, make sure you are not behind a NAT that could eat your unexpected packets.

Interact with this challenge at: http://SERVER_IP

Tools used:

  • scapy for packet manipulation
  • tshark wireshark's cli version
  • curl for the http client
  • nfqueue to intercept packets queued by the kernel packet filter

So after defeating a suspicious client that drops every incoming SYN/ACK packet, our next challenger a server that swaps the flags of every request (both inbound and outbound).

Want to establish a session? The server understands that you want to finish it.
Want to acknowledge that a packet is successfully received? The server understands that you're under a network congestion. Nonsense.

The goal is to reach the server correctly. Thus we must speak his language. The flags mapping is the following:

S ⇔ F

RA ⇔ SA

A ⇔ E

PA ⇔ FS

Preliminary tests

Let's try to connect to the server using curl.

❯ curl --local-port 44444 http://SERVER_IP
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

As expected, nothing happens as the SYN flag is interpreted as a FYN flag on server-side.

Now let's use scapy and send a FIN-flagged with a random sequence number segment.

#! /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)
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

The server understands "SYN" and sends us RST-ACK (which is SYN-ACK). So now he must be waiting for a a ECN (which is an ACK)... And so on. You got it.

Packet interception and modification with scapy and nfqueue

We could rewrite an HTTP client with scapy using the modified flags. But it is a long and tedious task as we must craft every request with scapy. A less painful solution would be to have a layer that intercepts every packet and just update the flags before sending/accepting them. And that's where nfqueue can help us.

Using a standard http client like curl, intercept all the incoming and outcoming packages with nfqueue, pass them to our scapy script that will change the flags accordingly and then send the modified packets to their destination.

Netfilter Queue is an iptables target which gives the decision on packets to the userspace. It is part of the Netfilter project that also provides iptables and nftables. It is commonly used as a proxy or for Man in the Middle attacks.

To intercept packets with nfqueue you must set firewall's chain rules accordingly. In this situation we want to intercept:

  • incoming packets from SERVER_IP (INPUT chain)
  • outgoing packets to SERVER_IP (OUTPUT chain)

Intercepting packets with nfqueue

Let's try this.

1. Let's set 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)
Enter fullscreen mode Exit fullscreen mode

First we update iptables rules to intercept the interesting packets to nfqueue. Then we init nfqueue and bind the queue number 0 to our callback function. We use the same queue for both incoming and outgoing packets.

In our callback function, we get a raw packet. To manipulate it, we create a scapy IP layer with the packet's payload as an argument. We print it and finally we accept the packet. If we interrupt the process (with Ctrl+C) then we unbind the queue and flush the iptables rules.

Important: check your INPUT and OUTPUT chain rules before flushing it as it flushes everything.

Let's see if it works.

❯ curl http://SERVER_IP --local-port 44444
Enter fullscreen mode Exit fullscreen mode
# 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)]
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

Great! Curl tries to initiate the session with the server using a SYN-flagged packet. When the packet reaches the raw_pkt.accept() instruction, it is sent to the client and we can notice it in tshark's output.

2. Changing the flag

In our callback function, let's try changing the packet's flag with 'F' and see what happens.

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()
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

Something is wrong, we send a FIN but get no answer... Oh right! checksums, of course. Let's update checksums accordingly.

3. Update checksums

# 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()
Enter fullscreen mode Exit fullscreen mode

I use the function show2() that recalculate checksums if they are none. This is functional but loud as it also prints the packet. I didn't find any other solution that does it quietly.

Let's try now.

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

Enter fullscreen mode Exit fullscreen mode

Yes! The server answers us back with a RST-ACK. But our curl client still doesn't understand what he means by that. Let's map all the flags accordingly now.

4. Map all the flags (final code)

#!/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)
Enter fullscreen mode Exit fullscreen mode

We use a dictionary to map all the flags. And then we use the Dict.update() method to add the same tuples but with the keys and values swaped. (e.g. "S":"F" adds "F":"S").

Let's try our new code now.

❯ curl http://SERVER_IP --local-port 44444
Enter fullscreen mode Exit fullscreen mode
<!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>
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

Seems like it worked pretty well. The flag seems to be in this gimmedafile.txt file. Let's try and get the file.

❯ curl http://SERVER_IP/gimmedafile.txt --local-port 44445
Voluptatem ipsum [...39404 characters...] dolor ut.

flag{e2960da061a85fbcabb0670e4ddb9e93}
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

And voilà! Here's the second flag: flag{e2960da061a85fbcabb0670e4ddb9e93}
So excited

I hope you enjoyed this introduction to packets manipulation. I would like to thank nneonneo and Kkevsterrr for their explanations. Join Nahamsec on Discord to reach them out.
Until next time!

Further Lectures

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .