Skip to content

Commit

Permalink
Netbios: detect query response (#4445)
Browse files Browse the repository at this point in the history
* Netbios: detect query response

* Move NetBIOSNameField to Python3
  • Loading branch information
gpotter2 committed Jun 27, 2024
1 parent b13236f commit 31b3588
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 18 deletions.
8 changes: 8 additions & 0 deletions doc/scapy/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,14 @@ Node status request (get NetbiosName from IP)
>>> sr1(IP(dst="192.168.122.17")/UDP()/NBNSHeader()/NBNSNodeStatusRequest())
NBNS Query Request (find by NetbiosName)
----------------------------------------

.. code::
>>> conf.checkIPaddr = False # Mandatory because we are using a broadcast destination
>>> sr1(IP(dst="192.168.0.255")/UDP()/NBNSHeader()/NBNSQueryRequest(QUESTION_NAME="DC1"))
Advanced traceroute
-------------------

Expand Down
20 changes: 12 additions & 8 deletions scapy/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
RandSLong, RandFloat
from scapy.data import EPOCH
from scapy.error import log_runtime, Scapy_Exception
from scapy.compat import bytes_hex, chb, orb, plain_str, raw, bytes_encode
from scapy.compat import bytes_hex, plain_str, raw, bytes_encode
from scapy.pton_ntop import inet_ntop, inet_pton
from scapy.utils import inet_aton, inet_ntoa, lhex, mac2str, str2mac, EDecimal
from scapy.utils6 import in6_6to4ExtractAddr, in6_isaddr6to4, \
Expand Down Expand Up @@ -1947,21 +1947,25 @@ def i2m(self, pkt, y):
x += b" " * len_pkt
x = x[:len_pkt]
x = b"".join(
chb(0x41 + (orb(b) >> 4)) +
chb(0x41 + (orb(b) & 0xf))
struct.pack(
"!BB",
0x41 + (b >> 4),
0x41 + (b & 0xf),
)
for b in x
) # noqa: E501
)
return b" " + x

def m2i(self, pkt, x):
# type: (Optional[Packet], bytes) -> bytes
x = x[1:].strip(b"\x00").strip(b" ")
x = x[1:].strip(b"\x00")
return b"".join(map(
lambda x, y: chb(
(((orb(x) - 1) & 0xf) << 4) + ((orb(y) - 1) & 0xf)
lambda x, y: struct.pack(
"!B",
(((x - 1) & 0xf) << 4) + ((y - 1) & 0xf)
),
x[::2], x[1::2]
))
)).rstrip(b" ")


class StrLenField(StrField):
Expand Down
16 changes: 13 additions & 3 deletions scapy/layers/netbios.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ class NBNSHeader(Packet):
ShortField("ARCOUNT", 0),
]

def hashret(self):
return b"NBNS" + struct.pack("!B", self.OPCODE)


# Name Query Request
# RFC1002 sect 4.2.12

Expand All @@ -144,7 +148,7 @@ class NBNSQueryRequest(Packet):

def mysummary(self):
return "NBNSQueryRequest who has '\\\\%s'" % (
self.QUESTION_NAME.strip().decode(errors="backslashreplace")
self.QUESTION_NAME.decode(errors="backslashreplace")
)


Expand Down Expand Up @@ -184,10 +188,16 @@ def mysummary(self):
if not self.ADDR_ENTRY:
return "NBNSQueryResponse"
return "NBNSQueryResponse '\\\\%s' is at %s" % (
self.RR_NAME.strip().decode(errors="backslashreplace"),
self.RR_NAME.decode(errors="backslashreplace"),
self.ADDR_ENTRY[0].NB_ADDRESS
)

def answers(self, other):
return (
isinstance(other, NBNSQueryRequest) and
other.QUESTION_NAME == self.RR_NAME
)


bind_layers(NBNSHeader, NBNSQueryResponse, # RD+AA
OPCODE=0x0, NM_FLAGS=0x50, RESPONSE=1, ANCOUNT=1)
Expand All @@ -206,7 +216,7 @@ class NBNSNodeStatusRequest(NBNSQueryRequest):

def mysummary(self):
return "NBNSNodeStatusRequest who has '\\\\%s'" % (
self.QUESTION_NAME.strip().decode(errors="backslashreplace")
self.QUESTION_NAME.decode(errors="backslashreplace")
)


Expand Down
8 changes: 4 additions & 4 deletions test/answering_machines.uts
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ assert DNS_am().make_reply(

= LLMNR_am
def check_LLMNR_am_am_reply(packet):
assert packet[Ether].src == get_if_hwaddr(conf.iface)
# assert packet[Ether].src == get_if_hwaddr(conf.iface)
assert packet[Ether].dst == "aa:aa:aa:aa:aa:aa"
assert packet[IP].src == get_if_addr(conf.iface)
# assert packet[IP].src == get_if_addr(conf.iface)
assert packet[IP].dst == "192.168.0.1"
assert packet[UDP].dport == 51938
assert packet[UDP].sport == 5355
Expand Down Expand Up @@ -261,13 +261,13 @@ def check_NBNS_am_reply(name):
packet.show()
assert packet[Ether].src != "ff:ff:ff:ff:ff:ff"
assert packet[Ether].dst == "aa:aa:aa:aa:aa:aa"
assert NBNSQueryResponse in packet and packet[NBNSQueryResponse].RR_NAME.strip() == bytes_encode(name)
assert NBNSQueryResponse in packet and packet[NBNSQueryResponse].RR_NAME == name
return check

for server_name in (None, "", b"test", "test"):
test_am(NBNS_am,
Ether(src="aa:aa:aa:aa:aa:aa", dst="ff:ff:ff:ff:ff:ff")/IP()/UDP()/NBNSHeader()/NBNSQueryRequest(QUESTION_NAME="test"),
check_NBNS_am_reply("test"),
check_NBNS_am_reply(b"test"),
server_name=server_name)

test_am(NBNS_am,
Expand Down
19 changes: 16 additions & 3 deletions test/scapy/layers/netbios.uts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ assert raw(z) == b'\x00\x00\x01\x10\x00\x01\x00\x00\x00\x00\x00\x00 FEEFFDFEDBCA

pkt = IP(dst='192.168.0.255')/UDP(sport=137, dport='netbios_ns')/z
pkt = IP(raw(pkt))
assert pkt.QUESTION_NAME == b'TEST1 '
assert pkt.QUESTION_NAME == b'TEST1'
assert pkt[NBNSQueryRequest].mysummary() == r"NBNSQueryRequest who has '\\TEST1'"

assert NBNSQueryRequest in NBNSHeader(raw(z))
Expand All @@ -36,10 +36,23 @@ z = NBNSQueryResponse(b' PPFCEFEECACACACACACACACACACACAAA\x00\x00 \x00\x01\x00\x
assert z.mysummary() == r"NBNSQueryResponse '\\\xffRED' is at 192.168.0.13"

z = NBNSHeader(b'/S\x85\x80\x00\x00\x00\x01\x00\x00\x00\x00 FAEPFEEBFEEPCACACACACACACACACAAA\x00\x00 \x00\x01\x00\x03\xf4\x80\x00\x06\x00\x00\xc0\xa8\x01A')
assert z.RR_NAME == b'POTATO '
assert z.RR_NAME == b'POTATO'
assert z.ADDR_ENTRY[0].G == 0
assert z.ADDR_ENTRY[0].NB_ADDRESS == "192.168.1.65"

= NBNSQueryResponse answers NBNSQueryRequest

req = IP(ihl=5, len=78, proto=17, chksum=8562, src='172.19.0.7', dst='172.19.0.255')/UDP(sport=137, dport=137, len=58, chksum=62101)/NBNSHeader(NM_FLAGS=17, QDCOUNT=1)/NBNSQueryRequest(QUESTION_NAME=b'Loremipsumdolor', SUFFIX=17217)
resp = IP(b'E\x00\x00Zn\xab@\x00@\x11s\xb5\xac\x13\x00\x05\xac\x13\x00\x07\x00\x89\x00\x89\x00FX\x8a\x00\x00\x85\x00\x00\x00\x00\x01\x00\x00\x00\x00 EMGPHCGFGNGJHAHDHFGNGEGPGMGPHCCA\x00\x00 \x00\x01\x00\x00\x00\xa5\x00\x06\x00\x00\xac\x13\x00\x05')

try:
conf.checkIPaddr = True
assert not resp.answers(req)
conf.checkIPaddr = False
assert resp.answers(req)
finally:
conf.checkIPaddr = True

= NBNSNodeStatusResponse - build & dissect

z = NBNSHeader()/NBNSNodeStatusResponse(NODE_NAME=[NBNSNodeStatusResponseService(NETBIOS_NAME="WINDOWS")], MAC_ADDRESS="aa:aa:aa:aa:aa:aa")
Expand All @@ -66,7 +79,7 @@ assert z.mysummary() == r"NBNSNodeStatusRequest who has '\\\xff'"
z = NBNSHeader()/NBNSWackResponse(RR_NAME="SARAH")
assert raw(z) == b'\x00\x00\xbc\x00\x00\x00\x00\x01\x00\x00\x00\x00 FDEBFCEBEICACACACACACACACACACAAA\x00\x00 \x00\x01\x00\x00\x00\x02\x00\x02)\x10'
pkt = NBNSHeader(raw(z))
assert pkt[NBNSWackResponse].RR_NAME == b'SARAH '
assert pkt[NBNSWackResponse].RR_NAME == b'SARAH'

= NBTSession

Expand Down

0 comments on commit 31b3588

Please sign in to comment.