Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
mssqlbof — A Beacon Object File suite for Microsoft SQL Server that speaks TDS 7.4 on the wire itself | Kitploit
Tools/GitHubGitHub/mazx0p/mssqlbof
Privilege EscalationPassword AttacksExploitationLateral MovementPost-ExploitationPenetration TestingCommand and ControlAuthenticationRed TeamingPayload DevelopmentDatabase Security
10184 months agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
GitHub
mazx0p/mssqlbof

mssqlbof

A Beacon Object File suite for Microsoft SQL Server that speaks TDS 7.4 on the wire itself

View Repository

mssqlbof

A Beacon Object File suite for Microsoft SQL Server that speaks TDS 7.4 on the wire itself, in C. No msodbcsql.dll, no sqloledb.dll, no .NET CLR, no PowerShell. One COFF per arch, loads into every beacon that honors the canonical Beacon API.

image

Why

SQL Server shows up on nearly every engagement. The two tools people reach for are SQLRecon / PowerUpSQL (CLR + PowerShell) and whatever wraps sqlcmd.exe. Both leave mscoree.dll, PowerShell AMSI events, or a full copy of the Microsoft ODBC driver sitting in beacon memory. None of that is necessary: TDS is just framed bytes over TCP with a Schannel handshake in front, and every BOF-capable beacon already has ws2_32, secur32, schannel, and bcrypt loaded.

So mssqlbof implements TDS by hand, in C, and plugs directly into whatever SSPI or BCrypt primitives the operator needs for the target. Beacon loads one ~48 KB object, runs SQL, unloads. Nothing else enters the process.

Compatibility

One object file per architecture. mssql.x64.o is the same binary on every framework — we only use the canonical Beacon API (BeaconPrintf, BeaconDataExtract, etc.) and the <LIB>$<fn> dynamic import pattern COFF loaders resolve at runtime.

Quickstart

root@kitploit:~
apt install gcc-mingw-w64 libssl-dev
make

Produces build/mssql.x64.o and build/mssql.x86.o. Drop on the team server, load with your C2's BOF runner.

Actions

Everything goes through one object file with --action <verb>:

root@kitploit:~
--action find                                   LDAP enum of MSSQLSvc SPNs in the current forest
--action info     --host <sql>                  server/version/current user/sysadmin/db
--action query    --host <sql> --sql "..."      arbitrary T-SQL, multi-row, multi-resultset
--action links    --host <sql>                  linked-server enumeration (single hop)
--action exec     --host <sql> --cmd "..."      xp_cmdshell with auto enable + restore
--action impersonate --host <sql> --discover    list logins you can EXECUTE AS
--action impersonate --host <sql> --login X --sql "..."
                                                run T-SQL as X via EXECUTE AS LOGIN
--action privesc  --host <sql>                  six-section privesc surface enumeration
--action coerce   --host <sql> --to "\\listener\x"
                                                xp_dirtree SMB auth coercion
--action passwords --host <sql>                 dump sys.linked_logins + sys.credentials
--action chain    --host <sql> --via LINK --sql "..."
                                                EXEC (...) AT [LinkedServer]

--action find runs without a host — it talks to the operator's DC via LDAP.

Authentication

Four modes. Every mode is verified end-to-end against SQL Server 2019 in both COFFLoader and Adaptix C2 on a real domain.

root@kitploit:~
--auth sspi                                     (default) current beacon thread token
                                                Kerberos if SPN exists, NTLM otherwise.
                                                Honors make_token / steal_token.

--auth ntlm --domain D --user U --pass P        explicit NTLM plaintext.
                                                Drives SSPI NTLM package, multi-leg.

--auth ntlm --domain D --user U --hash <NT>     pass-the-hash.
                                                Hand-rolled NTLMv2 (see below).
                                                No SSPI, no lsass, no make_token.

--auth sql  --user U --pass P                   SQL authentication.

--hash takes a 32-char hex NT hash or the LM:NT form that secretsdump emits.

Why the hash mode is not just SSPI + SEC_WINNT_AUTH_IDENTITY

AcquireCredentialsHandleW(NULL, "NTLM", ...) only accepts plaintext passwords in the credential identity structure. The NTLM provider derives the NT hash internally. Feeding it a hash requires patching lsass (what Mimikatz sekurlsa::pth does) or running the beacon under a sacrificial process that was already pre-authenticated.

The alternative — the one we took — is to skip SSPI for PTH entirely and generate the NTLMSSP messages ourselves. src/tds/ntlm_pth.c builds a Type 1 NEGOTIATE, parses the server's Type 2 CHALLENGE out of the TDS 0xED token, runs the NTLMv2 math with bcrypt.dll's HMAC-MD5 provider, and writes a Type 3 AUTHENTICATE that SQL Server happily passes to the DC.

The first cut failed with error 18452: login is from an untrusted domain. Capturing Impacket's working auth on the wire next to ours narrowed it down fast: we were sending 24 zeros for the LMv2 response and the full 0xe288... Windows negotiate flag soup. Matching Impacket's LMv2 computation and its smaller 0xa2880205 flag set (no KEY_EXCH, no SIGN, no ALWAYS_SIGN) made the server accept the hash. Write-up in BLOG.

Privesc for --action exec

root@kitploit:~
--impersonate auto          (default) try EXECUTE AS LOGIN, then TRUSTWORTHY hop
--impersonate login         EXECUTE AS LOGIN via an IMPERSONATE grant
--impersonate trustworthy   hop through dbo of a sysadmin-owned TRUSTWORTHY db
--impersonate none          fail if not sysadmin

privesc enumerates the surface before you pick a method: sysadmin membership, IMPERSONATE grants (with the target login's sysadmin status), TRUSTWORTHY databases owned by a sysadmin (with your access), linked servers, server-level permissions, and xp_cmdshell state.

Build

root@kitploit:~
apt install gcc-mingw-w64 libssl-dev
make                    # cross-compile BOFs to x64 + x86
make tds                # Linux shared library of the TDS core (for fuzzing / tests)

The Linux shared library shares every TDS source file with the Windows build; only tls_schannel.c / sspi.c / ntlm_pth.c swap out for their OpenSSL / stub equivalents.

Nothing calls libc or Win32 directly. Every external symbol goes through the <LIB>$<fn> dynamic import convention in src/common/dynimports.h. Verify with:

root@kitploit:~
x86_64-w64-mingw32-objdump -t build/mssql.x64.o | grep UND

Only MSVCRT$*, WS2_32$*, SECUR32$*, BCRYPT$*, CRYPT32$*, SCHANNEL$*, WLDAP32$*, KERNEL32$*, ADVAPI32$*, and __imp_Beacon* should show up. No msodbcsql.dll. No sqloledb.dll. No mscoree.dll.

OPSEC

Everything TLS is real Schannel (not a stub) with the SQL Server PRELOGIN-wrap quirk handled: the handshake runs inside TDS PRELOGIN type 0x12 packets, then LOGIN7 goes out as raw TLS application data, and the server answers that first login packet in plaintext. Multi-leg SSPI continuations also go plaintext — if you encrypt them with TLS, SRV02 just closes the socket.

Documentation

Status

v0.1.2 — multi-auth, PTH, 11 actions, lab-verified.

  • Four auth modes working: sspi, ntlm-plaintext, ntlm-hash (PTH), sql
  • Unified dispatch BOF (mssql.x64.o) with 11 actions
  • Four privesc methods for exec: login, trustworthy, auto, none
  • Multi-leg SSPI continuation with TDS EOM handling
  • Pass-the-hash via hand-rolled NTLMv2 + BCrypt
  • Full end-to-end verification: 38-case COFF sweep + Adaptix C2 sweep on a domain-joined SQL Server 2019

Known edge cases:

  • Single-hop linked-server walker only; recursive nested OPENQUERY chain is v0.2.
  • The first SQLBatch after a multi-leg SSPI login drops data on the floor. A primer SELECT in do_connect drains it — side effect is the [*] connected as ... line every action logs. Root cause is in the post-LOGINACK read path and will get a proper fix in v0.2.

Credits

  • Cobalt-Strike/bof_template for the canonical Beacon API surface this project hews to exactly.
  • TrustedSec/COFFLoader for an independent loader to test against.
  • impacket's ntlm.py and mssqlclient.py — the reference we diffed against when chasing the NTLMv2 flag soup.
  • [MS-TDS] and [MS-NLMP] — the specs that all this hand-rolling follows.
  • Opus 4.6 — Parts of the documentation were drafted with the help of Opus 4.6. All code is hand-written by ME and verified end-to-end in the lab.

License

MIT.

Download Tool
C2x64x86
Cobalt Strikeyesyes
Havocyesyes
Sliveryesyes
BruteRatelyesyes
Nighthawkyesyes
Outflank Stage1yesyes
AdaptixC2yesyes
Metasploit execute_bofyesyes
PoshC2yesyes
ActionExtra DLLs beyond beacon baselineServer-side traceNotes
findwldap32DC event 1644 (rare)LDAP only, no SQL touched
info / query / links / privesc / passwordssecur32 or bcrypt, schannel, ws2_32SQL audit 33205 if enabledPure TDS, no ODBC fingerprint
execsamexp_cmdshell + sp_configure in default traceLoud. Use --impersonate from a low-priv login to avoid landing as NT SERVICE
impersonatesameEXECUTE AS audit 33205 + 33206
coercesamexp_dirtree attempt loggedPoint it at responder / ntlmrelayx
chainsameEXEC AT logged on the linked server targetPivot primitive
DocWhat's in it
docs/PROTOCOL.mdTDS 7.4 deep dive: packet framing, PRELOGIN option stream, LOGIN7 password obfuscation, ALL_HEADERS on SQLBatch, the token stream grammar (COLMETADATA / ROW / NBCROW / DONE / LOGINACK / ENVCHANGE / 0xED SSPI continuation), the TLS handshake quirk, the multi-leg NTLM pump.
docs/OPERATOR.mdEnd-to-end lab guide: build, stand up an Adaptix listener, drop a beacon on a Windows host, run every action with every auth mode (including PTH), and cross-C2 portability notes.
docs/OPSEC.mdPer-action on-wire and in-memory footprint. What each action loads into the beacon, what it leaves in SQL audit, and what a defender can see.
docs/COMPATIBILITY.mdC2 framework matrix, SQL Server version matrix, and which auth modes are verified against which targets.
BLOGThe debugging narrative: how the pass-the-hash implementation actually came together, with wire captures, the LMv2 zero-bytes red herring, and the tshark diff against Impacket that broke it open.