seeker-junos 0.0.5

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.
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ gems
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,34 @@
1
+ # Seeker JunOS
2
+ Finds hidden commands from JunOS devices by brute-forcing commands over SSH
3
+
4
+ ## Install
5
+ ```
6
+ % gem install seeker-junos
7
+ % cat >> ~/.netrc
8
+ machine Seeker
9
+ login <username>
10
+ password <passwrod>
11
+ ^d
12
+ ```
13
+
14
+ ## Run
15
+ ```
16
+ % seeker-junos --level routing-options 193.88.239.31
17
+ I, [2014-03-22T13:15:09.320710 #66906] INFO -- : starting
18
+ I, [2014-03-22T13:25:09.544325 #66906] INFO -- : now at 'inhed', found 0 so far
19
+ I, [2014-03-22T13:29:00.236750 #66906] INFO -- : Found: 'kernel-options'
20
+ I, [2014-03-22T13:31:59.445597 #66906] INFO -- : Found: 'maximum-routes'
21
+ I, [2014-03-22T13:34:41.146006 #66906] INFO -- : Found: 'max-interface-supported'
22
+ I, [2014-03-22T13:35:10.340298 #66906] INFO -- : now at 'max-interface-supps', found 3 so far
23
+ I, [2014-03-22T13:39:07.812630 #66906] INFO -- : Found: 'nsr-phantom-holdtime'
24
+ I, [2014-03-22T13:43:15.168970 #66906] INFO -- : Found: 'rpd-server'
25
+ I, [2014-03-22T13:45:09.499291 #66906] INFO -- : now at 'task-accounc', found 5 so far
26
+ I, [2014-03-22T13:45:31.663623 #66906] INFO -- : Found: 'task-accounting'
27
+ I, [2014-03-22T13:47:18.411653 #66906] INFO -- : Found: 'task-scheduling'
28
+ I, [2014-03-22T13:49:00.204509 #66906] INFO -- : finishing, took 33.84805928741667 minutes
29
+ ["kernel-options", "maximum-routes", "max-interface-supported", "nsr-phantom-holdtime", "rpd-server", "task-accounting", "task-scheduling"]
30
+ ```
31
+ Reports progress every 10min, outputs list of found commands when finished
32
+
33
+ ## Todo
34
+ * Full recurse, not just single level. Not sure if sane, as it already takes very fscking long to run
@@ -0,0 +1,47 @@
1
+ begin
2
+ require 'rake/testtask'
3
+ require 'bundler'
4
+ Bundler.setup
5
+ rescue LoadError
6
+ warn 'bunler missing'
7
+ exit 42
8
+ end
9
+
10
+ gemspec = eval(File.read(Dir['*.gemspec'].first))
11
+ file = [gemspec.name, gemspec.version].join('-') + '.gem'
12
+
13
+ desc 'Validate gemspec'
14
+ task :gemspec do
15
+ gemspec.validate
16
+ end
17
+
18
+ desc 'Run minitest'
19
+ task :test do
20
+ Rake::TestTask.new do |t|
21
+ t.libs.push "lib"
22
+ t.test_files = FileList['spec/*_spec.rb']
23
+ t.verbose = true
24
+ end
25
+ end
26
+
27
+ desc 'Build gem'
28
+ task :build do
29
+ system "gem build #{gemspec.name}.gemspec"
30
+ FileUtils.mkdir_p 'gems'
31
+ FileUtils.mv file, 'gems'
32
+ end
33
+
34
+ desc 'Install gem'
35
+ task :install => :build do
36
+ system "sudo -E sh -c \'umask 022; gem install gems/#{file}\'"
37
+ end
38
+
39
+ desc 'Remove gems'
40
+ task :clean do
41
+ FileUtils.rm_rf 'gems'
42
+ end
43
+
44
+ desc 'Push to rubygems'
45
+ task :push do
46
+ system "gem push gems/#{file}"
47
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby20
2
+
3
+ $: << '/home/fisakytt/tmp/seeker/lib'
4
+
5
+ begin
6
+ require 'seeker/junos/cli'
7
+ p Seeker::JunosCLI.new.result
8
+ rescue => error
9
+ warn "#{error}"
10
+ raise if Seeker.debug > 0
11
+ end
@@ -0,0 +1,15 @@
1
+ require 'logger'
2
+
3
+ module Seeker
4
+ class SeekerError < StandardError; end
5
+ class << self
6
+ def debug
7
+ @debug
8
+ end
9
+ def debug= arg
10
+ @debug=arg
11
+ end
12
+ end
13
+ Log = Logger.new STDOUT
14
+ Log.level = Logger::INFO
15
+ end
@@ -0,0 +1,35 @@
1
+ require_relative '../seeker'
2
+ require 'slop'
3
+ require 'net/netrc'
4
+
5
+ module Seeker
6
+ class CLI
7
+ NETRC_MACHINE = 'Seeker'
8
+ attr_reader :result
9
+ class NoAuthFound < SeekerError; end
10
+ class MissingHost < SeekerError; end
11
+
12
+ private
13
+ def initialize
14
+ @opts = opts_parse
15
+ if not @opts.present? :debug
16
+ Seeker.debug = 0
17
+ elsif @opts[:debug]
18
+ Seeker.debug = @opts[:debug].to_i
19
+ else
20
+ Seeker.debug = 1
21
+ end
22
+ Log.level = Logger::DEBUG if Seeker.debug > 0
23
+ args = @opts.parse
24
+ @host = args.first
25
+ raise MissingHost, 'no host given as argument' unless @host
26
+ @user, @password = netrc_get
27
+ @result = seek
28
+ end
29
+
30
+ def netrc_get machine=NETRC_MACHINE
31
+ auth = Net::Netrc.locate machine
32
+ [auth.login, auth.password] rescue raise NoAuthFound, "could not find authentication for #{machine} in .netrc"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,109 @@
1
+ require_relative '../seeker'
2
+ require_relative 'ssh'
3
+
4
+ module Seeker
5
+ class Junos
6
+ KNOWN = %w( apply-flags apply-macro inherit )
7
+ CHARS = ('a'..'z').to_a + ('0'..'9').to_a + %w( - )
8
+ REPORT_INTERVAL = 10*60
9
+ INVALID_INTERFACES = [
10
+ /^error: invalid interface type/,
11
+ /^error: missing or invalid (?:device|fpc) number/,
12
+ ]
13
+
14
+ def seek
15
+ @ssh = ssh_connect
16
+ @ssh.cmd 'configure'
17
+ start = Time.now
18
+ @report = start+REPORT_INTERVAL
19
+ Log.info "starting"
20
+ seek_level @opts[:level]
21
+ Log.info "finishing, took #{(Time.now - start)/60} minutes"
22
+ @ssh.cmd 'rollback'
23
+ @ssh.cmd 'exit'
24
+ @ssh.close
25
+ @found
26
+ end
27
+
28
+ def seek_level level
29
+ @ssh.cmd 'edit ' + level if level
30
+ @known = known_get
31
+ find_hidden
32
+ @ssh.cmd 'top'
33
+ end
34
+
35
+ private
36
+
37
+ def find_hidden
38
+ CHARS.each do |c|
39
+ cmd = @cmd.join + c
40
+ if Time.now > @report
41
+ Log.info "now at '#{cmd}', found #{@found.size} so far"
42
+ @report += REPORT_INTERVAL
43
+ end
44
+ next if @known.include? cmd
45
+ o = @ssh.cmd 'set ' + cmd
46
+ if complete? o
47
+ Log.info "Found: '#{cmd}'"
48
+ @report += REPORT_INTERVAL
49
+ @found << cmd
50
+ elsif valid? o
51
+ @cmd << c
52
+ find_hidden
53
+ @cmd.pop
54
+ end
55
+ end
56
+ end
57
+
58
+ def valid? output
59
+ valid = true
60
+ output = output.split "\n"
61
+ output = output[1..-1]
62
+ if output[1].match(/^syntax error\./)
63
+ output = output.first.sub(/^(\s+).*/, '\1')
64
+ valid = false if output.size < @min_space
65
+ end
66
+ valid = false if INVALID_INTERFACES.any? {|re| output[0].match re}
67
+ valid
68
+ end
69
+
70
+ def complete? output
71
+ output = output.split "\n"
72
+ output = output[1..-1]
73
+ complete = true
74
+ complete = false if output[1].match(/^syntax error\./)
75
+ complete = false if output[1].match(/ is ambiguous\./)
76
+ complete = false if output[0].match(/^error: syntax error:/)
77
+ complete = false if INVALID_INTERFACES.any? {|re| output[0].match re}
78
+ complete
79
+ end
80
+
81
+ def known_get
82
+ o = @ssh.cmd 'set ?'
83
+ o = o.each_line.map do |line|
84
+ re = line.match(/^[\s>+] (\S+).*/)
85
+ re[1] if re
86
+ end
87
+ o.compact + KNOWN
88
+ end
89
+
90
+ def initialize host, user, password, opts
91
+ @host, @user, @password, @opts = host, user, password, opts
92
+ @ssh = nil
93
+ @known = nil
94
+ @min_space = nil
95
+ @report = nil
96
+ @cmd = []
97
+ @found = []
98
+ end
99
+
100
+ def ssh_connect
101
+ ssh = SSH.new @host, @user, @password
102
+ ssh.cmd 'set cli complete-on-space off'
103
+ ssh.cmd 'set cli screen-length 0'
104
+ @min_space = ssh.prompt_seen.size + 5
105
+ ssh
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../cli'
2
+ require_relative '../junos'
3
+ module Seeker
4
+ class JunosCLI < CLI
5
+ def opts_parse
6
+ opts = Slop.parse(:help=>true) do
7
+ banner 'Usage: junos-seeker [options] hostname'
8
+ on 'l=', 'level', 'Level to start from, e.g. chassis, system, routing-options'
9
+ on 'd', 'debug', 'Debug (optionally with level)', :optional_argument=>true
10
+ end
11
+ end
12
+
13
+ def seek
14
+ Junos.new(@host, @user, @password, @opts).seek
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,64 @@
1
+ require 'net/ssh'
2
+
3
+ module Seeker
4
+ class SSH
5
+ TIMEOUT = 5
6
+ PROMPT = /^[^\s>#]+[>#] /
7
+ SLEEP = 0.001
8
+ attr_reader :prompt_seen
9
+ class NoSshShell < SeekerError; end
10
+
11
+ def cmd command
12
+ Log.debug "SSH: #{command}"
13
+ @output = ''
14
+ @session.send_data command + "\n"
15
+ @session.process
16
+ expect @prompt
17
+ @output
18
+ end
19
+
20
+ def close command='exit'
21
+ @session.send_data command +"\n"
22
+ end
23
+
24
+ private
25
+
26
+ def initialize host, user, password, prompt=PROMPT
27
+ @output = ''
28
+ @prompt = prompt
29
+ @prompt_seen = nil
30
+ @session = nil
31
+ @ssh = Net::SSH.start host, user, :password=>password, :timeout=>TIMEOUT
32
+ shell_open
33
+ expect @prompt
34
+ end
35
+
36
+ def shell_open
37
+ @session = @ssh.open_channel do |channel|
38
+ channel.on_data do |channel, data|
39
+ $stderr.print data if Seeker.debug > 1
40
+ @output << data
41
+ end
42
+ channel.request_pty do |channel, success|
43
+ raise NoSshShell, "Can't get PTY" unless success
44
+ channel.send_channel_request 'shell' do |channel, success|
45
+ raise NoSshShell, "Can't get shell" unless success
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def expect regexp
52
+ Timeout::timeout(TIMEOUT) do
53
+ @ssh.loop(SLEEP) do
54
+ sleep SLEEP
55
+ re = @output.match regexp
56
+ @prompt_seen = re[0] if re
57
+ not re
58
+ end
59
+ end
60
+ end
61
+
62
+
63
+ end
64
+ end
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'seeker-junos'
3
+ s.version = '0.0.5'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.authors = [ 'Saku Ytti' ]
6
+ s.email = %w( saku@ytti.fi )
7
+ s.homepage = 'http://github.com/ytti/seeker-junos'
8
+ s.summary = 'find hidden commands in junos'
9
+ s.description = 'logs via ssh to junos devices and brute forces hidden passwords out'
10
+ s.rubyforge_project = s.name
11
+ s.files = `git ls-files`.split("\n")
12
+ s.executables = %w( seeker-junos )
13
+ s.require_path = 'lib'
14
+
15
+ s.add_dependency 'net-ssh'
16
+ s.add_dependency 'net-netrc'
17
+ s.add_dependency 'slop'
18
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: seeker-junos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Saku Ytti
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-ssh
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: net-netrc
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: slop
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: logs via ssh to junos devices and brute forces hidden passwords out
63
+ email:
64
+ - saku@ytti.fi
65
+ executables:
66
+ - seeker-junos
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - README.md
73
+ - Rakefile
74
+ - bin/seeker-junos
75
+ - lib/seeker.rb
76
+ - lib/seeker/cli.rb
77
+ - lib/seeker/junos.rb
78
+ - lib/seeker/junos/cli.rb
79
+ - lib/seeker/ssh.rb
80
+ - seeker-junos.gemspec
81
+ homepage: http://github.com/ytti/seeker-junos
82
+ licenses: []
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ segments:
94
+ - 0
95
+ hash: 526249442777840062
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ segments:
103
+ - 0
104
+ hash: 526249442777840062
105
+ requirements: []
106
+ rubyforge_project: seeker-junos
107
+ rubygems_version: 1.8.25
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: find hidden commands in junos
111
+ test_files: []