Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

badsmb

A from-scratch, userland SMB1 / SMB2 file server written in Go. It is a lightweight SMB server for ad-hoc file sharing, NTLM auth capture, and protocol experiment ground, with no Python runtime required.

The binary name is smbserver; the module path is github.com/jimmexploit/badsmb.

Why this is different from the other Go SMB servers

There are a handful of SMB servers in Go (for example hugelgupf/smbsrv, Adjsky/smbserver, and the many one-file utility SMB servers). This project deliberately occupies a different niche:

  • Focused scope. A lightweight SMB1/SMB2 file server: SMB1 core file commands, SMB2 equivalents, anonymous and NTLM auth, read-only or read-write shares, and auth / session logging. It does not aim to be a production SMB implementation. Out of scope by design: Kerberos as a server, SMB3 AES encryption, DFS, VSS, and Windows ACL semantics.

  • Single static binary. go build produces one no-cgo, statically linked executable with no Python and no pip install. Drop it on a box and run it.

  • Small memory footprint. A long-running or embedded SMB listener stays light compared with a Python process plus interpreter.

  • Easy cross-compilation. One GOOS/GOARCH change builds for Windows, Linux, macOS, and ARM targets (see "Cross-compile").

  • Library-first design for deep customization. Most Go SMB servers are a single binary with no sane import surface. Here every package (smb1, smb2, ntlm, vfs, server, config, rpc, srvsvc, wire, logx) is a public, importable Go package. There is no internal/ boundary. That is intentional: people routinely want custom NTLMv2 handling, a capture-only authenticator, a different logging backend, or their own VFS layer, and the public ntlm.Validator interface is the extension point for that. See "Use as a package".

  • Network share browsing works. Because real smbclient -L depends on it, the server ships with the named-pipe plumbing plus loopback DCERPC (rpc) and the SRVSVC / WKSTSRVC callbacks (srvsvc) that serve IPC$, so clients can enumerate shares over \srvsvc / \wkssvc.

If you need a hardened, enterprise SMB server (Kerberos, SMB3 encryption, full ACLs), a different project is the right fit. This one exists to provide a lightweight SMB server in a single deployable Go binary.

Protocol coverage

  • SMB1 (Core + NT): NEGOTIATE, SESSION_SETUP_ANDX (classic and extended SPNEGO/NTLM), TREE_CONNECT/DISCONNECT, LOGOFF, NT_CREATE_ANDX, READ/WRITE/CLOSE, QUERY_INFORMATION / QUERY_INFORMATION2 / QUERY_INFORMATION_DISK, CREATE_DIRECTORY / DELETE / DELETE_DIRECTORY / RENAME, FIND_FIRST2 / FIND_NEXT2, and the TRANSACTION2 subset.
  • SMB2 / SMB3: NEGOTIATE (dialect 0x0202), SESSION_SETUP, TREE_CONNECT/DISCONNECT, CREATE, READ, WRITE, CLOSE, QUERY_DIRECTORY, QUERY_INFO, SET_INFO, IOCTL (VALIDATE_NEGOTIATE_INFO and PIPE_TRANSCEIVE), ECHO, CHANGE_NOTIFY, LOGOFF.
  • Auth: anonymous/guest logons and NTLM (username + password, or username + LMHASH:NTHASH). Message signing is supported opportunistically.
  • Named pipes / DCERPC: IPC$ share with \srvsvc and \wkssvc served through loopback DCERPC, so network share enumeration works.

Needs the -smb2support flag (or SMB2Support = yes in a config file) to answer SMB2; otherwise the server stays SMB1 only.

Build

Requires Go 1.24 or newer (module targets go 1.24.4). No cgo, no third-party runtime dependencies beyond golang.org/x/crypto (indirect).

go build ./cmd/smbserver

This produces ./smbserver in the current directory. Verify it:

./smbserver -h

Install

Install the binary directly to $GOPATH/bin/smbserver:

go install github.com/jimmexploit/badsmb/cmd/smbserver@latest

Sanity-check the whole module:

go vet ./...
go build ./...

Cross-compile

Set GOOS and GOARCH at build time. The code has no cgo, so any target is a straight cross-build:

GOOS=windows GOARCH=amd64 go build ./cmd/smbserver
GOOS=linux   GOARCH=arm64 go build ./cmd/smbserver
GOOS=darwin  GOARCH=arm64 go build ./cmd/smbserver

To list all targets Go supports on this host:

go tool dist list

Use as a binary

Quick start

Share a local directory as tmp, listening on all interfaces:

./smbserver tmp /tmp

Connect from smbclient:

smbclient //127.0.0.1/tmp -U guest
smbclient //127.0.0.1/tmp -U SOMEUSER%PASSWORD -L //127.0.0.1

Options

usage: smbserver [options] shareName sharePath

Launch an SMB server sharing sharePath as shareName. Alternatively
load a .conf file with -conf instead of positional share arguments.
Option Description
-comment string Share comment reported during enumeration.
-username string Authenticate clients against this username.
-password string Password for -username.
-hashes string NTLM hashes for -username, format LMHASH:NTHASH.
-ip string Listen interface (default 0.0.0.0, or :: with -6).
-interface-address Alias for -ip.
-port int TCP port (default 445).
-readonly Only allow reads.
-smb2support Enable SMB2 negotiation (experimental).
-disablentlm Do not offer NTLM authentication.
-disablekerberos Parsed for parity; Kerberos is a non-goal, so inert.
-dropssp Disable NTLM ESS/SSP during negotiation.
-6, -ipv6 Listen on IPv6.
-ts Add a timestamp to every log line.
-debug Print DEBUG output.
-outputfile string Write log output to this file.
-conf string Load an INI .conf file instead of positionals.

-username requires either -password or -hashes. The server never prompts for a missing password; it exits with status 2 instead. -password is resolved to an NT hash (MD4 of the UTF-16LE password) at startup.

Without -username, the server runs in capture mode: any user is accepted and the NTLM challenge/response material is logged for downstream analysis.

Using a .conf file

The -conf flag loads an INI-style config. It is mutually exclusive with the positional share arguments and the -username / -password / -hashes / -comment flags. -ip and -port still apply on top.

[global]
server_name = BADSMBSRV
server_os = Windows 7 Ultimate 7601
server_domain = WORKGROUP
log_file = None
credentials_file = /path/to/creds.txt

[share1]
comment = Read-only share
read only = yes
path = /srv/share1

Global options: server_name, server_os, server_domain, log_file, credentials_file (required); challenge, jtr_dump_path, dump_hashes, SMB2Support, DropSSP, KerberosSupport, NTLMSupport, anonymous_logon, rpc_apis, and the computer_account_* block (optional). Share sections take comment, read only, share type (0 disk, 1 printer, 3 IPC), and path (required). A read only value of exactly yes makes a share read-only; any other value (including 1 or true) does not.

Listening on ports below 1024

Port 445 is privileged on many systems. Start as root or grant the capability, for example on Linux:

sudo setcap 'cap_net_bind_service=+ep' ./smbserver

Use as a package

Every package is public and importable; there is no internal/ boundary. This is the documented extension point for custom NTLMv2 handling, capture-only logging, alternate VFS backends, and custom logging.

Minimal embedded server

package main

import (
    "log"

    "github.com/jimmexploit/badsmb/config"
    "github.com/jimmexploit/badsmb/server"
)

func main() {
    cfg := config.Default()
    cfg.AddShare("tmp", "/tmp", "temporary share", /* readOnly */ false)

    srv, err := server.New(server.WithConfig(cfg))
    if err != nil {
        log.Fatal(err)
    }
    defer srv.Close()

    if err := srv.Listen(); err != nil {
        log.Fatal(err)
    }
    if err := srv.Serve(); err != nil {
        log.Fatal(err)
    }
}

Programmatic construction options

The server package uses functional options:

  • server.WithListenAddr(addr) - listen address in host:port form, e.g. "127.0.0.1:445" or ":445".
  • server.WithShares(shares) - shares from a []config.Share.
  • server.WithConfig(cfg) - a resolved config.Config (convenience path the CLI uses).
  • server.WithNTLMValidator(v) - custom NTLM auth logic; defaults to ntlm.DefaultValidator{}.
  • server.WithLogger(l) / server.WithLeveledLogger(l) - logging backends.

Custom NTLM behavior

The server depends on the ntlm.Validator interface, not on any concrete auth implementation:

type Validator interface {
    Validate(challenge []byte, resp *AuthenticateMessage, creds Credentials) (bool, error)
}

Implement it directly, or embed ntlm.DefaultValidator and override only what you need:

type LoggingValidator struct{ ntlm.DefaultValidator }

func (v *LoggingValidator) Validate(challenge []byte, resp *ntlm.AuthenticateMessage, creds ntlm.Credentials) (bool, error) {
    ok, err := v.DefaultValidator.Validate(challenge, resp, creds)
    log.Printf("auth attempt: user=%s ok=%v", resp.UserName, ok)
    return ok, err
}

srv, _ := server.New(server.WithNTLMValidator(&LoggingValidator{}))

A capture-only validator that always succeeds (and logs the material) is a common pattern enabled by this interface.

Low-level packages

  • ntlm - exported NEGOTIATE / CHALLENGE / AUTHENTICATE message structs (all fields public), SPNEGO tokens, NTLMv2 response parsing and NT hash computation, plus John-the-Ripper hash formatting helpers (JohnFormat, WriteJohnOutputToFile, JtrLogon).
  • config - Config, share definitions, auth mode, Default(), AddShare, SetHashes, and the .conf INI parser (Load).
  • vfs - share-to-path mapping, path jail guard, stat-to-FILE_* information encoders.
  • smb1, smb2 - the two dialects' headers, constants, and command handlers.
  • rpc, srvsvc - loopback DCERPC transport and the SRVSVC/WKSTSRVC share-addressing callbacks.
  • wire, logx - shared codec helpers and the leveled logging package.

References

  • Microsoft Open Specifications: MS-SMB, MS-CIFS, MS-SMB2, MS-FSCC, MS-NLMP, MS-ERREF, MS-SRVS, MS-WKST.
  • Impacket

The common SMB client used for manual validation is samba-client smbclient, plus mount -t cifs and Windows Explorer for connect, authenticate, list, read, and write checks.

Authorized use only

Use this only on systems you own or have explicit authorization to test.

License

This project is released under the MIT License.

About

A from-scratch Go SMB1/SMB2/3 server built for security research, with pluggable NTLM authentication and a library-first API.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages