network-utility 1.1.61 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/document/telnet.md CHANGED
@@ -209,6 +209,17 @@ module NE5000E_20
209
209
  end
210
210
  ```
211
211
 
212
+ ```ruby
213
+ @sign << ['VNE9000', 'telnet']
214
+
215
+ module VNE9000
216
+ def setting
217
+ @selectors = {" ---- More ----"=>' ',"Are you sure to display some information?(Y/N)[Y]:"=>'y'}
218
+ @erasers << " ---- More ----" << " " << " \x1b[1D" << " "
219
+ end
220
+ end
221
+ ```
222
+
212
223
  ```ruby
213
224
  @sign << ['NE8000E-X8', 'telnet']
214
225
 
data/network.rb CHANGED
@@ -11,7 +11,10 @@
11
11
  ].each{|mod|require "utility/#{mod}"}
12
12
 
13
13
  [
14
- 'snmp'
14
+ 'ssh/2310/detector/ssh_native_detector',
15
+ 'ssh/2310/detector/ssh_detector_final',
16
+ 'ssh/2310/detector/ssh_detector',
17
+ 'snmp/snmp'
15
18
  ].each{|mod|require "support/#{mod}"}
16
19
 
17
20
  [
@@ -23,5 +26,5 @@
23
26
  ].each{|mod|require mod}
24
27
 
25
28
  module Network
26
- VERSION = '1.1.61'
29
+ VERSION = '2.0.0'
27
30
  end
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env ruby
2
+ # SSH协议检测器 - 探测对端支持的算法套件
3
+ # 依赖: gem install net-ssh
4
+
5
+ require 'net/ssh'
6
+ require 'net/ssh/transport/session'
7
+ require 'socket'
8
+ require 'timeout'
9
+
10
+ class SSHProtocolDetector
11
+ attr_reader :host, :port, :results
12
+
13
+ # 已知的算法分类
14
+ KEX_ALGORITHMS = [
15
+ "curve25519-sha256", "curve25519-sha256@libssh.org",
16
+ "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521",
17
+ "diffie-hellman-group-exchange-sha256", "diffie-hellman-group16-sha512",
18
+ "diffie-hellman-group18-sha512", "diffie-hellman-group14-sha256",
19
+ "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
20
+ "kex-strict-s-v00@openssh.com"
21
+ ].freeze
22
+
23
+ CIPHER_ALGORITHMS = [
24
+ "chacha20-poly1305@openssh.com",
25
+ "aes256-gcm@openssh.com", "aes128-gcm@openssh.com",
26
+ "aes256-ctr", "aes192-ctr", "aes128-ctr",
27
+ "aes256-cbc", "aes192-cbc", "aes128-cbc",
28
+ "3des-cbc", "blowfish-cbc", "cast128-cbc",
29
+ "arcfour", "arcfour128", "arcfour256"
30
+ ].freeze
31
+
32
+ MAC_ALGORITHMS = [
33
+ "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com",
34
+ "hmac-sha1-etm@openssh.com",
35
+ "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1",
36
+ "hmac-md5", "hmac-md5-96", "hmac-sha1-96",
37
+ "umac-64-etm@openssh.com", "umac-128-etm@openssh.com",
38
+ "umac-64@openssh.com", "umac-128@openssh.com"
39
+ ].freeze
40
+
41
+ HOST_KEY_ALGORITHMS = [
42
+ "ssh-ed25519", "ssh-ed25519-cert-v01@openssh.com",
43
+ "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521",
44
+ "rsa-sha2-512", "rsa-sha2-256",
45
+ "ssh-rsa", "ssh-dss"
46
+ ].freeze
47
+
48
+ COMPRESSION_ALGORITHMS = [
49
+ "none", "zlib@openssh.com", "zlib"
50
+ ].freeze
51
+
52
+ def initialize(host, port = 22, timeout = 10)
53
+ @host = host
54
+ @port = port
55
+ @timeout = timeout
56
+ @results = {}
57
+ end
58
+
59
+ # 主检测方法
60
+ def detect
61
+ puts "🔍 正在检测 SSH 协议支持: #{@host}:#{@port}"
62
+ puts "=" * 60
63
+
64
+ # 1. 基础连通性检测
65
+ return false unless check_connectivity
66
+
67
+ # 2. 获取Banner信息
68
+ fetch_banner
69
+
70
+ # 3. 使用Net::SSH探测算法
71
+ probe_algorithms
72
+
73
+ # 4. 详细握手分析
74
+ analyze_handshake
75
+
76
+ display_results
77
+ true
78
+ rescue => e
79
+ puts "❌ 检测失败: #{e.message}"
80
+ false
81
+ end
82
+
83
+ private
84
+
85
+ # 检查TCP连通性
86
+ def check_connectivity
87
+ Timeout::timeout(@timeout) do
88
+ TCPSocket.new(@host, @port).close
89
+ end
90
+ puts "✅ TCP连接正常"
91
+ true
92
+ rescue => e
93
+ puts "❌ 无法连接到 #{@host}:#{@port} - #{e.message}"
94
+ false
95
+ end
96
+
97
+ # 获取SSH Banner
98
+ def fetch_banner
99
+ socket = TCPSocket.new(@host, @port)
100
+ banner = socket.gets&.chomp
101
+ socket.close
102
+
103
+ @results[:banner] = banner
104
+ puts "📢 SSH Banner: #{banner}"
105
+
106
+ # 解析版本
107
+ if banner =~ /SSH-(\d+\.\d+)-(.+)/
108
+ @results[:protocol_version] = $1
109
+ @results[:software_version] = $2
110
+ end
111
+ rescue => e
112
+ puts "⚠️ 获取Banner失败: #{e.message}"
113
+ end
114
+
115
+ # 探测支持的算法
116
+ def probe_algorithms
117
+ puts "\n🔬 正在探测算法支持..."
118
+
119
+ begin
120
+ # 创建临时SSH会话获取算法列表
121
+ session = Net::SSH::Transport::Session.new(
122
+ @host,
123
+ port: @port,
124
+ timeout: @timeout,
125
+ # 提供所有可能的算法进行协商
126
+ kex: KEX_ALGORITHMS,
127
+ host_key: HOST_KEY_ALGORITHMS,
128
+ encryption: CIPHER_ALGORITHMS,
129
+ hmac: MAC_ALGORITHMS,
130
+ compression: COMPRESSION_ALGORITHMS,
131
+ # 不验证主机密钥,仅探测
132
+ paranoid: false,
133
+ verify_host_key: :never,
134
+ # 使用空认证,仅完成KEX
135
+ auth_methods: ['none']
136
+ )
137
+
138
+ # 获取协商后的算法
139
+ algorithms = session.algorithms
140
+
141
+ @results[:negotiated] = {
142
+ kex: algorithms.kex,
143
+ host_key: algorithms.host_key,
144
+ encryption_client: algorithms.encryption[:client],
145
+ encryption_server: algorithms.encryption[:server],
146
+ mac_client: algorithms.hmac[:client],
147
+ mac_server: algorithms.hmac[:server],
148
+ compression_client: algorithms.compression[:client],
149
+ compression_server: algorithms.compression[:server]
150
+ }
151
+
152
+ session.close
153
+ rescue Net::SSH::AuthenticationFailed
154
+ # 认证失败是正常的,说明KEX已完成
155
+ puts "✅ 密钥交换成功 (认证阶段被拒绝)"
156
+ rescue => e
157
+ puts "⚠️ 算法探测警告: #{e.message}"
158
+ end
159
+ end
160
+
161
+ # 深度握手分析
162
+ def analyze_handshake
163
+ puts "\n🔍 分析密钥交换详情..."
164
+
165
+ socket = TCPSocket.new(@host, @port)
166
+
167
+ # 发送我们的Banner
168
+ socket.write("SSH-2.0-RubyDetector_1.0\r\n")
169
+
170
+ # 读取服务器Banner
171
+ banner = socket.gets
172
+ puts " 服务器回应: #{banner&.chomp}"
173
+
174
+ socket.close
175
+ rescue => e
176
+ puts "⚠️ 握手分析受限: #{e.message}"
177
+ end
178
+
179
+ # 显示结果
180
+ def display_results
181
+ puts "\n" + "=" * 60
182
+ puts "📊 检测结果汇总"
183
+ puts "=" * 60
184
+
185
+ if @results[:protocol_version]
186
+ puts "\n🌐 协议版本: SSH-#{@results[:protocol_version]}"
187
+ puts "🖥️ 软件版本: #{@results[:software_version]}"
188
+ end
189
+
190
+ if @results[:negotiated]
191
+ neg = @results[:negotiated]
192
+ puts "\n🤝 协商成功的算法组合:"
193
+ puts " 密钥交换: #{neg[:kex]}"
194
+ puts " 主机密钥: #{neg[:host_key]}"
195
+ puts " 加密算法(客户端→服务端): #{neg[:encryption_client]}"
196
+ puts " 加密算法(服务端→客户端): #{neg[:encryption_server]}"
197
+ puts " MAC算法(客户端→服务端): #{neg[:mac_client]}"
198
+ puts " MAC算法(服务端→客户端): #{neg[:mac_server]}"
199
+ puts " 压缩(客户端→服务端): #{neg[:compression_client]}"
200
+ puts " 压缩(服务端→客户端): #{neg[:compression_server]}"
201
+ end
202
+
203
+ puts "\n🔐 安全建议:"
204
+ check_security
205
+ end
206
+
207
+ # 安全检查
208
+ def check_security
209
+ neg = @results[:negotiated]
210
+ return unless neg
211
+
212
+ # 检查弱算法
213
+ weak_kex = ["diffie-hellman-group1-sha1", "diffie-hellman-group14-sha1"]
214
+ weak_cipher = ["3des-cbc", "blowfish-cbc", "cast128-cbc", "arcfour", "arcfour128", "arcfour256", "aes256-cbc", "aes192-cbc", "aes128-cbc"]
215
+ weak_mac = ["hmac-md5", "hmac-md5-96", "hmac-sha1-96"]
216
+
217
+ warnings = []
218
+
219
+ if weak_kex.include?(neg[:kex])
220
+ warnings << "⚠️ 使用弱密钥交换算法: #{neg[:kex]}"
221
+ end
222
+
223
+ if weak_cipher.include?(neg[:encryption_client]) || weak_cipher.include?(neg[:encryption_server])
224
+ warnings << "⚠️ 使用弱加密算法"
225
+ end
226
+
227
+ if weak_mac.include?(neg[:mac_client]) || weak_mac.include?(neg[:mac_server])
228
+ warnings << "⚠️ 使用弱MAC算法"
229
+ end
230
+
231
+ if warnings.empty?
232
+ puts " ✅ 算法配置安全"
233
+ else
234
+ warnings.each { |w| puts " #{w}" }
235
+ end
236
+
237
+ # 推荐配置
238
+ puts "\n💡 推荐算法:"
239
+ puts " KEX: curve25519-sha256, ecdh-sha2-nistp521"
240
+ puts " Cipher: chacha20-poly1305@openssh.com, aes256-gcm@openssh.com"
241
+ puts " MAC: hmac-sha2-256-etm@openssh.com"
242
+ end
243
+ end
244
+
245
+ # 命令行接口
246
+ if __FILE__ == $0
247
+ if ARGV.empty?
248
+ puts "用法: ruby ssh_detector.rb <host> [port]"
249
+ puts "示例: ruby ssh_detector.rb github.com"
250
+ puts " ruby ssh_detector.rb 192.168.1.1 2222"
251
+ exit 1
252
+ end
253
+
254
+ host = ARGV[0]
255
+ port = (ARGV[1] || 22).to_i
256
+
257
+ detector = SSHProtocolDetector.new(host, port)
258
+ detector.detect
259
+ end
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env ruby
2
+ # SSH协议检测器 (简化实用版)
3
+ # 功能: 检测SSH服务器支持的KEX/Cipher/MAC/HostKey算法
4
+ # 协议: 实现SSH-TRANS传输层协议 (RFC 4253)
5
+
6
+ require 'socket'
7
+ require 'timeout'
8
+
9
+ class SSHDetector
10
+ # SSH消息类型
11
+ SSH_MSG_KEXINIT = 20
12
+
13
+ # 测试用的算法列表
14
+ TEST_ALGORITHMS = {
15
+ kex: "curve25519-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512",
16
+ host_key: "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-rsa",
17
+ cipher: "chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr",
18
+ mac: "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512",
19
+ compression: "none,zlib@openssh.com"
20
+ }
21
+
22
+ def initialize(host, port = 22)
23
+ @host = host
24
+ @port = port
25
+ @socket = nil
26
+ end
27
+
28
+ def detect
29
+ puts "\n🔍 SSH协议检测: #{@host}:#{@port}"
30
+ puts "=" * 60
31
+
32
+ connect
33
+ banner = exchange_banner
34
+ kex_data = perform_kex_init
35
+
36
+ if kex_data
37
+ display_results(banner, kex_data)
38
+ else
39
+ puts "❌ 无法完成密钥交换"
40
+ end
41
+ rescue => e
42
+ puts "❌ 错误: #{e.message}"
43
+ ensure
44
+ @socket&.close
45
+ end
46
+
47
+ private
48
+
49
+ def connect
50
+ @socket = TCPSocket.new(@host, @port)
51
+ puts "✅ TCP连接成功"
52
+ end
53
+
54
+ def exchange_banner
55
+ # 发送客户端Banner
56
+ @socket.write("SSH-2.0-RubyDetector_1.0\r\n")
57
+
58
+ # 读取服务器Banner
59
+ banner = @socket.gets&.chomp
60
+ puts "📢 服务器Banner: #{banner}"
61
+
62
+ # 检查版本
63
+ if banner =~ /SSH-(\d+\.\d+)-(.+)/
64
+ version = $1
65
+ software = $2
66
+ puts " 协议版本: SSH-#{version}"
67
+ puts " 软件版本: #{software}"
68
+
69
+ if version.start_with?("1.")
70
+ puts "⚠️ 警告: 服务器支持SSH v1(不安全)"
71
+ end
72
+ end
73
+
74
+ banner
75
+ end
76
+
77
+ def perform_kex_init
78
+ puts "\n🔬 执行密钥交换探测..."
79
+
80
+ # 构造KEX_INIT包
81
+ send_kex_init
82
+
83
+ # 接收服务器KEX_INIT
84
+ payload = recv_packet
85
+ return nil unless payload
86
+
87
+ # 解析算法列表
88
+ parse_kex_init(payload)
89
+ end
90
+
91
+ def send_kex_init
92
+ # 构造KEXINIT payload
93
+ payload = [SSH_MSG_KEXINIT].pack("C") # 消息类型
94
+ payload += "\x00" * 16 # cookie (16字节随机,这里简化)
95
+
96
+ # 添加算法列表 (name-list: uint32长度 + 字符串)
97
+ TEST_ALGORITHMS.each do |_, algos|
98
+ payload += [algos.length].pack("N") + algos
99
+ end
100
+
101
+ # 语言列表 (空)
102
+ payload += [0].pack("N") # languages_client_to_server
103
+ payload += [0].pack("N") # languages_server_to_client
104
+ payload += [0].pack("C") # first_kex_packet_follows = false
105
+ payload += [0].pack("N") # 保留
106
+
107
+ # 包装成SSH包
108
+ send_packet(payload)
109
+ end
110
+
111
+ def send_packet(payload)
112
+ block_size = 8
113
+ min_padding = 4
114
+
115
+ payload_len = payload.length
116
+ padding_len = block_size - ((payload_len + 5) % block_size)
117
+ padding_len += block_size if padding_len < min_padding
118
+
119
+ packet_len = payload_len + padding_len + 1
120
+ packet = [packet_len].pack("N")
121
+ packet += [padding_len].pack("C")
122
+ packet += payload
123
+ packet += "\x00" * padding_len
124
+
125
+ @socket.write(packet)
126
+ end
127
+
128
+ def recv_packet
129
+ # 读取包长度
130
+ len_bytes = @socket.read(4)
131
+ return nil unless len_bytes && len_bytes.length == 4
132
+
133
+ packet_len = len_bytes.unpack1("N")
134
+ return nil if packet_len > 35000
135
+
136
+ # 读取包内容
137
+ packet = @socket.read(packet_len)
138
+ return nil unless packet && packet.length == packet_len
139
+
140
+ # 解析: padding_len(1) + payload + padding
141
+ padding_len = packet[0].unpack1("C")
142
+ packet[1..-(padding_len+1)]
143
+ end
144
+
145
+ def parse_kex_init(payload)
146
+ return nil if payload.nil? || payload.empty?
147
+
148
+ msg_type = payload[0].unpack1("C")
149
+ return nil unless msg_type == SSH_MSG_KEXINIT
150
+
151
+ # 跳过16字节cookie
152
+ offset = 17
153
+
154
+ # 读取name-list的辅助函数
155
+ read_list = lambda {
156
+ return nil if offset + 4 > payload.length
157
+ len = payload[offset, 4].unpack1("N")
158
+ offset += 4
159
+ return nil if offset + len > payload.length
160
+ str = payload[offset, len]
161
+ offset += len
162
+ str.split(",")
163
+ }
164
+
165
+ {
166
+ kex: read_list.call,
167
+ host_key: read_list.call,
168
+ cipher_c2s: read_list.call,
169
+ cipher_s2c: read_list.call,
170
+ mac_c2s: read_list.call,
171
+ mac_s2c: read_list.call,
172
+ compress_c2s: read_list.call,
173
+ compress_s2c: read_list.call
174
+ }
175
+ end
176
+
177
+ def display_results(banner, data)
178
+ puts "\n" + "=" * 60
179
+ puts "📊 检测结果"
180
+ puts "=" * 60
181
+
182
+ puts "\n🔑 密钥交换算法 (KEX) - #{data[:kex]&.length || 0}种:"
183
+ data[:kex]&.each_slice(4) { |slice| puts " • #{slice.join(' • ')}" }
184
+
185
+ puts "\n🔐 主机密钥算法 (Host Key) - #{data[:host_key]&.length || 0}种:"
186
+ data[:host_key]&.each_slice(4) { |slice| puts " • #{slice.join(' • ')}" }
187
+
188
+ puts "\n🔒 加密算法 (Cipher) - #{data[:cipher_c2s]&.length || 0}种:"
189
+ data[:cipher_c2s]&.each_slice(3) { |slice| puts " • #{slice.join(' • ')}" }
190
+
191
+ puts "\n📝 MAC算法 - #{data[:mac_c2s]&.length || 0}种:"
192
+ data[:mac_c2s]&.each_slice(3) { |slice| puts " • #{slice.join(' • ')}" }
193
+
194
+ puts "\n📦 压缩算法 - #{data[:compress_c2s]&.length || 0}种:"
195
+ puts " • #{data[:compress_c2s]&.join(' • ')}"
196
+
197
+ # 安全分析
198
+ analyze_security(data)
199
+
200
+ puts "\n📋 SSH协议栈结构:"
201
+ puts " ┌─────────────────────────────────────┐"
202
+ puts " │ SSH-CONN (连接层) - 端口转发/会话 │"
203
+ puts " ├─────────────────────────────────────┤"
204
+ puts " │ SSH-AUTH (认证层) - 用户认证 │"
205
+ puts " ├─────────────────────────────────────┤"
206
+ puts " │ SSH-TRANS (传输层) │"
207
+ puts " │ ├── KEX: #{data[:kex]&.first || 'N/A'}"[0..50]
208
+ puts " │ ├── Cipher: #{data[:cipher_c2s]&.first || 'N/A'}"[0..50]
209
+ puts " │ ├── MAC: #{data[:mac_c2s]&.first || 'N/A'}"[0..50]
210
+ puts " │ └── Compression: #{data[:compress_c2s]&.first || 'none'}"[0..50]
211
+ puts " └─────────────────────────────────────┘"
212
+ end
213
+
214
+ def analyze_security(data)
215
+ puts "\n🔐 安全分析:"
216
+
217
+ weak_algos = {
218
+ "diffie-hellman-group1-sha1" => "使用SHA1的DH组1(非常弱)",
219
+ "diffie-hellman-group14-sha1" => "使用SHA1的DH组14",
220
+ "3des-cbc" => "3DES加密(慢且不安全)",
221
+ "blowfish-cbc" => "Blowfish(已废弃)",
222
+ "arcfour" => "RC4(严重不安全)",
223
+ "hmac-md5" => "MD5哈希(不安全)",
224
+ "hmac-sha1" => "SHA1(考虑升级)"
225
+ }
226
+
227
+ issues = []
228
+ all_algos = (data[:kex] || []) + (data[:cipher_c2s] || []) + (data[:mac_c2s] || [])
229
+
230
+ weak_algos.each do |algo, desc|
231
+ if all_algos.any? { |a| a.include?(algo) }
232
+ issues << "⚠️ 发现弱算法 #{algo}: #{desc}"
233
+ end
234
+ end
235
+
236
+ if issues.empty?
237
+ puts " ✅ 未发现明显的弱算法"
238
+ else
239
+ issues.each { |i| puts " #{i}" }
240
+ end
241
+
242
+ # 推荐
243
+ puts "\n💡 推荐配置:"
244
+ puts " KEX: curve25519-sha256, ecdh-sha2-nistp521"
245
+ puts " Cipher: chacha20-poly1305@openssh.com, aes256-gcm@openssh.com"
246
+ puts " MAC: hmac-sha2-256-etm@openssh.com"
247
+ end
248
+ end
249
+
250
+ # 命令行入口
251
+ if __FILE__ == $0
252
+ if ARGV.empty?
253
+ puts "SSH协议检测器 (Ruby实现)"
254
+ puts "=" * 50
255
+ puts "用法: ruby ssh_detector.rb <主机> [端口]"
256
+ puts ""
257
+ puts "示例:"
258
+ puts " ruby ssh_detector.rb github.com"
259
+ puts " ruby ssh_detector.rb 192.168.1.100 2222"
260
+ puts ""
261
+ puts "说明:"
262
+ puts " • 实现SSH-TRANS传输层协议 (RFC 4253)"
263
+ puts " • 探测KEX/Cipher/MAC/HostKey算法支持"
264
+ puts " • 识别弱算法和安全风险"
265
+ puts " • 无需外部gem依赖"
266
+ exit 1
267
+ end
268
+
269
+ host = ARGV[0]
270
+ port = (ARGV[1] || 22).to_i
271
+
272
+ SSHDetector.new(host, port).detect
273
+ end