VYPR
Medium severity6.5NVD Advisory· Published May 23, 2026

aiograpi: Unsafe signup challenge path handling

CVE-2026-47157

Description

aiograpi versions before 0.9.10 accepted server-supplied signup challenge paths and used them to build request URLs before validating that the paths were relative Instagram API paths. A malicious or tampered challenge payload could cause challenge handling requests to be sent outside the intended Instagram host with the client\'s existing session headers. Version 0.9.10 validates challenge paths before building URLs, solving captcha challenges, or submitting phone/SMS challenge forms.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

aioigrapi before 0.9.10 accepts unsanitized server-supplied signup challenge paths, enabling request redirection to arbitrary hosts with session headers.

Vulnerability

In aiograpi versions before 0.9.10, the signup challenge handling code accepts a challenge path from the server response and uses it to construct request URLs without first validating that the path is a relative Instagram API path [1][2]. This lack of validation allows a malicious or tampered challenge payload to cause the client to send requests to arbitrary hosts while retaining the client's existing session headers. Every version before 0.9.10 is affected.

Exploitation

An attacker who can influence the challenge response—for example through a local network, DNS, or proxy compromise—can supply a crafted challenge path that points to an attacker-controlled host [1]. The aiograpi client will then build a URL using that path and send a request with the victim's session headers to that external host. No additional authentication or user interaction is required beyond the normal signup flow.

Impact

Successful exploitation allows an attacker to receive requests bearing the victim's Instagram session headers, leading to session hijacking and subsequent account takeover [1][2]. The compromise achieves credential-theft-level access without needing to compromise Instagram's servers.

Mitigation

The fix is available in aiograpi version 0.9.10, which validates challenge paths before building URLs, solving captcha challenges, or submitting phone/SMS challenge forms [1][2]. Users must upgrade to version 0.9.10 or later. No workaround is documented in the available references.

AI Insight generated on May 23, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
dcf461aadd7e

Harden signup challenge path validation

https://github.com/subzeroid/aiograpisubzeroidMay 17, 2026Fixed in 0.9.10via ghsa-release-walk
2 files changed · +97 4
  • aiograpi/mixins/signup.py+25 4 modified
    @@ -3,6 +3,7 @@
     import random
     import secrets
     import time
    +from urllib.parse import urlsplit
     from uuid import uuid4
     
     from aiograpi.exceptions import (
    @@ -27,6 +28,26 @@ class SignUpMixin(ClientMixin):
         adid = str(uuid4())
         wait_seconds = 5
     
    +    @staticmethod
    +    def _safe_challenge_api_path(api_path: str, field_name: str = "api_path") -> str:
    +        if not isinstance(api_path, str) or not api_path:
    +            raise ClientError(f"Malformed challenge data from Instagram (missing {field_name}).")
    +        parts = urlsplit(api_path)
    +        has_control_chars = any(ord(char) < 32 or ord(char) == 127 for char in api_path)
    +        if (
    +            parts.scheme
    +            or parts.netloc
    +            or not api_path.startswith("/")
    +            or api_path.startswith("//")
    +            or "\\" in api_path
    +            or has_control_chars
    +        ):
    +            raise ClientError(f"Unsafe challenge path from Instagram: {field_name}")
    +        return api_path
    +
    +    def _challenge_url(self, api_path: str, prefix: str = "", field_name: str = "api_path") -> str:
    +        return f"https://i.instagram.com{prefix}{self._safe_challenge_api_path(api_path, field_name)}"
    +
         async def signup(
             self,
             username: str,
    @@ -256,7 +277,7 @@ async def challenge_flow(self, data):
     
         async def challenge_api(self, data):
             resp = await self.private.get(
    -            f"https://i.instagram.com/api/v1{data['api_path']}",
    +            self._challenge_url(data.get("api_path"), prefix="/api/v1"),
                 params={
                     "guid": self.uuid,
                     "device_id": self.android_device_id,
    @@ -276,7 +297,7 @@ async def challenge_captcha(self, challenge_json_data):
                 )
                 raise ClientError("Malformed captcha challenge data from Instagram (missing site_key or api_path).")
     
    -        challenge_post_url = f"https://i.instagram.com{api_path}"
    +        challenge_post_url = self._challenge_url(api_path)
             captcha_details_for_solver = {
                 "site_key": site_key,
                 "challenge_type": challenge_type,
    @@ -307,7 +328,7 @@ async def challenge_captcha(self, challenge_json_data):
         async def challenge_submit_phone_number(self, data, phone_number):
             api_path = data.get("navigation", {}).get("forward")
             resp = await self.private.post(
    -            f"https://i.instagram.com{api_path}",
    +            self._challenge_url(api_path, field_name="navigation.forward"),
                 data={
                     "phone_number": phone_number,
                     "challenge_context": data["challenge_context"],
    @@ -318,7 +339,7 @@ async def challenge_submit_phone_number(self, data, phone_number):
         async def challenge_verify_sms_captcha(self, data, security_code):
             api_path = data.get("navigation", {}).get("forward")
             resp = await self.private.post(
    -            f"https://i.instagram.com{api_path}",
    +            self._challenge_url(api_path, field_name="navigation.forward"),
                 data={
                     "security_code": security_code,
                     "challenge_context": data["challenge_context"],
    
  • tests/regression/test_signup.py+72 0 added
    @@ -0,0 +1,72 @@
    +import unittest
    +from unittest.mock import AsyncMock, Mock
    +
    +from aiograpi import Client
    +from aiograpi.exceptions import ClientError
    +
    +
    +class SignupHelperRegressionTestCase(unittest.IsolatedAsyncioTestCase):
    +    async def test_challenge_api_rejects_external_api_path(self):
    +        client = Client()
    +        client.uuid = "uuid"
    +        client.android_device_id = "android-id"
    +        client.private.get = AsyncMock()
    +
    +        for api_path in ("@attacker.example/steal", "//attacker.example/steal"):
    +            with self.subTest(api_path=api_path):
    +                with self.assertRaises(ClientError):
    +                    await client.challenge_api(
    +                        {
    +                            "api_path": api_path,
    +                            "challenge_context": "{}",
    +                        }
    +                    )
    +
    +        client.private.get.assert_not_awaited()
    +
    +    async def test_challenge_captcha_rejects_external_api_path_before_solver(self):
    +        client = Client()
    +        client.captcha_resolve = AsyncMock(side_effect=AssertionError("solver should not be called"))
    +        client.private.post = AsyncMock()
    +
    +        with self.assertRaises(ClientError):
    +            await client.challenge_captcha(
    +                {
    +                    "api_path": "@attacker.example/steal",
    +                    "fields": {"sitekey": "site-key"},
    +                    "challengeType": "RecaptchaChallengeForm",
    +                }
    +            )
    +
    +        client.captcha_resolve.assert_not_awaited()
    +        client.private.post.assert_not_awaited()
    +
    +    async def test_challenge_submit_phone_number_rejects_external_forward_path(self):
    +        client = Client()
    +        client.private.post = AsyncMock(return_value=Mock(json=Mock(return_value={"status": "ok"})))
    +
    +        with self.assertRaises(ClientError):
    +            await client.challenge_submit_phone_number(
    +                {
    +                    "navigation": {"forward": "@attacker.example/steal"},
    +                    "challenge_context": "{}",
    +                },
    +                "+15551234567",
    +            )
    +
    +        client.private.post.assert_not_awaited()
    +
    +    async def test_challenge_verify_sms_captcha_rejects_external_forward_path(self):
    +        client = Client()
    +        client.private.post = AsyncMock(return_value=Mock(json=Mock(return_value={"status": "ok"})))
    +
    +        with self.assertRaises(ClientError):
    +            await client.challenge_verify_sms_captcha(
    +                {
    +                    "navigation": {"forward": "@attacker.example/steal"},
    +                    "challenge_context": "{}",
    +                },
    +                "123456",
    +            )
    +
    +        client.private.post.assert_not_awaited()
    

Vulnerability mechanics

Root cause

"Missing validation of server-supplied challenge paths allows request URLs to be built pointing to arbitrary external hosts."

Attack vector

An attacker who can tamper with or control the Instagram API response (e.g., through a man-in-the-middle position or a malicious server) can supply a crafted challenge path that points to an external host. The client then builds a request URL using this path and sends it with the existing session headers, potentially leaking authentication credentials or performing actions on the attacker's server. No authentication is required beyond the existing session the client holds.

Affected code

The vulnerability resides in the challenge handling logic of aiograpi before version 0.9.10. The code accepted server-supplied challenge paths from Instagram's API response and used them to construct request URLs without verifying that the paths were relative Instagram API paths. The patch modifies the challenge handling to validate that challenge paths are relative paths before building URLs or submitting challenge forms.

What the fix does

The patch [patch_id=1637871] adds validation to ensure that challenge paths are relative paths before they are used to build request URLs. By checking that the path does not point to an external host, the fix prevents the client from sending session-bearing requests to arbitrary servers. This closes the server-side request forgery (SSRF) vector that could leak session headers to an attacker-controlled endpoint.

Preconditions

  • networkAttacker must be able to tamper with or control the Instagram API response (e.g., via MITM or malicious server)
  • authClient must have an active session with valid headers

Generated on May 23, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.