GHSA-95px-34x5-p37h is a low-severity (CVSS 3) Server-Side Request Forgery (SSRF) vulnerability in mobsf. A fix is available for mobsf — see the affected versions and patch details below.
MobSF has SSRF port restriction bypass in assetlinks_check
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for GHSA-95px-34x5-p37h.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
GHSA-95px-34x5-p37h by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 381,682 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
mobsfReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
MobSF's Android App Link assetlinks checker validates only the manifest android:host value with valid_host(), but then appends the separate android:port value into the URL used for the server-side request. This bypasses the current port restriction in valid_host() and lets a crafted APK cause MobSF to fetch http://host:<attacker-port>/.well-known/assetlinks.json or https://host:<attacker-port>/.well-known/assetlinks.json.
Impact
An authenticated user who can upload or trigger analysis of a crafted APK can cause the MobSF server to make an outbound request to an attacker-selected port during Android manifest analysis. When the host is controlled by the attacker and uses DNS rebinding, the validation lookup can resolve to a public IP while the later HTTP client lookup resolves to an internal address, allowing SSRF to internal services on non-80/443 ports.
This is not arbitrary URL SSRF. The path remains fixed to /.well-known/assetlinks.json, and redirects are disabled. The bypass is that the final fetched URL is assembled after the host-only validation, so the current port guard is not applied to the actual URL.
Root cause
valid_host() rejects ports other than 80 and 443 when a port is included in the string being validated:
port = parsed.port
...
if port and port not in (80, 443):
return False
In get_browsable_activities(), only the host attribute is passed to valid_host():
host = data.getAttribute(f'{ns}:host')
port = data.getAttribute(f'{ns}:port')
...
if not valid_host(host):
logger.warning('Invalid Host: %s', host)
continue
shost = f'{scheme}://{host}'
if port and is_number(port):
c_url = f'{shost}:{port}{WELL_KNOWN_PATH}'
else:
c_url = f'{shost}{WELL_KNOWN_PATH}'
well_known[c_url] = shost
_check_url() then fetches the assembled URL after checking only path, query, and params:
purl = urlparse(url)
if (purl.path != WELL_KNOWN_PATH
or len(purl.query) > 0
or len(purl.params) > 0):
logger.warning('Invalid Assetlinks URL: %s', url)
continue
r = requests.get(url,
timeout=5,
allow_redirects=False,
proxies=proxies,
verify=verify)
Reproduction
Use an Android manifest with a browsable App Link data tag that has a benign-looking host and a restricted port:
<activity android:name=".DeepLink" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" android:host="rebind.example" android:port="22" />
</intent-filter>
</activity>
A safe local proof with DNS and HTTP monkeypatching shows that MobSF validates only rebind.example, then fetches a URL that preserves the unchecked port:
well_known map: {'http://rebind.example:22/.well-known/assetlinks.json': 'http://rebind.example'}
requests.get calls: [('http://rebind.example:22/.well-known/assetlinks.json', {'timeout': 5, 'allow_redirects': False, 'proxies': None, 'verify': True})]
findings: [{'url': 'http://rebind.example:22/.well-known/assetlinks.json', 'host': 'http://rebind.example', 'status_code': 200, 'status': True}]
VULNERABLE: valid_host validated only rebind.example, but assetlinks_check fetched unchecked port 22 via http://rebind.example:22/.well-known/assetlinks.json
The same code pattern is present in latest release v4.4.6 and current main.
Remediation
Validate the final URL after all manifest components have been applied. In particular:
- Build
c_url, then run validation on the full URL, including scheme, hostname, port, path, query, and params. - Reject
android:portvalues other than 80 and 443 before appending them to the URL. - Prevent DNS rebinding by pinning the validated DNS result to the outbound connection or otherwise ensuring the actual HTTP request cannot resolve to a different address than the validation step.
- Keep
allow_redirects=Falsefor the existing redirect mitigation.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | mobsf | all versions | 4.5.1pip install --upgrade 'mobsf==4.5.1' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for mobsf, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update mobsf to 4.5.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-95px-34x5-p37h is resolved across your whole dependency graph.
Workarounds
Restrict outbound requests from the affected component to an allowlist of hosts, block access to link-local and internal address ranges at the network layer, and require authentication on internal services so a forged request cannot reach them unauthenticated.
Frequently Asked Questions
Is GHSA-95px-34x5-p37h in your dependencies?
Find it across PyPI, including transitive dependencies.