Setting up BGP Layer Using Scapy - python

I am trying to use Scapy to send packets that have a BGP layer
I am currently stuck on a rudimentary part of this problem because I am unable to set up the BGP layer. I followed the instructions to set up the regular IP and TCP Layer.
Eg:
>>a=IP(src="192.168.1.1",dst="192.168.1.2")/TCP(sport=179,dport=50)
But the problem arises when I do this:
>>a=a/BGP()
NameError: name BGP is not defined
I have seen the BGP implementations in the contrib file from Scapy Github (https://github.com/secdev/scapy/blob/9201f1cf1318edd5768d7e2ee968b7fba0a24c5e/scapy/contrib/bgp.py) so I think Scapy does support BGP implementations
I am new to networking so I was wondering if you could help me set up the BGP layer
Thanks for taking the time to read this!

We want a BGP Layer using scapy. As BGP travels over TCP. So we must have a established (3 way handshake) tcp socket. And TCP travels over IP. Thus we can represent full packet in the below format.
packet = IP Layer / TCP Layer / BGP Layer
But BGP itself is divided in two parts, BGP Header and BGP Payload (EG: OPEN, UPDATE, etc ). So the above layer is represented as given below.
packet = IP Layer / TCP Layer / BGP Header / BGP payload
Here BGP Header specifies authentication, length and type of BGP Payload.To represent whole thing in scapy, we can do the following excercise. (I am assuming here that you have an established TCP socket.)
from scapy.layers.inet import IP, TCP
from scapy.contrib.bgp import BGPHeader, BGPUpdate, BGPPathAttr, BGPNLRI_IPv4, BGPPALocalPref
base = IP(src=src_ipv4_addr, dst=dst_ipv4_addr, proto=6, ttl=255) # proto=6 represents that, TCP will be travelling above this layer. This is simple IPV4 communication.
tcp = TCP(sport=established_port, dport=179, seq=current_seq_num, ack=expected_seq_num, flags='PA') # dport=179 means, we are communicating with bgp port of the destination router/ host. sport is a random port over which tcp is established. seq and ack are the sequence number and acknowledgement numbers. flags = PA are the PUSH and ACK flags.
hdr = BGPHeader(type=2, marker=0xffffffffffffffffffffffffffffffff) # type=2 means UPDATE packet will be the BGP Payload, marker field is for authentication. max hex int (all f) are used for no auth.
up = BGPUpdate(path_attr=[BGPPathAttr(type_flags=64, type_code=5, attribute=BGPPALocalPref(local_pref=100))], nlri=BGPNLRI_IPv4(prefix=NLRI_PREFIX)) # update packet consist of path attributes and NLRI (Network layer reachability information), type_code in path attributes is for which type of path attribute it is. [more][3]
packet = base / tcp / hdr / up
packet.show2()
Using the following variable values (for example purpose).
src_ipv4_addr = '10.110.99.2' # eth0
dst_ipv4_addr = '10.110.99.50'
established_port = 1223
expected_seq_num=1000 # ack
current_seq_num=1500 # seq
NLRI_PREFIX = '10.110.99.0/24'
Output will be following.
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 74
id = 1
flags =
frag = 0
ttl = 255
proto = tcp
chksum = 0xe09c
src = 10.110.99.2
dst = 10.110.99.50
\options \
###[ TCP ]###
sport = 1223
dport = bgp
seq = 1500
ack = 1000
dataofs = 5
reserved = 0
flags = PA
window = 8192
chksum = 0x102d
urgptr = 0
options = []
###[ HEADER ]###
marker = 0xffffffffffffffffffffffffffffffff
len = 34
type = UPDATE
###[ UPDATE ]###
withdrawn_routes_len= 0
\withdrawn_routes\
path_attr_len= 7
\path_attr \
|###[ BGPPathAttr ]###
| type_flags= Transitive
| type_code = LOCAL_PREF
| attr_len = 4
| \attribute \
| |###[ LOCAL_PREF ]###
| | local_pref= 100
\nlri \
|###[ IPv4 NLRI ]###
| prefix = 10.110.99.0/24

Just going to try and help here. I have zero experience with BGP type packets, but... I copied the bgp.py file from the link you provided into scapy/layers. Using ls() I found the following:
BGPAuthenticationData : BGP Authentication Data
BGPErrorSubcodes : BGP Error Subcodes
BGPHeader : BGP header
BGPNotification : BGP Notification fields
BGPOpen : BGP Open Header
BGPOptionalParameter : BGP Optional Parameters
BGPPathAttribute : BGP Attribute fields
BGPUpdate : BGP Update fields
I could then use say ls(BGPUpdate) to show this:
withdrawn_len : ShortField = (None)
withdrawn : FieldListField = ([])
tp_len : ShortField = (None)
total_path : PacketListField = ([])
nlri : FieldListField = ([])
and was able to create this packet:
pkt = pkt = IP()/TCP()/BGPUpdate()
pkt.show()
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = tcp
chksum = None
src = 127.0.0.1
dst = 127.0.0.1
\options \
###[ TCP ]###
sport = ftp_data
dport = http
seq = 0
ack = 0
dataofs = None
reserved = 0
flags = S
window = 8192
chksum = None
urgptr = 0
options = {}
###[ BGP Update fields ]###
withdrawn_len= None
withdrawn = []
tp_len = None
\total_path\
nlri = []
I'm not sure what all of the different types of BGP layers/packets are used for or where the Communities Number would be set. Possibly in BGPPathAttribute(type=x). Type 5 is "LOCAL_PREF" which may correspond to Community Values. Try this Link.
pkt = BGPPathAttribute(type=5)
pkt.show()
###[ BGP Attribute fields ]###
flags = Transitive
type = LOCAL_PREF
attr_len = None
value = ''
Anyway, hope that helps a little.
Edit:
Forgot. I also added "bgp" to the load_layers section of scapy/config.py. Line 373. Like this:
load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs", "hsrp", "inet6", "ir", "isakmp", "l2tp",
"mgcp", "mobileip", "netbios", "netflow", "ntp", "ppp", "radius", "rip", "rtp",
"sebek", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6", "llmnr", "sctp", "vrrp",
"ipsec","bgp"]

Related

Sending UDP RAW Data on windows with python

I want to sending simple data with UDP protocol, I have tested on ubuntu and it worked, here's my code
import time
import socket
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
s.bind(("eno2", 0))
ethernet = b'\x7c\x10\xc9\x51\xb7\x7b' # MAC Address Destination
ethernet += b'\x7c\x10\xc9\x51\xb3\xd6' # MAC Address Source
ethernet += b'\x08\x00' # Protocol-Type: IPv4
ip_header = b'\x45\x00\x00\x2a' # Version, IHL, Type of Service | Total Length
ip_header += b'\xa0\x6a\x00\x00' # Identification | Flags, Fragment Offset
ip_header += b'\x80\x11\x0e\xe5' # TTL, Protocol | Header Checksum
ip_header += b'\xc0\xa8\x05\x0f' # Source Address
ip_header += b'\xc0\xa8\x05\x14' # Destination Address
udp_header = b'\xce\x55\xc3\x55' # Source Port | Destination Port
udp_header += b'\x00\x16\x1f\x83' # Sequence Number
data = b'\x68\x65\x6c\x6c'
data += b'\x6f\x20\x77\x6f'
data += b'\x72\x6c\x64\x20'
data += b'\x31\x31'
padding = b'\x00\x00\x00\x00'
packet = ethernet + ip_header + udp_header + data
while True:
s.send(packet)
time.sleep(0.5)
but when I running on windows AF_PACKET has no attribute on Socket module, so what similar AF_PACKET on windows? someone can help me?
I just want to send UDP RAW Data on windows

How to depickle the object same as it was sent from socket in python?

I am trying to serialize the object (using pickle) of type scapy.layers.l2.Ether (please refer scapy import statement of code). But while deserializing that object, it is not being retrieved properly (also shown in output). How can I fix this ? and get the object as same as it was serialized ?
import socket
import time
import pickle #serialize the object
import sys # for command line argument
from scapy.all import * # for packet sniffing
HEADERSIZE = 10
clientsocket = 1
address = 1
#for serialization
def pkt_callback(pkt):
pkt.show()
msg = pickle.dumps(pkt)
print(12)
clientsocket.send(msg)
#check whether ip and port number provided or not
if len(sys.argv) != 3:
print ("Correct usage: script, IP address, port number", flush = True)
exit(0)
IP_address = str(sys.argv[1])
Port = int(sys.argv[2])
#socket creation ipv6
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.bind((IP_address, Port))
s.listen(5)
while True:
# now our endpoint knows about the OTHER endpoint.
clientsocket, address = s.accept()
print(f"Connection from {address} has been established.",flush=True)
sniff(iface="wlp3s0", prn=pkt_callback, filter="tcp", store=0)
clientsocket.close()
print('Data sent to server')
The above code serializes the object to be sent at pkt_callback
Now below code is from receiving side which deserializes the object
import socket
import pickle
import sys
from scapy.all import * # for packet sniffing
HEADERSIZE = 10
#check whether address and port number given or not
if len(sys.argv) != 3:
print ("Correct usage: script, IP address, port number",flush = True)
exit(0)
IP_address = str(sys.argv[1])
Port = int(sys.argv[2])
#create ipv6 socket
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.connect((IP_address, Port))
while True:
data = s.recv(4096)
data_recved = pickle.loads(data)
(scapy.layers.l2.Ether(data_recved)).show()
s.close()
The 1st code which serializes the object works fine and shows output as expected.
The 2nd code which deserializes the object works but gives wrong output as below
###[ Raw ]###
load = '\xb0\xfc6`\xe5\xe3\xe4\xa7\xc5I,\x1c\x08\x00E(\x004%\xd4#\x00h\x06s!o\xdd\x1d\xfe\xc0\xa8+#\x01\xbb\xda\xb4\xf5 \x05\xfbv\xac\xccj\x80\x10\x04\x02\xb6z\x00\x00\x01\x01\x08\n!\x84\xf3\xa3\x066\x0c\x99'
###[ Raw ]###
load = "\xb0\xfc6`\xe5\xe3\xe4\xa7\xc5I,\x1c\x08\x00E(\x01o%\xd5#\x00h\x06q\xe5o\xdd\x1d\xfe\xc0\xa8+#\x01\xbb\xda\xb4\xf5 \x05\xfbv\xac\xccj\x80\x19\x04\x02B\xbf\x00\x00\x01\x01\x08\n!\x84\xf3\xfd\x066\x0c\x99\x17\x03\x03\x016\x00\x00\x00\x00\x00\x00\x00\x01n\xb3\x18\x93\xd2xT\x00\xe3Xh\xa7\xc0?|\xb3n\xf6?g\x00\xdf\xcf\x97wx\xccw\x04\x08\xbfM\xf2L1^\x84\xa1\xb0\x06\xaas\xdb\xc9\xb1\xed\x99\x8cz\x9a5P\xa3{\x04y\xbb\xc38\xcfT\\\x98f\xf1z/F\x1e\xd6tD\x90pN\x92\xe1\xf9\xee#\xbc\x1a(\x89{x\xf2\\\xd1\x14\x1d?\x02\x07\x16N\xe4\x017\x140\xc5\x81\xb4\xcd\t\x8ai\x1a\xe7PZ\x01\xbek\xba \x84[\\\xcc\xf0\xecs\xc8^\xff!\xce\x19z,\x13\xbd\xa3\x0c\x0fq\xca=\xbc\x1a834\xee\xf9hC\x0b\xb4\x85\x114'5W\xee\xed&\x93Y0\xd1\x85\xc3(\xb3\x0e0u\\\x9d\xa7\xb5g\xb0\xb4C_S\xe7\xf8\xbaH\t\x94\x84\xaf\xa9\tS\xb6\xf4XYD\tM\xf8=`\xbd\x04\xeeo\x1f\xd3\x7f*\xef\x8e\xd2\x00\x8f\xfb\x8c\xfemB\x02\xc0\x12\x8a\r\x14\xdco\\K\xfd\x97%\xbc\x15}\xb1\xe2<\xdei\x15\x17\xeec>=\xa0\xc0]\xfaa\xeal`\x10c\xe8\x86B\xff'w\xe3\xe4\xce\x9d\xbf0\xdc\xb0F\x12\x8b{;9\xa1\xce\xfdf\x1c\x8b\x8f\xc4\xc0F\xf3\\\x86\xbd\xcdu\xdb\xb8>t\xf6\xff.~h"
###[ Raw ]###
load = "\xe4\xa7\xc5I,\x1c\xb0\xfc6`\xe5\xe3\x08\x00E\x00\x00SP\xcb#\x00#\x06p3\xc0\xa8+#o\xdd\x1d\xfe\xda\xb4\x01\xbbv\xac\xccj\xf5 \x077\x80\x18\x01>\x93\x10\x00\x00\x01\x01\x08\n\x066\r\xb7!\x84\xf3\xfd\x15\x03\x03\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x02Nkk'T\xcdv$\xb7e\xe00\xc9\x9f\xf2\xffw4"
###[ Raw ]###
load = '\xe4\xa7\xc5I,\x1c\xb0\xfc6`\xe5\xe3\x08\x00E\x00\x004P\xcc#\x00#\x06pQ\xc0\xa8+#o\xdd\x1d\xfe\xda\xb4\x01\xbbv\xac\xcc\x89\xf5 \x077\x80\x11\x01>\xb6j\x00\x00\x01\x01\x08\n\x066\r\xb7!\x84\xf3\xfd'
but it should be like
###[ Ethernet ]###
dst = e4:a7:c5:49:2c:1c
src = b0:fc:36:60:e5:e3
type = IPv6
###[ IPv6 ]###
version = 6
tc = 0
fl = 630855
plen = 32
nh = TCP
hlim = 255
src = 2409:4042:2ea4:752f:cd15:9fe5:6dc1:1575
dst = 2404:6800:4009:810::200e
###[ TCP ]###
sport = 36318
dport = https
seq = 3279176249
ack = 3388920021
dataofs = 8
reserved = 0
flags = A
window = 254
chksum = 0xf7e7
urgptr = 0
options = [('NOP', None), ('NOP', None), ('Timestamp', (427468701, 3715875499))]
How can I fix it ?
I have recently found the solution. Actually in second code snippet, the object show function on object was called by doing unnecessary typecasting. The line (scapy.layers.l2.Ether(data_recved)).show() should be simply like data_recved.show(). And then we can get desired output

scapy 3-way hand shake not work

I want to use scapy to do tcp 3-way hand shake, my code is below:
from scapy.all import *
# VARIABLES
src = sys.argv[1]
dst = sys.argv[2]
sport = random.randint(20000,65535)
dport = int(sys.argv[3])
print src
print dst
print sport
print dport
# SYN
ip=IP(src=src,dst=dst)
SYN=TCP(sport=sport,dport=dport,flags='S',seq=1000)
SYNACK=sr1(ip/SYN)
SYN.show()
SYNACK.show()
# ACK
seq=SYNACK.ack + 1
ack=SYNACK.seq + 1
print seq
print ack
ACK=TCP(sport=sport, dport=dport, flags='A', seq=seq, ack=ack)
sr1(ip/ACK)
ACK.show()
exec command:
python 3WSK.py 10.16.11.21 10.11.157.130 26789
And the SYNACK result is below:
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 44
id = 0
flags = DF
frag = 0L
ttl = 60
proto = tcp
chksum = 0x821a
src = 10.11.157.130
dst = 10.16.11.21
\options \
###[ TCP ]###
sport = 26789
dport = 35067
seq = 1918207620
ack = 1001
dataofs = 6L
reserved = 0L
flags = SA
window = 5840
chksum = 0xd630
urgptr = 0
options = [('MSS', 1460)]
###[ Padding ]###
load = '\x00\x00'
but my tcp server not log any thing.
for test:
I use telnet 10.11.157.130 26789 to connect my tcp server, then my tcp server can log something like: "INFO l.t.TcpServer - in, /10.16.11.21:34906"
So, I`m not sure my scapy code is connect successful or not, I also want to know
how to validate whether tcp connect is work or not. thanks
Check if you see a RST being sent from your os when the SYN+ACK arrives. If so have a look at this answer and drop the RST with iptables:
iptables -A OUTPUT -p tcp --tcp-flags RST RST -s <your_ip> -j DROP
If thats not the case have a look at this gist which works fine for me.

Scapy fails to filter certain packets

I have got a simple sniff function set up with scapy which forwards the packet to a handshake function (I have a webserver set up on port 102. However some weird errors have come by, then I decided to print pkt.show(), what I discovered was that some packages DID come through the filter somehow.
My sniff function:
a=sniff(filter="port 102", count=10, prn=handshake)
This packet manages to come through:
###[ Ethernet ]###
dst = 84:8f:69:f5:fe:ac
src = b8:27:eb:92:a3:3b
type = 0x800
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 44
id = 1
flags =
frag = 0L
ttl = 64
proto = tcp
chksum = 0xe6c6
src = 192.168.137.178
dst = 192.168.137.1
\options \
###[ TCP ]###
sport = iso_tsap
dport = 2426
seq = 605952828
ack = 605952829
dataofs = 6L
reserved = 0L
flags = SA
window = 8192
chksum = 0x5b7c
urgptr = 0
options = [('MSS', 1460)]
As you can see the destination port is 2426, which is definetely not port 102.
Have I done something dumb?
The source port in the enclosed packet is iso_tsap which is 102. If you want to filter by the destination port try the filter "dst port 102". If you need something a bit more sophisticated, here is the syntax of BPF, which is used by scapy.

Why can't set the source ip address to 0.0.0.0 in python?

I am trying to implement DHCP client in python.I used a raw socket.
s=socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
def ip_packet(source_ip,dest_ip):
ip_ihl = 5
ip_ver = 4
ip_tos = 0
ip_tot_len = 0
ip_id = 54321 #Id of this packet
ip_frag_off =0
ip_ttl = 255
ip_proto = socket.IPPROTO_UDP
ip_check = 0
ip_saddr = socket.inet_aton ( source_ip )
print ip_saddr.encode('hex')
ip_daddr = socket.inet_aton ( dest_ip )
ip_ihl_ver = (ip_ver << 4) + ip_ihl
ip_header = struct.pack('!BBHHHBBH4s4s' , ip_ihl_ver, ip_tos, ip_tot_len, ip_id, ip_frag_off, ip_ttl, ip_proto, ip_check, ip_saddr, ip_daddr)
return ip_header
s.sendto(packet,('255.255.255.255',67))
I sat the source ip address to 0.0.0.0 ,but when I sniffed the generated packet in wireshark ,I was surprised to see that the source ip address is sat to my real ip address.
But when I used Scapy everything worked as expacted
sendp(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","request")]),count=3)
So, how dose Scapy manage to set the source ip address to 0.0.0.0?? Is it possible to do that using raw socket ??

Categories

Resources