How to send packets in scapy from a specific socket? - python

I am trying to send some packet through scapy by creating a socket. I want to send the packets through the socket which I created. How can I accomplish this
here is some code which I tried
pkt = "\x00\x1c\x7fb\xb5\xfd\x00PV\xb8\x08\x9f\x08\x00E\x00\x000/t\x00\x00\x80\x11\x00\x00\n\xe7\xa0\xc6\n\xe7\x922\xd2\xb4\x05\xdc\x00\x1cH\xf4\t\x8d\x01\x00\x01\x01\x00\x10\xff\xff\xfe\xd4\x00\x00\x00\x00\xb2\x1a=\x0f"
Socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Socket.bind(('',udp_sport))
S = Socket.connect_ex(("10.146.144.51",1803))
Socket.settimeout(10)
sendp(pkt,socket=S)
I have seen in scapy library that there is an option to use Socket in sendp. How can I use this to send? Please help.

You can use a StreamSocket as a wrapper for your socket. See https://scapy.readthedocs.io/en/latest/layers/tcp.html?highlight=streamsocket#using-the-kernel-s-tcp-stack
In your case:
ss=StreamSocket(s,Raw)
ss.send(...)
Note: in your example, you are using sendp(). This means a layer 2 packet. This won't work with the layer 3 socket that you have created: make sure the packet you are sending is L3.

Related

Python How can I send an ICMP packet without spoofing?

I am trying to send an ICMP packet in python using scapy using my Debian VPS but I am not trying to spoof the IP or anything, but since my server doesn't spoof it won't send it anyways, but I cannot figure out any other way to create an ICMP packet and send it. How can I do this?
Build an IP and an ICMP layer, like this:
from scapy.layers.inet import IP, ICMP
DESTINATION = "192.168.111.4"
packet = IP(dst=DESTINATION, ttl=20) / ICMP()
# print(packet)

UDP fragmentation in Python standard library

Am I right that UDP fragmentation should not be handled by developer himself in Python?
Am I right that I'll get my UDP packet with all originally sent data and not just one fragment of it?
while True:
data, addr = s.recvfrom(65535)
# Process packet
You will get the whole datagram that was sent through another UDP socket in one send or write operation. UDP is not stream oriented like TCP.

Python sendto doesn't seem to send

I am new to Python and trying to send a byte array as a raw packet using a socket. My IP is 192.168.0.116 and the device I am sending this to is 192.168.0.64. This client device is a microcontroller based unit which is executing my code to simply sniff the Ethernet packets and check for a particular pattern. I am using UDP packets and tried the client side firmware by using 'Ostinato' in my PC to send a raw UDP packet. I am using Wireshark to monitor the network packet flow. The client seems to work fine with packets sent by Ostinato.
However when I try to send the same packet with Python (using raw packets as follows), it doesn't seem to spit out the bytes as I cant see anything on Wireshark nor the client get any data. But the return value from sendto() is correct. So it seems that Python feeds the byte array to a buffer to be sent (by OS?) but stops there.
import socket
CLNT_UDP_IP = '192.168.0.64'
CLNT_UDP_PORT = 5005
svr_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
send_data = [0x00, 0x11...]
send_data_arr = bytearray (send_data)
svr_sock.bind (('192.168.0.116',0))
bytes_send = svr_sock.sendto (send_data_arr, (CLNT_UDP_IP, CLNT_UDP_PORT))
svr_sock.close()
I have taken out the try-except blocks for clarity.
Another thing I noted is that when the socket is closing, it takes a bit of time. If I comment out the sendto statement, it exits immediately. So it seems like the socket close is trying to flush the send buffers, which failed to send the packet.
Any ideas?
Ilya is right, you should be opening a UDP socket with
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
You're binding to port 0, which is invalid. If this is not an inbound port, you don't need to bind it. This may explain why your sendto call is blocking.
The send_data array should contain only your data, not "the full ethernet packet".
The send_data array must be under the MTU size for your network, otherwise it may be dropped silently. It varies but under 1300 should work, over 1500 almost certainly won't work.

Unable to send UDP packets to local port with python, fails checksum according to WireShark

I've been pulling my hair out over this one. I'm trying to write a SOCKS5 server in Python to tunnel UDP traffic. I bind to a port, and receive data fine. I then parse the SOCKS5 UDP header (not the typical UDP header), and forward the datagram to the requested endpoint.
All is good. I then listen for a response from the endpoint (resending if timeout), and get a response. Great!
Here is where I'm losing my mind-
I get the datagram back from the endpoint. I re-encapsulate the returned datagram according to the SOCKS5 RFC, which is the same UDP header as before, except I have now changed the destination address and port to the original caller. I use:
sock_client = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock_client.sendto(packed_datagram, (self.client_ip, self.client_port))
to send the datagram back to client. The client never receives the reply! Ever!
Looking at WireShark, it says this: Header checksum: 0x0000 [incorrect, should be 0xcd1f (may be caused by "IP checksum offload"?)]
Shouldn't the python socket implementation, with socket.DGRAM set, automatically pack my data correctly in a UDP header and calculate the appropriate checksum? Why is it being set to 0x0000? I checked the payload in hex, the checksum is indeed set wrong. What the heck is going on?
The checksum calculation is done by the drivers in the operating system. In may cases, the calculation is done by the network card itself. IIRC, Wireshark grabs local packets just before they are handed off to the network stack. It's common to checksum errors for all locally generated packets.
I was not following two pieces of the SOCKS5 spec accurately.
A UDP association terminates when the TCP connection that the UDP
ASSOCIATE request arrived on terminates.
I was terminating the TCP socket immediately after completing UDP relay handshake. TCP must stay open until the back-and-forth is finished.
When a UDP relay server receives a reply datagram from a remote
host, it MUST encapsulate that datagram using the above UDP request
header, and any authentication-method-dependent encapsulation.
I was using the client's IP and port as the values in the datagram. It needs to be the remote server's IP and port, essentially the UDP header encapsulation is a clone of what the client passed in.
With these two issues solved, the SOCKS5 server works as anticipated.
If I understand your part of code correctly you create a new socket to send the data back to the client. Thus this will be a new socket with a random source IP, e.g. from the view of the client you have the following packet flow:
client_ip:client_port -> socks5_ip:socks_port
client_ip:client_port <- socks5_ip:random_port
The client has probably a connected socket to the socks5 server and thus expects replies coming from socks5_ip:socks_port, not the random_port.
So you should not create a new socket to the client but instead reply using the existing socket where you received the data from the client.
turn off tx-checksumming using the linux command:
ethtool -K eth0 tx off
OR use this function to calculate the checksum
def checksum(data):
s = 0
n = len(data) % 2
for i in range(0, len(data)-n, 2):
s+= ord(data[i]) + (ord(data[i+1]) << 8)
if n:
s+= ord(data[i+1])
while (s >> 16):
s = (s & 0xFFFF) + (s >> 16)
s = ~s & 0xffff
return s
Where the data is the pseudo header

Python - set source port number with sockets

I'd like to send a specific UDP broadcast packet. Unfortunately, I need to send the UDP packets from a very specific port.
Let's say I broadcast via UDP "BLABLAH". The server will only answer if my incoming packet source port was 1444; if not, then the packet is discarded.
My broadcast socket setup looks like this:
s = socket(AF_INET,SOCK_DGRAM)
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
How can I then set the source port in Python?
You need to bind the socket to the specific port you want to send from. The bind method takes an address tuple, much like connect, though you can use the wildcard address. For example:
s.bind(('0.0.0.0', 1444))
Use s.bind(('', port)).

Categories