I've been having fun with Scapy. While reading the documentation, it explains how to show the important information of a port-scan reply with "a simple loop", which goes like this:
ans.summary( lambda(s,r): r.sprintf("%TCP.sport% \t %TCP.flags%") )
I can sort of understand what lambda does, however what trips me up is everything after lambda. I assume (s,r) is mapping s and r to the "send" and "received" packets of the tuple returned by ans.summary. I've been looking around for sprintf, and can't find anything pertaining to python about it. I also assume that %TCP.sport% is a scapy specific format string.
What does it all mean and how does it work?
ans.summary() accepts a function as a parameter that will be used to represent each (sent packed, received response) tuple.
If no function is specified, something equivalent to lambda s, r: "%s ==> %s" % (s.summary(), r.summary()) is used.
The function must accept to arguments (the sent packet and the corresponding received answer), and return a string.
In your case, the function is lambda(s, r): r.sprintf("%TCP.sport% \t %TCP.flags%"). It only uses the second parameter (the answer), and calls .sprintf(), which is a method defined of Scapy Packet objects (that's not a Python standard, but something specific to these objects). The formatting elements in .sprintf() are related to layers and field values. Here, the call will return a string containing the TCP source port (from the answer packet) followed by "space tab space" and the TCP flags.
If you apply this to sr() result for a TCP scan, this will hence show each scanned port with "SA" if the port is open and "RA" if it is closed.
Related
I've been playing around with scapy and want to read through and analyse every hex byte. So far I've been using scapy simply because I don't know another way currently. Before just writing tools myself to go through the pcap files I was wondering if there was an easy way to do it. Here's what I've done so far.
packets = rdpcap('file.pcap')
tcpPackets = []
for packet in packets:
if packet.haslayer(TCP):
tcpPackets.append(packet)
When I run type(tcpPackets[0]) the type I get is:
<class 'scapy.layers.l2.Ether'>
Then when I try to covert the Ether object into a string it gives me a mix of hex and ascii (as noted by the random parenthesis and brackets).
str(tcpPackets[0])
"b'$\\xa2\\xe1\\xe6\\xee\\x9b(\\xcf\\xe9!\\x14\\x8f\\x08\\x00E\\x00\\x00[:\\xc6#\\x00#\\x06\\x0f\\xb9\\n\\x00\\x01\\x04\\xc6)\\x1e\\xf1\\xc0\\xaf\\x07[\\xc1\\xe1\\xff0y<\\x11\\xe3\\x80\\x18 1(\\xb8\\x00\\x00\\x01\\x01\\x08\\n8!\\xd1\\x888\\xac\\xc2\\x9c\\x10%\\x00\\x06MQIsdp\\x03\\x02\\x00\\x05\\x00\\x17paho/34AAE54A75D839566E'"
I have also tried using hexdump but I can't find a way to parse through it.
I can't find the proper dupe now, but this is just a miss-use/miss-understanding of str(). The original data is in a bytes format, for instance x = b'moo'.
When str() retrieves your bytes string, it will do so by calling the __str__ function of the bytes class/object. That will return a representation of itself. The representation will keep b at the beginning because it's believed to distinguish and make it easier for humans to understand that it's a bytes object, as well as avoid encoding issues I guess (alltho that's speculations).
Same as if you tried accessing tcpPackets[0] from a terminal, it would call __repr__ and show you something like <class 'scapy.layers.l2.Ether'> most likely.
As an example code you can experiment with, try this out:
class YourEther(bytes):
def __str__(self):
return '<Made Up Representation>'
print(YourEther())
Obviously scapy's returns another representation, not just a static string that says "made up representation". But you probably get the idea.
So in the case of <class 'scapy.layers.l2.Ether'> it's __repr__ or __str__ function probably returns b'$\\xa2\\....... instead of just it's default class representation (some correction here might be in place tho as I don't remember/know all the technical namification of the behaviors).
As a workaround, this might fix your issue:
hexlify(str(tcpPackets[0]))
All tho you probably have to account for the prepended b' as well as trailing ' and remove those accordingly. (Note: " won't be added in the beginning or end, those are just a second representation in your console when printing. They're not actually there in terms of data)
Scapy is probably more intended to use tcpPackets[0].dst rather than grabing the raw data. But I've got very little experience with Scapy, but it's an abstraction layer for a reason and it's probably hiding the raw data or it's in the core docs some where which I can't find right now.
More info on the __str__ description: Does python `str()` function call `__str__()` function of a class?
Last note, and that is if you actually want to access the raw data, it seams like you can access it with the Raw class: Raw load found, how to access?
You can put all the bytes of a packet into a numpy array as follows:
for p in tcpPackets:
raw_pack_data = np.frombuffer(p.load, dtype = np.uint8)
# Manipulate the bytes stored in raw_pack_data as you like.
This is fast. In my case, rdpcap takes ~20 times longer than putting all the packets into a big array in a similar for loop for a 1.5GB file.
I'm currently working on using scapy for sending data packets, and I've run into a weird issue. When I create a packet as such:
pack = IP(dst="127.0.0.1", id=local_ID)/UDP()/chunk
and then convert that packet to a string (so I can send it via a socket)
sendPack = str(pack)
the result of sendPack is wrong.
For instance, in my test file, I have the numbers 1-8000 ordered as such
1
2
3
...
then, when I print("SEND_PACK: "+sendPack)
it produces the following:
E
2
3
...
Everything else is perfect except for the E
I can't understand where that E is coming from, or what it means.
It's also worth noting that I have verified that pack contains the correct data, and that regardless of what the first line of the test file is, the first line of the output is always an E
Thanks!
To those interested, I fixed the issue by doing the following:
As pointed out above, the E was a result of me printing the packet, not it's contents. In order to access the contents I wanted, I had to do the following:
sendPack = pack[UDP].load #Get the packet's load at the UDP layer
id = pack[IP].ID #Get the ID at the IP layer
The documentation for Scapy is sparse, so I didn't realize that I could access the individual fields of each packet this way.
Here's where I found this fix
First, you are printing a packet, not the content of your UDP datagram. The first two fields of an IPv4 packet are version and IHL which values are respectively 4 (for IP version 4) and 5 (5 words of 32 bits) by default in Scapy. This gives 45 if you put it on one byte in hexadecimal which is letter "E" in ascii. This is why you are always seeing an E as the first byte of your string.
If you want to check what is in your packet, you should use Scapy's show() function: sendPack.show().
Second, you could use Scapy's send(), function to send your packet without bothering about sockets yourself: send(sendPack)
I've been skimming the Python documentation, but can't seem to find specific details like the above. Where are such details listed?
Since you want the documentation, the below is the closest thing to documentation I could get.
From Python documentation - -
The Python interface is a straightforward transliteration of the Unix
system call and library interface for sockets
And from Unix documentation for recv , under the section Return value -
When a stream socket peer has performed an orderly shutdown, the
return value will be 0 (the traditional "end-of-file" return).
And when you convert the 0 to a bytes object, you get an empty bytes object. Example -
>>> bytes(0)
b''
What I want is to send snmp traps just as the way that one of our network equipment do. The trap contains a name-value of DataTime and it's something like HEX String.
e.g. 1.3.6.1.4.1.193.82.1.8.1.4.0 : 07:de:07:10:0a:0c:1e:00:2b:08:00
When I use snmptrap command of the net-snmp to send the trap, our Trap decoder can successfully parse the hex string to dateandtime format(YYYY-MM-HH hh:mm:ss) just like that it got a real trap from our Network equipment.
The command I use is like this:
sudo /usr/local/net-snmp/bin/snmptrap -v 2c -c LI_OSS 10.184.74.66:162 "" 1.3.6.1.4.1.193.82.2.0.1 1.3.6.1.4.1.193.82.1.8.1.4.0 x "07de07100a0c1e002b0800"
x means that the string "07de07100a0c1e002b0800" is some kind of hex format.
When I try to use pysnmp to send complete the same task, our Trap decoder program do have received the trap but fail to parse the dateandtime.
Here is the code that I use to send the trap, it's the official example of pysnmp, here.
I only modified the host and below part:
ntfOrg.sendNotification(
snmpEngine,
# Notification targets
'my-notification',
# Trap OID (SNMPv2-MIB::coldStart)
(1,3,6,1,4,1,193,82,2,0,1),
# ( (oid, value), ... )
(
('1.3.6.1.4.1.193.82.1.8.1.4.0', rfc1902.OctetString('07de07100a0c1e002b0800'))
)
)
In order to figure out the differences, I captured the packages of sending traps that using snmptrap and pysnmp using WireShark, and here are the differences. Note that I am not using the same TRAP OID, but the phenomenon remain the same.
The first picture is that using snmptrap to send the trap, the other one is when using pysnmp.
The Octet Strings are just different.
Is anyone know why this happened? And how can I make it work to use pysnmp to send the trap in my situation? Thanks a lot in advance!
You passed an ASCII string as OctetString() object initialiser. You should indicate to OctetString constructor that your initialiser is to be interpreted as a hex string. This can be done with the hexValue keyword parameter. Consider:
>>> str(univ.OctetString('07de07100a0c1e002b0800'))
'07de07100a0c1e002b0800'
>>> str(univ.OctetString(hexValue='07de07100a0c1e002b0800'))
'\x07\xde\x07\x10\n\x0c\x1e\x00+\x08\x00'
I am using the python to send an snmpset message to an snmp agent. I have the correct OID as I can use it to get data with snmpget. However, it will not set the data. I am sending octets in hex format (two hex values) separated by a colon. I might have to put apostrophes around it, right? Here is the example of the line I am sending
foo = os.popen("snmpset -v 2c -c private 192.1.55.222
.1.2.6.5.4.1.24022.4.27.1.6.4.4 x 00:00:04:cf:00:00:00:00:00:00")
as you can see, I am sending an Octet string with type x.
Can anyone hazard a guess as to how I should pass in the set value of "00:00:04:cf:00:00:00:00:00:00". I know the setvalue is of the right length and type because I have used it in a MIB browser.
A couple of things:
1) there is a native python interface you could use instead of calling a system command. There are in fact multiple choices, including pysnmp (done in python) and Net-SNMP's python module (done in C).
2) The Net-SNMP snmpset command expects straight hex code without any :s in it. So if you remove the :s you may find it'll work.