package telnetmini
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/Azure/go-ntlmssp"
)
const (
SMB_COM_NEGOTIATE_PROTOCOL = 0x72
SMB_COM_SESSION_SETUP_ANDX = 0x73
SMB_COM_TREE_CONNECT_ANDX = 0x75
SMB_COM_NT_CREATE_ANDX = 0xA2
SMB_COM_READ_ANDX = 0x2E
SMB_COM_WRITE_ANDX = 0x2F
SMB_COM_CLOSE = 0x04
SMB_COM_TREE_DISCONNECT = 0x71
SMB_COM_LOGOFF_ANDX = 0x74
SMB_FLAGS_CANONICAL_PATHNAMES = 0x10
SMB_FLAGS_CASELESS_PATHNAMES = 0x08
SMB_FLAGS2_UNICODE_STRINGS = 0x8000
SMB_FLAGS2_ERRSTATUS = 0x4000
SMB_FLAGS2_READ_IF_EXECUTE = 0x2000
SMB_FLAGS2_32_BIT_ERRORS = 0x1000
SMB_FLAGS2_DFS = 0x0800
SMB_FLAGS2_EXTENDED_SECURITY = 0x0400
SMB_FLAGS2_REPARSE_PATH = 0x0200
SMB_FLAGS2_SMB_SECURITY_SIGNATURE = 0x0100
SMB_FLAGS2_SMB_SECURITY_SIGNATURE_REQUIRED = 0x0080
SMB_SECURITY_SHARE = 0x00
SMB_SECURITY_USER = 0x01
SMB_SECURITY_DOMAIN = 0x02
SMB_CAP_EXTENDED_SECURITY = 0x80000000
SMB_CAP_COMPRESSED_DATA = 0x40000000
SMB_CAP_BULK_TRANSFER = 0x20000000
SMB_CAP_UNIX = 0x00800000
SMB_CAP_LARGE_READX = 0x00400000
SMB_CAP_LARGE_WRITEX = 0x00200000
SMB_CAP_INFOLEVEL_PASSTHRU = 0x00100000
SMB_CAP_DFS = 0x00080000
SMB_CAP_NT_FIND = 0x00040000
SMB_CAP_LOCK_AND_READ = 0x00020000
SMB_CAP_LEVEL_II_OPLOCKS = 0x00010000
SMB_CAP_STATUS32 = 0x00008000
SMB_CAP_RPC_REMOTE_APIS = 0x00004000
SMB_CAP_NT_SMBS = 0x00002000
NTLMSSP_NEGOTIATE_56 = 0x80000000
NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000
NTLMSSP_NEGOTIATE_128 = 0x20000000
NTLMSSP_NEGOTIATE_VERSION = 0x02000000
NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000
NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000
NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000
NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY = 0x00080000
NTLMSSP_TARGET_TYPE_SERVER = 0x00020000
NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000
NTLMSSP_NEGOTIATE_NTLM = 0x00000200
NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080
NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040
NTLMSSP_NEGOTIATE_SEAL = 0x00000020
NTLMSSP_NEGOTIATE_SIGN = 0x00000010
NTLMSSP_REQUEST_TARGET = 0x00000004
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
)
type SMBPacket struct {
NetBIOSHeader []byte
SMBHeader []byte
SMBData []byte
}
type SMBHeader struct {
ProtocolID [4]byte
Command byte
Status uint32
Flags byte
Flags2 uint16
PIDHigh uint16
Signature [8]byte
Reserved uint16
TreeID uint16
ProcessID uint16
UserID uint16
MultiplexID uint16
}
func CreateSMBPacket(smbData []byte) *SMBPacket {
netbiosHeader := make([]byte, 4)
netbiosHeader[0] = 0x00
netbiosHeader[1] = 0x00
netbiosHeader[2] = 0x00
length := len(smbData)
netbiosHeader[3] = byte(length & 0xFF)
return &SMBPacket{
NetBIOSHeader: netbiosHeader,
SMBHeader: createSMBHeader(),
SMBData: smbData,
}
}
func createSMBHeader() []byte {
header := make([]byte, 32)
header[0] = 0xFF
header[1] = 'S'
header[2] = 'M'
header[3] = 'B'
header[4] = 0x00
binary.LittleEndian.PutUint32(header[5:9], 0)
header[9] = SMB_FLAGS_CANONICAL_PATHNAMES
binary.LittleEndian.PutUint16(header[10:12], SMB_FLAGS2_UNICODE_STRINGS|SMB_FLAGS2_EXTENDED_SECURITY)
binary.LittleEndian.PutUint16(header[12:14], 0)
binary.LittleEndian.PutUint16(header[20:22], 0)
binary.LittleEndian.PutUint16(header[22:24], 0)
binary.LittleEndian.PutUint16(header[24:26], 0)
binary.LittleEndian.PutUint16(header[26:28], 0)
binary.LittleEndian.PutUint16(header[28:30], 0)
return header
}
func CreateNegotiateProtocolPacket() []byte {
header := createSMBHeader()
header[4] = SMB_COM_NEGOTIATE_PROTOCOL
data := createNegotiateProtocolData()
packet := append(header, data...)
smbPacket := CreateSMBPacket(packet)
return smbPacket.Bytes()
}
func createNegotiateProtocolData() []byte {
var buf bytes.Buffer
_ = buf.WriteByte(0x00)
_ = buf.WriteByte(0x00)
dialects := []string{
"NT LM 0.12",
"SMB 2.002",
"SMB 2.???",
}
for _, dialect := range dialects {
_ = buf.WriteByte(byte(len(dialect)))
_, _ = buf.WriteString(dialect)
_ = buf.WriteByte(0x00)
}
data := buf.Bytes()
data[1] = byte(len(data) - 2)
return data
}
func CreateSessionSetupPacket(username, password, domain string, sessionKey uint64) []byte {
header := createSMBHeader()
header[4] = SMB_COM_SESSION_SETUP_ANDX
data := createSessionSetupData(username, password, domain, sessionKey)
packet := append(header, data...)
smbPacket := CreateSMBPacket(packet)
return smbPacket.Bytes()
}
func createSessionSetupData(username, password, domain string, sessionKey uint64) []byte {
var buf bytes.Buffer
_ = buf.WriteByte(0x0D)
_ = buf.WriteByte(0xFF)
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0xFFFF))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x01))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x00))
_ = binary.Write(&buf, binary.LittleEndian, uint32(sessionKey))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(password)))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(password)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00))
_ = binary.Write(&buf, binary.LittleEndian, uint32(SMB_CAP_EXTENDED_SECURITY|SMB_CAP_NT_SMBS))
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString(password)
_, _ = buf.WriteString(password)
_, _ = buf.WriteString(username)
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString(domain)
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString("Windows 2000 2195")
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString("Windows 2000 5.0")
_ = buf.WriteByte(0x00)
data := buf.Bytes()
data[len(data)-1] = byte(len(data) - 0x21)
return data
}
func CreateTreeConnectPacket(shareName string, password string) []byte {
header := createSMBHeader()
header[4] = SMB_COM_TREE_CONNECT_ANDX
data := createTreeConnectData(shareName, password)
packet := append(header, data...)
smbPacket := CreateSMBPacket(packet)
return smbPacket.Bytes()
}
func createTreeConnectData(shareName, password string) []byte {
var buf bytes.Buffer
_ = buf.WriteByte(0x04)
_ = buf.WriteByte(0xFF)
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x00))
_ = buf.WriteByte(byte(len(password)))
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString(password)
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString(shareName)
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString("?????")
_ = buf.WriteByte(0x00)
data := buf.Bytes()
data[7] = byte(len(data) - 0x0B)
return data
}
func CreateNTCreatePacket(fileName string) []byte {
header := createSMBHeader()
header[4] = SMB_COM_NT_CREATE_ANDX
data := createNTCreateData(fileName)
packet := append(header, data...)
smbPacket := CreateSMBPacket(packet)
return smbPacket.Bytes()
}
func createNTCreateData(fileName string) []byte {
var buf bytes.Buffer
_ = buf.WriteByte(0x18)
_ = buf.WriteByte(0xFF)
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(fileName)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint64(0x0000000000000000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000001))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000002))
_ = buf.WriteByte(0x00)
_ = buf.WriteByte(0x00)
_ = buf.WriteByte(0x00)
_, _ = buf.WriteString(fileName)
_ = buf.WriteByte(0x00)
data := buf.Bytes()
data[0x3B] = byte(len(data) - 0x3C)
return data
}
func (p *SMBPacket) Bytes() []byte {
var result bytes.Buffer
result.Write(p.NetBIOSHeader)
result.Write(p.SMBHeader)
result.Write(p.SMBData)
return result.Bytes()
}
func CreateNTLMNegotiatePacket() []byte {
var buf bytes.Buffer
_, _ = buf.WriteString("NTLMSSP")
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint32(1))
flags := uint32(NTLMSSP_NEGOTIATE_56 |
NTLMSSP_NEGOTIATE_128 |
NTLMSSP_NEGOTIATE_VERSION |
NTLMSSP_NEGOTIATE_TARGET_INFO |
NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY |
NTLMSSP_TARGET_TYPE_SERVER |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NTLMSSP_NEGOTIATE_NTLM |
NTLMSSP_NEGOTIATE_UNICODE)
_ = binary.Write(&buf, binary.LittleEndian, flags)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0))
_ = buf.WriteByte(0x05)
_ = buf.WriteByte(0x02)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0A28))
_, _ = buf.Write(make([]byte, 3))
_ = buf.WriteByte(0x0F)
return buf.Bytes()
}
func CreateNTLMChallengePacket(challenge []byte, targetInfo []byte) []byte {
var buf bytes.Buffer
_, _ = buf.WriteString("NTLMSSP")
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint32(2))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint32(56))
flags := uint32(NTLMSSP_NEGOTIATE_56 |
NTLMSSP_NEGOTIATE_128 |
NTLMSSP_NEGOTIATE_VERSION |
NTLMSSP_NEGOTIATE_TARGET_INFO |
NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY |
NTLMSSP_TARGET_TYPE_SERVER |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NTLMSSP_NEGOTIATE_NTLM |
NTLMSSP_NEGOTIATE_UNICODE)
_ = binary.Write(&buf, binary.LittleEndian, flags)
_, _ = buf.Write(challenge)
_, _ = buf.Write(make([]byte, 8))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(targetInfo)))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(targetInfo)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(56))
_ = buf.WriteByte(0x05)
_ = buf.WriteByte(0x02)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0A28))
_, _ = buf.Write(make([]byte, 3))
_ = buf.WriteByte(0x0F)
_, _ = buf.Write(targetInfo)
return buf.Bytes()
}
func CreateNTLMAuthPacket(username, password, domain, workstation string, challenge []byte, lmResponse, ntResponse []byte) []byte {
var buf bytes.Buffer
_, _ = buf.WriteString("NTLMSSP")
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint32(3))
baseOffset := uint32(72)
lmOffset := baseOffset
ntOffset := lmOffset + uint32(len(lmResponse))
domainOffset := ntOffset + uint32(len(ntResponse))
userOffset := domainOffset + uint32(len(domain))
workOffset := userOffset + uint32(len(username))
sessionKeyOffset := workOffset + uint32(len(workstation))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(lmResponse)))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(lmResponse)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(lmOffset))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(ntResponse)))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(ntResponse)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(ntOffset))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(domain)))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(domain)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(domainOffset))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(username)))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(username)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(userOffset))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(workstation)))
_ = binary.Write(&buf, binary.LittleEndian, uint16(len(workstation)))
_ = binary.Write(&buf, binary.LittleEndian, uint32(workOffset))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0))
_ = binary.Write(&buf, binary.LittleEndian, uint32(sessionKeyOffset))
flags := uint32(NTLMSSP_NEGOTIATE_56 |
NTLMSSP_NEGOTIATE_128 |
NTLMSSP_NEGOTIATE_VERSION |
NTLMSSP_NEGOTIATE_TARGET_INFO |
NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY |
NTLMSSP_TARGET_TYPE_SERVER |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NTLMSSP_NEGOTIATE_NTLM |
NTLMSSP_NEGOTIATE_UNICODE)
_ = binary.Write(&buf, binary.LittleEndian, flags)
_ = buf.WriteByte(0x05)
_ = buf.WriteByte(0x02)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0A28))
_, _ = buf.Write(make([]byte, 3))
_ = buf.WriteByte(0x0F)
_, _ = buf.Write(lmResponse)
_, _ = buf.Write(ntResponse)
_, _ = buf.WriteString(domain)
_, _ = buf.WriteString(username)
_, _ = buf.WriteString(workstation)
return buf.Bytes()
}
func CreateLMResponse(challenge []byte, password string) []byte {
type2 := CreateNTLMChallengePacket(challenge, []byte{})
authMsg, err := ntlmssp.NewAuthenticateMessage(type2, "", password, nil)
if err != nil {
return nil
}
if len(authMsg) < 20 {
return nil
}
lmLen := binary.LittleEndian.Uint16(authMsg[12:14])
lmOffset := binary.LittleEndian.Uint32(authMsg[16:20])
if int(lmOffset)+int(lmLen) > len(authMsg) {
return nil
}
return authMsg[lmOffset : lmOffset+uint32(lmLen)]
}
func CreateNTResponse(challenge []byte, password string) []byte {
type2 := CreateNTLMChallengePacket(challenge, []byte{})
authMsg, err := ntlmssp.NewAuthenticateMessage(type2, "", password, nil)
if err != nil {
return nil
}
if len(authMsg) < 28 {
return nil
}
ntLen := binary.LittleEndian.Uint16(authMsg[20:22])
ntOffset := binary.LittleEndian.Uint32(authMsg[24:28])
if int(ntOffset)+int(ntLen) > len(authMsg) {
return nil
}
return authMsg[ntOffset : ntOffset+uint32(ntLen)]
}
func CreateSMBv2NegotiatePacket() []byte {
var buf bytes.Buffer
_ = buf.WriteByte(0xFE)
_, _ = buf.WriteString("SMB")
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = buf.WriteByte(0x00)
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0000))
_ = binary.Write(&buf, binary.LittleEndian, uint64(0x0000000000000001))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000000))
_ = binary.Write(&buf, binary.LittleEndian, uint64(0x0000000000000000))
_, _ = buf.Write(make([]byte, 16))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x24))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0001))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0001))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0000))
_ = binary.Write(&buf, binary.LittleEndian, uint32(0x00000001))
_, _ = buf.Write(make([]byte, 16))
_ = binary.Write(&buf, binary.LittleEndian, uint64(0x0000000000000000))
_ = binary.Write(&buf, binary.LittleEndian, uint16(0x0311))
return buf.Bytes()
}
func ParseSMBResponse(data []byte) (*SMBHeader, error) {
if len(data) < 32 {
return nil, fmt.Errorf("SMB response too short: %d bytes", len(data))
}
header := &SMBHeader{}
if data[0] != 0xFF || data[1] != 'S' || data[2] != 'M' || data[3] != 'B' {
return nil, fmt.Errorf("invalid SMB protocol ID")
}
header.Command = data[4]
header.Status = binary.LittleEndian.Uint32(data[5:9])
header.Flags = data[9]
header.Flags2 = binary.LittleEndian.Uint16(data[10:12])
header.PIDHigh = binary.LittleEndian.Uint16(data[12:14])
copy(header.Signature[:], data[14:22])
header.Reserved = binary.LittleEndian.Uint16(data[22:24])
header.TreeID = binary.LittleEndian.Uint16(data[24:26])
header.ProcessID = binary.LittleEndian.Uint16(data[26:28])
header.UserID = binary.LittleEndian.Uint16(data[28:30])
header.MultiplexID = binary.LittleEndian.Uint16(data[30:32])
return header, nil
}