Skip to content

Commit 3cc8f0f

Browse files
v0.1
1 parent a552ca8 commit 3cc8f0f

File tree

4 files changed

+168
-0
lines changed

4 files changed

+168
-0
lines changed

Program.cs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System.Net.Sockets;
2+
using System.Text;
3+
using Renci.SshNet;
4+
using Renci.SshNet.Common;
5+
6+
class Program
7+
{
8+
static void Main(string[] args)
9+
{
10+
string userName = "dummyUser";
11+
12+
if (args.Length != 1)
13+
{
14+
Console.WriteLine("Usage: SshBatchChecker <targets.txt>");
15+
Console.WriteLine(" targets.txt should contain lines like 192.168.1.100:22");
16+
return;
17+
}
18+
19+
var filePath = args[0];
20+
if (!File.Exists(filePath))
21+
{
22+
Console.Error.WriteLine($"File not found: {filePath}");
23+
return;
24+
}
25+
26+
foreach (var raw in File.ReadAllLines(filePath))
27+
{
28+
var line = raw.Trim();
29+
if (string.IsNullOrWhiteSpace(line)) continue;
30+
31+
// split into at most 2 parts
32+
var parts = line.Split(new[] { ':' }, 2);
33+
string host;
34+
int port;
35+
36+
if (parts.Length == 1)
37+
{
38+
// no port specified → use default 22
39+
host = parts[0];
40+
port = 22;
41+
}
42+
else if (!int.TryParse(parts[1], out port))
43+
{
44+
Console.WriteLine($"[INVALID] “{line}” – port is not a number");
45+
continue;
46+
}
47+
else
48+
{
49+
host = parts[0];
50+
}
51+
52+
if (TryGetSshAuthMethods(host, port, userName, out var methods))
53+
{
54+
Console.WriteLine($"[{host}:{port}] SSH detected");
55+
56+
// Check password auth
57+
bool allowsPassword = methods.Contains("password");
58+
Console.WriteLine($"Password authentication:\t{(allowsPassword ? "Allowed" : "Not allowed")}");
59+
60+
// Check publickey auth
61+
bool allowsPubKey = methods.Contains("publickey");
62+
Console.WriteLine($"Public-key authentication:\t{(allowsPubKey ? "Allowed" : "Not allowed")}");
63+
64+
// (You can add more checks below, e.g. keyboard-interactive, gssapi-…)
65+
bool allowsKeyboard = methods.Contains("keyboard-interactive");
66+
Console.WriteLine($"Keyboard-interactive auth:\t{(allowsKeyboard ? "Allowed" : "Not allowed")}");
67+
68+
Console.WriteLine();
69+
}
70+
else
71+
{
72+
Console.WriteLine("Not an SSH port or unable to detect auth methods.");
73+
}
74+
}
75+
}
76+
77+
public static bool TryGetSshAuthMethods(string host, int port, string userName, out string[] supportedMethods)
78+
{
79+
supportedMethods = Array.Empty<string>();
80+
try
81+
{
82+
using var tcp = new TcpClient();
83+
tcp.Connect(host, port);
84+
using var netStream = tcp.GetStream();
85+
using var reader = new StreamReader(netStream, Encoding.ASCII, false, 256, leaveOpen: true);
86+
87+
// The SSH server should immediately send a line like "SSH-2.0-OpenSSH_8.9"
88+
var banner = reader.ReadLine();
89+
if (banner == null || !banner.StartsWith("SSH-"))
90+
return false; // Not SSH :contentReference[oaicite:0]{index=0}
91+
}
92+
catch (SocketException)
93+
{
94+
return false; // Port closed or not reachable
95+
}
96+
97+
var noneAuth = new NoneAuthenticationMethod(userName);
98+
var connectionInfo = new ConnectionInfo(host, port, userName, noneAuth);
99+
100+
using var ssh = new SshClient(connectionInfo);
101+
try
102+
{
103+
ssh.Connect(); // This will always throw SshAuthenticationException for "none"
104+
}
105+
catch (SshAuthenticationException ex)
106+
{
107+
// SSH-2 servers advertise allowed methods by failing the "none" request :contentReference[oaicite:1]{index=1}
108+
var msg = ex.Message;
109+
var start = msg.IndexOf('(');
110+
var end = msg.IndexOf(')');
111+
if (start >= 0 && end > start)
112+
{
113+
var list = msg.Substring(start + 1, end - start - 1);
114+
supportedMethods = list.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(m => m.Trim()).ToArray();
115+
return true;
116+
}
117+
}
118+
119+
return false;
120+
}
121+
}

Properties/launchSettings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"profiles": {
3+
"SlipperyPete": {
4+
"commandName": "Project",
5+
"commandLineArgs": "D:\\temp\\targets.txt"
6+
}
7+
}
8+
}

SlipperyPete.csproj

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="SSH.NET" Version="2025.0.0" />
12+
</ItemGroup>
13+
14+
</Project>

SlipperyPete.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36301.6 d17.14
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlipperyPete", "SlipperyPete.csproj", "{EAF1465A-B818-46E3-9511-C1D269D137FD}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{EAF1465A-B818-46E3-9511-C1D269D137FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{EAF1465A-B818-46E3-9511-C1D269D137FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{EAF1465A-B818-46E3-9511-C1D269D137FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{EAF1465A-B818-46E3-9511-C1D269D137FD}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {879B7845-C194-42F1-B174-A98437003E00}
24+
EndGlobalSection
25+
EndGlobal

0 commit comments

Comments
 (0)