Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Netbios query response is missing an answer() #4443

Closed
lawndoc opened this issue Jun 26, 2024 · 9 comments · Fixed by #4445
Closed

Netbios query response is missing an answer() #4443

lawndoc opened this issue Jun 26, 2024 · 9 comments · Fixed by #4445

Comments

@lawndoc
Copy link

lawndoc commented Jun 26, 2024

Brief description

I am using sr1 to send UDP packets for protocols like LLMNR and NBNS. Using wireshark and tcpdump, I am able to see the packets being sent and the responses being received, but the Scapy library is not capturing the response after it hits the interface on the host.

Scapy version

2.6.0-dev (main)

Python version

3.12.3

Operating system

Ubuntu 24.04 LTS (Linux 6.8.0-35-generic)

Additional environment information

Both hosts in the below pcap are in 172.19.0.0/24. The pcaps were captured on the interface of the host running Scapy using tcpdump.

nbns pcap: nbns.zip
llmnr pcap: llmnr.zip

The host sending responses is running Responder to ensure that all LLMNR and NBNS requests have a response sent back.

How to reproduce

Code to reproduce:

from scapy.all import *
from scapy.layers.inet import IP, UDP
from scapy.layers.netbios import NBNSQueryRequest, NBNSQueryResponse, NBNSHeader

# change IP(dst= to your local broadcast IP
packet = IP(dst="172.19.0.255")/UDP(sport=137, dport=137)/NBNSHeader(OPCODE=0x0, NM_FLAGS=0x11, QDCOUNT=1)/NBNSQueryRequest(SUFFIX="file server service", QUESTION_NAME="Loremipsumdolorsitamet", QUESTION_TYPE="NB")
response = sr1(packet, timeout=3, verbose=0)
if response is not None and response.haslayer(NBNSQueryResponse):
    # Print all resolved IP addresses
    for i in range(response[NBNSQueryResponse].RDLENGTH):
        print(f"Got a response from: {response[NBNSQueryResponse].ADDR_ENTRY[i].NB_ADDRESS}")

Have Responder running on the same subnet or query a valid hostname of a Windows host on that subnet to get a response.

Actual result

No response

Expected result

No response

Related resources

No response

@lawndoc
Copy link
Author

lawndoc commented Jun 26, 2024

CC: #4275

@lawndoc
Copy link
Author

lawndoc commented Jun 26, 2024

There might be a race condition happening here.

As a workaround for this issue, we were able to use AsyncSniffer to capture packets rather than relying on the data returned from sr1. That code is below:

from scapy.all import *
from scapy.layers.inet import IP, UDP
from scapy.layers.netbios import NBNSQueryRequest, NBNSQueryResponse, NBNSHeader

# change IP(dst= to your local broadcast IP
packet = IP(dst="172.19.0.255")/UDP(sport=137, dport=137)/NBNSHeader(OPCODE=0x0, NM_FLAGS=0x11, QDCOUNT=1)/NBNSQueryRequest(SUFFIX="file server service", QUESTION_NAME="Loremipsumdolorsitamet", QUESTION_TYPE="NB")
sniffer = AsyncSniffer(filter="udp dst port 137", store=True)
sniffer.start()
sleep(0.5)  # !!! needed to add a delay before or we would sometimes miss the packets
sr1(packet, timeout=self.timeout, verbose=0)
sleep(self.timeout)
response = sniffer.stop()
if not response:
    if self.verbosity >= 1:
        print("No response (NBNS -> {self.hostname})")
    return
if self.verbosity >=1:
    for p in response:
        print(p)
# Print all resolved IP addresses
for sniffed_packet in response:
    if sniffed_packet is not None and sniffed_packet.haslayer(NBNSQueryResponse):
        for answer in sniffed_packet[NBNSQueryResponse].ADDR_ENTRY:
            print(f"Got a response from: {response[NBNSQueryResponse].ADDR_ENTRY[i].NB_ADDRESS}")

Originally, we didn't have the sleep(0.5) call between sniffer.start() and sending the query with sr1. We were running into a strange issue where it would inconsistently not capture any packets. After adding the delay between sniffer.start() and sr1, we were consistently capturing all requests and responses.

I wonder if this issue we are having with sr1 not capturing responses is due to the same reason, it's not starting the sniffer fast enough to capture the responses?

@gpotter2 gpotter2 changed the title Certain UDP protocols not receiving response with sr1 Netbios query response is missing an answer() Jun 27, 2024
@gpotter2
Copy link
Member

gpotter2 commented Jun 27, 2024

There might be a race condition happening here.

No.

You're missing conf.checkIPaddr = False.

Matching Netbios Query answers was however not implemented. This is fixed by #4445. Thanks for reporting that

@lawndoc
Copy link
Author

lawndoc commented Jun 27, 2024

I see now that config flag was called out in the usage documentation in the DHCP section. I wish you wouldn't say it like it was obvious, though. I just started using Scapy 2 weeks ago. I was just trying to be helpful and provide ideas. 😄

Thank you for discovering the Netbios Query answer bug, that would have been the next thing I ran into. I appreciate the work you do as an open source maintainer. If there's anything I can do better when reporting or troubleshooting issues, let me know.

@gpotter2
Copy link
Member

I wish you wouldn't say it like it was obvious, though

Sorry about that :P this flag is honestly hard to guess, it's alright.

I've added a doc example to #4445 for Netbios to make this a bit easier to find.

I appreciate the work you do as an open source maintainer.

You're welcome :)

@lawndoc
Copy link
Author

lawndoc commented Jun 27, 2024

Sorry to pester, but I tested again after the merge (scapy-2.6.0rc1.dev44) using the code below and am still not getting the netbios response captured by Scapy.

from scapy.all import *
from scapy.layers.inet import IP, UDP
from scapy.layers.netbios import NBNSQueryRequest, NBNSQueryResponse, NBNSHeader

# required for broadcast requests
conf.checkIPaddr = False

hostname = "Loremipsumdolorsitamet"

# change IP(dst= to your local broadcast IP
packet = IP(dst="172.19.0.255")/UDP(sport=137, dport=137)/NBNSHeader(OPCODE=0x0, NM_FLAGS=0x11, QDCOUNT=1)/NBNSQueryRequest(SUFFIX="file server service", QUESTION_NAME="Loremipsumdolorsitamet", QUESTION_TYPE="NB")
response = sr1(packet, timeout=1, verbose=0)
if not response:
    print("No response (NBNS -> {hostname})")
    exit()
for p in response:
    print(p)
# Print all resolved IP addresses
for sniffed_packet in response:
    if sniffed_packet is not None and sniffed_packet.haslayer(NBNSQueryResponse):
        for answer in sniffed_packet[NBNSQueryResponse].ADDR_ENTRY:
            print(f"Got a response from: {response[NBNSQueryResponse].ADDR_ENTRY[i].NB_ADDRESS}")

pcap from host running the above code: netbios.zip

Also, this code will actually work unlike the code I sent originally which still had self references. Sorry about that 😄

I'll do some of the troubleshooting steps you suggested here and report back.

@lawndoc
Copy link
Author

lawndoc commented Jun 27, 2024

Looks like we are getting the following results in the debug object:

<Sent: TCP:0 UDP:1 ICMP:0 Other:0>
<Received: TCP:0 UDP:1 ICMP:0 Other:6>
<Results: TCP:0 UDP:0 ICMP:0 Other:0>

I'll also take a look around the code that was changed in the PR, but I'm not as well versed at the byte level implementation of the packets.

@lawndoc
Copy link
Author

lawndoc commented Jun 27, 2024

Based on the fact that your unit tests are passing, I'm guessing there's something else I'm doing wrong lol.

@lawndoc
Copy link
Author

lawndoc commented Jun 28, 2024

I figured out the problem! The hostname we were querying was longer than the max length 15 of the netbios host field.

I added logic to my code to slice the string to [:15] before sending the request. Interestingly, it looks like Scapy already handles the slicing when it sends the packets, but the value of self.RR_NAME in answers contains the full-length hostname.

I might open a PR to handle that gracefully in the answers function. It would prevent future noobs like me from making the same mistake, and I don't think it would hurt anything to have Scapy handle that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants