├── .gitignore ├── LICENSE ├── auth ├── gssapi.go ├── gssapi_targ.go ├── gssapi_targ_test.go ├── gssapi_test.go ├── ntlm_flags.go ├── ntlmssp.go └── ntlmssp_test.go ├── command.go ├── compression.go ├── dialect.go ├── encryption.go ├── erref.go ├── example └── serverlet.go ├── flags.go ├── go.mod ├── go.sum ├── kdf.go ├── kdf_test.go ├── main.go ├── negotiate_context.go ├── negotiate_request.go ├── negotiate_request_test.go ├── negotiate_response.go ├── negotiate_response_test.go ├── netname.go ├── ntlmssp ├── challenge_message.go ├── messages.go ├── messages_test.go └── version.go ├── packet.go ├── packet_test.go ├── preauth_integrity.go ├── rdma_transform.go ├── server.go ├── session_setup_request.go ├── session_setup_request_test.go ├── session_setup_response.go ├── session_setup_response_test.go ├── signing.go └── transport.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | example/example 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Pichu Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /auth/gssapi.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | encoding_asn1 "encoding/asn1" 5 | "encoding/hex" 6 | "fmt" 7 | 8 | "golang.org/x/crypto/cryptobyte" 9 | "golang.org/x/crypto/cryptobyte/asn1" 10 | ) 11 | 12 | var ( 13 | OBJECT_IDENTIFIER = 0x06 14 | ) 15 | 16 | var DefaultNegoPayload = func() []byte { 17 | r, _ := hex.DecodeString("604806062b0601050502a03e303ca00e300c060a2b06010401823702020aa32a3028a0261b246e6f745f646566696e65645f696e5f5246433431373840706c656173655f69676e6f7265") 18 | 19 | return r 20 | }() 21 | 22 | type InitPayload struct { 23 | OID encoding_asn1.ObjectIdentifier `asn1:"set,tag:6"` 24 | Token NegotiationToken `asn1:"set,tag:0"` 25 | } 26 | 27 | // RFC 4178 - 4.2. NegotiationToken 28 | 29 | // NegotiationToken ::= CHOICE { 30 | // negTokenInit [0] NegTokenInit, 31 | // negTokenResp [1] NegTokenResp 32 | // } 33 | 34 | type NegotiationToken struct { 35 | NegTokenInit NegTokenInitData `asn1:"application,tag:0"` 36 | // NegTokenResp *NegTokenResp 37 | } 38 | 39 | // RFC 4178 - 4.2.1. NegTokenInit 40 | // NegTokenInit ::= SEQUENCE { 41 | // mechTypes [0] MechTypeList, 42 | // reqFlags [1] ContextFlags OPTIONAL, 43 | // -- inherited from RFC 2478 for backward compatibility, 44 | // -- RECOMMENDED to be left out 45 | // mechToken [2] OCTET STRING OPTIONAL, 46 | // mechListMIC [3] OCTET STRING OPTIONAL, 47 | // ... 48 | // } 49 | 50 | type NegTokenInitData struct { 51 | MechTypes MechTypeList `asn1:"application,tag:0"` 52 | ReqFlags encoding_asn1.BitString `asn1:"application,tag:1"` 53 | MechToken []byte `asn1:"application,tag:2"` 54 | NegHints []byte `asn1:"application,tag:3"` 55 | MechListMIC []byte `asn1:"application,tag:3"` 56 | } 57 | 58 | type ContextFlags int32 59 | 60 | // var ( 61 | // DELEG_FLAG ContextFlags = 0 62 | // MUTUAL_FLAG ContextFlags = 1 63 | // REPLAY_FLAG ContextFlags = 2 64 | // SEQUENCE_FLAG ContextFlags = 3 65 | // ANNO_FLAG ContextFlags = 4 66 | // CONF_FLAG ContextFlags = 5 67 | // INTEG_FLAG ContextFlags = 6 68 | // ) 69 | 70 | // RFC 4178 - 4.2.2. NegTokenResp 71 | 72 | type MechTypeList []MechType 73 | 74 | type MechType encoding_asn1.ObjectIdentifier 75 | 76 | func NewInitPayload(b []byte) (*InitPayload, error) { 77 | var input = cryptobyte.String(b) 78 | var inner = cryptobyte.String{} 79 | ret := &InitPayload{} 80 | 81 | if !input.ReadASN1(&inner, asn1.Tag(0).Constructed()|0x40 /* application */) { 82 | return nil, fmt.Errorf("ReadASN1 for tag 0x%0x failed", asn1.Tag(0).Constructed()|0x40) 83 | } 84 | 85 | if !inner.ReadASN1ObjectIdentifier(&ret.OID) { 86 | return nil, fmt.Errorf("ReadASN1ObjectIdentifier failed") 87 | } 88 | 89 | var SPNObjectString = cryptobyte.String{} 90 | var SPNObjectStringInner = cryptobyte.String{} 91 | if !inner.ReadASN1(&SPNObjectString, 0xA0 /* asn1.ClassContextSpecific */) || 92 | !SPNObjectString.ReadASN1(&SPNObjectStringInner, asn1.SEQUENCE) { 93 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA0) 94 | } 95 | 96 | var MechTypesInner = cryptobyte.String{} 97 | var MechTypeInner = cryptobyte.String{} 98 | if !SPNObjectStringInner.ReadASN1(&MechTypesInner, 0xA0 /* asn1.ClassContextSpecific */) || 99 | !MechTypesInner.ReadASN1(&MechTypeInner, asn1.SEQUENCE) { 100 | return nil, fmt.Errorf("cryptobyte.String(%v).ReadASN1(&actual, asn1.ClassContextSpecific, 0) = false; want true", b) 101 | } 102 | 103 | list := MechTypeList{} 104 | for !MechTypeInner.Empty() { 105 | // var mechType = MechType{} 106 | oid := encoding_asn1.ObjectIdentifier{} 107 | if !MechTypeInner.ReadASN1ObjectIdentifier(&oid) { 108 | return nil, fmt.Errorf("ReadASN1ObjectIdentifier failed") 109 | } 110 | list = append(list, MechType(oid)) 111 | } 112 | 113 | ret.Token.NegTokenInit.MechTypes = list 114 | 115 | // fmt.Println("remain: ", MechTypeInner) 116 | // fmt.Println("remain: ", MechTypesInner) 117 | var outPresent bool 118 | var reqFlagInner = cryptobyte.String{} 119 | if !SPNObjectStringInner.ReadOptionalASN1(&reqFlagInner, &outPresent, 0xA1 /* asn1.ClassContextSpecific */) { 120 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA1) 121 | } 122 | if outPresent { 123 | var reqFlag = encoding_asn1.BitString{} 124 | if !reqFlagInner.ReadASN1BitString(&reqFlag) { 125 | return nil, fmt.Errorf("ReadASN1BitString with tag 1 failed") 126 | } 127 | ret.Token.NegTokenInit.ReqFlags = reqFlag 128 | } 129 | 130 | var mechTokenInner = cryptobyte.String{} 131 | if !SPNObjectStringInner.ReadOptionalASN1(&mechTokenInner, &outPresent, 0xA2 /* asn1.ClassContextSpecific */) { 132 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA2) 133 | } 134 | if outPresent { 135 | var mechToken = []byte{} 136 | if !mechTokenInner.ReadASN1Bytes(&mechToken, asn1.OCTET_STRING) { 137 | return nil, fmt.Errorf("ReadASN1Bytes with tag 2 failed") 138 | } 139 | ret.Token.NegTokenInit.MechToken = mechToken 140 | } 141 | // fmt.Println("remain: ", SPNObjectStringInner) 142 | 143 | var mechListMICInner = cryptobyte.String{} 144 | if !SPNObjectStringInner.ReadOptionalASN1(&mechListMICInner, &outPresent, 0xA3 /* asn1.ClassContextSpecific */) { 145 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA3) 146 | } 147 | if outPresent { 148 | // fmt.Printf("mechListMICInner: %v\n", mechListMICInner) 149 | // fmt.Printf("mechListMICInner: %s\n", mechListMICInner) 150 | var mechListMIC = []byte{} 151 | var tag asn1.Tag 152 | var inner = cryptobyte.String{} 153 | if mechListMICInner.ReadAnyASN1Element(&inner, &tag) { 154 | if tag == asn1.OCTET_STRING { 155 | // is MechListMIC 156 | if !inner.ReadASN1Bytes(&mechListMIC, asn1.OCTET_STRING) { 157 | return nil, fmt.Errorf("ReadASN1Bytes failed") 158 | } 159 | } else if tag == asn1.SEQUENCE { 160 | var negHints = cryptobyte.String{} 161 | if !inner.ReadASN1(&negHints, asn1.SEQUENCE) { 162 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA3) 163 | } 164 | var negHintsInner = cryptobyte.String{} 165 | if !negHints.ReadASN1(&negHintsInner, 0xA0) { 166 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA0) 167 | } 168 | // fmt.Printf("negHintsInner2: %v\n", negHintsInner) 169 | var negHintsBytes []byte 170 | if !negHintsInner.ReadASN1Bytes(&negHintsBytes, asn1.GeneralString) { // asn1.GeneralString 171 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA3) 172 | } 173 | ret.Token.NegTokenInit.NegHints = negHintsBytes 174 | 175 | } else { 176 | return nil, fmt.Errorf("tag is not asn1.OCTET_STRING") 177 | } 178 | } 179 | ret.Token.NegTokenInit.MechListMIC = mechListMIC 180 | } 181 | // fmt.Println("remain: ", SPNObjectStringInner) 182 | 183 | var negHintsInner = cryptobyte.String{} 184 | // if there are more than 3 optional fields, the negHints will be the 3th field, and the mechListMIC will be the 4th field 185 | if !SPNObjectStringInner.ReadOptionalASN1(&negHintsInner, &outPresent, 0xA4 /* asn1.ClassContextSpecific */) { 186 | return nil, fmt.Errorf("ReadASN1 for SPNObjectString tag %v failed", 0xA4) 187 | } 188 | if outPresent { 189 | var inner = []byte{} 190 | if !negHintsInner.ReadASN1Bytes(&inner, asn1.OCTET_STRING) { 191 | return nil, fmt.Errorf("ReadASN1Bytes failed") 192 | } 193 | ret.Token.NegTokenInit.MechListMIC = inner 194 | } 195 | 196 | // fmt.Println("remain: ", SPNObjectStringInner) 197 | // fmt.Println("remain: ", SPNObjectString) 198 | 199 | return ret, nil 200 | } 201 | 202 | func (payload *InitPayload) Bytes() ([]byte, error) { 203 | var builder cryptobyte.Builder 204 | builder.AddASN1(asn1.Tag(0).Constructed()|0x40, func(builder *cryptobyte.Builder) { 205 | builder.AddASN1ObjectIdentifier(payload.OID) 206 | builder.AddASN1(0xA0, func(builder *cryptobyte.Builder) { 207 | builder.AddASN1(asn1.SEQUENCE, func(builder *cryptobyte.Builder) { 208 | builder.AddASN1(0xA0, func(builder *cryptobyte.Builder) { 209 | builder.AddASN1(asn1.SEQUENCE, func(builder *cryptobyte.Builder) { 210 | for _, mechType := range payload.Token.NegTokenInit.MechTypes { 211 | builder.AddASN1ObjectIdentifier(encoding_asn1.ObjectIdentifier(mechType)) 212 | } 213 | }) 214 | }) 215 | 216 | if payload.Token.NegTokenInit.ReqFlags.BitLength > 0 { 217 | builder.AddASN1(0xA1, func(builder *cryptobyte.Builder) { 218 | builder.AddASN1BitString(payload.Token.NegTokenInit.ReqFlags.Bytes) 219 | }) 220 | } 221 | 222 | if len(payload.Token.NegTokenInit.MechToken) > 0 { 223 | builder.AddASN1(0xA2, func(builder *cryptobyte.Builder) { 224 | builder.AddASN1OctetString(payload.Token.NegTokenInit.MechToken) 225 | }) 226 | } 227 | 228 | if len(payload.Token.NegTokenInit.NegHints) > 0 { 229 | builder.AddASN1(0xA3, func(builder *cryptobyte.Builder) { 230 | builder.AddASN1(asn1.SEQUENCE, func(builder *cryptobyte.Builder) { 231 | builder.AddASN1(0xA0, func(builder *cryptobyte.Builder) { 232 | builder.AddASN1(asn1.GeneralString, func(builder *cryptobyte.Builder) { 233 | builder.AddBytes(payload.Token.NegTokenInit.NegHints) 234 | }) 235 | }) 236 | }) 237 | }) 238 | } 239 | }) 240 | }) 241 | 242 | }) 243 | return builder.Bytes() 244 | } 245 | -------------------------------------------------------------------------------- /auth/gssapi_targ.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | encoding_asn1 "encoding/asn1" 5 | "fmt" 6 | 7 | "golang.org/x/crypto/cryptobyte" 8 | "golang.org/x/crypto/cryptobyte/asn1" 9 | ) 10 | 11 | type TargPayload struct { 12 | NegResult uint8 13 | SupportedMech MechType 14 | ResponseToken []byte 15 | MechListMIC []byte 16 | } 17 | 18 | func NewTargPayload(b []byte) (*TargPayload, error) { 19 | var input = cryptobyte.String(b) 20 | var inner = cryptobyte.String{} 21 | var outPresent bool 22 | var intValue int 23 | ret := &TargPayload{} 24 | // a181c4 25 | if !input.ReadASN1(&inner, 0xa1) { 26 | return nil, fmt.Errorf("ReadASN1 for tag 0x%0x failed", 0xa1) 27 | } 28 | // 3081c1 29 | var seq cryptobyte.String 30 | if !inner.ReadASN1(&seq, asn1.SEQUENCE) { 31 | return nil, fmt.Errorf("ReadASN1 for tag 0x%0x failed", asn1.SEQUENCE) 32 | } 33 | 34 | // a003 35 | if !seq.ReadOptionalASN1(&inner, &outPresent, 0xa0) { 36 | return nil, fmt.Errorf("ReadASN1 for negResult 0x%0x failed", 0xa0) 37 | } 38 | 39 | if outPresent { 40 | // 0a0101 41 | if !inner.ReadASN1Enum(&intValue) { 42 | return nil, fmt.Errorf("ReadASN1Bytes for negResult value 0x%0x failed", 0x0a) 43 | } 44 | ret.NegResult = uint8(intValue) 45 | } 46 | 47 | // a10c 48 | if !seq.ReadOptionalASN1(&inner, &outPresent, 0xa1) { 49 | return nil, fmt.Errorf("ReadASN1 for supportedMech 0x%0x failed", 0xa1) 50 | } 51 | 52 | if outPresent { 53 | // 060a2b06010401823702020a 54 | oid := encoding_asn1.ObjectIdentifier{} 55 | if !inner.ReadASN1ObjectIdentifier(&oid) { 56 | return nil, fmt.Errorf("ReadASN1ObjectIdentifier for supportedMech oid failed") 57 | } 58 | ret.SupportedMech = MechType(oid) 59 | } 60 | 61 | // a281ab 62 | if !seq.ReadOptionalASN1(&inner, &outPresent, 0xa2) { 63 | return nil, fmt.Errorf("ReadASN1 for responseToken 0x%0x failed", 0xa2) 64 | } 65 | 66 | if outPresent { 67 | // 0481a84e5 44c4d5353500002000000140014003800000015828ae2b5bdb4abf704918f00000000000000005c005c004c000000060100000000000f4d00420056004d00320032003100320030003800020014004d00420056004d00320032003100320030003800010014004d00420056004d0032003200310032003000380004000000030014006d00620076006d003200320031003200300038000700080060bbda0f486dd90100000000 68 | if !inner.ReadASN1Bytes(&ret.ResponseToken, asn1.OCTET_STRING) { 69 | return nil, fmt.Errorf("ReadASN1Bytes responseToken failed") 70 | } 71 | } 72 | 73 | // a300 74 | if !seq.ReadOptionalASN1(&inner, &outPresent, 0xa3) { 75 | return nil, fmt.Errorf("ReadASN1 for mechListMIC 0x%0x failed", 0xa3) 76 | } 77 | 78 | if outPresent { 79 | // 0400 80 | if !inner.ReadASN1Bytes(&ret.MechListMIC, asn1.OCTET_STRING) { 81 | return nil, fmt.Errorf("ReadASN1Bytes mechListMIC failed") 82 | } 83 | } 84 | 85 | return ret, nil 86 | } 87 | 88 | func (payload *TargPayload) Bytes() ([]byte, error) { 89 | var builder cryptobyte.Builder 90 | builder.AddASN1(0xA1, func(builder *cryptobyte.Builder) { 91 | builder.AddASN1(asn1.SEQUENCE, func(builder *cryptobyte.Builder) { 92 | builder.AddASN1(0xA0, func(builder *cryptobyte.Builder) { 93 | builder.AddASN1Enum(int64(payload.NegResult)) 94 | }) 95 | 96 | builder.AddASN1(0xA1, func(builder *cryptobyte.Builder) { 97 | builder.AddASN1ObjectIdentifier(encoding_asn1.ObjectIdentifier(payload.SupportedMech)) 98 | }) 99 | 100 | if len(payload.ResponseToken) > 0 { 101 | builder.AddASN1(0xA2, func(builder *cryptobyte.Builder) { 102 | builder.AddASN1OctetString(payload.ResponseToken) 103 | }) 104 | } 105 | 106 | if len(payload.MechListMIC) > 0 { 107 | builder.AddASN1(0xA3, func(builder *cryptobyte.Builder) { 108 | builder.AddASN1OctetString(payload.MechListMIC) 109 | }) 110 | } 111 | }) 112 | }) 113 | return builder.Bytes() 114 | } 115 | -------------------------------------------------------------------------------- /auth/gssapi_targ_test.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | encoding_asn1 "encoding/asn1" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestGSSAPINegTokenTargDecode(t *testing.T) { 10 | cases := []struct { 11 | name string 12 | input []byte 13 | expected TargPayload 14 | }{ 15 | { 16 | name: "response from samba, NTLMSSP_NEGOTIATE", 17 | input: func() []byte { 18 | r, _ := hex.DecodeString("a181c43081c1a0030a0101a10c060a2b06010401823702020aa281ab0481a84e544c4d5353500002000000140014003800000015828ae2b5bdb4abf704918f00000000000000005c005c004c000000060100000000000f4d00420056004d00320032003100320030003800020014004d00420056004d00320032003100320030003800010014004d00420056004d0032003200310032003000380004000000030014006d00620076006d003200320031003200300038000700080060bbda0f486dd90100000000") 19 | return r 20 | }(), 21 | expected: TargPayload{ 22 | NegResult: 0x01, 23 | SupportedMech: MechType(encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 2, 2, 10})), 24 | ResponseToken: func() []byte { 25 | r, _ := hex.DecodeString("4e544c4d5353500002000000140014003800000015828ae2b5bdb4abf704918f00000000000000005c005c004c000000060100000000000f4d00420056004d00320032003100320030003800020014004d00420056004d00320032003100320030003800010014004d00420056004d0032003200310032003000380004000000030014006d00620076006d003200320031003200300038000700080060bbda0f486dd90100000000") 26 | return r 27 | }(), 28 | }, 29 | }, 30 | } 31 | 32 | for _, c := range cases { 33 | t.Run(c.name, func(t *testing.T) { 34 | actual, err := NewTargPayload(c.input) 35 | if err != nil { 36 | t.Errorf("NewTargPayload() = %v, want %v", err, nil) 37 | return 38 | } 39 | 40 | if actual.NegResult != c.expected.NegResult { 41 | t.Errorf("NewTargPayload().NegResult = %v, want %v", actual.NegResult, c.expected.NegResult) 42 | } 43 | 44 | if encoding_asn1.ObjectIdentifier(actual.SupportedMech).String() != encoding_asn1.ObjectIdentifier(c.expected.SupportedMech).String() { 45 | t.Errorf("NewTargPayload().SupportedMech = %v, want %v", actual.SupportedMech, c.expected.SupportedMech) 46 | } 47 | 48 | if hex.EncodeToString(actual.ResponseToken) != hex.EncodeToString(c.expected.ResponseToken) { 49 | t.Errorf("NewTargPayload().ResponseToken = %v, want %v", actual.ResponseToken, c.expected.ResponseToken) 50 | } 51 | 52 | if hex.EncodeToString(actual.MechListMIC) != hex.EncodeToString(c.expected.MechListMIC) { 53 | t.Errorf("NewTargPayload().MechListMIC = %v, want %v", actual.MechListMIC, c.expected.MechListMIC) 54 | } 55 | }) 56 | } 57 | } 58 | 59 | func TestGSSAPINegTokenTargEncode(t *testing.T) { 60 | cases := []struct { 61 | name string 62 | input TargPayload 63 | expected []byte 64 | }{ 65 | { 66 | name: "basic", 67 | input: TargPayload{ 68 | NegResult: 0x01, 69 | SupportedMech: MechType(encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 2, 2, 10})), 70 | ResponseToken: func() []byte { 71 | r, _ := hex.DecodeString("4e544c4d5353500002000000140014003800000015828ae2b5bdb4abf704918f00000000000000005c005c004c000000060100000000000f4d00420056004d00320032003100320030003800020014004d00420056004d00320032003100320030003800010014004d00420056004d0032003200310032003000380004000000030014006d00620076006d003200320031003200300038000700080060bbda0f486dd90100000000") 72 | return r 73 | }(), 74 | }, 75 | expected: func() []byte { 76 | r, _ := hex.DecodeString("a181c43081c1a0030a0101a10c060a2b06010401823702020aa281ab0481a84e544c4d5353500002000000140014003800000015828ae2b5bdb4abf704918f00000000000000005c005c004c000000060100000000000f4d00420056004d00320032003100320030003800020014004d00420056004d00320032003100320030003800010014004d00420056004d0032003200310032003000380004000000030014006d00620076006d003200320031003200300038000700080060bbda0f486dd90100000000") 77 | return r 78 | }(), 79 | }, 80 | } 81 | 82 | for _, c := range cases { 83 | t.Run(c.name, func(t *testing.T) { 84 | actual, err := c.input.Bytes() 85 | if err != nil { 86 | t.Errorf("Encode() = %v, want %v", err, nil) 87 | return 88 | } 89 | if hex.EncodeToString(actual) != hex.EncodeToString(c.expected) { 90 | if len(actual) != len(c.expected) { 91 | t.Logf("actual length = %d, want %d", len(actual), len(c.expected)) 92 | for i := range actual { 93 | if actual[i] != c.expected[i] { 94 | t.Logf("actual[%d] = %0x, want %0x", i, actual[i], c.expected[i]) 95 | break 96 | } 97 | } 98 | } else { 99 | for i := range actual { 100 | if actual[i] != c.expected[i] { 101 | t.Logf("actual[%d] = %0x, want %0x", i, actual[i], c.expected[i]) 102 | } 103 | } 104 | } 105 | t.Errorf("Encode() = %0x, want %0x", actual, c.expected) 106 | } 107 | }) 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /auth/gssapi_test.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | encoding_asn1 "encoding/asn1" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestGSSAPIDecode(t *testing.T) { 10 | cases := []struct { 11 | name string 12 | input []byte 13 | expected InitPayload 14 | }{ 15 | { 16 | name: "test", 17 | input: func() []byte { 18 | r, _ := hex.DecodeString("604806062b0601050502a03e303ca00e300c060a2b06010401823702020aa22a04284e544c4d5353500001000000158288e2000000000000000000000000000000000a0000000000000f") 19 | return r 20 | }(), 21 | expected: InitPayload{ 22 | OID: encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 2}), 23 | Token: NegotiationToken{ 24 | NegTokenInit: NegTokenInitData{ 25 | MechTypes: MechTypeList{ 26 | MechType(encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 2, 2, 10})), 27 | }, 28 | ReqFlags: encoding_asn1.BitString{Bytes: []byte{}, BitLength: 0}, 29 | MechToken: []byte{ 30 | 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x82, 0x88, 0xe2, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x0a, 00, 00, 00, 00, 00, 00, 0x0f, 31 | }, 32 | }, 33 | }, 34 | }, 35 | }, 36 | { 37 | name: "response from samba", 38 | input: func() []byte { 39 | r, _ := hex.DecodeString("604806062b0601050502a03e303ca00e300c060a2b06010401823702020aa32a3028a0261b246e6f745f646566696e65645f696e5f5246433431373840706c656173655f69676e6f7265") 40 | return r 41 | }(), 42 | expected: InitPayload{ 43 | OID: encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 2}), 44 | Token: NegotiationToken{ 45 | NegTokenInit: NegTokenInitData{ 46 | MechTypes: MechTypeList{ 47 | MechType(encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 2, 2, 10})), 48 | }, 49 | ReqFlags: encoding_asn1.BitString{Bytes: []byte{}, BitLength: 0}, 50 | NegHints: []byte("not_defined_in_RFC4178@please_ignore"), 51 | MechToken: []byte{}, 52 | }, 53 | }, 54 | }, 55 | }, 56 | } 57 | 58 | for _, c := range cases { 59 | t.Run(c.name, func(t *testing.T) { 60 | // var actual InitPayload 61 | actual, err := NewInitPayload(c.input) 62 | if err != nil { 63 | t.Errorf("NewInitPayload() = %v, want %v", err, nil) 64 | return 65 | } 66 | if actual.OID.String() != c.expected.OID.String() { 67 | t.Errorf("NewInitPayload().OID = %v, want %v", actual.OID, c.expected.OID) 68 | } 69 | 70 | if actual.Token.NegTokenInit.ReqFlags.BitLength != c.expected.Token.NegTokenInit.ReqFlags.BitLength || 71 | hex.EncodeToString(actual.Token.NegTokenInit.ReqFlags.Bytes) != hex.EncodeToString(c.expected.Token.NegTokenInit.ReqFlags.Bytes) { 72 | t.Errorf("NewInitPayload().Token.NegTokenInit.ReqFlags = %v, want %v", actual.Token.NegTokenInit.ReqFlags, c.expected.Token.NegTokenInit.ReqFlags) 73 | } 74 | 75 | if hex.EncodeToString(actual.Token.NegTokenInit.MechToken) != hex.EncodeToString(c.expected.Token.NegTokenInit.MechToken) { 76 | t.Errorf("NewInitPayload().Token.NegTokenInit.MechToken = %v, want %v", actual.Token.NegTokenInit.MechToken, c.expected.Token.NegTokenInit.MechToken) 77 | } 78 | 79 | if len(actual.Token.NegTokenInit.MechTypes) != len(c.expected.Token.NegTokenInit.MechTypes) { 80 | t.Errorf("NewInitPayload().Token.NegTokenInit.MechTypes = %v, want %v", actual.Token.NegTokenInit.MechTypes, c.expected.Token.NegTokenInit.MechTypes) 81 | } 82 | 83 | for i, mechType := range actual.Token.NegTokenInit.MechTypes { 84 | if encoding_asn1.ObjectIdentifier(mechType).String() != encoding_asn1.ObjectIdentifier(c.expected.Token.NegTokenInit.MechTypes[i]).String() { 85 | t.Errorf("NewInitPayload().Token.NegTokenInit.MechTypes[%v] = %v, want %v", i, mechType, c.expected.Token.NegTokenInit.MechTypes[i]) 86 | } 87 | } 88 | 89 | if hex.EncodeToString(actual.Token.NegTokenInit.MechListMIC) != hex.EncodeToString(c.expected.Token.NegTokenInit.MechListMIC) { 90 | t.Errorf("NewInitPayload().Token.NegTokenInit.MechListMIC = %v, want %v", actual.Token.NegTokenInit.MechListMIC, c.expected.Token.NegTokenInit.MechListMIC) 91 | } 92 | 93 | if hex.EncodeToString(actual.Token.NegTokenInit.NegHints) != hex.EncodeToString(c.expected.Token.NegTokenInit.NegHints) { 94 | t.Errorf("NewInitPayload().Token.NegTokenInit.NegHints = %v, want %v", actual.Token.NegTokenInit.NegHints, c.expected.Token.NegTokenInit.NegHints) 95 | } 96 | 97 | // TODO 98 | }) 99 | } 100 | 101 | } 102 | 103 | func TestGSSAPIEncode(t *testing.T) { 104 | cases := []struct { 105 | name string 106 | input InitPayload 107 | expected []byte 108 | }{ 109 | { 110 | name: "basic", 111 | input: InitPayload{ 112 | OID: encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 2}), 113 | Token: NegotiationToken{ 114 | NegTokenInit: NegTokenInitData{ 115 | MechTypes: MechTypeList{ 116 | MechType(encoding_asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 2, 2, 10})), 117 | }, 118 | ReqFlags: encoding_asn1.BitString{Bytes: []byte{}, BitLength: 0}, 119 | NegHints: []byte("not_defined_in_RFC4178@please_ignore"), 120 | MechToken: []byte{}, 121 | }, 122 | }, 123 | }, 124 | expected: func() []byte { 125 | r, _ := hex.DecodeString("604806062b0601050502a03e303ca00e300c060a2b06010401823702020aa32a3028a0261b246e6f745f646566696e65645f696e5f5246433431373840706c656173655f69676e6f7265") 126 | return r 127 | }(), 128 | }, 129 | } 130 | 131 | for _, c := range cases { 132 | t.Run(c.name, func(t *testing.T) { 133 | actual, err := c.input.Bytes() 134 | if err != nil { 135 | t.Errorf("Encode() = %v, want %v", err, nil) 136 | return 137 | } 138 | if hex.EncodeToString(actual) != hex.EncodeToString(c.expected) { 139 | t.Errorf("Encode() = %0x, want %0x", actual, c.expected) 140 | } 141 | }) 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /auth/ntlm_flags.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | const ( 4 | NTLMSSP_NEGOTIATE_UNICODE uint32 = 1 << iota 5 | NTLMSSP_NEGOTIATE_OEM 6 | NTLMSSP_REQUEST_TARGET 7 | r10 8 | NTLMSSP_NEGOTIATE_SIGN 9 | NTLMSSP_NEGOTIATE_SEAL 10 | NTLMSSP_NEGOTIATE_DATAGRAM 11 | NTLMSSP_NEGOTIATE_LM_KEY 12 | r9 13 | NTLMSSP_NEGOTIATE_NTLM 14 | r8 15 | NTLMSSP_NEGOTIATE_ANONYMOUS 16 | NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED 17 | NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED 18 | r7 19 | NTLMSSP_NEGOTIATE_ALWAYS_SIGN 20 | NTLMSSP_TARGET_TYPE_DOMAIN 21 | NTLMSSP_TARGET_TYPE_SERVER 22 | r6 23 | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 24 | NTLMSSP_NEGOTIATE_IDENTIFY 25 | r5 26 | NTLMSSP_REQUEST_NON_NT_SESSION_KEY 27 | NTLMSSP_NEGOTIATE_TARGET_INFO 28 | r4 29 | NTLMSSP_NEGOTIATE_VERSION 30 | r3 31 | r2 32 | r1 33 | NTLMSSP_NEGOTIATE_128 34 | NTLMSSP_NEGOTIATE_KEY_EXCH 35 | NTLMSSP_NEGOTIATE_56 36 | ) 37 | -------------------------------------------------------------------------------- /auth/ntlmssp.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "encoding/binary" 5 | "strings" 6 | ) 7 | 8 | type NTLMMessage []byte 9 | 10 | const ( 11 | NTLMSSP_NEGOTIATE uint32 = 1 12 | NTLMSSP_AUTH uint32 = 3 13 | ) 14 | 15 | func (p NTLMMessage) IsInvalid() bool { 16 | // MS-SMB2, MUST be set to 12 17 | if len(p) < 12 { 18 | return true 19 | } 20 | 21 | if strings.Compare("NTLMSSP", string(p[0:7])) != 0 { 22 | return true 23 | } 24 | 25 | return false 26 | } 27 | 28 | func (p NTLMMessage) MessageType() uint32 { 29 | return binary.LittleEndian.Uint32(p[8:12]) 30 | } 31 | 32 | func (p NTLMMessage) SetMessageType(v uint32) { 33 | binary.LittleEndian.PutUint32(p[8:12], v) 34 | } 35 | 36 | type NTLMNegotiateMessage []byte 37 | 38 | func (p NTLMNegotiateMessage) IsInvalid() bool { 39 | if len(p) < 32 { 40 | return true 41 | } 42 | 43 | return false 44 | } 45 | 46 | func (p NTLMNegotiateMessage) Flags() uint32 { 47 | return binary.LittleEndian.Uint32(p[12:16]) 48 | } 49 | 50 | func (p NTLMNegotiateMessage) SetFlags(v uint32) { 51 | binary.LittleEndian.PutUint32(p[12:16], v) 52 | } 53 | 54 | func (p NTLMNegotiateMessage) DomainNameLen() uint16 { 55 | return binary.LittleEndian.Uint16(p[16:18]) 56 | } 57 | 58 | func (p NTLMNegotiateMessage) SetDomainNameLen(v uint16) { 59 | binary.LittleEndian.PutUint16(p[16:18], v) 60 | } 61 | 62 | func (p NTLMNegotiateMessage) DomainNameMaxLen() uint16 { 63 | return binary.LittleEndian.Uint16(p[18:20]) 64 | } 65 | 66 | func (p NTLMNegotiateMessage) SetDomainNameMaxLen(v uint16) { 67 | binary.LittleEndian.PutUint16(p[18:20], v) 68 | } 69 | 70 | func (p NTLMNegotiateMessage) DomainNameBufferOffset() uint32 { 71 | return binary.LittleEndian.Uint32(p[20:24]) 72 | } 73 | 74 | func (p NTLMNegotiateMessage) SetDomainNameBufferOffset(v uint32) { 75 | binary.LittleEndian.PutUint32(p[20:24], v) 76 | } 77 | 78 | func (p NTLMNegotiateMessage) WorkstationLen() uint16 { 79 | return binary.LittleEndian.Uint16(p[24:26]) 80 | } 81 | 82 | func (p NTLMNegotiateMessage) SetWorkstationLen(v uint16) { 83 | binary.LittleEndian.PutUint16(p[24:26], v) 84 | } 85 | 86 | func (p NTLMNegotiateMessage) WorkstationMaxLen() uint16 { 87 | return binary.LittleEndian.Uint16(p[26:28]) 88 | } 89 | 90 | func (p NTLMNegotiateMessage) SetWorkstationMaxLen(v uint16) { 91 | binary.LittleEndian.PutUint16(p[26:28], v) 92 | } 93 | 94 | func (p NTLMNegotiateMessage) WorkstationBufferOffset() uint32 { 95 | return binary.LittleEndian.Uint32(p[28:32]) 96 | } 97 | 98 | func (p NTLMNegotiateMessage) SetWorkstationBufferOffset(v uint32) { 99 | binary.LittleEndian.PutUint32(p[28:32], v) 100 | } 101 | 102 | func (p NTLMNegotiateMessage) MajorVersion() uint8 { 103 | return p[32] 104 | } 105 | 106 | func (p NTLMNegotiateMessage) MinorVersion() uint8 { 107 | return p[33] 108 | } 109 | 110 | func (p NTLMNegotiateMessage) BuildNumber() uint16 { 111 | return binary.LittleEndian.Uint16(p[34:36]) 112 | } 113 | 114 | func (p NTLMNegotiateMessage) SetBuildNumber(v uint16) { 115 | binary.LittleEndian.PutUint16(p[34:36], v) 116 | } 117 | 118 | func (p NTLMNegotiateMessage) VersionReserved() []byte { 119 | return p[36:39] 120 | } 121 | 122 | func (p NTLMNegotiateMessage) NTLMRevisionCurrent() uint8 { 123 | // NTLMSSP_REVISION_W2K3 = 0x0F 124 | return p[39] 125 | } 126 | -------------------------------------------------------------------------------- /auth/ntlmssp_test.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | ) 7 | 8 | func TestNTLMSSPDecode(t *testing.T) { 9 | cases := []struct { 10 | name string 11 | input []byte 12 | expected map[string]interface{} 13 | }{ 14 | { 15 | name: "test", 16 | input: func() []byte { 17 | r, _ := hex.DecodeString("4e544c4d5353500001000000158288e2000000000000000000000000000000000a0000000000000f") 18 | return r 19 | }(), 20 | expected: map[string]interface{}{ 21 | "IsInvalid": false, 22 | "Flags": NTLMSSP_NEGOTIATE_56 | 23 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_VERSION | 24 | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | 25 | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_SIGN | 26 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_UNICODE, 27 | "DomainNameLen": uint16(0), 28 | "DomainNameMaxLen": uint16(0), 29 | "DomainNameBufferOffset": uint32(0), 30 | "WorkstationLen": uint16(0), 31 | "WorkstationMaxLen": uint16(0), 32 | "WorkstationBufferOffset": uint32(0), 33 | "MajorVersion": uint8(10), 34 | "MinorVersion": uint8(0), 35 | "BuildNumber": uint16(0), 36 | "NTLMRevisionCurrent": uint8(15), 37 | }, 38 | }, 39 | } 40 | 41 | for _, c := range cases { 42 | t.Run(c.name, func(t *testing.T) { 43 | nr := NTLMNegotiateMessage(c.input) 44 | if nr.IsInvalid() != c.expected["IsInvalid"].(bool) { 45 | t.Errorf("NTLMSSP.IsInvalid() = %v, want %v", nr.IsInvalid(), c.expected["IsInvalid"]) 46 | } 47 | if nr.Flags() != c.expected["Flags"].(uint32) { 48 | t.Errorf("NTLMSSP.Flags() = %0x, want %0x", nr.Flags(), c.expected["Flags"]) 49 | } 50 | if nr.DomainNameLen() != c.expected["DomainNameLen"].(uint16) { 51 | t.Errorf("NTLMSSP.DomainNameLen() = %v, want %v", nr.DomainNameLen(), c.expected["DomainNameLen"]) 52 | } 53 | if nr.DomainNameMaxLen() != c.expected["DomainNameMaxLen"].(uint16) { 54 | t.Errorf("NTLMSSP.DomainNameMaxLen() = %v, want %v", nr.DomainNameMaxLen(), c.expected["DomainNameMaxLen"]) 55 | } 56 | if nr.DomainNameBufferOffset() != c.expected["DomainNameBufferOffset"].(uint32) { 57 | t.Errorf("NTLMSSP.DomainNameBufferOffset() = %v, want %v", nr.DomainNameBufferOffset(), c.expected["DomainNameBufferOffset"]) 58 | } 59 | if nr.WorkstationLen() != c.expected["WorkstationLen"].(uint16) { 60 | t.Errorf("NTLMSSP.WorkstationLen() = %v, want %v", nr.WorkstationLen(), c.expected["WorkstationLen"]) 61 | } 62 | if nr.WorkstationMaxLen() != c.expected["WorkstationMaxLen"].(uint16) { 63 | t.Errorf("NTLMSSP.WorkstationMaxLen() = %v, want %v", nr.WorkstationMaxLen(), c.expected["WorkstationMaxLen"]) 64 | } 65 | if nr.WorkstationBufferOffset() != c.expected["WorkstationBufferOffset"].(uint32) { 66 | t.Errorf("NTLMSSP.WorkstationBufferOffset() = %v, want %v", nr.WorkstationBufferOffset(), c.expected["WorkstationBufferOffset"]) 67 | } 68 | if nr.MajorVersion() != c.expected["MajorVersion"].(uint8) { 69 | t.Errorf("NTLMSSP.MajorVersion() = %v, want %v", nr.MajorVersion(), c.expected["MajorVersion"]) 70 | } 71 | if nr.MinorVersion() != c.expected["MinorVersion"].(uint8) { 72 | t.Errorf("NTLMSSP.MinorVersion() = %v, want %v", nr.MinorVersion(), c.expected["MinorVersion"]) 73 | } 74 | if nr.BuildNumber() != c.expected["BuildNumber"].(uint16) { 75 | t.Errorf("NTLMSSP.BuildNumber() = %v, want %v", nr.BuildNumber(), c.expected["BuildNumber"]) 76 | } 77 | if nr.NTLMRevisionCurrent() != c.expected["NTLMRevisionCurrent"].(uint8) { 78 | t.Errorf("NTLMSSP.NTLMRevisionCurrent() = %v, want %v", nr.NTLMRevisionCurrent(), c.expected["NTLMRevisionCurrent"]) 79 | } 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /command.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type Command uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 33/481 7 | SMB2_NEGOTIATE Command = 0x0000 8 | SMB2_SESSION_SETUP Command = 0x0001 9 | SMB2_LOGOFF Command = 0x0002 10 | SMB2_TREE_CONNECT Command = 0x0003 11 | SMB2_TREE_DISCONNECT Command = 0x0004 12 | SMB2_CREATE Command = 0x0005 13 | SMB2_CLOSE Command = 0x0006 14 | SMB2_FLUSH Command = 0x0007 15 | SMB2_READ Command = 0x0008 16 | SMB2_WRITE Command = 0x0009 17 | SMB2_LOCK Command = 0x000A 18 | SMB2_IOCTL Command = 0x000B 19 | SMB2_CANCEL Command = 0x000C 20 | SMB2_ECHO Command = 0x000D 21 | SMB2_QUERY_DIRECTORY Command = 0x000E 22 | SMB2_CHANGE_NOTIFY Command = 0x000F 23 | SMB2_QUERY_INFO Command = 0x0010 24 | SMB2_SET_INFO Command = 0x0011 25 | SMB2_OPLOCK_BREAK Command = 0x0012 26 | ) 27 | 28 | func (c Command) String() string { 29 | switch c { 30 | case SMB2_NEGOTIATE: 31 | return "SMB2_NEGOTIATE" 32 | case SMB2_SESSION_SETUP: 33 | return "SMB2_SESSION_SETUP" 34 | case SMB2_LOGOFF: 35 | return "SMB2_LOGOFF" 36 | case SMB2_TREE_CONNECT: 37 | return "SMB2_TREE_CONNECT" 38 | case SMB2_TREE_DISCONNECT: 39 | return "SMB2_TREE_DISCONNECT" 40 | case SMB2_CREATE: 41 | return "SMB2_CREATE" 42 | case SMB2_CLOSE: 43 | return "SMB2_CLOSE" 44 | case SMB2_FLUSH: 45 | return "SMB2_FLUSH" 46 | case SMB2_READ: 47 | return "SMB2_READ" 48 | case SMB2_WRITE: 49 | return "SMB2_WRITE" 50 | case SMB2_LOCK: 51 | return "SMB2_LOCK" 52 | case SMB2_IOCTL: 53 | return "SMB2_IOCTL" 54 | case SMB2_CANCEL: 55 | return "SMB2_CANCEL" 56 | case SMB2_ECHO: 57 | return "SMB2_ECHO" 58 | case SMB2_QUERY_DIRECTORY: 59 | return "SMB2_QUERY_DIRECTORY" 60 | case SMB2_CHANGE_NOTIFY: 61 | return "SMB2_CHANGE_NOTIFY" 62 | case SMB2_QUERY_INFO: 63 | return "SMB2_QUERY_INFO" 64 | case SMB2_SET_INFO: 65 | return "SMB2_SET_INFO" 66 | case SMB2_OPLOCK_BREAK: 67 | return "SMB2_OPLOCK_BREAK" 68 | default: 69 | return "UNKNOWN" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /compression.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type CompressionAlgorithm uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 49/481 7 | SMB2_COMPRESSION_CAPABILITIES_NONE CompressionAlgorithm = 0x0000 8 | SMB2_COMPRESSION_CAPABILITIES_LZNT1 CompressionAlgorithm = 0x0001 9 | SMB2_COMPRESSION_CAPABILITIES_LZ77 CompressionAlgorithm = 0x0002 10 | SMB2_COMPRESSION_CAPABILITIES_LZ77_HUFF CompressionAlgorithm = 0x0003 11 | SMB2_COMPRESSION_CAPABILITIES_PATTERN_V1 CompressionAlgorithm = 0x0004 12 | ) 13 | 14 | type CompressionCapability []byte 15 | 16 | func (c CompressionCapability) CompressionAlgorithmCount() uint16 { 17 | return uint16(le.Uint16(c[0:2])) 18 | } 19 | 20 | func (c CompressionCapability) SetCompressionAlgorithmCount(v uint16) { 21 | le.PutUint16(c[0:2], v) 22 | } 23 | 24 | func (c CompressionCapability) CompressionAlgorithms() []CompressionAlgorithm { 25 | var res []CompressionAlgorithm 26 | for i := 0; i < int(c.CompressionAlgorithmCount()); i++ { 27 | res = append(res, CompressionAlgorithm(le.Uint16(c[4+i*2:6+i*2]))) 28 | } 29 | return res 30 | } 31 | -------------------------------------------------------------------------------- /dialect.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type Dialect uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 46/481 7 | SMB2_DIALECT_202 Dialect = 0x0202 8 | SMB2_DIALECT_21 Dialect = 0x0210 9 | SMB2_DIALECT_30 Dialect = 0x0300 10 | SMB2_DIALECT_302 Dialect = 0x0302 11 | SMB2_DIALECT_311 Dialect = 0x0311 12 | 13 | SMB2_DIALECT_2xx Dialect = 0x02FF 14 | ) 15 | 16 | func (d Dialect) String() string { 17 | switch d { 18 | case SMB2_DIALECT_202: 19 | return "SMB2_DIALECT_202" 20 | case SMB2_DIALECT_21: 21 | return "SMB2_DIALECT_21" 22 | case SMB2_DIALECT_30: 23 | return "SMB2_DIALECT_30" 24 | case SMB2_DIALECT_302: 25 | return "SMB2_DIALECT_302" 26 | case SMB2_DIALECT_311: 27 | return "SMB2_DIALECT_311" 28 | case SMB2_DIALECT_2xx: 29 | return "SMB2_DIALECT_2xx" 30 | } 31 | return "Unknown" 32 | } 33 | -------------------------------------------------------------------------------- /encryption.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type Cipher uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 48/481 7 | SMB2_ENCRYPTION_AES128_CCM Cipher = 0x0001 8 | SMB2_ENCRYPTION_AES128_GCM Cipher = 0x0002 9 | SMB2_ENCRYPTION_AES256_CCM Cipher = 0x0003 10 | SMB2_ENCRYPTION_AES256_GCM Cipher = 0x0004 11 | ) 12 | 13 | func (c Cipher) String() string { 14 | switch c { 15 | case SMB2_ENCRYPTION_AES128_CCM: 16 | return "SMB2_ENCRYPTION_AES128_CCM" 17 | case SMB2_ENCRYPTION_AES128_GCM: 18 | return "SMB2_ENCRYPTION_AES128_GCM" 19 | case SMB2_ENCRYPTION_AES256_CCM: 20 | return "SMB2_ENCRYPTION_AES256_CCM" 21 | case SMB2_ENCRYPTION_AES256_GCM: 22 | return "SMB2_ENCRYPTION_AES256_GCM" 23 | } 24 | return "Unknown" 25 | } 26 | 27 | type EncryptionCapability []byte 28 | 29 | func (e EncryptionCapability) CipherCount() uint16 { 30 | return uint16(le.Uint16(e[0:2])) 31 | } 32 | 33 | func (e EncryptionCapability) SetCipherCount(c uint16) { 34 | le.PutUint16(e[0:2], c) 35 | } 36 | 37 | func (e EncryptionCapability) Ciphers() []Cipher { 38 | var res []Cipher 39 | for i := 0; i < int(e.CipherCount()); i++ { 40 | res = append(res, Cipher(le.Uint16(e[4+i*2:6+i*2]))) 41 | } 42 | return res 43 | } 44 | -------------------------------------------------------------------------------- /erref.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | const ( 4 | STATUS_MORE_PROCESSING_REQUIRED uint32 = 0xC0000016 5 | STATUS_LOGON_FAILURE uint32 = 0xC000006D 6 | ) 7 | -------------------------------------------------------------------------------- /example/serverlet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/PichuChen/simba" 7 | ) 8 | 9 | func main() { 10 | fmt.Printf("Hello, world.") 11 | 12 | // Listen 445 Port 13 | 14 | s := &simba.Server{} 15 | s.ListenAndServe("0.0.0.0:1445") 16 | 17 | } 18 | -------------------------------------------------------------------------------- /flags.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type FLAGS uint32 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 33/481 7 | SMB2_FLAGS_SERVER_TO_REDIR FLAGS = 0x00000001 8 | SMB2_FLAGS_ASYNC_COMMAND FLAGS = 0x00000002 9 | SMB2_FLAGS_RELATED_OPERATIONS FLAGS = 0x00000004 10 | SMB2_FLAGS_SIGNED FLAGS = 0x00000008 11 | SMB2_FLAGS_PRIORITY_MASK FLAGS = 0x00000070 12 | SMB2_FLAGS_DFS_OPERATIONS FLAGS = 0x10000000 13 | SMB2_FLAGS_REPLAY_OPERATION FLAGS = 0x20000000 14 | ) 15 | 16 | type SessionFlags uint8 17 | 18 | const ( 19 | // MS-SMB2 - v20211006 page 56/481 20 | SMB2_SESSION_FLAG_BINDING SessionFlags = 0x01 21 | ) 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/PichuChen/simba 2 | 3 | go 1.18 4 | 5 | require ( 6 | golang.org/x/crypto v0.4.0 7 | golang.org/x/sys v0.3.0 8 | ) 9 | 10 | require github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= 2 | github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= 3 | golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= 4 | golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= 5 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 6 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 7 | -------------------------------------------------------------------------------- /kdf.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha256" 6 | ) 7 | 8 | // r = 32, L = 128 if CipherID is 128CCM or 12GCM, L = 256 if CipherID is 256CCM or 256GCM 9 | // MS-SMB2 3.1.4.2, PRF is HMAC-SHA256, KDF algorithm is Counter Mode 10 | func smb3kdf(sessionKey []byte, label, context string) []byte { 11 | h := hmac.New(sha256.New, sessionKey) 12 | 13 | h.Write([]byte{0x00, 0x00, 0x00, 0x01}) // i = 1, r = 32 14 | h.Write([]byte(label)) 15 | h.Write([]byte{0x00}) 16 | h.Write([]byte(context)) 17 | h.Write([]byte{0x00, 0x00, 0x00, 0x80}) // L = 128, r = 32 18 | 19 | return h.Sum(nil)[:16] 20 | 21 | } 22 | -------------------------------------------------------------------------------- /kdf_test.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestKDF(t *testing.T) { 10 | cases := []struct { 11 | name string 12 | input []byte 13 | labal string 14 | context string 15 | expected []byte 16 | }{ 17 | { 18 | name: "case 1", 19 | input: func() []byte { 20 | r, _ := hex.DecodeString("7CD451825D0450D235424E44BA6E78CC") 21 | return r 22 | }(), 23 | labal: "SMB2AESCMAC\x00", 24 | context: "SmbSign\x00", 25 | expected: func() []byte { 26 | r, _ := hex.DecodeString("0B7E9C5CAC36C0F6EA9AB275298CEDCE") 27 | return r 28 | }(), 29 | }, 30 | { 31 | name: "case 2", 32 | input: func() []byte { 33 | r, _ := hex.DecodeString("7CD451825D0450D235424E44BA6E78CC") 34 | return r 35 | }(), 36 | labal: "SMB2APP\x00", 37 | context: "SmbRpc\x00", 38 | expected: func() []byte { 39 | r, _ := hex.DecodeString("BB23A4575AA26C721AF525AF15A87B4F") 40 | return r 41 | }(), 42 | }, 43 | { 44 | name: "case 3", 45 | input: func() []byte { 46 | r, _ := hex.DecodeString("7CD451825D0450D235424E44BA6E78CC") 47 | return r 48 | }(), 49 | labal: "SMB2AESCCM\x00", 50 | context: "ServerIn \x00", 51 | expected: func() []byte { 52 | r, _ := hex.DecodeString("FAD27796665B313EBB578F388632B4F7") 53 | return r 54 | }(), 55 | }, 56 | { 57 | name: "case 4", 58 | input: func() []byte { 59 | r, _ := hex.DecodeString("7CD451825D0450D235424E44BA6E78CC") 60 | return r 61 | }(), 62 | labal: "SMB2AESCCM\x00", 63 | context: "ServerOut\x00", 64 | expected: func() []byte { 65 | r, _ := hex.DecodeString("B0F0427F7CEB416D1D9DCC0CD4F99447") 66 | return r 67 | }(), 68 | }, 69 | } 70 | 71 | for _, c := range cases { 72 | t.Run(c.name, func(t *testing.T) { 73 | actual := smb3kdf(c.input, c.labal, c.context) 74 | if !bytes.Equal(actual, c.expected) { 75 | t.Errorf("expected %0x, actual %0x", c.expected, actual) 76 | } 77 | }) 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/binary" 5 | "net" 6 | ) 7 | 8 | type Server struct { 9 | Addr string 10 | } 11 | 12 | var ( 13 | le = binary.LittleEndian 14 | ) 15 | 16 | func (srv *Server) ListenAndServe(port string) error { 17 | // addr := srv.Addr 18 | ln, err := net.Listen("tcp", port) 19 | if err != nil { 20 | return err 21 | } 22 | return srv.Serve(ln) 23 | } 24 | -------------------------------------------------------------------------------- /negotiate_context.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type ContextType uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 47/481 7 | SMB2_PREAUTH_INTEGRITY_CAPABILITIES ContextType = 0x0001 8 | SMB2_ENCRYPTION_CAPABILITIES ContextType = 0x0002 9 | SMB2_COMPRESSION_CAPABILITIES ContextType = 0x0003 10 | SMB2_NETNAME_NEGOTIATE_CONTEXT_ID ContextType = 0x0005 11 | SMB2_TRANSPORT_CAPABILITIES ContextType = 0x0006 12 | SMB2_RDMA_TRANSFORM_CAPABILITIES ContextType = 0x0007 13 | SMB2_SIGNING_CAPABILITIES ContextType = 0x0008 14 | ) 15 | 16 | func (c ContextType) String() string { 17 | switch c { 18 | case SMB2_PREAUTH_INTEGRITY_CAPABILITIES: 19 | return "SMB2_PREAUTH_INTEGRITY_CAPABILITIES" 20 | case SMB2_ENCRYPTION_CAPABILITIES: 21 | return "SMB2_ENCRYPTION_CAPABILITIES" 22 | case SMB2_COMPRESSION_CAPABILITIES: 23 | return "SMB2_COMPRESSION_CAPABILITIES" 24 | case SMB2_NETNAME_NEGOTIATE_CONTEXT_ID: 25 | return "SMB2_NETNAME_NEGOTIATE_CONTEXT_ID" 26 | case SMB2_TRANSPORT_CAPABILITIES: 27 | return "SMB2_TRANSPORT_CAPABILITIES" 28 | case SMB2_RDMA_TRANSFORM_CAPABILITIES: 29 | return "SMB2_RDMA_TRANSFORM_CAPABILITIES" 30 | case SMB2_SIGNING_CAPABILITIES: 31 | return "SMB2_SIGNING_CAPABILITIES" 32 | } 33 | return "Unknown" 34 | } 35 | 36 | type NegotiateContext []byte 37 | 38 | func (c NegotiateContext) ContextType() ContextType { 39 | return ContextType(le.Uint16(c[0:2])) 40 | } 41 | 42 | func (c NegotiateContext) SetContextType(t ContextType) { 43 | le.PutUint16(c[0:2], uint16(t)) 44 | } 45 | 46 | func (c NegotiateContext) DataLength() uint16 { 47 | return le.Uint16(c[2:4]) 48 | } 49 | 50 | func (c NegotiateContext) SetDataLength(l uint16) { 51 | le.PutUint16(c[2:4], l) 52 | } 53 | 54 | func (c NegotiateContext) Reserved() uint32 { 55 | return le.Uint32(c[4:8]) 56 | } 57 | 58 | func (c NegotiateContext) SetReserved(r uint32) { 59 | le.PutUint32(c[4:8], r) 60 | } 61 | 62 | func (c NegotiateContext) Data() []byte { 63 | return c[8:] 64 | } 65 | 66 | func (c NegotiateContext) SetData(d []byte) { 67 | copy(c[8:], d) 68 | } 69 | -------------------------------------------------------------------------------- /negotiate_request.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/binary" 5 | "log" 6 | ) 7 | 8 | type NegotiateRequest []byte 9 | 10 | func (p NegotiateRequest) IsInvalid() bool { 11 | // MS-SMB2, MUST be set to 36 12 | if len(p) < 36 { 13 | return true 14 | } 15 | 16 | return false 17 | } 18 | 19 | func (p NegotiateRequest) StructureSize() uint16 { 20 | return binary.LittleEndian.Uint16(p[0:2]) 21 | } 22 | 23 | func (p NegotiateRequest) SetStructureSize() { 24 | binary.LittleEndian.PutUint16(p[0:2], 36) 25 | } 26 | 27 | func (p NegotiateRequest) DialectCount() uint16 { 28 | return binary.LittleEndian.Uint16(p[2:4]) 29 | } 30 | 31 | func (p NegotiateRequest) SetDialectCount(v uint16) { 32 | binary.LittleEndian.PutUint16(p[2:4], v) 33 | } 34 | 35 | func (p NegotiateRequest) SecurityMode() NegotiateSigning { 36 | return NegotiateSigning(binary.LittleEndian.Uint16(p[4:6])) 37 | } 38 | 39 | func (p NegotiateRequest) SetSecurityMode(v NegotiateSigning) { 40 | binary.LittleEndian.PutUint16(p[4:6], uint16(v)) 41 | } 42 | 43 | func (p NegotiateRequest) Capabilities() uint32 { 44 | return binary.LittleEndian.Uint32(p[8:12]) 45 | } 46 | 47 | func (p NegotiateRequest) SetCapabilities(v uint32) { 48 | binary.LittleEndian.PutUint32(p[8:12], v) 49 | } 50 | 51 | func (p NegotiateRequest) ClientGuid() []byte { 52 | return p[12:28] 53 | } 54 | 55 | func (p NegotiateRequest) SetClientGuid(v []byte) { 56 | copy(p[12:28], v) 57 | } 58 | 59 | func (p NegotiateRequest) NegotiateContextOffset() uint32 { 60 | return binary.LittleEndian.Uint32(p[28:32]) 61 | 62 | } 63 | 64 | func (p NegotiateRequest) SetNegotiateContextOffset(v uint32) { 65 | binary.LittleEndian.PutUint32(p[28:32], v) 66 | } 67 | 68 | func (p NegotiateRequest) NegotiateContextCount() uint16 { 69 | return binary.LittleEndian.Uint16(p[32:34]) 70 | } 71 | 72 | func (p NegotiateRequest) SetNegotiateContextCount(v uint16) { 73 | binary.LittleEndian.PutUint16(p[32:34], v) 74 | } 75 | 76 | func (p NegotiateRequest) Dialects() []Dialect { 77 | if p.DialectCount() > 5 { 78 | log.Fatal("DialectCount > 5") 79 | return nil 80 | } 81 | dialects := make([]Dialect, p.DialectCount()) 82 | for i := 0; i < int(p.DialectCount()); i++ { 83 | dialects[i] = Dialect(binary.LittleEndian.Uint16(p[36+i*2 : 36+i*2+2])) 84 | } 85 | return dialects 86 | } 87 | 88 | func (p NegotiateRequest) SetDialects(v []Dialect) { 89 | for i := 0; i < len(v); i++ { 90 | binary.LittleEndian.PutUint16(p[36+i*2:36+i*2+2], uint16(v[i])) 91 | } 92 | } 93 | 94 | func (p NegotiateRequest) NegotiateContextList() []NegotiateContext { 95 | offset := p.NegotiateContextOffset() - 64 96 | if offset > uint32(len(p)) { 97 | log.Fatalf("NegotiateContextOffset > len(p) %d > %d", offset, len(p)) 98 | return nil 99 | } 100 | count := p.NegotiateContextCount() 101 | if count == 0 { 102 | return nil 103 | } 104 | contexts := make([]NegotiateContext, count) 105 | for i := 0; i < int(count); i++ { 106 | if offset > uint32(len(p)) { 107 | log.Fatalf("num:%d, NegotiateContextOffset > len(p) %d > %d", i, offset, len(p)) 108 | return nil 109 | } 110 | contexts[i] = NegotiateContext(p[offset:]) 111 | offset += uint32(contexts[i].DataLength()) + 8 112 | if offset%8 != 0 { 113 | offset += 8 - offset%8 114 | } 115 | } 116 | return contexts 117 | } 118 | -------------------------------------------------------------------------------- /negotiate_request_test.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestNegotiateRequest(t *testing.T) { 10 | 11 | cases := []struct { 12 | name string 13 | input []byte 14 | expected map[string]interface{} 15 | }{ 16 | { 17 | name: "test", 18 | input: func() []byte { 19 | r, _ := hex.DecodeString("2400050001000000440000002c51da83fcb210b928e7cfd82ab2e9a870000000020000001103020300031002020200000100260000000000010020000100a7c3f2609f1852aa4b6ec3f093ff21ede8587383e88e5c633848e007066ff41e00000200060000000000020002000100") 20 | return r 21 | }(), 22 | expected: map[string]interface{}{ 23 | "StructureSize": 0x24, 24 | "DialectCount": 5, 25 | "SecurityMode": SMB2_NEGOTIATE_SIGNING_ENABLED, 26 | "Capabilities": SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_ENCRYPTION, 27 | "ClientGuid": "2c51da83fcb210b928e7cfd82ab2e9a8", 28 | "NegotiateContextOffset": 0x70, 29 | "NegotiateContextCount": 2, 30 | "Dialects": []Dialect{ 31 | SMB2_DIALECT_311, 32 | SMB2_DIALECT_302, 33 | SMB2_DIALECT_30, 34 | SMB2_DIALECT_21, 35 | SMB2_DIALECT_202, 36 | }, 37 | "NegotiateContextList": []ContextType{SMB2_PREAUTH_INTEGRITY_CAPABILITIES, SMB2_ENCRYPTION_CAPABILITIES}, 38 | }, 39 | }, 40 | } 41 | for _, c := range cases { 42 | t.Run(c.name, func(t *testing.T) { 43 | nr := NegotiateRequest(c.input) 44 | if nr.IsInvalid() { 45 | t.Errorf("NegotiateRequest.IsInvalid() = true, want false") 46 | } 47 | if nr.StructureSize() != uint16(c.expected["StructureSize"].(int)) { 48 | t.Errorf("NegotiateRequest.StructureSize() = %v, want %v", nr.StructureSize(), c.expected["StructureSize"]) 49 | } 50 | if nr.DialectCount() != uint16(c.expected["DialectCount"].(int)) { 51 | t.Errorf("NegotiateRequest.DialectCount() = %v, want %v", nr.DialectCount(), c.expected["DialectCount"]) 52 | } 53 | if nr.SecurityMode() != c.expected["SecurityMode"].(NegotiateSigning) { 54 | t.Errorf("NegotiateRequest.SecurityMode() = %v, want %v", nr.SecurityMode(), c.expected["SecurityMode"]) 55 | } 56 | if nr.Capabilities() != uint32(c.expected["Capabilities"].(Capabilities)) { 57 | t.Errorf("NegotiateRequest.Capabilities() = %v, want %v", nr.Capabilities(), c.expected["Capabilities"]) 58 | } 59 | if fmt.Sprintf("%x", nr.ClientGuid()) != c.expected["ClientGuid"].(string) { 60 | t.Errorf("NegotiateRequest.ClientGuid() = %x, want %v", nr.ClientGuid(), c.expected["ClientGuid"]) 61 | } 62 | if nr.NegotiateContextOffset() != uint32(c.expected["NegotiateContextOffset"].(int)) { 63 | t.Errorf("NegotiateRequest.NegotiateContextOffset() = %v, want %v", nr.NegotiateContextOffset(), c.expected["NegotiateContextOffset"]) 64 | } 65 | if nr.NegotiateContextCount() != uint16(c.expected["NegotiateContextCount"].(int)) { 66 | t.Errorf("NegotiateRequest.NegotiateContextCount() = %v, want %v", nr.NegotiateContextCount(), c.expected["NegotiateContextCount"]) 67 | } 68 | if len(nr.Dialects()) != len(c.expected["Dialects"].([]Dialect)) { 69 | t.Errorf("NegotiateRequest.Dialects() = %v, want %v", nr.Dialects(), c.expected["Dialects"]) 70 | } 71 | for i, d := range nr.Dialects() { 72 | if d != c.expected["Dialects"].([]Dialect)[i] { 73 | t.Errorf("NegotiateRequest.Dialects() = %v, want %v", nr.Dialects(), c.expected["Dialects"]) 74 | } 75 | } 76 | if len(nr.NegotiateContextList()) != len(c.expected["NegotiateContextList"].([]ContextType)) { 77 | t.Errorf("NegotiateRequest.NegotiateContextList() = %v, want %v", nr.NegotiateContextList(), c.expected["NegotiateContextList"]) 78 | } 79 | for i, d := range nr.NegotiateContextList() { 80 | if d.ContextType() != c.expected["NegotiateContextList"].([]ContextType)[i] { 81 | t.Errorf("i: %d, d.ContextType() = %v d: %v, want %v", i, d.ContextType(), d, c.expected["NegotiateContextList"].([]ContextType)[i]) 82 | } 83 | } 84 | 85 | // TODO 86 | }) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /negotiate_response.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | type NegotiateSigning uint16 9 | 10 | const ( 11 | // MS-SMB2 - v20211006 page 53/481 12 | SMB2_NEGOTIATE_SIGNING_ENABLED NegotiateSigning = 0x0001 13 | SMB2_NEGOTIATE_SIGNING_REQUIRED NegotiateSigning = 0x0002 14 | ) 15 | 16 | type Capabilities uint32 17 | 18 | const ( 19 | // MS-SMB2 - v20211006 page 53/481 20 | SMB2_GLOBAL_CAP_DFS Capabilities = 0x00000001 21 | SMB2_GLOBAL_CAP_LEASING Capabilities = 0x00000002 22 | SMB2_GLOBAL_CAP_LARGE_MTU Capabilities = 0x00000004 23 | SMB2_GLOBAL_CAP_MULTI_CHANNEL Capabilities = 0x00000008 24 | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES Capabilities = 0x00000010 25 | SMB2_GLOBAL_CAP_DIRECTORY_LEASING Capabilities = 0x00000020 26 | SMB2_GLOBAL_CAP_ENCRYPTION Capabilities = 0x00000040 27 | ) 28 | 29 | type NegotiateResponse []byte 30 | 31 | func (p NegotiateResponse) IsInvalid() bool { 32 | // MS-SMB2, MUST be set to 36 33 | if len(p) < 36 { 34 | return true 35 | } 36 | 37 | return false 38 | } 39 | 40 | func (r NegotiateResponse) StructureSize() uint16 { 41 | return le.Uint16(r[0:2]) 42 | } 43 | 44 | func (r NegotiateResponse) SetStructureSize(v uint16) { 45 | le.PutUint16(r[0:2], v) 46 | } 47 | 48 | func (r NegotiateResponse) SecurityMode() NegotiateSigning { 49 | return NegotiateSigning(le.Uint16(r[2:4])) 50 | } 51 | 52 | func (r NegotiateResponse) SetSecurityMode(v NegotiateSigning) { 53 | le.PutUint16(r[2:4], uint16(v)) 54 | } 55 | 56 | func (r NegotiateResponse) DialectRevision() Dialect { 57 | return Dialect(le.Uint16(r[4:6])) 58 | } 59 | 60 | func (r NegotiateResponse) SetDialectRevision(v Dialect) { 61 | le.PutUint16(r[4:6], uint16(v)) 62 | } 63 | 64 | func (r NegotiateResponse) NegotiateContextCount() uint16 { 65 | return le.Uint16(r[6:8]) 66 | } 67 | 68 | func (r NegotiateResponse) SetNegotiateContextCount(v uint16) { 69 | le.PutUint16(r[6:8], v) 70 | } 71 | 72 | func (r NegotiateResponse) ServerGuid() []byte { 73 | return r[8:24] 74 | } 75 | 76 | func (r NegotiateResponse) SetServerGuid(v []byte) { 77 | copy(r[8:24], v) 78 | } 79 | 80 | func (r NegotiateResponse) Capabilities() Capabilities { 81 | return Capabilities(le.Uint32(r[24:28])) 82 | } 83 | 84 | func (r NegotiateResponse) SetCapabilities(v Capabilities) { 85 | le.PutUint32(r[24:28], uint32(v)) 86 | } 87 | 88 | func (r NegotiateResponse) MaxTransactSize() uint32 { 89 | return le.Uint32(r[28:32]) 90 | } 91 | 92 | func (r NegotiateResponse) SetMaxTransactSize(v uint32) { 93 | le.PutUint32(r[28:32], v) 94 | } 95 | 96 | func (r NegotiateResponse) MaxReadSize() uint32 { 97 | return le.Uint32(r[32:36]) 98 | } 99 | 100 | func (r NegotiateResponse) SetMaxReadSize(v uint32) { 101 | le.PutUint32(r[32:36], v) 102 | } 103 | 104 | func (r NegotiateResponse) MaxWriteSize() uint32 { 105 | return le.Uint32(r[36:40]) 106 | } 107 | 108 | func (r NegotiateResponse) SetMaxWriteSize(v uint32) { 109 | le.PutUint32(r[36:40], v) 110 | } 111 | 112 | func (r NegotiateResponse) SystemTime() time.Time { 113 | dwLowDateTime := le.Uint32(r[40:44]) 114 | dwHighDateTime := le.Uint32(r[44:48]) 115 | dateTime := uint64(dwHighDateTime)<<32 | uint64(dwLowDateTime) 116 | return time.Unix(0, int64(dateTime-116444736000000000)*100) 117 | } 118 | 119 | func (r NegotiateResponse) SetSystemTime(v time.Time) { 120 | dateTime := v.UnixNano()/100 + 116444736000000000 121 | dwLowDateTime := uint32(dateTime) 122 | dwHighDateTime := uint32(dateTime >> 32) 123 | le.PutUint32(r[40:44], dwLowDateTime) 124 | le.PutUint32(r[44:48], dwHighDateTime) 125 | } 126 | 127 | func (r NegotiateResponse) ServerStartTime() time.Time { 128 | dwLowDateTime := le.Uint32(r[48:52]) 129 | dwHighDateTime := le.Uint32(r[52:56]) 130 | dateTime := uint64(dwHighDateTime)<<32 | uint64(dwLowDateTime) 131 | if dateTime == 0 { 132 | return time.Time{} 133 | } 134 | return time.Unix(0, int64(dateTime-116444736000000000)*100) 135 | } 136 | 137 | func (r NegotiateResponse) SetServerStartTime(v time.Time) { 138 | log.Println("SMB2: SetServerStartTime", v) 139 | if v.IsZero() { 140 | log.Println("SMB2: ServerStartTime is zero") 141 | le.PutUint32(r[48:52], 0) 142 | le.PutUint32(r[52:56], 0) 143 | return 144 | } 145 | dateTime := v.UnixNano()/100 + 116444736000000000 146 | dwLowDateTime := uint32(dateTime) 147 | dwHighDateTime := uint32(dateTime >> 32) 148 | le.PutUint32(r[48:52], dwLowDateTime) 149 | le.PutUint32(r[52:56], dwHighDateTime) 150 | } 151 | 152 | func (r NegotiateResponse) SecurityBufferOffset() uint16 { 153 | return le.Uint16(r[56:58]) 154 | } 155 | 156 | func (r NegotiateResponse) SetSecurityBufferOffset(v uint16) { 157 | le.PutUint16(r[56:58], v) 158 | } 159 | 160 | func (r NegotiateResponse) SecurityBufferLength() uint16 { 161 | return le.Uint16(r[58:60]) 162 | } 163 | 164 | func (r NegotiateResponse) SetSecurityBufferLength(v uint16) { 165 | le.PutUint16(r[58:60], v) 166 | } 167 | 168 | // 3.1.1 only 169 | func (r NegotiateResponse) NegotiateContextOffset() uint32 { 170 | return le.Uint32(r[60:64]) 171 | } 172 | 173 | // 3.1.1 only 174 | func (r NegotiateResponse) SetNegotiateContextOffset(v uint32) { 175 | le.PutUint32(r[60:64], v) 176 | } 177 | 178 | func (r NegotiateResponse) Buffer() []byte { 179 | offset := r.SecurityBufferOffset() - 64 180 | length := r.SecurityBufferLength() 181 | if offset+length > uint16(len(r)) { 182 | log.Printf("warning: negotiate response buffer is out of bounds (offset=%d, length=%d, len=%d)", offset, length, len(r)) 183 | return nil 184 | } 185 | return r[offset : offset+length] 186 | } 187 | 188 | func (r NegotiateResponse) SetBuffer(v []byte) { 189 | offset := r.SecurityBufferOffset() - 64 190 | length := r.SecurityBufferLength() 191 | copy(r[offset:offset+length], v) 192 | } 193 | 194 | func (r NegotiateResponse) NegotiateContexts() []NegotiateContext { 195 | offset := r.NegotiateContextOffset() - 64 196 | length := r.NegotiateContextCount() 197 | if offset > uint32(len(r)) { 198 | log.Printf("warning: negotiate response negotiate contexts are out of bounds") 199 | return nil 200 | } 201 | ret := make([]NegotiateContext, length) 202 | for i := uint16(0); i < length; i++ { 203 | if offset > uint32(len(r)) { 204 | log.Printf("warning: negotiate response negotiate context is out of bounds") 205 | return nil 206 | } 207 | ret[i] = NegotiateContext(r[offset:]) 208 | length := uint32(ret[i].DataLength()) 209 | ret[i] = NegotiateContext(r[offset : offset+length+8]) 210 | offset += 8 + uint32(length) 211 | if offset%8 != 0 { 212 | offset += 8 - offset%8 213 | } 214 | } 215 | return ret 216 | } 217 | 218 | func (r NegotiateResponse) SetNegotiateContexts(v []NegotiateContext) { 219 | offset := r.NegotiateContextOffset() - 64 220 | for _, c := range v { 221 | length := uint32(c.DataLength()) 222 | copy(r[offset:offset+length+8], c) 223 | offset += 8 + uint32(length) 224 | if offset%8 != 0 { 225 | offset += 8 - offset%8 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /negotiate_response_test.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestNegotiateResponse(t *testing.T) { 11 | 12 | cases := []struct { 13 | name string 14 | input []byte 15 | expected map[string]interface{} 16 | }{ 17 | { 18 | name: "test", 19 | input: func() []byte { 20 | r, _ := hex.DecodeString("41000100110302003139322e3136382e352e3133350000000700000000008000000080000000800048f69555fcf4d801000000000000000080006000e0000000605e06062b0601050502a0543052a024302206092a864882f71201020206092a864886f712010202060a2b06010401823702020aa32a3028a0261b246e6f745f646566696e65645f696e5f5246433431373840706c656173655f69676e6f726501002600000000000100200001007ef77bcacbac9320a00216936111a47899589c6dd49eb0c190b306a6cc84439d0000020004000000000001000100") 21 | return r 22 | }(), 23 | expected: map[string]interface{}{ 24 | "StructureSize": 65, 25 | "SecurityMode": 1, 26 | "DialectRevision": SMB2_DIALECT_311, 27 | "ServerGuid": "3139322e3136382e352e313335000000", 28 | "Capabilities": 7, 29 | "MaxTransactSize": 8388608, 30 | "MaxReadSize": 8388608, 31 | "MaxWriteSize": 8388608, 32 | "SystemTime": func() time.Time { 33 | r, _ := time.Parse("2006-01-02 15:04:05.999999999Z07:00", "2022-11-10 12:02:41.225684000+00:00") 34 | return r 35 | }(), 36 | "ServerStartTime": time.Unix(0, 0), 37 | "SecurityBufferOffset": 0x80, 38 | "SecurityBufferLength": 96, 39 | "Buffer": "605e06062b0601050502a0543052a024302206092a864882f71201020206092a864886f712010202060a2b06010401823702020aa32a3028a0261b246e6f745f646566696e65645f696e5f5246433431373840706c656173655f69676e6f7265", 40 | "NegotiateContextList": []ContextType{ 41 | SMB2_PREAUTH_INTEGRITY_CAPABILITIES, 42 | SMB2_ENCRYPTION_CAPABILITIES, 43 | }, 44 | }, 45 | }, 46 | } 47 | 48 | for _, c := range cases { 49 | t.Run(c.name, func(t *testing.T) { 50 | nr := NegotiateResponse(c.input) 51 | if nr.IsInvalid() { 52 | t.Errorf("NegotiateResponse.IsInvalid() = true, want false") 53 | } 54 | if nr.StructureSize() != uint16(c.expected["StructureSize"].(int)) { 55 | t.Errorf("NegotiateResponse.StructureSize() = %v, want %v", nr.StructureSize(), c.expected["StructureSize"]) 56 | } 57 | if nr.SecurityMode() != NegotiateSigning(c.expected["SecurityMode"].(int)) { 58 | t.Errorf("NegotiateResponse.SecurityMode() = %v, want %v", nr.SecurityMode(), c.expected["SecurityMode"]) 59 | } 60 | if nr.DialectRevision() != Dialect(c.expected["DialectRevision"].(Dialect)) { 61 | t.Errorf("NegotiateResponse.DialectRevision() = %v, want %v", nr.DialectRevision(), c.expected["DialectRevision"]) 62 | } 63 | if fmt.Sprintf("%x", nr.ServerGuid()) != c.expected["ServerGuid"].(string) { 64 | t.Errorf("NegotiateResponse.ServerGuid() = %x, want %v", nr.ServerGuid(), c.expected["ServerGuid"]) 65 | } 66 | if nr.Capabilities() != Capabilities(c.expected["Capabilities"].(int)) { 67 | t.Errorf("NegotiateResponse.Capabilities() = %v, want %v", nr.Capabilities(), c.expected["Capabilities"]) 68 | } 69 | if nr.MaxTransactSize() != uint32(c.expected["MaxTransactSize"].(int)) { 70 | t.Errorf("NegotiateResponse.MaxTransactSize() = %v, want %v", nr.MaxTransactSize(), c.expected["MaxTransactSize"]) 71 | } 72 | if nr.MaxReadSize() != uint32(c.expected["MaxReadSize"].(int)) { 73 | t.Errorf("NegotiateResponse.MaxReadSize() = %v, want %v", nr.MaxReadSize(), c.expected["MaxReadSize"]) 74 | } 75 | if nr.MaxWriteSize() != uint32(c.expected["MaxWriteSize"].(int)) { 76 | t.Errorf("NegotiateResponse.MaxWriteSize() = %v, want %v", nr.MaxWriteSize(), c.expected["MaxWriteSize"]) 77 | } 78 | if nr.SystemTime().Unix() != c.expected["SystemTime"].(time.Time).Unix() { 79 | t.Errorf("NegotiateResponse.SystemTime() = %v, want %v", nr.SystemTime(), c.expected["SystemTime"]) 80 | } 81 | // if nr.ServerStartTime().Unix() != c.expected["ServerStartTime"].(time.Time).Unix() { 82 | // t.Errorf("NegotiateResponse.ServerStartTime() = %v, want %v", nr.ServerStartTime(), c.expected["ServerStartTime"]) 83 | // } 84 | if nr.SecurityBufferOffset() != uint16(c.expected["SecurityBufferOffset"].(int)) { 85 | t.Errorf("NegotiateResponse.SecurityBufferOffset() = %v, want %v", nr.SecurityBufferOffset(), c.expected["SecurityBufferOffset"]) 86 | } 87 | if nr.SecurityBufferLength() != uint16(c.expected["SecurityBufferLength"].(int)) { 88 | t.Errorf("NegotiateResponse.SecurityBufferLength() = %v, want %v", nr.SecurityBufferLength(), c.expected["SecurityBufferLength"]) 89 | } 90 | if fmt.Sprintf("%x", nr.Buffer()) != c.expected["Buffer"].(string) { 91 | t.Errorf("NegotiateResponse.Buffer() = %x, want %v", nr.Buffer(), c.expected["Buffer"]) 92 | } 93 | 94 | if len(nr.NegotiateContexts()) != len(c.expected["NegotiateContextList"].([]ContextType)) { 95 | t.Errorf("NegotiateResponse.NegotiateContexts() = %v, want %v", nr.NegotiateContexts(), c.expected["NegotiateContextList"]) 96 | } 97 | for i, v := range nr.NegotiateContexts() { 98 | if v.ContextType() != c.expected["NegotiateContextList"].([]ContextType)[i] { 99 | t.Errorf("NegotiateResponse.NegotiateContexts() = %v, want %v", nr.NegotiateContexts(), c.expected["NegotiateContextList"]) 100 | } 101 | } 102 | 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /netname.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type NetnameNegotitateContentID string 4 | -------------------------------------------------------------------------------- /ntlmssp/challenge_message.go: -------------------------------------------------------------------------------- 1 | package ntlmssp 2 | 3 | import "encoding/binary" 4 | 5 | type ChallengeMessage []byte 6 | 7 | func (p ChallengeMessage) IsInvalid() bool { 8 | // MS-NLMP, Signature, MUST be set to NTLMSSP 9 | if len(p) < 40 { 10 | return true 11 | } 12 | 13 | if string(p[0:8]) != "NTLMSSP\x00" { 14 | return true 15 | } 16 | // Check MessageType == 2 17 | if p[8] != 2 { 18 | return true 19 | } 20 | 21 | return false 22 | } 23 | 24 | func (p ChallengeMessage) TargetName() string { 25 | // Check TargetNameLen 26 | if len(p) < 20 { 27 | return "" 28 | } 29 | 30 | targetNameLen := binary.LittleEndian.Uint16(p[12:14]) 31 | targetNameMaxLen := binary.LittleEndian.Uint16(p[14:16]) 32 | targetNameBufferOffset := binary.LittleEndian.Uint32(p[16:20]) 33 | 34 | if targetNameLen == 0 || targetNameMaxLen == 0 || targetNameBufferOffset == 0 { 35 | return "" 36 | } 37 | 38 | buf := p[targetNameBufferOffset : targetNameBufferOffset+uint32(targetNameLen)] 39 | return string(buf) // TODO: unicode 40 | 41 | } 42 | 43 | func (p ChallengeMessage) SetTargetName(input string) { 44 | // Check TargetNameLen 45 | if len(p) < 20 { 46 | return 47 | } 48 | 49 | inputLen := len(input) 50 | 51 | targetNameLen := binary.LittleEndian.Uint16(p[12:14]) 52 | targetNameMaxLen := binary.LittleEndian.Uint16(p[14:16]) 53 | targetNameBufferOffset := binary.LittleEndian.Uint32(p[16:20]) 54 | 55 | if targetNameLen == 0 || targetNameMaxLen == 0 || targetNameBufferOffset == 0 { 56 | return 57 | } 58 | 59 | if inputLen > int(targetNameMaxLen) { 60 | // it will waste original buffer space and leak original buffer data 61 | targetNameBufferOffset = uint32(len(p)) 62 | p = append(p, make([]byte, inputLen)...) 63 | binary.LittleEndian.PutUint32(p[16:20], targetNameBufferOffset) 64 | } 65 | // allocate new buffer 66 | binary.LittleEndian.PutUint16(p[12:14], uint16(inputLen)) 67 | binary.LittleEndian.PutUint16(p[14:16], uint16(inputLen)) // on the MS-NLMP - v20220429, this field SHOULD be set to the same value as TargetNameLen 68 | buf := p[targetNameBufferOffset : targetNameBufferOffset+uint32(targetNameLen)] 69 | copy(buf, input) // TODO: unicode 70 | } 71 | 72 | func (p ChallengeMessage) NegotiateFlags() NegotiateFlags { 73 | return NegotiateFlags(binary.LittleEndian.Uint32(p[20:24])) 74 | } 75 | 76 | func (p ChallengeMessage) SetNegotiateFlags(v NegotiateFlags) { 77 | binary.LittleEndian.PutUint32(p[20:24], uint32(v)) 78 | } 79 | 80 | func (p ChallengeMessage) ServerChallenge() []byte { 81 | return p[24:32] 82 | } 83 | 84 | func (p ChallengeMessage) SetServerChallenge(v []byte) { 85 | copy(p[24:32], v) 86 | } 87 | 88 | func (p ChallengeMessage) Reserved() []byte { 89 | return p[32:40] 90 | } 91 | 92 | func (p ChallengeMessage) SetReserved(v []byte) { 93 | copy(p[32:40], v) 94 | } 95 | 96 | func (p ChallengeMessage) TargetInfo() []byte { 97 | // Check TargetInfoLen 98 | if len(p) < 48 { 99 | return []byte{} 100 | } 101 | 102 | targetInfoLen := binary.LittleEndian.Uint16(p[40:42]) 103 | targetInfoMaxLen := binary.LittleEndian.Uint16(p[42:44]) 104 | targetInfoBufferOffset := binary.LittleEndian.Uint32(p[44:48]) 105 | 106 | if targetInfoLen == 0 || targetInfoMaxLen == 0 || targetInfoBufferOffset == 0 { 107 | return []byte{} 108 | } 109 | 110 | return p[targetInfoBufferOffset : targetInfoBufferOffset+uint32(targetInfoLen)] 111 | } 112 | 113 | func (p ChallengeMessage) SetTargetInfo(v []byte) { 114 | // Check TargetInfoLen 115 | if len(p) < 48 { 116 | return 117 | } 118 | 119 | inputLen := len(v) 120 | 121 | targetInfoLen := binary.LittleEndian.Uint16(p[40:42]) 122 | targetInfoMaxLen := binary.LittleEndian.Uint16(p[42:44]) 123 | targetInfoBufferOffset := binary.LittleEndian.Uint32(p[44:48]) 124 | 125 | if targetInfoLen == 0 || targetInfoMaxLen == 0 || targetInfoBufferOffset == 0 { 126 | return 127 | } 128 | 129 | if inputLen > int(targetInfoMaxLen) { 130 | // it will waste original buffer space and leak original buffer data 131 | targetInfoBufferOffset = uint32(len(p)) 132 | p = append(p, make([]byte, inputLen)...) 133 | binary.LittleEndian.PutUint32(p[44:48], targetInfoBufferOffset) 134 | } 135 | // allocate new buffer 136 | binary.LittleEndian.PutUint16(p[40:42], uint16(inputLen)) 137 | binary.LittleEndian.PutUint16(p[42:44], uint16(inputLen)) // on the MS-NLMP - v20220429, this field SHOULD be set to the same value as TargetInfoLen 138 | buf := p[targetInfoBufferOffset : targetInfoBufferOffset+uint32(targetInfoLen)] 139 | copy(buf, v) 140 | } 141 | 142 | func (p ChallengeMessage) Version() Version { 143 | return Version(p[48:56]) 144 | } 145 | 146 | func (p ChallengeMessage) SetVersion(v Version) { 147 | binary.LittleEndian.PutUint64(p[48:56], uint64(v)) 148 | } 149 | -------------------------------------------------------------------------------- /ntlmssp/messages.go: -------------------------------------------------------------------------------- 1 | package ntlmssp 2 | 3 | type NegotiateFlags uint32 4 | 5 | const ( 6 | // MS-NLMP v20220429 33/98 7 | NTLMSSP_NEGOTIATE_56 NegotiateFlags = 1 << iota // aka W 8 | NTLMSSP_NEGOTIATE_KEY_EXCH // aka V 9 | NTLMSSP_NEGOTIATE_128 // aka U 10 | NTLMSSP_RESERVED1 // aka r1 11 | NTLMSSP_RESERVED2 // aka r2 12 | NTLMSSP_RESERVED3 // aka r3 13 | NTLMSSP_NEGOTIATE_VERSION // aka T 14 | NTLMSSP_RESERVED4 // aka r4 15 | NTLMSSP_TARGET_INFO // aka S 16 | NTLMSSP_REQUEST_NON_NT_SESSION_KEY // aka R 17 | NTLMSSP_RESERVED5 // aka r5 18 | NTLMSSP_NEGOTIATE_IDENTIFY // aka Q 19 | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY // aka P 20 | NTLMSSP_RESERVED6 // aka r6 21 | NTLMSSP_TARGET_TYPE_SERVER // aka O 22 | NTLMSSP_TARGET_TYPE_DOMAIN // aka N 23 | NTLMSSP_NEGOTIATE_ALWAYS_SIGN // aka M 24 | NTLMSSP_RESERVED7 // aka r7 25 | NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED // aka L 26 | NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED // aka K 27 | NTLMSSP_NEGOTIATE_ANONYMOUS // aka J 28 | NTLMSSP_RESERVED8 // aka r8 29 | NTLMSSP_NEGOTIATE_NTLM // aka H 30 | NTLMSSP_RESERVED9 // aka r9 31 | NTLMSSP_NEGOTIATE_LM_KEY // aka G 32 | NTLMSSP_NEGOTIATE_DATAGRAM // aka F 33 | NTLMSSP_NEGOTIATE_SEAL // aka E 34 | NTLMSSP_NEGOTIATE_SIGN // aka D 35 | NTLMSSP_RESERVED10 // aka r10 36 | NTLMSSP_REQUEST_TARGET // aka C 37 | NTLMSSP_NEGOTIATE_OEM // aka B 38 | NTLMSSP_NEGOTIATE_UNICODE // aka A 39 | ) 40 | 41 | func NewChallengeMessage() { 42 | 43 | } 44 | -------------------------------------------------------------------------------- /ntlmssp/messages_test.go: -------------------------------------------------------------------------------- 1 | package ntlmssp 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/Azure/go-ntlmssp" 9 | ) 10 | 11 | func MustDecodeHex(s string) []byte { 12 | v, err := hex.DecodeString(s) 13 | if err != nil { 14 | panic(err) 15 | } 16 | return v 17 | } 18 | 19 | func compareBytes(a, b []byte) bool { 20 | if len(a) != len(b) { 21 | return false 22 | } 23 | 24 | for k, v := range a { 25 | if v != b[k] { 26 | return false 27 | } 28 | } 29 | 30 | return true 31 | } 32 | 33 | // Print out hex dump of byte slice 34 | // example: 35 | // 000000: 4e 54 4c 4d 53 53 50 00 02 00 00 00 00 00 00 00 NTLMSSP......... 36 | // 000010: 00 00 00 00 00 00 00 00 ........ 37 | func dumpHex(b []byte) string { 38 | lineN := len(b) / 16 39 | out := "" 40 | for i := 0; i < lineN; i++ { 41 | line := b[i*16 : i*16+16] 42 | // print address 43 | out += fmt.Sprintf("%06X:", i*16) 44 | // print hex 45 | for _, v := range line { 46 | out += fmt.Sprintf(" %02x", v) 47 | } 48 | // print ascii 49 | out += fmt.Sprintf(" ") 50 | for _, v := range line { 51 | if v >= 0x20 && v <= 0x7e { 52 | out += fmt.Sprintf("%c", v) 53 | } else { 54 | out += fmt.Sprintf(".") 55 | } 56 | } 57 | out += fmt.Sprintf("\n") 58 | } 59 | 60 | // last line 61 | if len(b)%16 == 0 { 62 | return out 63 | } 64 | line := b[lineN*16:] 65 | // print address 66 | out += fmt.Sprintf("%06X:", lineN*16) 67 | // print hex 68 | for _, v := range line { 69 | out += fmt.Sprintf(" %02x", v) 70 | } 71 | // print padding 72 | for i := 0; i < 16-len(line); i++ { 73 | out += fmt.Sprintf(" ") 74 | } 75 | // print ascii 76 | out += fmt.Sprintf(" ") 77 | for _, v := range line { 78 | if v >= 0x20 && v <= 0x7e { 79 | out += fmt.Sprintf("%c", v) 80 | } else { 81 | out += fmt.Sprintf(".") 82 | } 83 | } 84 | 85 | out += fmt.Sprintf("\n") 86 | return out 87 | } 88 | 89 | func TestNewNegotianteMessage(t *testing.T) { 90 | cases := []struct { 91 | domainName string 92 | workstation string 93 | expected []byte 94 | }{ 95 | { 96 | "Domain", 97 | "WORKGROUP", 98 | MustDecodeHex("" + 99 | "4e544c4d53535000" + // Signature 100 | "01000000013088a0" + // MessageType, NegotiateFlags 101 | "0600060028000000" + // DomainNameFields 102 | "090009002e000000" + // WorkstationFields 103 | "0601b11d0000000f" + // Version 104 | "444f4d41494e" + // DOMAIN 105 | "574f524b47524f5550"), // Workstation 106 | }, 107 | } 108 | 109 | for _, c := range cases { 110 | actual, err := ntlmssp.NewNegotiateMessage(c.domainName, c.workstation) 111 | if err != nil { 112 | t.Errorf("NewNegotiateMessage(%s, %s) == %v, expected %v", c.domainName, c.workstation, err, c.expected) 113 | } 114 | 115 | if !compareBytes(actual, c.expected) { 116 | t.Errorf("NewNegotiateMessage(%s, %s) == actual:\n%v, expected:\n%v", c.domainName, c.workstation, dumpHex(actual), dumpHex(c.expected)) 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /ntlmssp/version.go: -------------------------------------------------------------------------------- 1 | package ntlmssp 2 | 3 | import "encoding/binary" 4 | 5 | type Version []byte 6 | 7 | func (p Version) ProductMajorVersion() uint8 { 8 | return p[0] 9 | } 10 | 11 | func (p Version) SetProductMajorVersion(v uint8) { 12 | p[0] = v 13 | } 14 | 15 | func (p Version) ProductMinorVersion() uint8 { 16 | return p[1] 17 | } 18 | 19 | func (p Version) SetProductMinorVersion(v uint8) { 20 | p[1] = v 21 | } 22 | 23 | func (p Version) ProductBuild() uint16 { 24 | return binary.LittleEndian.Uint16(p[2:4]) 25 | } 26 | 27 | func (p Version) SetProductBuild(v uint16) { 28 | binary.LittleEndian.PutUint16(p[2:4], v) 29 | } 30 | 31 | // Reserved for 3 bytes 32 | 33 | // Should be NTLMSSP_REVISION_W2K3 (0x0F) 34 | func (p Version) NTLMRevisionCurrent() uint8 { 35 | return p[7] 36 | } 37 | 38 | // Should be NTLMSSP_REVISION_W2K3 (0x0F) 39 | func (p Version) SetNTLMRevisionCurrent(v uint8) { 40 | p[7] = v 41 | } 42 | -------------------------------------------------------------------------------- /packet.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/binary" 5 | "log" 6 | ) 7 | 8 | type PacketCodec []byte 9 | 10 | func (p PacketCodec) IsInvalid() bool { 11 | // MS-SMB2, MUST be set to 64 12 | if len(p) < 64 { 13 | log.Println("PacketCodec IsInvalid: len(p) < 64") 14 | return true 15 | } 16 | 17 | // check if the packet is a valid SMB2 packet 18 | // MS-SMB2, MUST be set to (0xFF ~ FC), 'S', 'M', 'B' 19 | // MS-SMB2 v20230920 - page 386 / 488 20 | if p[1] != 0x53 || p[2] != 0x4d || p[3] != 0x42 { 21 | log.Println("PacketCodec IsInvalid: p[1] != 0x53 || p[2] != 0x4d || p[3] != 0x42") 22 | return true 23 | } 24 | return false 25 | } 26 | 27 | func (p PacketCodec) ProtocolId() []byte { 28 | return p[:4] 29 | } 30 | 31 | func (p PacketCodec) SetProtocolId() { 32 | copy(p[:4], []byte{0xfe, 0x53, 0x4d, 0x42}) 33 | } 34 | 35 | func (p PacketCodec) StructureSize() uint16 { 36 | return binary.LittleEndian.Uint16(p[4:6]) 37 | } 38 | 39 | func (p PacketCodec) SetStructureSize() { 40 | binary.LittleEndian.PutUint16(p[4:6], 64) 41 | } 42 | 43 | func (p PacketCodec) CreditCharge() uint16 { 44 | return binary.LittleEndian.Uint16(p[6:8]) 45 | } 46 | 47 | func (p PacketCodec) SetCreditCharge(v uint16) { 48 | binary.LittleEndian.PutUint16(p[6:8], v) 49 | } 50 | 51 | // In SMB 3.x, this field is ChannelSequence field followed by Reserved field 52 | func (p PacketCodec) ChannelSequence() uint16 { 53 | return binary.LittleEndian.Uint16(p[8:10]) 54 | } 55 | 56 | func (p PacketCodec) SetChannelSequence(v uint16) { 57 | binary.LittleEndian.PutUint16(p[8:10], v) 58 | } 59 | 60 | func (p PacketCodec) Reserved() uint16 { 61 | return binary.LittleEndian.Uint16(p[10:12]) 62 | } 63 | 64 | func (p PacketCodec) SetReserved(v uint16) { 65 | binary.LittleEndian.PutUint16(p[10:12], v) 66 | } 67 | 68 | // In SMB 2.0.2 and SMB 2.1 dialects, this field is Status field 69 | func (p PacketCodec) Status() uint32 { 70 | return binary.LittleEndian.Uint32(p[8:12]) 71 | } 72 | 73 | func (p PacketCodec) SetStatus(v uint32) { 74 | binary.LittleEndian.PutUint32(p[8:12], v) 75 | } 76 | 77 | func (p PacketCodec) Command() Command { 78 | return Command(binary.LittleEndian.Uint16(p[12:14])) 79 | } 80 | 81 | func (p PacketCodec) SetCommand(v Command) { 82 | binary.LittleEndian.PutUint16(p[12:14], uint16(v)) 83 | } 84 | 85 | func (p PacketCodec) CreditRequestResponse() uint16 { 86 | return binary.LittleEndian.Uint16(p[14:16]) 87 | } 88 | 89 | func (p PacketCodec) SetCreditRequestResponse(v uint16) { 90 | binary.LittleEndian.PutUint16(p[14:16], v) 91 | } 92 | 93 | func (p PacketCodec) Flags() FLAGS { 94 | return FLAGS(binary.LittleEndian.Uint32(p[16:20])) 95 | } 96 | 97 | func (p PacketCodec) SetFlags(v FLAGS) { 98 | binary.LittleEndian.PutUint32(p[16:20], uint32(v)) 99 | } 100 | 101 | func (p PacketCodec) NextCommand() uint32 { 102 | return binary.LittleEndian.Uint32(p[20:24]) 103 | } 104 | 105 | func (p PacketCodec) SetNextCommand(v uint32) { 106 | binary.LittleEndian.PutUint32(p[20:24], v) 107 | } 108 | 109 | func (p PacketCodec) MessageId() uint64 { 110 | return binary.LittleEndian.Uint64(p[24:32]) 111 | } 112 | 113 | func (p PacketCodec) SetMessageId(v uint64) { 114 | binary.LittleEndian.PutUint64(p[24:32], v) 115 | } 116 | 117 | // If the SMB2_FLAGS_ASYNC_COMMAND flag is set in the Flags field, this field will be AsyncId 118 | func (p PacketCodec) AsyncId() uint64 { 119 | return binary.LittleEndian.Uint64(p[32:40]) 120 | } 121 | 122 | // If SMB2_FLAGS_ASYNC_COMMAND set 123 | func (p PacketCodec) SetAsyncId(v uint64) { 124 | binary.LittleEndian.PutUint64(p[32:40], v) 125 | } 126 | 127 | // If SMB2_FLAGS_ASYNC_COMMAND not set 128 | func (p PacketCodec) TreeId() uint32 { 129 | return binary.LittleEndian.Uint32(p[36:40]) 130 | } 131 | 132 | func (p PacketCodec) SetTreeId(v uint32) { 133 | binary.LittleEndian.PutUint32(p[36:40], v) 134 | } 135 | 136 | func (p PacketCodec) SessionId() uint64 { 137 | return binary.LittleEndian.Uint64(p[40:48]) 138 | } 139 | 140 | func (p PacketCodec) SetSessionId(v uint64) { 141 | binary.LittleEndian.PutUint64(p[40:48], v) 142 | } 143 | 144 | func (p PacketCodec) Signature() []byte { 145 | return p[48:64] 146 | } 147 | 148 | func (p PacketCodec) SetSignature(v []byte) { 149 | copy(p[48:64], v) 150 | } 151 | -------------------------------------------------------------------------------- /packet_test.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | ) 7 | 8 | func TestPacket(t *testing.T) { 9 | cases := []struct { 10 | name string 11 | input []byte 12 | expected map[string]interface{} 13 | }{ 14 | { 15 | name: "test", 16 | input: func() []byte { 17 | r, _ := hex.DecodeString("fe534d424000010000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400050001000000440000002c51da83fcb210b928e7cfd82ab2e9a870000000020000001103020300031002020200000100260000000000010020000100a7c3f2609f1852aa4b6ec3f093ff21ede8587383e88e5c633848e007066ff41e00000200060000000000020002000100") 18 | return r 19 | }(), 20 | expected: map[string]interface{}{ 21 | "ProtocolID": "fe534d42", 22 | "StructureSize": 64, 23 | "CreditCharge": 1, 24 | "Status": 0, 25 | "Command": 0, 26 | "Credits": 1, 27 | "Flags": FLAGS(0), 28 | "NextCommand": 0, 29 | "MessageID": 0, 30 | "Reserved": 0, 31 | "TreeID": 0, 32 | "SessionID": 0, 33 | "Signature": "00000000000000000000000000000000", 34 | }, 35 | }, 36 | { 37 | name: "samba4-response", 38 | input: func() []byte { 39 | r, _ := hex.DecodeString("fe534d4240000100000000000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041000100110302003139322e3136382e352e3133350000000700000000008000000080000000800048f69555fcf4d801000000000000000080006000e0000000605e06062b0601050502a0543052a024302206092a864882f71201020206092a864886f712010202060a2b06010401823702020aa32a3028a0261b246e6f745f646566696e65645f696e5f5246433431373840706c656173655f69676e6f726501002600000000000100200001007ef77bcacbac9320a00216936111a47899589c6dd49eb0c190b306a6cc84439d0000020004000000000001000100") 40 | return r 41 | }(), 42 | expected: map[string]interface{}{ 43 | "ProtocolID": "fe534d42", 44 | "StructureSize": 64, 45 | "CreditCharge": 1, 46 | "Status": 0, 47 | "Command": 0, 48 | "Credits": 1, 49 | "Flags": SMB2_FLAGS_SERVER_TO_REDIR, 50 | "NextCommand": 0, 51 | "MessageID": 0, 52 | "Reserved": 0, 53 | "TreeID": 0, 54 | "SessionID": 0, 55 | "Signature": "00000000000000000000000000000000", 56 | }, 57 | }, 58 | } 59 | for _, c := range cases { 60 | t.Run(c.name, func(t *testing.T) { 61 | nr := PacketCodec(c.input) 62 | if nr.IsInvalid() { 63 | t.Errorf("NegotiateRequest.IsInvalid() = true, want false") 64 | } 65 | if got := hex.EncodeToString(nr.ProtocolId()); got != c.expected["ProtocolID"] { 66 | t.Errorf("NegotiateRequest.ProtocolId() = %v, want %v", got, c.expected["ProtocolID"]) 67 | } 68 | if nr.StructureSize() != uint16(c.expected["StructureSize"].(int)) { 69 | t.Errorf("NegotiateRequest.StructureSize() = %d, want %d", nr.StructureSize(), c.expected["StructureSize"]) 70 | } 71 | if nr.CreditCharge() != uint16(c.expected["CreditCharge"].(int)) { 72 | t.Errorf("NegotiateRequest.CreditCharge() = %v, want %v", nr.CreditCharge(), c.expected["CreditCharge"]) 73 | } 74 | if nr.Status() != uint32(c.expected["Status"].(int)) { 75 | t.Errorf("NegotiateRequest.Status() = %v, want %v", nr.Status(), c.expected["Status"]) 76 | } 77 | if nr.Command() != Command(c.expected["Command"].(int)) { 78 | t.Errorf("NegotiateRequest.Command() = %v, want %v", nr.Command(), c.expected["Command"]) 79 | } 80 | if nr.CreditRequestResponse() != uint16(c.expected["Credits"].(int)) { 81 | t.Errorf("NegotiateRequest.Credits() = %v, want %v", nr.CreditRequestResponse(), c.expected["Credits"]) 82 | } 83 | if nr.Flags() != c.expected["Flags"].(FLAGS) { 84 | t.Errorf("NegotiateRequest.Flags() = %v, want %v", nr.Flags(), c.expected["Flags"]) 85 | } 86 | if nr.NextCommand() != uint32(c.expected["NextCommand"].(int)) { 87 | t.Errorf("NegotiateRequest.NextCommand() = %v, want %v", nr.NextCommand(), c.expected["NextCommand"]) 88 | } 89 | if nr.MessageId() != uint64(c.expected["MessageID"].(int)) { 90 | t.Errorf("NegotiateRequest.MessageId() = %v, want %v", nr.MessageId(), c.expected["MessageID"]) 91 | } 92 | 93 | if nr.TreeId() != uint32(c.expected["TreeID"].(int)) { 94 | t.Errorf("NegotiateRequest.TreeId() = %v, want %v", nr.TreeId(), c.expected["TreeID"]) 95 | } 96 | if nr.SessionId() != uint64(c.expected["SessionID"].(int)) { 97 | t.Errorf("NegotiateRequest.SessionId() = %v, want %v", nr.SessionId(), c.expected["SessionID"]) 98 | } 99 | if got := hex.EncodeToString(nr.Signature()); got != c.expected["Signature"] { 100 | t.Errorf("NegotiateRequest.Signature() = %v, want %v", got, c.expected["Signature"]) 101 | } 102 | 103 | // TODO 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /preauth_integrity.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type HashAlgorithm uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 48/481 7 | SMB2_PREAUTH_INTEGRITY_SHA512 HashAlgorithm = 0x0001 8 | ) 9 | 10 | type PreauthIntegrityCapability []byte 11 | 12 | func (p PreauthIntegrityCapability) HashAlgorithmCount() uint16 { 13 | return le.Uint16(p[0:2]) 14 | } 15 | 16 | func (p PreauthIntegrityCapability) SetHashAlgorithmCount(c uint16) { 17 | le.PutUint16(p[0:2], c) 18 | } 19 | 20 | func (p PreauthIntegrityCapability) SaltLength() uint16 { 21 | return le.Uint16(p[2:4]) 22 | } 23 | 24 | func (p PreauthIntegrityCapability) SetSaltLength(l uint16) { 25 | le.PutUint16(p[2:4], l) 26 | } 27 | 28 | func (p PreauthIntegrityCapability) HashAlgorithms() []HashAlgorithm { 29 | var res []HashAlgorithm 30 | for i := 0; i < int(p.HashAlgorithmCount()); i++ { 31 | res = append(res, HashAlgorithm(le.Uint16(p[4+i*2:6+i*2]))) 32 | } 33 | return res 34 | } 35 | 36 | func (p PreauthIntegrityCapability) SetHashAlgorithms(a []HashAlgorithm) { 37 | p.SetHashAlgorithmCount(uint16(len(a))) 38 | for i, v := range a { 39 | le.PutUint16(p[4+i*2:6+i*2], uint16(v)) 40 | } 41 | } 42 | 43 | func (p PreauthIntegrityCapability) Salt() []byte { 44 | return p[4+int(p.HashAlgorithmCount())*2:] 45 | } 46 | 47 | func (p PreauthIntegrityCapability) SetSalt(s []byte) { 48 | p.SetSaltLength(uint16(len(s))) 49 | copy(p[4+int(p.HashAlgorithmCount())*2:], s) 50 | } 51 | -------------------------------------------------------------------------------- /rdma_transform.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type RDMATransform uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 50/481 7 | SMB2_RDMA_TRANSFORM_NONE RDMATransform = 0x0000 8 | SMB2_RDMA_TRANSFORM_ENCRYPTION RDMATransform = 0x0001 9 | SMB2_RDMA_TRANSFORM_SIGNING RDMATransform = 0x0002 10 | ) 11 | 12 | type RDMATransformCapability []byte 13 | 14 | func (c RDMATransformCapability) TransformCount() uint16 { 15 | return uint16(le.Uint16(c[0:2])) 16 | } 17 | 18 | func (c RDMATransformCapability) SetTransformCount(v uint16) { 19 | le.PutUint16(c[0:2], v) 20 | } 21 | 22 | func (c RDMATransformCapability) RDMATransforms() []RDMATransform { 23 | var res = make([]RDMATransform, c.TransformCount()) 24 | for i := 0; i < int(c.TransformCount()); i++ { 25 | res[i] = RDMATransform(le.Uint16(c[8+i*2 : 8+i*2])) 26 | } 27 | return res 28 | } 29 | 30 | func (c RDMATransformCapability) SetRDMATransforms(v []RDMATransform) { 31 | c.SetTransformCount(uint16(len(v))) 32 | for i, t := range v { 33 | le.PutUint16(c[8+i*2:8+i*2], uint16(t)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "log" 7 | "net" 8 | "time" 9 | 10 | "github.com/PichuChen/simba/auth" 11 | ) 12 | 13 | var serverGUID = []byte{0x6d, 0x62, 0x76, 0x6d, 0x32, 0x32, 0x31, 0x32, 0x30, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 14 | 15 | // TODO: change to a session manager 16 | var sessionID = uint64(2023) 17 | 18 | type conn struct { 19 | server *Server 20 | 21 | // rwc is the underlying network connection. 22 | rwc net.Conn 23 | 24 | remoteAddr string 25 | } 26 | 27 | type response struct { 28 | conn *conn 29 | } 30 | 31 | func (srv *Server) Serve(l net.Listener) error { 32 | for { 33 | rw, err := l.Accept() 34 | if err != nil { 35 | 36 | return err 37 | } 38 | fmt.Printf("Accept a new connection: %v\n", rw) 39 | c := srv.newConn(rw) 40 | go c.serve() 41 | } 42 | } 43 | 44 | func (srv *Server) newConn(rw net.Conn) *conn { 45 | c := &conn{ 46 | server: srv, 47 | rwc: rw, 48 | } 49 | return c 50 | } 51 | 52 | func (c *conn) serve() { 53 | fmt.Printf("remote addr: %v\n", c.rwc.RemoteAddr()) 54 | c.remoteAddr = c.rwc.RemoteAddr().String() 55 | 56 | defer c.rwc.Close() 57 | 58 | for { 59 | r, err := c.readRequest() 60 | if err != nil { 61 | fmt.Printf("readRequest error: %v\n", err) 62 | return 63 | } 64 | fmt.Printf("readRequest: %v\n", r) 65 | 66 | switch r.Command() { 67 | case SMB2_NEGOTIATE: 68 | fmt.Printf("SMB2_NEGOTIATE\n") 69 | msg := NegotiateRequest(r[64:]) 70 | c.handleNegotiate(r, msg) 71 | case SMB2_SESSION_SETUP: 72 | fmt.Printf("SMB2_SESSION_SETUP\n") 73 | msg := SessionSetupRequest(r[64:]) 74 | c.handleSessionSetup(r, msg) 75 | 76 | default: 77 | fmt.Printf("unknown command: %v (%d)\n", r.Command(), r.Command()) 78 | } 79 | } 80 | 81 | } 82 | 83 | func (c *conn) readRequest() (w PacketCodec, err error) { 84 | var buf [1024]byte 85 | n, err := c.rwc.Read(buf[:]) 86 | if err != nil { 87 | return nil, err 88 | } 89 | 90 | // From NetBIOS 91 | fmt.Printf("readRequest: %v len: %d\n", hex.EncodeToString(buf[:n]), n) 92 | // zero := buf[0] 93 | stringProtocolLength := (uint32(buf[1]) << 16) + (uint32(buf[2]) << 8) + uint32(buf[3]) 94 | // TODO: using loop to read all data 95 | if n < int(stringProtocolLength) { 96 | n2, err := c.rwc.Read(buf[n:]) 97 | if err != nil { 98 | return nil, err 99 | } 100 | n += n2 101 | } 102 | 103 | smb2Message := buf[4 : 4+stringProtocolLength] 104 | 105 | msg := PacketCodec(smb2Message) 106 | fmt.Printf("msg: len: %d data: %+v\n", len(msg), msg) 107 | if msg.IsInvalid() { 108 | fmt.Printf("msg is invalid\n") 109 | return nil, fmt.Errorf("msg is invalid") 110 | } 111 | 112 | fmt.Printf("msg type: %v\n", msg.Command()) 113 | 114 | return msg, nil 115 | 116 | } 117 | 118 | func (c *conn) handleNegotiate(p PacketCodec, msg NegotiateRequest) error { 119 | fmt.Printf("handleNegotiate: %v\n", msg.ClientGuid()) 120 | 121 | securityBufferPayload := auth.DefaultNegoPayload 122 | 123 | negotiateContextPreauth := NegotiateContext(make([]byte, 8+38)) 124 | negotiateContextPreauth.SetContextType(SMB2_PREAUTH_INTEGRITY_CAPABILITIES) 125 | negotiateContextPreauth.SetDataLength(38) 126 | negotiateContextPreauth.SetReserved(0) 127 | negotiateContextPreauth.SetData([]byte{ 128 | 0x01, 0x00, // hash algorithm count 129 | 0x20, 0x00, // salt length 130 | 0x01, 0x00, // hash algorithm: SHA-512 131 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 132 | 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}) 133 | 134 | negotiateContextEncryption := NegotiateContext(make([]byte, 8+4)) 135 | negotiateContextEncryption.SetContextType(SMB2_ENCRYPTION_CAPABILITIES) 136 | negotiateContextEncryption.SetDataLength(4) 137 | negotiateContextEncryption.SetReserved(0) 138 | negotiateContextEncryption.SetData([]byte{0x01, 0x00, 0x02, 0x00}) 139 | 140 | pkt := []byte{} 141 | responseHdr := NegotiateResponse(make([]byte, 65+len(securityBufferPayload)+len(negotiateContextPreauth)+19)) 142 | responseHdr.SetStructureSize(65) 143 | responseHdr.SetSecurityMode(SMB2_NEGOTIATE_SIGNING_ENABLED) 144 | responseHdr.SetDialectRevision(0x311) 145 | responseHdr.SetNegotiateContextCount(2) 146 | responseHdr.SetServerGuid(serverGUID) 147 | responseHdr.SetCapabilities(SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU) 148 | responseHdr.SetMaxTransactSize(8388608) // 8MB 149 | responseHdr.SetMaxReadSize(8388608) 150 | responseHdr.SetMaxWriteSize(8388608) 151 | responseHdr.SetSystemTime(time.Now()) 152 | responseHdr.SetServerStartTime(time.Time{}) 153 | responseHdr.SetSecurityBufferOffset(0x80) 154 | responseHdr.SetSecurityBufferLength(uint16(len(securityBufferPayload))) 155 | responseHdr.SetBuffer(securityBufferPayload) 156 | 157 | responseHdr.SetNegotiateContextOffset(0xD0) 158 | responseHdr.SetNegotiateContexts([]NegotiateContext{negotiateContextPreauth, negotiateContextEncryption}) 159 | 160 | smb2Header := PacketCodec(make([]byte, 64, 64)) 161 | smb2Header.SetProtocolId() 162 | smb2Header.SetStructureSize() 163 | smb2Header.SetCreditCharge(1) 164 | smb2Header.SetCommand(SMB2_NEGOTIATE) 165 | smb2Header.SetStatus(0) 166 | smb2Header.SetCreditRequestResponse(1) 167 | smb2Header.SetFlags(SMB2_FLAGS_SERVER_TO_REDIR) 168 | smb2Header.SetNextCommand(0) 169 | smb2Header.SetMessageId(p.MessageId()) 170 | smb2Header.SetTreeId(0) 171 | smb2Header.SetSessionId(p.SessionId()) 172 | smb2Header.SetSignature([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) 173 | 174 | l := len(smb2Header) + len(responseHdr) 175 | netBIOSHeader := []byte{0x00, 0x00, 0x00, 0x00} 176 | netBIOSHeader[3] = byte(l) 177 | netBIOSHeader[2] = byte(l >> 8) 178 | 179 | // smb2Header := []byte{0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 180 | 181 | pkt = append(pkt, netBIOSHeader...) 182 | pkt = append(pkt, smb2Header...) 183 | pkt = append(pkt, responseHdr...) 184 | // pkt = append(pkt, responseBody...) 185 | 186 | fmt.Printf("handleNegotiate: %v\n", hex.EncodeToString(pkt)) 187 | c.rwc.Write(pkt) 188 | fmt.Printf("send response: %d\n", len(pkt)) 189 | 190 | return nil 191 | 192 | } 193 | 194 | func (c *conn) handleSessionSetup(p PacketCodec, msg SessionSetupRequest) error { 195 | fmt.Printf("handleSessionSetup request: %x\n", msg) 196 | 197 | // get NTLMSSP message 198 | gssBuffer := msg.Buffer() 199 | var mechToken []byte 200 | if gssBuffer[0] == 0x60 { 201 | gssPayload, err := auth.NewInitPayload(gssBuffer) 202 | if err != nil { 203 | log.Printf("handleSessionSetup NewInitPayload: %v", err) 204 | return fmt.Errorf("handleSessionSetup NewInitPayload: %v", err) 205 | } 206 | mechToken = gssPayload.Token.NegTokenInit.MechToken 207 | } else if gssBuffer[0] == 0xa1 { 208 | gssPayload, err := auth.NewTargPayload(gssBuffer) 209 | if err != nil { 210 | log.Printf("handleSessionSetup NewTargPayload: %v", err) 211 | return fmt.Errorf("handleSessionSetup NewTargPayload: %v", err) 212 | } 213 | mechToken = gssPayload.ResponseToken 214 | 215 | } 216 | 217 | // get NTLMSSP message 218 | fmt.Printf("mechToken: %v\n", hex.EncodeToString(mechToken)) 219 | 220 | ntlmsspPayload := auth.NTLMMessage(mechToken) 221 | if ntlmsspPayload.IsInvalid() { 222 | return fmt.Errorf("handleSessionSetup NTLMMessage is invalid") 223 | } 224 | 225 | switch ntlmsspPayload.MessageType() { 226 | case auth.NTLMSSP_NEGOTIATE: 227 | log.Printf("NTLM_NEGOTIATE: %v\n", len(ntlmsspPayload)) 228 | return c.handleSessionSetupNtmlsspNetotiate(p, msg, auth.NTLMNegotiateMessage(mechToken)) 229 | case auth.NTLMSSP_AUTH: 230 | log.Printf("NTLMSSP_AUTH: %v\n", len(ntlmsspPayload)) 231 | return c.handleSessionSetupNtmlsspAuth(p, msg, auth.NTLMNegotiateMessage(mechToken)) 232 | default: 233 | fmt.Printf("NTLMSSP unknown message type: %0x\n", ntlmsspPayload.MessageType()) 234 | // case auth.NTLM_CHALLENGE: 235 | // fmt.Printf("NTLM_CHALLENGE: %v\n", ntlmsspPayload) 236 | // case auth.NTLM_AUTHENTICATE: 237 | // fmt.Printf("NTLM_AUTHENTICATE: %v\n", ntlmsspPayload) 238 | } 239 | return fmt.Errorf("unknown ntlm message type: %0x\n", ntlmsspPayload.MessageType()) 240 | } 241 | func (c *conn) handleSessionSetupNtmlsspNetotiate(p PacketCodec, msg SessionSetupRequest, ntlpPayload auth.NTLMNegotiateMessage) error { 242 | 243 | pkt := []byte{} 244 | securityBuffer, _ := hex.DecodeString("a181c43081c1a0030a0101a10c060a2b06010401823702020aa281ab0481a84e544c4d5353500002000000140014003800000015828ae2ade8f7c5b20b941000000000000000005c005c004c000000060100000000000f4d00420056004d00320032003100320030003800020014004d00420056004d00320032003100320030003800010014004d00420056004d0032003200310032003000380004000000030014006d00620076006d0032003200310032003000380007000800a421b4497870d90100000000") 245 | log.Printf("securityBuffer lenght: %v", len(securityBuffer)) 246 | responseHdr := SessionSetupResponse(make([]byte, 8+len(securityBuffer))) 247 | responseHdr.SetStructureSize() 248 | responseHdr.SetSecurityBufferOffset(0x48) 249 | responseHdr.SetSecurityBufferLength(uint16(len(securityBuffer))) 250 | responseHdr.SetBuffer(securityBuffer) 251 | // responseHdr.SetSecurityMode(Securi) 252 | 253 | smb2Header := PacketCodec(make([]byte, 64, 64)) 254 | smb2Header.SetProtocolId() 255 | smb2Header.SetStructureSize() 256 | smb2Header.SetCreditCharge(1) 257 | smb2Header.SetCommand(SMB2_SESSION_SETUP) 258 | smb2Header.SetStatus(STATUS_MORE_PROCESSING_REQUIRED) 259 | smb2Header.SetCreditRequestResponse(1) 260 | smb2Header.SetFlags(SMB2_FLAGS_SERVER_TO_REDIR) 261 | smb2Header.SetNextCommand(0) 262 | smb2Header.SetMessageId(p.MessageId()) 263 | smb2Header.SetTreeId(0) 264 | // if sessionID == 0 { 265 | // sessionID = 0xebc20a15 266 | // } else { 267 | // sessionID++ 268 | // } 269 | fmt.Printf("p.SessionId(): %v\n", p.SessionId()) 270 | smb2Header.SetSessionId(sessionID) 271 | smb2Header.SetSignature([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) 272 | 273 | l := len(smb2Header) + len(responseHdr) 274 | log.Printf("smb2 length: %v, resp len: %v\n", len(smb2Header), len(responseHdr)) 275 | netBIOSHeader := []byte{0x00, 0x00, 0x00, 0x00} 276 | netBIOSHeader[3] = byte(l) 277 | netBIOSHeader[2] = byte(l >> 8) 278 | 279 | // smb2Header := []byte{0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 280 | 281 | pkt = append(pkt, netBIOSHeader...) 282 | pkt = append(pkt, smb2Header...) 283 | pkt = append(pkt, responseHdr...) 284 | 285 | fmt.Printf("handleSessionSetup response 1: %v\n", hex.EncodeToString(pkt)) 286 | c.rwc.Write(pkt) 287 | fmt.Printf("send response: %d\n", len(pkt)) 288 | 289 | return nil 290 | } 291 | 292 | func (c *conn) handleSessionSetupNtmlsspAuth(p PacketCodec, msg SessionSetupRequest, ntlpPayload auth.NTLMNegotiateMessage) error { 293 | 294 | pkt := []byte{} 295 | responseHdr := SessionSetupResponse(make([]byte, 8)) 296 | responseHdr.SetStructureSize() 297 | responseHdr.SetSessionFlags(SMB2_SESSION_FLAG_IS_GUEST | SMB2_SESSION_FLAG_IS_NULL) 298 | responseHdr.SetSecurityBufferOffset(0) 299 | responseHdr.SetSecurityBufferLength(0) 300 | // responseHdr.SetSecurityMode(Securi) 301 | 302 | smb2Header := PacketCodec(make([]byte, 64)) 303 | smb2Header.SetProtocolId() 304 | smb2Header.SetStructureSize() 305 | smb2Header.SetCreditCharge(1) 306 | smb2Header.SetCommand(SMB2_SESSION_SETUP) 307 | smb2Header.SetStatus(STATUS_LOGON_FAILURE) 308 | smb2Header.SetCreditRequestResponse(1) 309 | smb2Header.SetFlags(SMB2_FLAGS_SERVER_TO_REDIR) 310 | smb2Header.SetNextCommand(0) 311 | smb2Header.SetMessageId(p.MessageId()) 312 | smb2Header.SetTreeId(0) 313 | // if sessionID == 0 { 314 | // sessionID = 0xebc20a15 315 | // } else { 316 | // sessionID++ 317 | // } 318 | smb2Header.SetSessionId(p.SessionId()) 319 | smb2Header.SetSignature([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) 320 | 321 | l := len(smb2Header) + len(responseHdr) 322 | log.Printf("smb2 length: %v, resp len: %v\n", len(smb2Header), len(responseHdr)) 323 | netBIOSHeader := []byte{0x00, 0x00, 0x00, 0x00} 324 | netBIOSHeader[3] = byte(l) 325 | netBIOSHeader[2] = byte(l >> 8) 326 | 327 | // smb2Header := []byte{0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 328 | 329 | pkt = append(pkt, netBIOSHeader...) 330 | pkt = append(pkt, smb2Header...) 331 | pkt = append(pkt, responseHdr...) 332 | 333 | fmt.Printf("handleSessionSetup response 2: %v\n", hex.EncodeToString(pkt)) 334 | c.rwc.Write(pkt) 335 | fmt.Printf("send response: %d\n", len(pkt)) 336 | 337 | return nil 338 | } 339 | -------------------------------------------------------------------------------- /session_setup_request.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | type SessionSetupRequest []byte 8 | 9 | func (p SessionSetupRequest) IsInvalid() bool { 10 | // MS-SMB2, MUST be set to 25 11 | if len(p) < 25 { 12 | return true 13 | } 14 | 15 | return false 16 | } 17 | 18 | func (p SessionSetupRequest) StructureSize() uint16 { 19 | return binary.LittleEndian.Uint16(p[0:2]) 20 | } 21 | 22 | func (p SessionSetupRequest) SetStructureSize() { 23 | binary.LittleEndian.PutUint16(p[0:2], 25) 24 | } 25 | 26 | func (p SessionSetupRequest) Flags() SessionFlags { 27 | return SessionFlags(p[2]) 28 | } 29 | 30 | func (p SessionSetupRequest) SetFlags(v SessionFlags) { 31 | p[2] = uint8(v) 32 | } 33 | 34 | func (p SessionSetupRequest) SecurityMode() NegotiateSigning { 35 | return NegotiateSigning(p[3]) 36 | } 37 | 38 | func (p SessionSetupRequest) SetSecurityMode(v NegotiateSigning) { 39 | p[3] = uint8(v) 40 | } 41 | 42 | func (p SessionSetupRequest) Capabilities() Capabilities { 43 | return Capabilities(binary.LittleEndian.Uint32(p[4:8])) 44 | } 45 | 46 | func (p SessionSetupRequest) SetCapabilities(v Capabilities) { 47 | binary.LittleEndian.PutUint32(p[4:8], uint32(v)) 48 | } 49 | 50 | func (p SessionSetupRequest) Channel() uint32 { 51 | return binary.LittleEndian.Uint32(p[8:12]) 52 | } 53 | 54 | func (p SessionSetupRequest) SetChannel(v uint32) { 55 | binary.LittleEndian.PutUint32(p[8:12], v) 56 | } 57 | 58 | func (p SessionSetupRequest) SecurityBufferOffset() uint16 { 59 | return binary.LittleEndian.Uint16(p[12:14]) 60 | } 61 | 62 | func (p SessionSetupRequest) SetSecurityBufferOffset(v uint16) { 63 | binary.LittleEndian.PutUint16(p[12:14], v) 64 | } 65 | 66 | func (p SessionSetupRequest) SecurityBufferLength() uint16 { 67 | return binary.LittleEndian.Uint16(p[14:16]) 68 | } 69 | 70 | func (p SessionSetupRequest) SetSecurityBufferLength(v uint16) { 71 | binary.LittleEndian.PutUint16(p[14:16], v) 72 | } 73 | 74 | func (p SessionSetupRequest) PreviousSessionId() uint64 { 75 | return binary.LittleEndian.Uint64(p[16:24]) 76 | } 77 | 78 | func (p SessionSetupRequest) SetPreviousSessionId(v uint64) { 79 | binary.LittleEndian.PutUint64(p[16:24], v) 80 | } 81 | 82 | func (p SessionSetupRequest) Buffer() []byte { 83 | offset := p.SecurityBufferOffset() - 64 84 | return p[offset : offset+p.SecurityBufferLength()] 85 | } 86 | 87 | func (p SessionSetupRequest) SetBuffer(v []byte) { 88 | if len(v) > 0 { 89 | p.SetSecurityBufferOffset(20 + 64) 90 | p.SetSecurityBufferLength(uint16(len(v))) 91 | copy(p[20:], v) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /session_setup_request_test.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | ) 7 | 8 | func TestSessionSetupRequest(t *testing.T) { 9 | 10 | cases := []struct { 11 | name string 12 | input []byte 13 | expected map[string]interface{} 14 | }{ 15 | { 16 | name: "test", 17 | input: func() []byte { 18 | r, _ := hex.DecodeString("19000001000000000000000058004a000000000000000000604806062b0601050502a03e303ca00e300c060a2b06010401823702020aa22a04284e544c4d5353500001000000158288e2000000000000000000000000000000000a0000000000000f") 19 | return r 20 | }(), 21 | expected: map[string]interface{}{ 22 | "StructureSize": 0x19, 23 | "Flags": 0, 24 | "SecurityMode": SMB2_NEGOTIATE_SIGNING_ENABLED, 25 | "Capabilities": 0, 26 | "Channel": 0, 27 | "PreviousSessionId": 0, 28 | "SecurityBufferOffset": 0x58, 29 | "SecurityBufferLength": 74, 30 | "Buffer": "604806062b0601050502a03e303ca00e300c060a2b06010401823702020aa22a04284e544c4d5353500001000000158288e2000000000000000000000000000000000a0000000000000f", 31 | }, 32 | }, 33 | } 34 | for _, c := range cases { 35 | t.Run(c.name, func(t *testing.T) { 36 | ssr := SessionSetupRequest(c.input) 37 | if ssr.IsInvalid() { 38 | t.Errorf("SessionSetupRequest.IsInvalid() = true, want false") 39 | } 40 | if ssr.StructureSize() != uint16(c.expected["StructureSize"].(int)) { 41 | t.Errorf("SessionSetupRequest.StructureSize() = %v, want %v", ssr.StructureSize(), c.expected["StructureSize"]) 42 | } 43 | if ssr.Flags() != SessionFlags(c.expected["Flags"].(int)) { 44 | t.Errorf("SessionSetupRequest.Flags() = %v, want %v", ssr.Flags(), c.expected["Flags"]) 45 | } 46 | if ssr.SecurityMode() != (c.expected["SecurityMode"].(NegotiateSigning)) { 47 | t.Errorf("SessionSetupRequest.SecurityMode() = %v, want %v", ssr.SecurityMode(), c.expected["SecurityMode"]) 48 | } 49 | if ssr.Capabilities() != Capabilities(c.expected["Capabilities"].(int)) { 50 | t.Errorf("SessionSetupRequest.Capabilities() = %v, want %v", ssr.Capabilities(), c.expected["Capabilities"]) 51 | } 52 | if ssr.Channel() != uint32(c.expected["Channel"].(int)) { 53 | t.Errorf("SessionSetupRequest.Channel() = %v, want %v", ssr.Channel(), c.expected["Channel"]) 54 | } 55 | if ssr.PreviousSessionId() != uint64(c.expected["PreviousSessionId"].(int)) { 56 | t.Errorf("SessionSetupRequest.PreviousSessionId() = %v, want %v", ssr.PreviousSessionId(), c.expected["PreviousSessionId"]) 57 | } 58 | if ssr.SecurityBufferOffset() != uint16(c.expected["SecurityBufferOffset"].(int)) { 59 | t.Errorf("SessionSetupRequest.SecurityBufferOffset() = %v, want %v", ssr.SecurityBufferOffset(), c.expected["SecurityBufferOffset"]) 60 | } 61 | if ssr.SecurityBufferLength() != uint16(c.expected["SecurityBufferLength"].(int)) { 62 | t.Errorf("SessionSetupRequest.SecurityBufferLength() = %v, want %v", ssr.SecurityBufferLength(), c.expected["SecurityBufferLength"]) 63 | } 64 | if hex.EncodeToString(ssr.Buffer()) != c.expected["Buffer"].(string) { 65 | t.Errorf("SessionSetupRequest.Buffer() = %v, want %v", hex.EncodeToString(ssr.Buffer()), c.expected["Buffer"]) 66 | } 67 | 68 | // TODO 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /session_setup_response.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | // MS-SMB2 2.2.6 SMB2 SESSION_SETUP Response 8 | type SessionSetupResponse []byte 9 | 10 | type SessionSetupSessionFlags uint16 11 | 12 | const ( 13 | // MS-SMB2 - v20211006 page 57/481 14 | // MS-SMB2 - v20230920 page 59/488 15 | SMB2_SESSION_FLAG_IS_GUEST SessionSetupSessionFlags = 0x0001 16 | SMB2_SESSION_FLAG_IS_NULL SessionSetupSessionFlags = 0x0002 17 | SMB2_SESSION_FLAG_ENCRYPT_DATA SessionSetupSessionFlags = 0x0004 18 | ) 19 | 20 | func (p SessionSetupResponse) IsInvalid() bool { 21 | // MS-SMB2, MUST be set to 8 22 | if len(p) < 8 { 23 | return true 24 | } 25 | 26 | return false 27 | } 28 | 29 | func (p SessionSetupResponse) StructureSize() uint16 { 30 | return binary.LittleEndian.Uint16(p[0:2]) 31 | } 32 | 33 | func (p SessionSetupResponse) SetStructureSize() { 34 | binary.LittleEndian.PutUint16(p[0:2], 9) 35 | } 36 | 37 | func (p SessionSetupResponse) SessionFlags() SessionSetupSessionFlags { 38 | return SessionSetupSessionFlags(binary.LittleEndian.Uint16(p[2:4])) 39 | } 40 | 41 | func (p SessionSetupResponse) SetSessionFlags(v SessionSetupSessionFlags) { 42 | binary.LittleEndian.PutUint16(p[2:4], uint16(v)) 43 | } 44 | 45 | func (p SessionSetupResponse) SecurityBufferOffset() uint16 { 46 | return binary.LittleEndian.Uint16(p[4:6]) 47 | } 48 | 49 | func (p SessionSetupResponse) SetSecurityBufferOffset(v uint16) { 50 | binary.LittleEndian.PutUint16(p[4:6], v) 51 | } 52 | 53 | func (p SessionSetupResponse) SecurityBufferLength() uint16 { 54 | return binary.LittleEndian.Uint16(p[6:8]) 55 | } 56 | 57 | func (p SessionSetupResponse) SetSecurityBufferLength(v uint16) { 58 | binary.LittleEndian.PutUint16(p[6:8], v) 59 | } 60 | 61 | func (p SessionSetupResponse) Buffer() []byte { 62 | return p[p.SecurityBufferOffset()-64 : p.SecurityBufferOffset()+p.SecurityBufferLength()] 63 | } 64 | 65 | func (p SessionSetupResponse) SetBuffer(v []byte) { 66 | // p.SetSecurityBufferOffset(8) 67 | offset := p.SecurityBufferOffset() - 64 68 | length := p.SecurityBufferLength() 69 | copy(p[offset:offset+length], v) 70 | } 71 | -------------------------------------------------------------------------------- /session_setup_response_test.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | ) 7 | 8 | func TestSessionSetupResponse(t *testing.T) { 9 | 10 | cases := []struct { 11 | name string 12 | input []byte 13 | expected map[string]interface{} 14 | }{ 15 | { 16 | name: "test", 17 | input: func() []byte { 18 | r, _ := hex.DecodeString("0900000000000000") 19 | return r 20 | }(), 21 | expected: map[string]interface{}{ 22 | "StructureSize": 0x09, 23 | "SessionFlags": 0, 24 | "SecurityBufferOffset": 0x0, 25 | "SecurityBufferLength": 0, 26 | "Buffer": "", 27 | }, 28 | }, 29 | } 30 | for _, c := range cases { 31 | t.Run(c.name, func(t *testing.T) { 32 | ssr := SessionSetupResponse(c.input) 33 | if ssr.IsInvalid() { 34 | t.Errorf("SessionSetupRequest.IsInvalid() = true, want false") 35 | } 36 | if ssr.StructureSize() != uint16(c.expected["StructureSize"].(int)) { 37 | t.Errorf("SessionSetupRequest.StructureSize() = %v, want %v", ssr.StructureSize(), c.expected["StructureSize"]) 38 | } 39 | if ssr.SessionFlags() != SessionSetupSessionFlags(c.expected["SessionFlags"].(int)) { 40 | t.Errorf("SessionSetupRequest.SessionFlags() = %v, want %v", ssr.SessionFlags(), c.expected["SessionFlags"]) 41 | } 42 | if ssr.SecurityBufferOffset() != uint16(c.expected["SecurityBufferOffset"].(int)) { 43 | t.Errorf("SessionSetupRequest.SecurityBufferOffset() = %v, want %v", ssr.SecurityBufferOffset(), c.expected["SecurityBufferOffset"]) 44 | } 45 | if ssr.SecurityBufferLength() != uint16(c.expected["SecurityBufferLength"].(int)) { 46 | t.Errorf("SessionSetupRequest.SecurityBufferLength() = %v, want %v", ssr.SecurityBufferLength(), c.expected["SecurityBufferLength"]) 47 | } 48 | if hex.EncodeToString(ssr.Buffer()) != c.expected["Buffer"].(string) { 49 | t.Errorf("SessionSetupRequest.Buffer() = %v, want %v", hex.EncodeToString(ssr.Buffer()), c.expected["Buffer"]) 50 | } 51 | 52 | // TODO 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /signing.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type SingingAlgorithm uint16 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 51/481 7 | SMB2_SIGNING_ALGORITHM_HMAC_SHA256 SingingAlgorithm = 0x0000 8 | SMB2_SIGNING_ALGORITHM_AES_CMAC SingingAlgorithm = 0x0001 9 | SMB2_SIGNING_ALGORITHM_AES_GMAC SingingAlgorithm = 0x0002 10 | ) 11 | 12 | type SigningCapability []byte 13 | 14 | func (c SigningCapability) SigningAlgorithmCount() uint16 { 15 | return uint16(le.Uint16(c[0:2])) 16 | } 17 | 18 | func (c SigningCapability) SetSigningAlgorithmCount(v uint16) { 19 | le.PutUint16(c[0:2], v) 20 | } 21 | 22 | func (c SigningCapability) SigningAlgorithms() []SingingAlgorithm { 23 | var res = make([]SingingAlgorithm, c.SigningAlgorithmCount()) 24 | for i := 0; i < int(c.SigningAlgorithmCount()); i++ { 25 | res[i] = SingingAlgorithm(le.Uint16(c[4+i*2 : 6+i*2])) 26 | } 27 | return res 28 | } 29 | 30 | func (c SigningCapability) SetSigningAlgorithms(v []SingingAlgorithm) { 31 | c.SetSigningAlgorithmCount(uint16(len(v))) 32 | for i, t := range v { 33 | le.PutUint16(c[4+i*2:6+i*2], uint16(t)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /transport.go: -------------------------------------------------------------------------------- 1 | package simba 2 | 3 | type TransportFlag uint32 4 | 5 | const ( 6 | // MS-SMB2 - v20211006 page 50/481 7 | SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY TransportFlag = 0x00000001 8 | ) 9 | 10 | type TransportCapability []byte 11 | 12 | func (c TransportCapability) Capabilities() TransportFlag { 13 | return TransportFlag(le.Uint32(c[0:4])) 14 | } 15 | 16 | func (c TransportCapability) SetCapabilities(f TransportFlag) { 17 | le.PutUint32(c[0:4], uint32(f)) 18 | } 19 | --------------------------------------------------------------------------------