package telnet
import (
"context"
"fmt"
"net"
"strconv"
"time"
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
"github.com/praetorian-inc/fingerprintx/pkg/plugins/services/telnet"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/telnetmini"
)
const (
IAC = 255
WILL = 251
WONT = 252
DO = 253
DONT = 254
SB = 250
SE = 240
ECHO = 1
SUPPRESS_GO_AHEAD = 3
TERMINAL_TYPE = 24
NAWS = 31
ENCRYPT = 38
)
type (
IsTelnetResponse struct {
IsTelnet bool
Banner string
}
TelnetInfoResponse struct {
SupportsEncryption bool
Banner string
Options map[int][]int
}
TelnetClient struct{}
)
func IsTelnet(ctx context.Context, host string, port int) (IsTelnetResponse, error) {
executionId := ctx.Value("executionId").(string)
return memoizedisTelnet(executionId, host, port)
}
func isTelnet(executionId string, host string, port int) (IsTelnetResponse, error) {
resp := IsTelnetResponse{}
timeout := 5 * time.Second
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return IsTelnetResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return resp, err
}
defer func() {
_ = conn.Close()
}()
telnetPlugin := telnet.TELNETPlugin{}
service, err := telnetPlugin.Run(conn, timeout, plugins.Target{Host: host})
if err != nil {
return resp, err
}
if service == nil {
return resp, nil
}
resp.Banner = service.Metadata().(plugins.ServiceTelnet).ServerData
resp.IsTelnet = true
return resp, nil
}
func (c *TelnetClient) Connect(ctx context.Context, host string, port int, username string, password string) (bool, error) {
executionId := ctx.Value("executionId").(string)
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return false, fmt.Errorf("dialers not initialized for %s", executionId)
}
if !protocolstate.IsHostAllowed(executionId, host) {
return false, protocolstate.ErrHostDenied.Msgf(host)
}
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return false, err
}
client := telnetmini.New(conn)
defer func() {
_ = client.Close()
}()
if username != "" && password != "" {
authCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
if err := client.Auth(authCtx, username, password); err != nil {
return false, err
}
}
return true, nil
}
func (c *TelnetClient) Info(ctx context.Context, host string, port int) (TelnetInfoResponse, error) {
executionId := ctx.Value("executionId").(string)
if !protocolstate.IsHostAllowed(executionId, host) {
return TelnetInfoResponse{}, protocolstate.ErrHostDenied.Msgf(host)
}
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return TelnetInfoResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return TelnetInfoResponse{}, err
}
defer func() {
_ = conn.Close()
}()
encryptionInfo, err := telnetmini.DetectEncryption(conn, 7*time.Second)
if err != nil {
return TelnetInfoResponse{}, err
}
return TelnetInfoResponse{
SupportsEncryption: encryptionInfo.SupportsEncryption,
Banner: encryptionInfo.Banner,
Options: encryptionInfo.Options,
}, nil
}
func (c *TelnetClient) GetTelnetNTLMInfo(ctx context.Context, host string, port int) (*telnetmini.NTLMInfoResponse, error) {
executionId := ctx.Value("executionId").(string)
if !protocolstate.IsHostAllowed(executionId, host) {
return nil, protocolstate.ErrHostDenied.Msgf(host)
}
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", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return nil, err
}
defer func() {
_ = conn.Close()
}()
client := telnetmini.New(conn)
defer func() {
_ = client.Close()
}()
_ = conn.SetDeadline(time.Now().Add(10 * time.Second))
tnapLoginPacket := telnetmini.CreateTNAPLoginPacket()
_, err = conn.Write(tnapLoginPacket)
if err != nil {
return nil, fmt.Errorf("failed to send MS-TNAP login packet: %w", err)
}
buffer := make([]byte, 4096)
n, err := conn.Read(buffer)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if n == 0 {
return nil, fmt.Errorf("no response received")
}
response := buffer[:n]
ntlmInfo, err := telnetmini.ParseNTLMResponse(response)
if err != nil {
return nil, fmt.Errorf("failed to parse NTLM response: %w", err)
}
return ntlmInfo, nil
}