Главная

Sunday, 12 June 2022

Scapy packet manipulation.

Всем привет.

Этим я открываю небольшую серию по итогам презентации на тему "Анализ сетевых пакетов" от автора "CE 340/S. Kondakci, IEU, Computer Engineering". По ходу будет масса примеров анализа с помощью таких инструментов как Scapy, Nmap, Nping и tcpdump. Устраивайтесь поудобнее и начнем. А начнем мы со Scapy.

Scapy packet manipulation agenda:

  • Creating a packet
  • Send/Receiving packets
  • Basic Scapy commands
  • Capturing packets and reading packet capture files into Scapy
  • Layering packets

Four steps before:

1. Install Python 3.5+

2. Download and install Scapy

3. Install additional software for special features (optional)

4. Run Scapy with root privileges.


Example: Hello World

send(IP(dst= 127.0.0.1 )/ICMP()/ HelloWorld )

  • send - this tells Scapy that you want to send a packet (just a single packet)
  • IP - the protocol of the packet you want to create
  • (dst= 127.0.0.1 ) - the destination IP to send the packet to 
  • /ICMP() - Create an ICMP packet with the default values provided by Scapy
  • / HelloWorld  - the payload to include in the ICMP packet


Wireshark capture:

Scapy command: send(IP(dst= 127.0.0.1 )/ICMP()/ HelloWorld )

Wireshark output: 

Internet Protocol Version 4, Src: 127.0.0.12 (127.0.0.12), Dst: 127.0.0.1 (127.0.0.1)

Protocol: ICMP

Data: 48656c6c6f576f726c64 or  HelloWorld 


Example: fabricate an ICMP Packet

send(IP(src= 127.0.0.1 , dst= 127.0.0.1 , ttl=128)/ICMP()/ HelloWorld )

Wireshark:

Internet Protocol Version 4, Src: 127.0.0.1 (127.0.0.1), Dst: 127.0.0.1 (127.0.0.1) Time to live: 128


What does this ICMP packet mean?

Internet Protocol Version 4, Src: 127.0.0.1 (127.0.0.1), Dst: 127.0.0.1 (127.0.0.1) Internet Control Message Protocol

Type: 0 (Echo (ping) reply)


Sending a ping packet:

ip=IP() # Creates an IP header ip.src=’192.168.1.25? # Source address in the IP header with local IP ip.dst =’ 192.168.1.100? # Destination address in the IP header. icmp=ICMP() # Creates an ICMP header icmp.type=8 # Type value inserted in ICMP header as 8 for ping icmp.code=0 # Code value inserted in ICMP header as 0 for ping send(ip/icmp) # Sending ping packet. 


Sending a ping packet with random source IP:

ip=IP() # Creates an IP header ip.src=RandIP() # The source address in the IP header with a random IP ip.dst =’ 192.168.1.100? # Destination address in the IP header. icmp=ICMP() # Creates an ICMP header icmp.type=8 # Type value inserted in ICMP header as 8 for ping crafting icmp.code=0 # Code value inserted in ICMP header as 0 for ping crafting. send(ip/icmp) # Sending ping packet. 


Sending & receiving Layer 3 and 2 packets:

sr() – This function sends packets and receives answers. It returns a couple of packet and answers, and the unanswered packets.

sr1() - This function is a variant that only returns one packet which answered the sent packet sent.

Exp:  Simple ICMP packet (layer 3)

h=sr1(IP(dst= 127.0.0.1 )/ICMP()/ Hello World )

srp() - This function does the same for layer 2 packets (Ethernet, 802.3, etc).


Show the packet contents:

h=sr1(IP(dst= 127.0.0.1 )/ICMP()/ Hello World )

h.show()

###[ IP ]###

version= 4L

ihl= 5L

tos= 0x0

len= 38

id= 7395

flags=

frag= 0L

ttl= 64

proto= icmp

chksum= 0x83d7

src= 127.0.0.1

dst= 127.0.0.1

\options\

###[ ICMP ]###

type= echo-reply

code= 0

chksum= 0x0

id= 0x0

seq= 0x0

###[ Raw ]###

load= 'HelloWorld'

###[ Padding ]###

load= '\x00\x00\x00\x00\xe7\x03N\x99'

>>>


Show the TTL of the ICMP reply packet:

ip=IP() # Create an IP header

ip.src=’192.168.1.25? # Source address in the IP header is the loca IP 

ip.dst =’ 192.168.1.100? # Destination address in the IP header.

icmp=ICMP() # Create an ICMP header

icmp.type=8 # Type value inserted in ICMP header as 8 for ping crafting

icmp.code=0 # Code value inserted in ICMP header as 0 for ping crafting.

p=sr1(ip/icmp) # Send and receive the packet in the variable p

p.ttl # Displays the TTL value in the received IP header of the packet. 


Create an ARP request:

ether=Ether() # Creates an ethernet header

ether.src=’00:e0:1c:3c:22:b4? # Source MAC address in the ethernet header

ether.dst=’FF:FF:FF:FF:FF:FF’ # Destination MAC address 

arp=ARP() # Create an ARP header

arp.op=1 # Set the ARP type as 1

arp.hwsrc=’00:e0:1c:3c:22:b4? # Set the sender MAC address for local IP 

arp.psrc=’192.168.1.25? # Set the sender IP address for that MAC addr.

arp.pdst=’192.168.1.100? # Set the target IP address 

arp.hwdst=’00:00:00:00:00:00? # Set the target MAC address as NULL

p=srp1(ether/arp) # Send the packet at layer 2 using the command srp1, appending the ether and arp headers. 


UDP Scanning:

  • No handshake, so less useful than TCP scans
  • Much more powerful in newer versions of Nmap
  • Sends valid UDP requests to well-known ports
  • Send a DNS query to port 53, etc.
  • Response indicates open UDP port


TCP Packets:

p=sr(IP(dst= 127.0.0.1 )/TCP(dport=23))

Begin emission:

.Finished to send 1 packets.

*

Received 2 packets, got 1 answers, remaining 0 packets

>>> p

(<Results: TCP:1 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

>>>

If you try and use p.show() you now get an error message:

>>> p.show()

Traceback (most recent call last):

File  <console> , line 1, in <module>

AttributeError: 'tuple' object has no attribute 'show'

>>> ans.summary()

IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:telnet S ==> IP / TCP 127.0.0.1:telnet > 127.0.0.1:ftp_data

RA / Padding


a=sr(IP(dst= 127.0.0.1 )/TCP(dport=[23,80,53]))


Begin emission:

.**Finished to send 3 packets.

*

Received 4 packets, got 3 answers, remaining 0 packets

>>> a

(<Results: TCP:3 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

>>>


TCP SYN to port 80:

tcp=TCP() # Create a TCP header tcp.dport=80 # The destination port in the TCP header is 80. tcp.flags=’S’ # Set the flag in the TCP header with the SYN bit. ip=IP() # Create an IP header ip.src=’192.168.1.25? # Source address in the IP header is local IP address ip.dst =’ 192.168.1.100? # Destination address in the IP header.

send(ip/tcp) # Send the crafted tcp packet. 


Details of the TCP packet:

>>> p

(<Results: TCP:3 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

>>>

>>> ans,unans=_

>>> ans.summary()

IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:telnet S ==> IP / TCP 127.0.0.1:telnet > 127.0.0.1:ftp_data

RA / Padding

IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:http S ==> IP / TCP 127.0.0.1:http > 127.0.0.1:ftp_data SA /

Padding

IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:domain S ==> IP / TCP 127.0.0.1:domain >

127.0.0.1:ftp_data SA / Padding

>>>


The http (port 80) packet:

IP / TCP 127.0.0.15:ftp_data > 127.0.0.1:http S ==> IP / TCP 127.0.0.1:http > 127.0.0.15:ftp_data SA /

Padding

S = SYN from client (request from the client))

SA = SYN-ACK from the server (reply from the server)


The telnet (port 23) Packet:

IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:telnet S ==> IP / TCP 127.0.0.1:telnet > 127.0.0.1:ftp_data RA / Padding

SYN Sent from the source

Destination responded with a RSTACK (RA) which is a RESet & ACKnowledge flag in the TCP packet telling the source to reset the connection


Port Scan (TCP-SYN Scan):

a=sr(IP(dst= 127.0.0.1 )/TCP(sport=666,dport=[22,80,21,443], flags= S ))

Source port=666

Destination ports: 22,80,21,and 443

flags= S = SYN scan

>>> p=sr(IP(dst= 127.0.0.1 )/TCP(sport=666,dport=[22,80,21,443], flags= S ))

Begin emission:

***Finished to send 4 packets.

*

Received 4 packets, got 4 answers, remaining 0 packets

>>> p

(<Results: TCP:4 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

>>> ans,unans=_

>>> ans.summary()

IP / TCP 127.0.0.15:666 > 127.0.0.1:ssh S ==> IP / TCP 127.0.0.1:ssh > 127.0.0.15:666 SA / Padding

IP / TCP 127.0.0.15:666 > 127.0.0.1:http S ==> IP / TCP 127.0.0.1:http > 127.0.0.15:666 SA / Padding

IP / TCP 127.0.0.15:666 > 127.0.0.1:ftp S ==> IP / TCP 127.0.0.1:ftp > 127.0.0.15:666 RA / Padding

IP / TCP 127.0.0.15:666 > 127.0.0.1:https S ==> IP / TCP 127.0.0.1:https > 127.0.0.15:666 RA /

Padding

>>>

TCP ACK flag sent after SYN flag:

>>> p=sr(IP(dst= 127.0.0.1 )/TCP(sport=888,dport=[21,22,80,443], flags= A ))

Begin emission:

.***Finished to send 4 packets.

*

Received 5 packets, got 4 answers, remaining 0 packets

>>> p

(<Results: TCP:4 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

>>> ans,unans=_

>>> ans.summary()

IP / TCP 127.0.0.15:888 > 127.0.0.1:ftp A ==> IP / TCP 127.0.0.1:ftp > 127.0.0.15:888 R / Padding

IP / TCP 127.0.0.15:888 > 127.0.0.1:ssh A ==> IP / TCP 127.0.0.1:ssh > 127.0.0.15:888 R / Padding

IP / TCP 127.0.0.15:888 > 127.0.0.1:http A ==> IP / TCP 127.0.0.1:http > 127.0.0.15:888 R / Padding

IP / TCP 127.0.0.15:888 > 127.0.0.1:https A ==> IP / TCP 127.0.0.1:https > 127.0.0.15:888 R / Padding

>>>

Notice: 

the A (ACK) flag on the sent packet, with a R (RST) flag on the response, why? 

Because we sent a packet that it's only supposed to receive after a SYN-ACK packet and so it's reset by the destination.


DNS Query:

sr1(IP(dst= 127.0.0.1 )/UDP()/DNS(rd=1,qd=DNSQR(qname= www.ieu.edu.tr )))

dst=27.0.0.1 = destionation IP (DNS server)

/UDP() = DNS uses UDP protocol

/DNS = This is a DNS packet

rd=1 = Telling Scapy that recursion is desired

qd=DNSQR(qname= www.ieu.edu.tr ) = Get the DNS info about www.ieu.edu.tr


Traceroute:

traceroute ([ www.google.com ], maxttl=20)

Begin emission:

..*Finished to send 20 packets.

*****************

Received 20 packets, got 18 answers, remaining 2 packets

74.125.132.99:tcp80

1 172.1.16.2 11

3 80.3.129.161 11

4 212.43.163.221 11

5 62.252.192.157 11

6 62.253.187.178 11

17 74.125.132.99 SA

18 74.125.132.99 SA

19 74.125.132.99 SA

20 74.125.132.99 SA

(<Traceroute: TCP:7 UDP:0 ICMP:11 Other:0>, <Unanswered: TCP:2 UDP:0 ICMP:0 Other:0>)

>>>


ARP scan on A network:

>>> arping( 172.1.16.* )

***Finished to send 256 packets.

*

Received 4 packets, got 4 answers, remaining 252 packets

30:46:9a:83:ab:70 172.1.16.2

00:25:64:8b:ed:1a 172.1.16.18

00:26:55:00:fc:fe 172.1.16.12

d8:9e:3f:b1:29:9b 172.1.16.22

(<ARPing: TCP:0 UDP:0 ICMP:0 Other:4>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:252>)


ICMP, TCP, and UDP Ping:

ans,unans=sr(IP(dst= 172.1.1-254 )/ICMP())

ans,unans=sr( IP(dst= 172.1.1.* )/TCP(dport=80, flags= S ) )

ans,unans=sr( IP(dst= 172.1.1.* /UDP(dport=0) )


Packet Sniffing:

sniff()

CTRL-C (to stop sniffing) get something like

<Sniffed: TCP:43 UDP:24 ICMP:2 Other:0>

a=_

a.nsummary()

0003 Ether / IP / UDP / DNS Qry  daisy.ubuntu.com. 

0004 Ether / IP / UDP / DNS Qry  daisy.ubuntu.com. 

0005 Ether / IP / UDP / DNS Qry  daisy.ubuntu.com. 

0006 Ether / IP / UDP / DNS Qry  daisy.ubuntu.com. 

0007 Ether / IP / UDP / DNS Qry  daisy.ubuntu.com. 

0008 Ether / IP / UDP / DNS Ans  91.189.95.54 

0009 Ether / IP / UDP / DNS Ans  91.189.95.54 

0010 Ether / IP / UDP / DNS Ans  91.189.95.54 

0011 Ether / IP / UDP / DNS Ans  91.189.95.55


ICMP traffic through eth0 interface:

sniff(iface= eth0 , filter= icmp , count=10)

a=_

>>> a.nsummary()

0000 Ether / IP / ICMP / IPerror / UDPerror / DNS Ans  91.189.95.55 

0001 Ether / IP / ICMP / IPerror / UDPerror / DNS Ans  91.189.95.54 

0002 Ether / IP / ICMP 10.1.99.25 > 74.125.132.103 echo-request 0 / Raw

0003 Ether / IP / ICMP 74.125.132.103 > 10.1.99.25 echo-reply 0 / Raw

0004 Ether / IP / ICMP 10.1.99.25 > 74.125.132.103 echo-request 0 / Raw

0005 Ether / IP / ICMP 74.125.132.103 > 10.1.99.25 echo-reply 0 / Raw

0006 Ether / IP / ICMP 10.1.99.25 > 74.125.132.103 echo-request 0 / Raw

0007 Ether / IP / ICMP 74.125.132.103 > 10.1.99.25 echo-reply 0 / Raw

0008 Ether / IP / ICMP / IPerror / UDPerror / DNS Ans  wb-in-f103.1e100.net. 

0009 Ether / IP / ICMP / IPerror / UDPerror / DNS Ans  wb-in-f103.1e100.net. 

a[2]

<Ether dst=30:46:9a:83:ab:70 src=00:22:19:e7:90:ae type=0x800 |<IP version=4L ihl=5L tos=0x0

len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0xfeaa src=10.1.99.25 dst=74.125.132.103


Writing a Python Script:



Pcap file from tcpdump:

Script output:



No comments:

Post a Comment

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