2023-09-16 16:02:17 +05:30
|
|
|
package smb
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
|
2024-01-30 04:15:59 +05:30
|
|
|
"github.com/projectdiscovery/go-smb2"
|
2023-10-17 17:44:13 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
2023-09-16 16:02:17 +05:30
|
|
|
"github.com/zmap/zgrab2/lib/smb/smb"
|
|
|
|
|
)
|
|
|
|
|
|
2024-02-07 21:45:40 +05:30
|
|
|
type (
|
|
|
|
|
// SMBClient is a client for SMB servers.
|
|
|
|
|
// Internally client uses github.com/zmap/zgrab2/lib/smb/smb driver.
|
|
|
|
|
// github.com/projectdiscovery/go-smb2 driver
|
|
|
|
|
// @example
|
|
|
|
|
// ```javascript
|
|
|
|
|
// const smb = require('nuclei/smb');
|
2024-03-01 16:38:56 +05:30
|
|
|
// const client = new smb.SMBClient();
|
2024-02-07 21:45:40 +05:30
|
|
|
// ```
|
|
|
|
|
SMBClient struct{}
|
|
|
|
|
)
|
2023-09-16 16:02:17 +05:30
|
|
|
|
|
|
|
|
// ConnectSMBInfoMode tries to connect to provided host and port
|
|
|
|
|
// and discovery SMB information
|
|
|
|
|
// Returns handshake log and error. If error is not nil,
|
|
|
|
|
// state will be false
|
2024-02-07 21:45:40 +05:30
|
|
|
// @example
|
|
|
|
|
// ```javascript
|
|
|
|
|
// const smb = require('nuclei/smb');
|
2024-03-01 16:38:56 +05:30
|
|
|
// const client = new smb.SMBClient();
|
2024-02-07 21:45:40 +05:30
|
|
|
// const info = client.ConnectSMBInfoMode('acme.com', 445);
|
|
|
|
|
// log(to_json(info));
|
|
|
|
|
// ```
|
2025-07-09 14:47:26 -05:00
|
|
|
func (c *SMBClient) ConnectSMBInfoMode(ctx context.Context, host string, port int) (*smb.SMBLog, error) {
|
|
|
|
|
executionId := ctx.Value("executionId").(string)
|
|
|
|
|
return memoizedconnectSMBInfoMode(executionId, host, port)
|
2024-03-01 16:10:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @memo
|
2025-07-09 14:47:26 -05:00
|
|
|
func connectSMBInfoMode(executionId string, host string, port int) (*smb.SMBLog, error) {
|
|
|
|
|
if !protocolstate.IsHostAllowed(executionId, host) {
|
2024-01-18 04:39:15 +05:30
|
|
|
// host is not valid according to network policy
|
2025-08-25 15:06:58 +07:00
|
|
|
return nil, protocolstate.ErrHostDenied.Msgf(host)
|
2024-01-18 04:39:15 +05:30
|
|
|
}
|
2025-07-09 14:47:26 -05:00
|
|
|
dialer := protocolstate.GetDialersWithId(executionId)
|
|
|
|
|
if dialer == nil {
|
|
|
|
|
return nil, fmt.Errorf("dialers not initialized for %s", executionId)
|
|
|
|
|
}
|
|
|
|
|
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
|
2023-09-16 16:02:17 +05:30
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-01-18 04:39:15 +05:30
|
|
|
// try to get SMBv2/v3 info
|
2024-03-01 16:10:18 +03:00
|
|
|
result, err := getSMBInfo(conn, true, false)
|
2024-01-18 04:39:15 +05:30
|
|
|
_ = conn.Close() // close regardless of error
|
|
|
|
|
if err == nil {
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
2023-09-16 16:02:17 +05:30
|
|
|
|
2024-01-18 04:39:15 +05:30
|
|
|
// try to negotiate SMBv1
|
2025-07-09 14:47:26 -05:00
|
|
|
conn, err = dialer.Fastdialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
|
2023-09-16 16:02:17 +05:30
|
|
|
if err != nil {
|
2024-01-18 04:39:15 +05:30
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-07-01 00:40:44 +07:00
|
|
|
defer func() {
|
2025-07-09 14:47:26 -05:00
|
|
|
_ = conn.Close()
|
|
|
|
|
}()
|
2024-03-01 16:10:18 +03:00
|
|
|
result, err = getSMBInfo(conn, true, true)
|
2024-01-18 04:39:15 +05:30
|
|
|
if err != nil {
|
|
|
|
|
return result, nil
|
2023-09-16 16:02:17 +05:30
|
|
|
}
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListSMBv2Metadata tries to connect to provided host and port
|
|
|
|
|
// and list SMBv2 metadata.
|
|
|
|
|
// Returns metadata and error. If error is not nil,
|
|
|
|
|
// state will be false
|
2024-02-07 21:45:40 +05:30
|
|
|
// @example
|
|
|
|
|
// ```javascript
|
|
|
|
|
// const smb = require('nuclei/smb');
|
2024-03-01 16:38:56 +05:30
|
|
|
// const client = new smb.SMBClient();
|
2024-02-07 21:45:40 +05:30
|
|
|
// const metadata = client.ListSMBv2Metadata('acme.com', 445);
|
|
|
|
|
// log(to_json(metadata));
|
|
|
|
|
// ```
|
2025-07-09 14:47:26 -05:00
|
|
|
func (c *SMBClient) ListSMBv2Metadata(ctx context.Context, host string, port int) (*plugins.ServiceSMB, error) {
|
|
|
|
|
executionId := ctx.Value("executionId").(string)
|
|
|
|
|
if !protocolstate.IsHostAllowed(executionId, host) {
|
2023-09-16 16:02:17 +05:30
|
|
|
// host is not valid according to network policy
|
2025-08-25 15:06:58 +07:00
|
|
|
return nil, protocolstate.ErrHostDenied.Msgf(host)
|
2023-09-16 16:02:17 +05:30
|
|
|
}
|
2025-07-09 14:47:26 -05:00
|
|
|
return memoizedcollectSMBv2Metadata(executionId, host, port, 5*time.Second)
|
2023-09-16 16:02:17 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListShares tries to connect to provided host and port
|
|
|
|
|
// and list shares by using given credentials.
|
|
|
|
|
// Credentials cannot be blank. guest or anonymous credentials
|
|
|
|
|
// can be used by providing empty password.
|
2024-02-07 21:45:40 +05:30
|
|
|
// @example
|
|
|
|
|
// ```javascript
|
|
|
|
|
// const smb = require('nuclei/smb');
|
2024-03-01 16:38:56 +05:30
|
|
|
// const client = new smb.SMBClient();
|
2024-02-07 21:45:40 +05:30
|
|
|
// const shares = client.ListShares('acme.com', 445, 'username', 'password');
|
|
|
|
|
//
|
|
|
|
|
// for (const share of shares) {
|
|
|
|
|
// log(share);
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// ```
|
2025-07-09 14:47:26 -05:00
|
|
|
func (c *SMBClient) ListShares(ctx context.Context, host string, port int, user, password string) ([]string, error) {
|
|
|
|
|
executionId := ctx.Value("executionId").(string)
|
|
|
|
|
return memoizedlistShares(executionId, host, port, user, password)
|
2024-03-01 16:10:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @memo
|
2025-07-09 14:47:26 -05:00
|
|
|
func listShares(executionId string, host string, port int, user string, password string) ([]string, error) {
|
|
|
|
|
if !protocolstate.IsHostAllowed(executionId, host) {
|
2024-01-18 04:39:15 +05:30
|
|
|
// host is not valid according to network policy
|
2025-08-25 15:06:58 +07:00
|
|
|
return nil, protocolstate.ErrHostDenied.Msgf(host)
|
2024-01-18 04:39:15 +05:30
|
|
|
}
|
2025-07-09 14:47:26 -05:00
|
|
|
dialer := protocolstate.GetDialersWithId(executionId)
|
|
|
|
|
if dialer == nil {
|
|
|
|
|
return nil, fmt.Errorf("dialers not initialized for %s", executionId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
|
2023-09-16 16:02:17 +05:30
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-07-01 00:40:44 +07:00
|
|
|
defer func() {
|
2025-07-09 14:47:26 -05:00
|
|
|
_ = conn.Close()
|
|
|
|
|
}()
|
2023-09-16 16:02:17 +05:30
|
|
|
|
|
|
|
|
d := &smb2.Dialer{
|
|
|
|
|
Initiator: &smb2.NTLMInitiator{
|
|
|
|
|
User: user,
|
|
|
|
|
Password: password,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
s, err := d.Dial(conn)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
_ = s.Logoff()
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
names, err := s.ListSharenames()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return names, nil
|
|
|
|
|
}
|