Skip to content

Commit

Permalink
[SECURITY] Security update to fix vulnerabilities reported by Positiv…
Browse files Browse the repository at this point in the history
…e Technologies researchers (#2488)

* Fix Stored XSS in iOS Dynamic Analysis, GHSA-cxqq-w3x5-7ph3
* Fix DOS by loose re_path check and strict check inside function, GHSA-jrm8-xgf3-fwqr
* Fix API Key leakage, replace REST API with authenticated endpoint, GHSA-79f6-p65j-3m2m
* Update SECURITY.md
  • Loading branch information
ajinabraham authored Feb 5, 2025
1 parent d1d3b7a commit 05206e7
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 73 deletions.
3 changes: 3 additions & 0 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Please report all security issues [here](https://github.com/MobSF/Mobile-Securit

| Vulnerability | Affected Versions |
| ------- | ------------------ |
| [Partial Denial of Service due to strict regex check in iOS report view URL](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-jrm8-xgf3-fwqr) | `<=4.3.0` |
| [Local Privilege escalation due to leaked REST API key in web UI](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-79f6-p65j-3m2m) | `<=4.3.0` |
| [Stored Cross-Site Scripting in iOS dynamic_analysis view via `bundle` id](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-cxqq-w3x5-7ph3) | `<=4.3.0` |
| [Stored Cross-Site Scripting Vulnerability in Recent Scans "Diff or Compare"](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-5jc6-h9w7-jm3p) | `<=4.2.8` |
| [Zip Slip Vulnerability in .a extraction](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-4hh3-vj32-gr6j) | `<=4.0.6` |
| [Open Redirect in Login redirect](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-8m9j-2f32-2vx4) | `<=4.0.4` |
Expand Down
2 changes: 2 additions & 0 deletions mobsf/DynamicAnalyzer/views/ios/corellium_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,8 @@ def download_data(request, bundle_id, api=False):
if failed:
return send_response(failed, api)
if not strict_package_check(bundle_id):
# Check bundle_id during call, as the check
# is not done in REST API/URL repath.
data['message'] = 'Invalid iOS Bundle id'
return send_response(data, api)
ci = CorelliumInstanceAPI(instance_id)
Expand Down
5 changes: 3 additions & 2 deletions mobsf/DynamicAnalyzer/views/ios/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ def ios_view_report(request, bundle_id, api=False):
else:
dev = ''
if not strict_package_check(bundle_id):
# We need this check since bundleid
# is not validated in REST API
# bundle_id is not validated in REST API.
# Also bundleid is not strictly validated
# in URL path.
return print_n_send_error_response(
request,
'Invalid iOS Bundle id',
Expand Down
2 changes: 1 addition & 1 deletion mobsf/MobSF/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

logger = logging.getLogger(__name__)

VERSION = '4.3.0'
VERSION = '4.3.1'
BANNER = r"""
__ __ _ ____ _____ _ _ _____
| \/ | ___ | |__/ ___|| ___|_ _| || | |___ /
Expand Down
2 changes: 1 addition & 1 deletion mobsf/MobSF/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

from . import settings

bundle_id_regex = r'(?P<bundle_id>([a-zA-Z0-9]{1}[\w.-]{1,255}))$'
bundle_id_regex = r'(?P<bundle_id>.+)$'
checksum_regex = r'(?P<checksum>[0-9a-f]{32})'
paginate = r'(?P<page_size>[0-9]{1,10})/(?P<page_number>[0-9]{1,10})'

Expand Down
2 changes: 0 additions & 2 deletions mobsf/StaticAnalyzer/views/android/views/source_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
render,
)

from mobsf.MobSF.init import api_key
from mobsf.MobSF.utils import (
is_md5,
print_n_send_error_response,
Expand Down Expand Up @@ -71,7 +70,6 @@ def run(request):
'hash': md5,
'source_type': typ,
'version': settings.MOBSF_VER,
'api_key': api_key(settings.MOBSF_HOME),
}
template = 'static_analysis/source_tree.html'
return render(request, template, context)
Expand Down
32 changes: 25 additions & 7 deletions mobsf/StaticAnalyzer/views/android/views/view_source.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# -*- coding: utf_8 -*-
"""View Source of a file."""

import logging
import ntpath
from pathlib import Path

from django.conf import settings
from django.shortcuts import render
from django.utils.html import escape
from django.http import JsonResponse

from mobsf.MobSF.forms import FormUtil
from mobsf.MobSF.utils import (
Expand All @@ -28,9 +28,26 @@
logger = logging.getLogger(__name__)


def send_json(data):
return JsonResponse(data, safe=False)


def send_error(request, err, api_mode, json_resp, exp=None):
"""Send error message as dict or JSON."""
if exp:
res = print_n_send_error_response(request, err, api_mode, exp)
else:
res = print_n_send_error_response(request, err, api_mode)
if json_resp:
return send_json(res)
return res


@login_required
def run(request, api=False):
"""View the source of a file."""
json_resp = request.GET.get('json', '0') == '1'
api_mode = api or json_resp
try:
logger.info('View Java Source File')
exp = 'Error Description'
Expand All @@ -46,8 +63,7 @@ def run(request, api=False):
viewsource_form = ViewSourceAndroidForm(request.GET)
if not viewsource_form.is_valid():
err = FormUtil.errors_message(viewsource_form)
return print_n_send_error_response(request, err, api, exp)

return send_error(request, err, api_mode, json_resp, exp)
base = Path(settings.UPLD_DIR) / md5
if typ == 'smali':
src = base / 'smali_source'
Expand All @@ -57,12 +73,12 @@ def run(request, api=False):
src, syntax, _ = find_java_source_folder(base)
except StopIteration:
msg = 'Invalid directory or file extension'
return print_n_send_error_response(request, msg, api)
return send_error(request, msg, api_mode, json_resp)

sfile = src / fil
if not is_safe_path(src, sfile.as_posix()):
msg = 'Path Traversal Detected!'
return print_n_send_error_response(request, msg, api)
return send_error(request, msg, api_mode, json_resp)
context = {
'title': escape(ntpath.basename(fil)),
'file': escape(ntpath.basename(fil)),
Expand All @@ -72,11 +88,13 @@ def run(request, api=False):
'version': settings.MOBSF_VER,
}
template = 'general/view.html'
if api:
if json_resp:
return send_json(context)
if api_mode:
return context
return render(request, template, context)
except Exception as exp:
logger.exception('Error Viewing Source')
msg = str(exp)
exp = exp.__doc__
return print_n_send_error_response(request, msg, api, exp)
return send_error(request, msg, api_mode, json_resp, exp)
2 changes: 1 addition & 1 deletion mobsf/templates/dynamic_analysis/ios/dynamic_analysis.html
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ <h4 class="modal-title">Create a Corellium iOS VM</h4>
<td><p>
<a class="btn btn-success disable" onclick="dynamic_loader()" href="${url}"><i class="fab fa-apple"></i> Start Dynamic Analysis</a>
<a class="btn btn-info ${buttonState}" href="../../ios/view_report/${escapeHtml(bundle)}"><i class="fa fa-mobile"></i> View Report </a>
<a class="btn btn-warning" id="${$('#ios_dynamic').val()}" onclick="remove_app(this, '${bundle}')"><i class="fas fa-trash-alt"></i> Uninstall</a>
<a class="btn btn-warning" id="${$('#ios_dynamic').val()}" onclick="remove_app(this, '${escapeHtml(bundle)}')"><i class="fas fa-trash-alt"></i> Uninstall</a>
</p>
</td></tr>`);
}
Expand Down
12 changes: 2 additions & 10 deletions mobsf/templates/static_analysis/source_tree.html
Original file line number Diff line number Diff line change
Expand Up @@ -275,17 +275,9 @@ <h2>

function show_file(strPath) {
$.ajax({
type: "POST",
url: "{% url "api_view_source" %}",
type: "GET",
url: "{% url "view_source" %}?json=1&md5={{ hash }}&type={{ source_type }}&file=" + strPath,
dataType: 'json',
headers: {
"Authorization": "{{ api_key }}"
},
data: {
type: "{{ source_type }}",
hash: "{{ hash }}",
file: strPath
},
success: function(strFileData) {
var code_block = $("pre#fileoutput")
code_block.text(strFileData.data);
Expand Down
Loading

0 comments on commit 05206e7

Please sign in to comment.