Skip to content

Commit dbeebda

Browse files
adding telnet login + crypto (#6419)
* adding telnet login + crypto * smbauth lib porting + ntlm parsing over telnet * gen lib * adding telnet test * adding breakout after max iterations * fix(utils): broken pkt creation & impl `Create{LN,NT}Response` Signed-off-by: Dwi Siswanto <[email protected]> * chore(utils): satisfy lints Signed-off-by: Dwi Siswanto <[email protected]> --------- Signed-off-by: Dwi Siswanto <[email protected]> Co-authored-by: Dwi Siswanto <[email protected]>
1 parent 891dffb commit dbeebda

File tree

11 files changed

+1833
-4
lines changed

11 files changed

+1833
-4
lines changed

cmd/integration-test/javascript.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ var jsTestcases = []TestCaseInfo{
2222
{Path: "protocols/javascript/mysql-connect.yaml", TestCase: &javascriptMySQLConnect{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
2323
{Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}},
2424
{Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}},
25+
{Path: "protocols/javascript/telnet-auth-test.yaml", TestCase: &javascriptTelnetAuthTest{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
2526
}
2627

2728
var (
2829
redisResource *dockertest.Resource
2930
sshResource *dockertest.Resource
3031
oracleResource *dockertest.Resource
3132
vncResource *dockertest.Resource
33+
telnetResource *dockertest.Resource
3234
postgresResource *dockertest.Resource
3335
mysqlResource *dockertest.Resource
3436
rsyncResource *dockertest.Resource
@@ -292,6 +294,38 @@ func (j *javascriptRsyncTest) Execute(filePath string) error {
292294
return multierr.Combine(errs...)
293295
}
294296

297+
type javascriptTelnetAuthTest struct{}
298+
299+
func (j *javascriptTelnetAuthTest) Execute(filePath string) error {
300+
if telnetResource == nil || pool == nil {
301+
// skip test as telnet is not running
302+
return nil
303+
}
304+
tempPort := telnetResource.GetPort("23/tcp")
305+
finalURL := "localhost:" + tempPort
306+
defer purge(telnetResource)
307+
errs := []error{}
308+
for i := 0; i < defaultRetry; i++ {
309+
results := []string{}
310+
var err error
311+
_ = pool.Retry(func() error {
312+
//let telnet server start
313+
time.Sleep(3 * time.Second)
314+
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug)
315+
return nil
316+
})
317+
if err != nil {
318+
return err
319+
}
320+
if err := expectResultsCount(results, 1); err == nil {
321+
return nil
322+
} else {
323+
errs = append(errs, err)
324+
}
325+
}
326+
return multierr.Combine(errs...)
327+
}
328+
295329
// purge any given resource if it is not nil
296330
func purge(resource *dockertest.Resource) {
297331
if resource != nil && pool != nil {
@@ -447,4 +481,22 @@ func init() {
447481
if err := rsyncResource.Expire(30); err != nil {
448482
log.Printf("Could not expire Rsync resource: %s", err)
449483
}
484+
485+
// setup a temporary telnet server
486+
// username: dev
487+
// password: mysecret
488+
telnetResource, err = pool.RunWithOptions(&dockertest.RunOptions{
489+
Repository: "alpine",
490+
Tag: "latest",
491+
Cmd: []string{"sh", "-c", "apk add --no-cache busybox-extras shadow && useradd -m dev && echo 'dev:mysecret' | chpasswd && exec /usr/sbin/telnetd -F -p 23 -l /bin/login"},
492+
Platform: "linux/amd64",
493+
})
494+
if err != nil {
495+
log.Printf("Could not start Telnet resource: %s", err)
496+
return
497+
}
498+
// by default expire after 30 sec
499+
if err := telnetResource.Expire(30); err != nil {
500+
log.Printf("Could not expire Telnet resource: %s", err)
501+
}
450502
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ require (
5050
code.gitea.io/sdk/gitea v0.17.0
5151
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
5252
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
53+
github.com/Azure/go-ntlmssp v0.1.0
5354
github.com/DataDog/gostackparse v0.7.0
5455
github.com/Masterminds/semver/v3 v3.2.1
5556
github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057
@@ -138,7 +139,6 @@ require (
138139
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
139140
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
140141
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
141-
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
142142
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
143143
github.com/BurntSushi/toml v1.3.2 // indirect
144144
github.com/Microsoft/go-winio v0.6.2 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 h1:nVocQV40OQne5613E
6464
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0/go.mod h1:7QJP7dr2wznCMeqIrhMgWGf7XpAQnVrJqDm9nvV3Cu4=
6565
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
6666
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
67-
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
68-
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
67+
github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=
68+
github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
6969
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
7070
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
7171
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
id: telnet-auth-test
2+
3+
info:
4+
name: Telnet Authentication Test
5+
author: pdteam
6+
severity: info
7+
metadata:
8+
shodan-query: port:23
9+
10+
11+
javascript:
12+
- code: |
13+
var m = require("nuclei/telnet");
14+
var c = m.TelnetClient();
15+
c.Connect(Host, Port, User, Password);
16+
17+
args:
18+
Host: "{{Host}}"
19+
Port: "23"
20+
User: "dev"
21+
Password: "mysecret"
22+
23+
matchers:
24+
- type: dsl
25+
dsl:
26+
- "response == true"
27+
- "success == true"
28+
condition: and

pkg/js/generated/go/libtelnet/telnet.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package telnet
22

33
import (
44
lib_telnet "github.com/projectdiscovery/nuclei/v3/pkg/js/libs/telnet"
5+
telnetmini "github.com/projectdiscovery/nuclei/v3/pkg/utils/telnetmini"
56

67
"github.com/Mzack9999/goja"
78
"github.com/projectdiscovery/nuclei/v3/pkg/js/gojs"
@@ -20,7 +21,10 @@ func init() {
2021
// Var and consts
2122

2223
// Objects / Classes
23-
"IsTelnetResponse": gojs.GetClassConstructor[lib_telnet.IsTelnetResponse](&lib_telnet.IsTelnetResponse{}),
24+
"TelnetClient": gojs.GetClassConstructor[lib_telnet.TelnetClient](&lib_telnet.TelnetClient{}),
25+
"IsTelnetResponse": gojs.GetClassConstructor[lib_telnet.IsTelnetResponse](&lib_telnet.IsTelnetResponse{}),
26+
"TelnetInfoResponse": gojs.GetClassConstructor[lib_telnet.TelnetInfoResponse](&lib_telnet.TelnetInfoResponse{}),
27+
"NTLMInfoResponse": gojs.GetClassConstructor[telnetmini.NTLMInfoResponse](&telnetmini.NTLMInfoResponse{}),
2428
},
2529
).Register()
2630
}

pkg/js/generated/ts/telnet.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,65 @@ export function IsTelnet(host: string, port: number): IsTelnetResponse | null {
1313
return null;
1414
}
1515

16+
/**
17+
* TelnetClient is a client for Telnet servers.
18+
* @example
19+
* ```javascript
20+
* const telnet = require('nuclei/telnet');
21+
* const client = new telnet.TelnetClient();
22+
* ```
23+
*/
24+
export class TelnetClient {
25+
26+
/**
27+
* Connect tries to connect to provided host and port with telnet.
28+
* Optionally provides username and password for authentication.
29+
* Returns state of connection. If the connection is successful,
30+
* the function will return true, otherwise false.
31+
* @example
32+
* ```javascript
33+
* const telnet = require('nuclei/telnet');
34+
* const client = new telnet.TelnetClient();
35+
* const connected = client.Connect('acme.com', 23, 'username', 'password');
36+
* ```
37+
*/
38+
public Connect(host: string, port: number, username: string, password: string): boolean {
39+
return false;
40+
}
1641

42+
/**
43+
* Info gathers information about the telnet server including encryption support.
44+
* Uses the telnetmini library's DetectEncryption helper function.
45+
* WARNING: The connection used for detection becomes unusable after this call.
46+
* @example
47+
* ```javascript
48+
* const telnet = require('nuclei/telnet');
49+
* const client = new telnet.TelnetClient();
50+
* const info = client.Info('acme.com', 23);
51+
* log(toJSON(info));
52+
* ```
53+
*/
54+
public Info(host: string, port: number): TelnetInfoResponse | null {
55+
return null;
56+
}
57+
58+
/**
59+
* GetTelnetNTLMInfo implements the Nmap telnet-ntlm-info.nse script functionality.
60+
* This function uses the telnetmini library and SMB packet crafting functions to send
61+
* MS-TNAP NTLM authentication requests with null credentials. It might work only on
62+
* Microsoft Telnet servers.
63+
* @example
64+
* ```javascript
65+
* const telnet = require('nuclei/telnet');
66+
* const client = new telnet.TelnetClient();
67+
* const ntlmInfo = client.GetTelnetNTLMInfo('acme.com', 23);
68+
* log(toJSON(ntlmInfo));
69+
* ```
70+
*/
71+
public GetTelnetNTLMInfo(host: string, port: number): NTLMInfoResponse | null {
72+
return null;
73+
}
74+
}
1775

1876
/**
1977
* IsTelnetResponse is the response from the IsTelnet function.
@@ -32,3 +90,76 @@ export interface IsTelnetResponse {
3290
Banner?: string,
3391
}
3492

93+
/**
94+
* TelnetInfoResponse is the response from the Info function.
95+
* @example
96+
* ```javascript
97+
* const telnet = require('nuclei/telnet');
98+
* const client = new telnet.TelnetClient();
99+
* const info = client.Info('acme.com', 23);
100+
* log(toJSON(info));
101+
* ```
102+
*/
103+
export interface TelnetInfoResponse {
104+
105+
SupportsEncryption?: boolean,
106+
107+
Banner?: string,
108+
109+
Options?: { [key: number]: number[] },
110+
}
111+
112+
/**
113+
* NTLMInfoResponse represents the response from NTLM information gathering.
114+
* This matches exactly the output structure from the Nmap telnet-ntlm-info.nse script.
115+
* @example
116+
* ```javascript
117+
* const telnet = require('nuclei/telnet');
118+
* const client = new telnet.TelnetClient();
119+
* const ntlmInfo = client.GetTelnetNTLMInfo('acme.com', 23);
120+
* log(toJSON(ntlmInfo));
121+
* ```
122+
*/
123+
export interface NTLMInfoResponse {
124+
125+
/**
126+
* Target_Name from script (target_realm in script)
127+
*/
128+
TargetName?: string,
129+
130+
/**
131+
* NetBIOS_Domain_Name from script
132+
*/
133+
NetBIOSDomainName?: string,
134+
135+
/**
136+
* NetBIOS_Computer_Name from script
137+
*/
138+
NetBIOSComputerName?: string,
139+
140+
/**
141+
* DNS_Domain_Name from script
142+
*/
143+
DNSDomainName?: string,
144+
145+
/**
146+
* DNS_Computer_Name from script (fqdn in script)
147+
*/
148+
DNSComputerName?: string,
149+
150+
/**
151+
* DNS_Tree_Name from script (dns_forest_name in script)
152+
*/
153+
DNSTreeName?: string,
154+
155+
/**
156+
* Product_Version from script
157+
*/
158+
ProductVersion?: string,
159+
160+
/**
161+
* Raw timestamp for skew calculation
162+
*/
163+
Timestamp?: number,
164+
}
165+

0 commit comments

Comments
 (0)