phaethon / kamene

Network packet and pcap file crafting/sniffing/manipulation/visualization security tool. Originally forked from scapy in 2015 and providing python3 compatibility since then.
GNU General Public License v2.0
868 stars 191 forks source link

How to port a file from the original scapy's contrib folder? #140

Open bsmelo opened 7 years ago

bsmelo commented 7 years ago

Hello,

I'm new to scapy (scapy3k as well), but I want to use it to manipulate some CoAP packets.

I've found a CoAP layer implementation on the contrib folder of the original scapy, consisting of two files (one of them is just a unittest, as far as I understand): contrib/coap.uts contrib/coap.py

I've added these two files (just copied) to scrapy3k's contrib folder, but I'm having some problems using this layer (I want to use it with scapy3k because I will also need another library in my program, which is only available for Python >= 3.5).

 $ python3.5
Python 3.5.3 (default, Feb 14 2017, 22:21:24) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from scapy.all import *
WARNING: No route found for IPv6 destination :: (no default route?). This affects only IPv6
>>> from scapy.contrib.coap import *
>>> from aiocoap import *
>>> p = UDP()/CoAP()
>>> p
<UDP  dport=5683 |<CoAP  |>>
>>> ls(p)
sport      : ShortEnumField       = 53              (53)
dport      : ShortEnumField       = 5683            (53)
len        : ShortField           = None            (None)
chksum     : XShortField          = None            (None)
--
ver        : BitField             = 1               (1)
type       : BitEnumField         = 0               (0)
tkl        : BitFieldLenField     = None            (None)
code       : ByteEnumField        = 0               (0)
msg_id     : ShortField           = 0               (0)
token      : StrLenField          = b''             (b'')
options    : _CoAPOptsField       = []              ([])
>>> str(p)
WARNING: Unless called manually, this could indicate deprecated use. Should be changed to bytes(self)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 287, in __str__
    return repr(bytes(self)) 
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 289, in __bytes__
    return self.build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 357, in build
    p = self.do_build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 349, in do_build
    pay = self.do_build_payload()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 341, in do_build_payload
    return self.payload.do_build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 346, in do_build
    pkt = self.self_build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 337, in self_build
    p = f.addfield(self, p, val)
  File "/usr/local/lib/python3.5/site-packages/scapy/fields.py", line 375, in addfield
    return s+self.i2m(pkt, val)
TypeError: can't concat bytes to str
>>> bytes(p)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 289, in __bytes__
    return self.build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 357, in build
    p = self.do_build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 349, in do_build
    pay = self.do_build_payload()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 341, in do_build_payload
    return self.payload.do_build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 346, in do_build
    pkt = self.self_build()
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 337, in self_build
    p = f.addfield(self, p, val)
  File "/usr/local/lib/python3.5/site-packages/scapy/fields.py", line 375, in addfield
    return s+self.i2m(pkt, val)
TypeError: can't concat bytes to str
>>> a = rdpcap("captures/simple.pcap")
>>> a
<simple.pcap: TCP:0 UDP:1 ICMP:0 Other:0>
>>> a[0]
<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  version=4 ihl=5 tos=0x0 len=49 id=33371 flags=DF frag=0 ttl=64 proto=udp chksum=0xba5e src=127.0.0.1 dst=127.0.0.1 options=[] |<UDP  sport=50684 dport=5683 len=29 chksum=0xfe30 |<Raw  load=b'@\x01&\xc7\xbb.well-known\x04core' |>>>>
>>> ls(a[0])
dst        : DestMACField         = '00:00:00:00:00:00' (None)
src        : SourceMACField       = '00:00:00:00:00:00' (None)
type       : XShortEnumField      = 2048            (36864)
--
version    : BitField             = 4               (4)
ihl        : BitField             = 5               (None)
tos        : XByteField           = 0               (0)
len        : ShortField           = 49              (None)
id         : ShortField           = 33371           (1)
flags      : FlagsField           = 2               (0)
frag       : BitField             = 0               (0)
ttl        : ByteField            = 64              (64)
proto      : ByteEnumField        = 17              (0)
chksum     : XShortField          = 47710           (None)
src        : Emph                 = '127.0.0.1'     (None)
dst        : Emph                 = '127.0.0.1'     ('127.0.0.1')
options    : PacketListField      = []              ([])
--
sport      : ShortEnumField       = 50684           (53)
dport      : ShortEnumField       = 5683            (53)
len        : ShortField           = 29              (None)
chksum     : XShortField          = 65072           (None)
--
load       : StrField             = b'@\x01&\xc7\xbb.well-known\x04core' (b'')
>>> bytes(a[0][3])
b'@\x01&\xc7\xbb.well-known\x04core'
>>> CoAP(bytes(a[0][3]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.5/site-packages/scapy/base_classes.py", line 199, in __call__
    i.__init__(*args, **kargs)
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 87, in __init__
    self.dissect(_pkt)
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 623, in dissect
    s = self.do_dissect(s)
  File "/usr/local/lib/python3.5/site-packages/scapy/packet.py", line 594, in do_dissect
    assert(raw.endswith(s))
TypeError: endswith first arg must be bytes or a tuple of bytes, not str

Could someone please give me some pointers on how to solve this issue? I don't know if it's simple enough, or if I could at least get some tips on where to focus when looking/changing things on coap.py... I'd really appreciate it!

tmblazek commented 7 years ago

Ok the issue here is that in python2 strings were byte-strings, so one character equals one byte. in python3, strings are utf-8 strings. That means, if you want your strings to mean bytes, you have to do conversion manually. This is what you are missing in this instance. For the first example you give, you have to make sure coap's i2m method on the _CoAPOptsField returns byte strings. to do that, you change line 174 to

return b""

and line 189 to

return (opts).encode("ascii")

string literals starting with b" are bytestrings, and encoding as ascii makes the result also bytestrings. I don't know if that fixes your second issue too, you would have to provide me with the pcap file so I can be sure. However, I do suspect you have to make sure that the getfield method on line 159 returns bytestrings, so maybe replacing "" with b"" on that line does the trick.

phaethon commented 7 years ago

@bsmelo You cannot just copy module from scapy2 to scapy3k. It has to be ported. @Inachos already described one of the differences related to str vs. bytes. But there are more differences between python2 and python3.