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.
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/README.md +34 -0
- data/Rakefile +47 -0
- data/bin/seeker-junos +11 -0
- data/lib/seeker.rb +15 -0
- data/lib/seeker/cli.rb +35 -0
- data/lib/seeker/junos.rb +109 -0
- data/lib/seeker/junos/cli.rb +17 -0
- data/lib/seeker/ssh.rb +64 -0
- data/seeker-junos.gemspec +18 -0
- metadata +111 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/bin/seeker-junos
ADDED
data/lib/seeker.rb
ADDED
data/lib/seeker/cli.rb
ADDED
@@ -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
|
data/lib/seeker/junos.rb
ADDED
@@ -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
|
data/lib/seeker/ssh.rb
ADDED
@@ -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: []
|