CVE-2021-43829: Unrestricted Upload of File with Dangerous Type (CWE-434)
value: PatrOwl is a free and open-source solution for orchestrating Security Operations. In versions prior to 1.7.7 PatrowlManager unrestrictly handle upload files in the findings import feature. This vulnerability is capable of uploading dangerous type of file to server leading to XSS attacks and potentially other forms of code injection. Users are advised to update to 1.7.7 as soon as possible. There are no known workarounds for this issue.
CWE description: The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
CVSS v3 base score: 8.8
Published: 2021-12-14T20:15Z
Repository: https://github.com/Patrowl/PatrowlManager
Language: Python
Files changed: app/urls.py, findings/forms.py
Commit date: 2021-12-14 11:25:29 +0100
Commit message: Fix Unrestricted Upload of File with Dangerous Type (from Huntr bug bounty report)

Diff: diff --git a/app/urls.py b/app/urls.py
index 2c7649c..1c3d068 100644
--- a/app/urls.py
+++ b/app/urls.py
@@ -26,11 +26,11 @@ handler400 = 'app.views.custom_bad_request'
 handler403 = 'app.views.custom_permission_denied'
 handler404 = 'app.views.custom_page_not_found'
 handler500 = 'app.views.custom_error'
 
 
-api_schema_view = get_swagger_view(title='PatrOwl Manager REST-API')
+api_schema_view = get_swagger_view(title='PatrowlManager REST-API')
 
 urlpatterns = [
     url(r'^apis-doc', api_schema_view),
     url(r'^ht/', include('health_check.urls')),
     url(r'^auth-jwt/obtain_jwt_token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
diff --git a/findings/forms.py b/findings/forms.py
index 21f2d6f..315e2a0 100644
--- a/findings/forms.py
+++ b/findings/forms.py
@@ -1,16 +1,24 @@
 # -*- coding: utf-8 -*-
 
+import os
 from django import forms
 from .models import Finding, FINDING_SEVERITIES
 
 ENGINE_TYPES = (
     ('json', 'json'),
     ('nessus', 'Nessus'),
 )
 
 
+def validate_file_extension(value):
+    ext = os.path.splitext(value.name)[1]
+    valid_extensions = ['.xml', '.nessus', '.json']
+    if ext not in valid_extensions:
+        raise ValidationError(u'File not supported!')
+
+
 class ImportFindingsForm(forms.Form):
     class Meta:
         fields = ['engine', 'min_level', 'file']
 
     engine = forms.CharField(widget=forms.Select(
@@ -18,18 +26,23 @@ class ImportFindingsForm(forms.Form):
         choices=ENGINE_TYPES))
     min_level = forms.CharField(widget=forms.Select(
         attrs={'class': 'form-control form-control-sm'},
         choices=FINDING_SEVERITIES),
         label='Minimum severity')
-    file = forms.FileField()
+    file = forms.FileField(widget=forms.FileInput(
+        attrs={'accept': 'text/xml,application/json'}),
+        validators=[validate_file_extension]
+    )
 
 
 class FindingForm(forms.ModelForm):
     class Meta:
         model = Finding
-        fields = ['title', 'type', 'severity', 'status', 'description', 'tags',
-            'solution', 'risk_info', 'vuln_refs', 'links', 'comments', 'asset']
+        fields = [
+            'title', 'type', 'severity', 'status', 'description', 'tags',
+            'solution', 'risk_info', 'vuln_refs', 'links', 'comments', 'asset'
+        ]
         widgets = {
             'description': forms.Textarea(
                 attrs={'class': 'form-control form-control-sm'}),
             'solution': forms.Textarea(
                 attrs={'class': 'form-control form-control-sm'}),

Vulnerable code: api_schema_view = get_swagger_view(title='PatrOwl Manager REST-API')
    file = forms.FileField()
        fields = ['title', 'type', 'severity', 'status', 'description', 'tags',
            'solution', 'risk_info', 'vuln_refs', 'links', 'comments', 'asset']
Fixed code: api_schema_view = get_swagger_view(title='PatrowlManager REST-API')
import os
def validate_file_extension(value):
    ext = os.path.splitext(value.name)[1]
    valid_extensions = ['.xml', '.nessus', '.json']
    if ext not in valid_extensions:
        raise ValidationError(u'File not supported!')


    file = forms.FileField(widget=forms.FileInput(
        attrs={'accept': 'text/xml,application/json'}),
        validators=[validate_file_extension]
    )
        fields = [
            'title', 'type', 'severity', 'status', 'description', 'tags',
            'solution', 'risk_info', 'vuln_refs', 'links', 'comments', 'asset'
        ]