SQL Browser detection for MSSQL protocol
Description
Adds support for SQL Browser detection and automatic fallback to the dynamic SQL server port if only one instance is running.
This slows down the scan a bit but can detect MSSQL servers without a detailed port scan on each
I chose to add SQL Browser detection by default and added a blacklit flag --no-sqlbrowser, but it can easily be changed to --sqlbrowser if it's more appropriate.
When using --port with a non-default port, the fallback will be ignored. When multiple instances are detected, no fallback happens.
We could also remove the port fallback part and simply report it and let the user rerun with --port. Let me know what's best.
Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [x] This change requires a documentation update
- [ ] This requires a third party update (such as Impacket, Dploot, lsassy, etc)
Setup guide for the review
OS : Windows Server 2022 with SQL Server Express installed
Expose the SQL server to the network : Open SQL Server Network Configuration > Protocols for INSTANCENAME
- Click TCP/IP protocol
- Set
Enabledto true and ensureListen Allis set toYes
Enable the SQL Browser service : Open SQL Server Services
- Right-click SQL Server Browser > Properties > Service : Start-Mode = Automatic
- Start the service
Disable the Windows Firewall if you're lazy (SQL Server rules are not added by default, this is for testing purposes only).
Restart the SQL Server service.
Note down the dynamic port used by SQL server :
You can now run a set of tests targeted at a single SQL server instance.
To add more instances, run SQL Server 2022 Installation Center, Installation, click New SQL Server Standalone installation or..., select This PC > C: > SQL2022 > Express_ENU and follow instructions. Once the instance is installed, repeat the steps above for the new instance.
Screenshots
Running on a /21 without --no-sqlbrowser (slower)
Running on a /21 with --no-sqlbrowser (faster) yields no results
The fallback works as expected when running other modules like command execution :
--port has priority and works as expected
Multiple instances setup
Checklist:
- [x] I have ran Ruff against my changes (via poetry:
poetry run python -m ruff check . --preview, use--fixto automatically fix what it can) - [x] I have added or updated the tests/e2e_commands.txt file if necessary
- [x] New and existing e2e tests pass locally with my changes
- All tests related to MSSQL pass, except
--rid-brutebecause test server was not domain-joined - [x] If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)
- Waiting for approval on the args / automatic fallback before sending PR
Man that is a fantastic update! I wasn't aware of this MSSQL Browser thing!!
Man that is a fantastic update! I wasn't aware of this MSSQL Browser thing!!
Thanks ! SQL Browser is not enabled by default and requires a bit of setup, and I haven't been able to test it outside of the lab yet to assess the efficiency of scanning an additional UDP port by default.
I usually blind spray mssql for initial access, and I'm sure I missed some instances because I didn't know about the dynamic port until yesterday...
The SQL server can still run on a random port even if the SQL browser is disabled, so it's not a 100% guaranteed hit, but it will certainly improve automatic discovery.
Yeah don't worry about the testing, I'm actually finishing integrating encryption and channel binding on Impacket, and then on NXC, myself so I have got a lab on which I can test your PR as well! Will look ASAP :)
Very interesting, thanks for the PR! Do you have any resources about that topic that you could share?
Very interesting, thanks for the PR! Do you have any resources about that topic that you could share?
Not so much, I just learned about it 😅
SQL Browser uses the TDS protocol on UDP 1434 : https://learn.microsoft.com/en-us/sql/relational-databases/security/networking/tds-8?view=sql-server-ver17
So in reality, the protocol is TDS. Yet MS states it uses SQL Server Resolution Protocol (SSRP)... Confusing.
Impacket already implements TDS behind the scenes. I'm unsure if there's more setup required for encrypted connections or if it works out of the box.
Instances can be hidden from SQL Browser, meaning the service can run but not list any instances. There's probably a way to try catch getInstances to detect whether the service actually runs THEN enumerating instances, rather than only relying on the array size. But I don't think that makes a huge operational difference.
MSDOCS for SQL Browser : https://learn.microsoft.com/en-us/sql/tools/configuration-manager/sql-server-browser-service?view=sql-server-ver16
I'll see what I can do to move the detection to enum_host_info !
Oh wait there's something about DAC that we could definitely look into : https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/diagnostic-connection-for-database-administrators?view=sql-server-ver16
It runs on the same 1434 port and potentially gives access to the database even when it's not directly exposed.
I'll try to look into it later today.
It's getting more complicated than I thought !
SQL browser also lists named-pipe SQL instances ! (which is great)
For now I'll only fallback to TCP until I understand how we can fallback to named pipes without breaking other modules.
Hey man! I think I missed something in the SQL browser configuration cause I can't seem to have a dynamic port here:
Althought I have followed you setup instructions. Is there something else you might have configured ?
Hey man! I think I missed something in the SQL browser configuration cause I can't seem to have a dynamic port here:
Althought I have followed you setup instructions. Is there something else you might have configured ?
Hey ;) Remove the static port value from IPAll and restart the service, that should be enough.
Leaving this here just in case : https://learn.microsoft.com/en-us/sql/tools/configuration-manager/tcp-ip-properties-ip-addresses-tab?view=sql-server-ver17#static-vs-dynamic-ports
I'll be looking into this one that week mate! Sorry for the long delay but I had to implement the CBT thing before ahah!
There is something I don't get:
Which is produced by that code:
# Get number of mssql instance
self.mssql_instances = self.conn.getInstances()
print(self.mssql_instances)
You mentionned there is another way of finding the correct value of instances ?
There is something I don't get:
Which is produced by that code:
# Get number of mssql instance self.mssql_instances = self.conn.getInstances() print(self.mssql_instances)You mentionned there is another way of finding the correct value of instances ?
Sorry for the delay, I haven't checked my GitHub inbox in a while.
There's no output when you don't specify any --port, that's expected when the SQL browser service is not detected. The service is not mandatory to use dynamic ports, it's just here to advertise.
Is the SQL browser service actually running ?

Which is produced by that code: