pstream 0.1.7 → 0.1.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2afaa0231c97802387625086c64da95b5ce114c2
4
- data.tar.gz: 6e187acbce7c6c027faafacdcf1cd732f63e9ecf
3
+ metadata.gz: 1ee81e7ea543491338bb800254b0b2cc4c0850c3
4
+ data.tar.gz: d877d6ff3c9deca5cc7b3ef7e07531e9e1f587de
5
5
  SHA512:
6
- metadata.gz: becd5a9ac9389461223797903374a9b4d8d26384748bacd8f2d1bd4dbb512df26ed0b3a6d525d6407f36040dfb15c7230e52f188f5c26b62b2b504a2fcb832e1
7
- data.tar.gz: 3a1f74c8c74442bb8b8a31743bdb2295b31686592d802a5992e5d1fadc4c9fd2d638719d598ddda6c40078f9a591b4c37daa2ac34062433a872160714da8b4d9
6
+ metadata.gz: f65be907ced9b779793e85607d5b0e7a80306d8527dd7981890c0585da482cd4ab35a2de2ede23aa53b5c73b3ed224ec02e20aa76bc6c6a7f4a5a42d23ee5447
7
+ data.tar.gz: 6cace69f3b2e5eaa02ffc81dffb7a90bda72cd9dfe6413547f532f4ed289916fa2ca92558853679a9e442e2858300963e456e7e75eb7e628f351641174f0cdca
data/bin/pstream CHANGED
@@ -92,51 +92,23 @@ end
92
92
  options = parse(ARGV)
93
93
 
94
94
  begin
95
- pstream = PStream.new(options["pcap"])
95
+ pstream = PStream.new(
96
+ options["pcap"],
97
+ !String.disable_colorization
98
+ )
96
99
 
97
100
  if (options["stream"])
98
- pstream.get_stream(
101
+ puts pstream.get_stream(
99
102
  options["stream"].to_i,
100
103
  options["prot"]
101
- ).contents.split("\n").each do |line|
102
- m = line.match(/([0-9A-Fa-f]{8}) (.*) (.{17})/)
103
- puts [
104
- m[1].light_blue,
105
- m[2].light_green,
106
- m[3].white
107
- ].join(" ")
108
- end
104
+ ).to_s
109
105
  elsif (options["ciphers"])
110
- puts pstream.ciphers
111
- else
112
- pstream.to_s.split("\n").each do |line|
113
- case line
114
- when /.*:$/
115
- # Headers
116
- puts line.white
117
- when /<->/
118
- # Streams
119
- m = line.match(/([0-9]+) \| (.+) \| ([0-9]+ Frames)/)
120
- puts [
121
- m[1].light_blue,
122
- m[2].light_green,
123
- m[3].light_white
124
- ].join(" | ")
125
- else
126
- case line
127
- when /Unknown/
128
- puts line.light_yellow
129
- when /NULL|MD5|RC4|anon/
130
- # Bad cipher suites
131
- puts line.light_red
132
- when /E?(EC)?DHE?|AES_256/
133
- # Great cipher suites
134
- puts line.light_green
135
- else
136
- puts line.white
137
- end
138
- end
106
+ pstream.cipher_negotiations.each do |negotiation|
107
+ puts negotiation.to_s
139
108
  end
109
+ else
110
+ # Summarize
111
+ puts pstream.to_s
140
112
  end
141
113
  rescue PStream::Error => e
142
114
  $stderr.puts e.message.red
data/lib/pstream.rb CHANGED
@@ -4,13 +4,115 @@ require "scoobydoo"
4
4
  class PStream
5
5
  attr_reader :streams
6
6
 
7
- def ciphers
7
+ def cipher_negotiations
8
+ negotiations = Hash.new
9
+
8
10
  # List ciphers during ssl handshake
9
11
  out = %x(
10
12
  tshark -r #{@pcap} -Y ssl.handshake.ciphersuite -V 2>&1 \
11
- | \grep -E "Internet Protocol|Hostname:|Cipher Suite"
13
+ | \grep -E "(Handshake|Internet) Prot|Cipher Suite"
12
14
  )
13
- return out
15
+
16
+ negotiation = nil
17
+ hello = nil
18
+ out.split("\n").each do |line|
19
+ case line.gsub(/^ +/, "")
20
+ when /^Cipher Suite:/
21
+ m = line.match(/Cipher Suite: ([^ ]+) (.*)$/)
22
+ case hello
23
+ when "Client"
24
+ case m[1]
25
+ when "Unknown"
26
+ negotiation.suites.push("#{m[1]} #{m[2]}")
27
+ else
28
+ negotiation.suites.push(m[1])
29
+ end
30
+ when "Server"
31
+ id = "#{negotiation.dst} <-> #{negotiation.src}"
32
+ # Ignore partial handshakes that are server side
33
+ # only
34
+ if (negotiations[id])
35
+ case m[1]
36
+ when "Unknown"
37
+ negotiations[id].suite = "#{m[1]} #{m[2]}"
38
+ else
39
+ negotiations[id].suite = m[1]
40
+ end
41
+ end
42
+ negotiation = nil
43
+ end
44
+ when /^Cipher Suites Length:/
45
+ m = line.match(/Cipher Suites Length: ([0-9]+)$/)
46
+ negotiation.length = m[1].to_i
47
+ when /^Handshake Protocol:/
48
+ m = line.match(/Handshake Protocol: ([^ ]+) Hello$/)
49
+ hello = m[1]
50
+ when /^Internet Protocol Version/
51
+ if (negotiation)
52
+ id = "#{negotiation.src} <-> #{negotiation.dst}"
53
+ negotiations[id] = negotiation
54
+ end
55
+
56
+ m = line.gsub("Internet Protocol Version", "").match(
57
+ /(4|6), Src: ([^,]+), Dst: (.*)$/
58
+ )
59
+
60
+ ipv = m[1]
61
+ src = m[2]
62
+ dst = m[3]
63
+
64
+ negotiation = PStream::CipherNegotiation.new(
65
+ self,
66
+ ipv,
67
+ src,
68
+ dst,
69
+ @colorize
70
+ )
71
+ end
72
+ end
73
+
74
+ # Keep parital handshakes that are client side only
75
+ if (negotiation)
76
+ id = "#{negotiation.src} <-> #{negotiation.dst}"
77
+ negotiations[id] = negotiation
78
+ end
79
+
80
+ return negotiations.values
81
+ end
82
+
83
+ def colorize_cipher_suite(suite)
84
+ return suite if (!@colorize)
85
+
86
+ case suite
87
+ when /Unknown/
88
+ # Unknown
89
+ return suite.light_yellow
90
+ when /NULL|MD5|RC4|anon/
91
+ # Bad cipher suites
92
+ return suite.light_red
93
+ when /E?(EC)?DHE?|AES_256/
94
+ # Great cipher suites
95
+ return suite.light_green
96
+ else
97
+ # Maybe OK
98
+ return suite.light_white
99
+ end
100
+ end
101
+
102
+ def colorize_header(header)
103
+ return header if (!@colorize)
104
+ return header.light_cyan
105
+ end
106
+
107
+ def colorize_stream(stream)
108
+ if (!@colorize)
109
+ return "#{stream.id} | #{stream.desc} | #{stream.frames}"
110
+ end
111
+ return [
112
+ "#{stream.id}".light_blue,
113
+ stream.desc.light_green,
114
+ stream.frames.light_white
115
+ ].join(" | ")
14
116
  end
15
117
 
16
118
  def get_stream(stream, prot = "tcp")
@@ -60,7 +162,16 @@ class PStream
60
162
  count = 0
61
163
  out.split("\n").each do |line|
62
164
  desc, frames = line.split(" | ")
63
- streams.push(Stream.new(@pcap, prot, count, desc, frames))
165
+ streams.push(
166
+ Stream.new(
167
+ @pcap,
168
+ prot,
169
+ count,
170
+ desc,
171
+ frames,
172
+ @colorize
173
+ )
174
+ )
64
175
  count += 1
65
176
  end
66
177
 
@@ -68,7 +179,9 @@ class PStream
68
179
  end
69
180
  private :get_streams
70
181
 
71
- def initialize(pcap)
182
+ def initialize(pcap, colorize = false)
183
+ @colorize = colorize
184
+
72
185
  if (ScoobyDoo.where_are_you("tshark").nil?)
73
186
  raise PStream::Error::TsharkNotFound.new
74
187
  end
@@ -87,31 +200,23 @@ class PStream
87
200
  end
88
201
  end
89
202
 
90
- def negotiated_ciphers
91
- f = "ssl.handshake.ciphersuite && ssl.handshake.type == 2"
92
- out = %x(
93
- tshark -r #{@pcap} -Y "#{f}" -V 2>&1 | \
94
- \grep -E "Cipher Suite:" | \
95
- sed -r "s|^ +Cipher Suite: ||g" | sort -u
96
- )
97
- return out.split("\n")
98
- end
99
-
100
203
  def summary
101
204
  ret = Array.new
102
205
 
103
206
  # List streams
104
207
  ["tcp", "udp"].each do |prot|
105
- ret.push("#{prot} streams:")
106
- @streams[prot].each do |s|
107
- ret.push("#{s.id} | #{s.desc} | #{s.frames}")
208
+ ret.push(colorize_header("#{prot} streams:"))
209
+ @streams[prot].each do |stream|
210
+ ret.push(colorize_stream(stream))
108
211
  end
109
212
  ret.push("")
110
213
  end
111
214
 
112
215
  # List ciphers that were actually selected
113
- ret.push("Ciphers in use:")
114
- ret.concat(negotiated_ciphers)
216
+ ret.push(colorize_header("Ciphers in use:"))
217
+ cipher_negotiations.each do |negotiation|
218
+ ret.push(colorize_cipher_suite(negotiation.suite))
219
+ end
115
220
 
116
221
  return ret.join("\n")
117
222
  end
@@ -122,5 +227,6 @@ class PStream
122
227
  end
123
228
  end
124
229
 
230
+ require "pstream/cipher_negotiation"
125
231
  require "pstream/error"
126
232
  require "pstream/stream"
@@ -0,0 +1,56 @@
1
+ class PStream::CipherNegotiation
2
+ attr_accessor :length
3
+ attr_accessor :suite
4
+ attr_accessor :suites
5
+
6
+ attr_reader :dst
7
+ attr_reader :ipv
8
+ attr_reader :pstream
9
+ attr_reader :src
10
+
11
+ def colorize_hosts(src, dst)
12
+ return "#{src} <-> #{dst}" if (!@colorize)
13
+ return "#{src} <-> #{dst}".light_cyan
14
+ end
15
+
16
+ def colorize_ipv(ipv)
17
+ return "IPv#{ipv}" if (!@colorize)
18
+ return "IPv#{ipv}".light_cyan
19
+ end
20
+
21
+ def colorize_selected_suite(suite)
22
+ return [
23
+ "Selected".light_blue,
24
+ @pstream.colorize_cipher_suite(suite),
25
+ "from:".light_blue
26
+ ].join(" ")
27
+ end
28
+
29
+ def initialize(pstream, ipv, src, dst, colorize)
30
+ @colorize = colorize
31
+ @dst = dst
32
+ @ipv = ipv
33
+ @length = nil
34
+ @pstream = pstream
35
+ @src = src
36
+ @suite = nil
37
+ @suites = Array.new
38
+ end
39
+
40
+ def summary
41
+ ret = Array.new
42
+ ret.push(
43
+ "#{colorize_ipv(@ipv)} #{colorize_hosts(@src, @dst)}"
44
+ )
45
+ ret.push(" #{colorize_selected_suite(@suite)}") if (@suite)
46
+ @suites.each do |suite|
47
+ ret.push(" #{@pstream.colorize_cipher_suite(suite)}")
48
+ end
49
+
50
+ return ret.join("\n")
51
+ end
52
+
53
+ def to_s
54
+ return summary
55
+ end
56
+ end
@@ -3,24 +3,51 @@ class PStream::Stream
3
3
  attr_reader :frames
4
4
  attr_reader :id
5
5
 
6
+ def colorize_address(address)
7
+ return address if (!@colorize)
8
+ return address.light_blue
9
+ end
10
+
11
+ def colorize_ascii(ascii)
12
+ return ascii if (!@colorize)
13
+ return ascii.light_white
14
+ end
15
+
16
+ def colorize_hex(hex)
17
+ return hex if (!@colorize)
18
+ return hex.light_green
19
+ end
20
+
6
21
  def contents
7
22
  case @prot
8
23
  when /^tcp$/i
9
- id=@id
24
+ stream=@id
10
25
  when /^udp$/i
11
- id=@desc.gsub(" <-> ", ",")
26
+ stream=@desc.gsub(" <-> ", ",")
12
27
  else
13
28
  raise PStream::Error::ProtocolNotSupported.new(@prot)
14
29
  end
15
30
 
16
- out = %x(
17
- tshark -r #{@pcap} -z follow,#{@prot},hex,#{id} | \
31
+ ret = Array.new
32
+ %x(
33
+ tshark -r #{@pcap} -z follow,#{@prot},hex,#{stream} | \
18
34
  sed "s|^ ||" | \grep -E "^[0-9A-Fa-f]{8}"
19
- )
20
- return out
35
+ ).split("\n").each do |line|
36
+ m = line.match(/([0-9A-Fa-f]{8}) (.*) (.{17})/)
37
+ ret.push(
38
+ [
39
+ colorize_address(m[1]),
40
+ colorize_hex(m[2]),
41
+ colorize_ascii(m[3])
42
+ ].join(" ")
43
+ )
44
+ end
45
+
46
+ return ret.join("\n")
21
47
  end
22
48
 
23
- def initialize(pcap, prot, id, desc, frames)
49
+ def initialize(pcap, prot, id, desc, frames, colorize = false)
50
+ @colorize = colorize
24
51
  @desc = desc
25
52
  @frames = frames
26
53
  @id = id
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pstream
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Whittaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-04 00:00:00.000000000 Z
11
+ date: 2016-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -59,6 +59,7 @@ extra_rdoc_files: []
59
59
  files:
60
60
  - bin/pstream
61
61
  - lib/pstream.rb
62
+ - lib/pstream/cipher_negotiation.rb
62
63
  - lib/pstream/error.rb
63
64
  - lib/pstream/error/pcap_not_found.rb
64
65
  - lib/pstream/error/pcap_not_readable.rb