Unrestricted Upload of File with Dangerous Type in crater-invoice/crater
Description
crater is vulnerable to Unrestricted Upload of File with Dangerous Type
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Crater prior to commit cdc913d1 allows authenticated users to upload arbitrary PHP files, leading to remote code execution.
Vulnerability
Crater, an open-source invoicing application, is vulnerable to unrestricted file upload of dangerous types. The flaw resides in endpoints such as expense receipt upload, company logo upload, and avatar upload, which lacked proper file type validation before the fix [1][2]. Affected versions are those before the commit cdc913d16cf624aee852bc9163a7c6ffc8d1da9d.
Exploitation
An authenticated user with permission to update expenses, company settings, or profile can upload a malicious file (e.g., a PHP script) via the vulnerable upload forms. No additional user interaction is required beyond the attacker's own actions [1].
Impact
Successful exploitation allows arbitrary code execution on the server, potentially leading to full compromise of the application and its data. This can result in data theft, modification, or further attacks [2].
Mitigation
The vulnerability is fixed in commit cdc913d16cf624aee852bc9163a7c6ffc8d1da9d, which introduces proper validation via dedicated request classes (ExpenseRequest, AvatarRequest, CompanyLogoRequest). Users should update to the latest version that includes this patch [1][3]. If immediate update is not possible, restrict access to file upload endpoints and enforce strict file type validation at the web server level.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
bytefury/craterPackagist | < 6.0.0 | 6.0.0 |
Affected products
2- Range: unspecified
Patches
1cdc913d16cf6Unrestricted php file upload fix (#681)
7 files changed · +176 −8
app/Http/Controllers/V1/Admin/Expense/ExpensesController.php+2 −2 modified@@ -39,7 +39,7 @@ public function index(Request $request) /** * Store a newly created resource in storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\ExpenseRequest $request * @return \Illuminate\Http\JsonResponse */ public function store(ExpenseRequest $request) @@ -67,7 +67,7 @@ public function show(Expense $expense) /** * Update the specified resource in storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\ExpenseRequest $request * @param \Crater\Models\Expense $expense * @return \Illuminate\Http\JsonResponse */
app/Http/Controllers/V1/Admin/Expense/UploadReceiptController.php+3 −2 modified@@ -5,17 +5,18 @@ use Crater\Http\Controllers\Controller; use Crater\Models\Expense; use Illuminate\Http\Request; +use Crater\Http\Requests\ExpenseRequest; class UploadReceiptController extends Controller { /** * Upload the expense receipts to storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\ExpenseRequest $request * @param Expense $expense * @return \Illuminate\Http\JsonResponse */ - public function __invoke(Request $request, Expense $expense) + public function __invoke(ExpenseRequest $request, Expense $expense) { $this->authorize('update', $expense);
app/Http/Controllers/V1/Admin/Settings/CompanyController.php+6 −4 modified@@ -9,6 +9,8 @@ use Crater\Http\Resources\UserResource; use Crater\Models\Company; use Illuminate\Http\Request; +use Crater\Http\Requests\AvatarRequest; +use Crater\Http\Requests\CompanyLogoRequest; class CompanyController extends Controller { @@ -58,10 +60,10 @@ public function updateCompany(CompanyRequest $request) /** * Upload the company logo to storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\CompanyLogoRequest $request * @return \Illuminate\Http\JsonResponse */ - public function uploadCompanyLogo(Request $request) + public function uploadCompanyLogo(CompanyLogoRequest $request) { $company = Company::find($request->header('company')); @@ -89,10 +91,10 @@ public function uploadCompanyLogo(Request $request) /** * Upload the Admin Avatar to public storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\AvatarRequest $request * @return \Illuminate\Http\JsonResponse */ - public function uploadAvatar(Request $request) + public function uploadAvatar(AvatarRequest $request) { $user = auth()->user();
app/Http/Requests/AvatarRequest.php+40 −0 added@@ -0,0 +1,40 @@ +<?php + +namespace Crater\Http\Requests; + +use Crater\Rules\Base64Mime; +use Illuminate\Foundation\Http\FormRequest; + +class AvatarRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize() + { + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'admin_avatar' => [ + 'nullable', + 'file', + 'mimes:gif,jpg,png', + 'max:20000' + ], + 'avatar' => [ + 'nullable', + new Base64Mime(['gif', 'jpg', 'png']) + ] + ]; + } +}
app/Http/Requests/CompanyLogoRequest.php+34 −0 added@@ -0,0 +1,34 @@ +<?php + +namespace Crater\Http\Requests; + +use Crater\Rules\Base64Mime; +use Illuminate\Foundation\Http\FormRequest; + +class CompanyLogoRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize() + { + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'company_logo' => [ + 'nullable', + new Base64Mime(['gif', 'jpg', 'png']) + ] + ]; + } +}
app/Http/Requests/ExpenseRequest.php+6 −0 modified@@ -51,6 +51,12 @@ public function rules() 'currency_id' => [ 'required' ], + 'attachment_receipt' => [ + 'nullable', + 'file', + 'mimes:jpg,png,pdf,doc,docx,xls,xlsx,ppt,pptx', + 'max:20000' + ] ]; if ($companyCurrency && $this->currency_id) {
app/Rules/Base64Mime.php+85 −0 added@@ -0,0 +1,85 @@ +<?php + +namespace Crater\Rules; + +use Illuminate\Contracts\Validation\Rule; + +class Base64Mime implements Rule +{ + private $attribute; + private $extensions; + + /** + * Create a new rule instance. + * + * @return void + */ + public function __construct(array $extensions) + { + $this->extensions = $extensions; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + $this->attribute = $attribute; + + try { + $data = json_decode($value)->data; + } catch (\Exception $e) { + return False; + } + + $pattern = '/^data:\w+\/[\w\+]+;base64,[\w\+\=\/]+$/'; + + if(!preg_match($pattern, $data)) { + return False; + } + + $data = explode(',', $data); + + if(!isset($data[1]) || empty($data[1])) { + return False; + } + + try { + $data = base64_decode($data[1]); + $f = finfo_open(); + $result = finfo_buffer($f, $data, FILEINFO_EXTENSION); + + if($result === '???') + return False; + + if(strpos($result, '/')) { + foreach(explode('/', $result) as $ext) { + if(in_array($ext, $this->extensions)) + return True; + } + } else { + if(in_array($result, $this->extensions)) + return True; + } + } catch (\Exception $e) { + return False; + } + + return False; + + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return 'The ' . $this->attribute . ' must be a json with file of type: ' . implode(', ', $this->extensions) . ' encoded in base64.'; + } +}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-wppj-3pjr-9w79ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-4080ghsaADVISORY
- github.com/crater-invoice/crater/commit/cdc913d16cf624aee852bc9163a7c6ffc8d1da9dghsax_refsource_MISCWEB
- huntr.dev/bounties/d7453360-baca-4e56-985f-481275fa38dbghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.