{"id":35,"date":"2024-12-18T02:11:11","date_gmt":"2024-12-18T02:11:11","guid":{"rendered":"https:\/\/infosecin.com\/?p=35"},"modified":"2024-12-18T19:27:03","modified_gmt":"2024-12-18T19:27:03","slug":"attacking-weak-password-reset-implementation","status":"publish","type":"post","link":"https:\/\/infosecin.com\/?p=35","title":{"rendered":"Attacking weak password reset implementation"},"content":{"rendered":"\n<p>In this blog post, I detail how I uncovered a critical vulnerability in a popular online service&#8217;s password reset feature. Due to poor password reset implementation practices, I was able to gain unauthorized access to user accounts. In this research, I will lay out the thought process from a hacker&#8217;s perspective to achieve an account takeover. I am not allowed to reveal the service&#8217;s identity, so we will call the vulnerable site site.com throughout the research.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Technical Analysis<\/h4>\n\n\n\n<p>site.com allows users to reset passwords using the forgot password feature. Users who have lost their login password can request a unique link to reset the password using their email address. Once a user has asked for a reset password link they can visit the link received via email and set a new password.<\/p>\n\n\n\n<p>The reset password links are in this format:<\/p>\n\n\n\n<p><code>https:\/\/www.site.com\/tracking\/{{unique_hash_1}}\/{{unique_hash_2}}<\/code><\/p>\n\n\n\n<p>example reset password link:<\/p>\n\n\n\n<p><code>https:\/\/www.site.com\/tracking\/6658085a4557ad8\/6659c0b412fd5469<\/code><\/p>\n\n\n\n<p>Once a GET request to this link is sent, the response contains the actual password reset link as a part of the Location header which is in the format of:<\/p>\n\n\n\n<p><code>https:\/\/www.site.com\/account\/updatePassword\/{{unique_password_rest_token}}<\/code><\/p>\n\n\n\n<p>Now, if an attacker gets the correct tracking link they can find the password reset URL for the victim and takeover the victim&#8217;s account.<\/p>\n\n\n\n<p>The tracking link contains two random hash values which are actually a value in the&nbsp;<code>hex<\/code>&nbsp;format. Also, if we look at the multiple tracking links requested in the nearby time the&nbsp;<code>hex<\/code>&nbsp;values seem like they are sequential and not random.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">The randomness in tracking links and how to brute force it<\/h5>\n\n\n\n<p>Let&#8217;s take a look at multiple tracking links requested for different accounts within almost the same second time.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nhttps:\/\/www.site.com\/tracking\/65c2e8db4557ad4\/65c4e8f812fd5465\nhttps:\/\/www.site.com\/tracking\/65c2eb504557ad5\/65c4e78812fd5466\nhttps:\/\/www.site.com\/tracking\/65c43aa64557ad6\/65c5a68912fd5467\nhttps:\/\/www.site.com\/tracking\/65c762584557ad7\/65c9972112fd5468\nhttps:\/\/www.site.com\/tracking\/6658085a4557ad8\/6659c0b412fd5469\nhttps:\/\/www.site.com\/tracking\/665cb6064557ad9\/665ea6a412fd546a\n\n<\/pre><\/div>\n\n\n<p>Let&#8217;s extract&nbsp;<code>hash 1<\/code>&nbsp;from the above links and see how random the values are:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n65c2e8db4557ad4\n65c2eb504557ad5\n65c43aa64557ad6\n65c762584557ad7\n6658085a4557ad8\n665cb6064557ad9\n\n<\/pre><\/div>\n\n\n<p>If we observe these numbers they are not random but in ascending order. To make it easier we can even convert them to decimal numbers by using any hex-to-decimal converter.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n      HEX       -       DECIMAL \n65c2e8db4557ad4 - 458292448235715284\n65c2eb504557ad5 - 458292617081617109\n65c43aa64557ad6 - 458315661191772886\n65c762584557ad7 - 458371165591010007\n6658085a4557ad8 - 460915848351415000\n665cb6064557ad9 - 460998151735966425\n\n<\/pre><\/div>\n\n\n<p>This also gives us a hint on which account the reset password link was requested first and how the sequence was followed.<\/p>\n\n\n\n<p>Let&#8217;s assume, the email address associated which each of these tracking links was in this order:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nemail    - account1@attacker.com\npw reset - https:\/\/www.site.com\/tracking\/65c2e8db4557ad4\/65c4e8f812fd5465\nemail    - account2@attacker.com\npw reset - https:\/\/www.site.com\/tracking\/65c2eb504557ad5\/65c4e78812fd5466\nemail    - victim@victim.com\npw reset - https:\/\/www.site.com\/tracking\/65c43aa64557ad6\/65c5a68912fd5467\nemail    - account3@attacker.com\npw reset - https:\/\/www.site.com\/tracking\/65c762584557ad7\/65c9972112fd5468\nemail    - account4@attacker.com\npw reset - https:\/\/www.site.com\/tracking\/6658085a4557ad8\/6659c0b412fd5469\nemail    - account5@attacker.com\npw reset - https:\/\/www.site.com\/tracking\/665cb6064557ad9\/665ea6a412fd546a\n\n<\/pre><\/div>\n\n\n<p>Meaning, the attacker made the password reset request for attacker-controlled accounts and a victim account in a short period of time (fraction of second) then the attacker can actually identify the missing link by observing the hash 1 in the received tracking links.<\/p>\n\n\n\n<p>In the above example, it was evident that the Hash-1 between&nbsp;<code>...ad5<\/code>&nbsp;and&nbsp;<code>...ad7<\/code>&nbsp;was missing for the tracking link his accounts received to reset the password. Meaning, the victim must have received a tracking link between these two value.<\/p>\n\n\n\n<p>This is how an attacker can actually map the range of hash-1. For hash-2, The hash-2 is also sequential and it is also easier to find a range as it will be in the range of&nbsp;<code>hash-2<\/code>&nbsp;of&nbsp;<code>hash-1<\/code>&nbsp;such as in the above example&nbsp;<code>hash-2<\/code>&nbsp;of the link with hash-1&nbsp;<code>...ad5<\/code>&nbsp;and&nbsp;<code>hash-2<\/code>&nbsp;of the link with hash-1&nbsp;<code>..ad7<\/code>.<\/p>\n\n\n\n<p>In the above example,<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nThe range of hash-1 in hex - &#x5B;65c2eb504557ad5 - 65c762584557ad7]\nThe range of hash-2 in hex - &#x5B;65c4e78812fd5466 - 65c9972112fd5468]\n\n<\/pre><\/div>\n\n\n<p>An attacker can brute force this range to find the password reset link. However, there are two ranges meaning for each value in hash-1 the attacker needs to map all the values of hash-2 which makes a total combination value which no modern computer can brute force.<\/p>\n\n\n\n<p>However, if we look closely we can actually reduce the range by finding which placeholder in the given hex values is random and what are the static values. Also, is there any sequence which we can guess? Let&#8217;s see:<\/p>\n\n\n\n<p>For predicting hash-1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;Static Values] &#x5B;Random Values] &#x5B;Sequence]\n65c              2eb50           4557ad5\n65c              xxxxx           4557ad6\n65c              76258           4557ad7\n\n<\/pre><\/div>\n\n\n<p>This drastically reduces the total combinations as now the range for hash-1 becomes:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;2eb50 - 76258] (in hex)\n&#x5B;191312 - 483928] (in decimal) \n\n<\/pre><\/div>\n\n\n<p>Making total combinations as 483928 &#8211; 191312 = 292616<\/p>\n\n\n\n<p>But again, if the total combinations of hash-1 is around 300000 and for hash-2 if it is also around 300000 again it is almost impossible to brute force 300000^300000 combinations.<\/p>\n\n\n\n<p>Upon further testing, I identified that&nbsp;<code>Hash-1<\/code>&nbsp;is actually not being validated by the backend and only&nbsp;<code>Hash-2<\/code>&nbsp;is attached with the password reset link. Meaning,&nbsp;<code>Hash-1<\/code>&nbsp;can be any valid&nbsp;<code>Hash-1<\/code>&nbsp;value and&nbsp;<code>Hash-2<\/code>&nbsp;has to be the right value received by the victim.<\/p>\n\n\n\n<p>We can now again reduce the range of total bruteforce attempts.<\/p>\n\n\n\n<p>Let&#8217;s investigate hash-2 from the above example:-<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;Static Values] &#x5B;Random Values] &#x5B;Sequence]\n65c              4e788           12fd5466\n65c              xxxxx           12fd5467\n65c              99721           12fd5468\n\n<\/pre><\/div>\n\n\n<p>The static value should be&nbsp;<code>65c<\/code>&nbsp;and the last sequence has to be&nbsp;<code>12fd5467<\/code>. We are left with random values to guess. The range for this is:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;4e788 - 99721] (in hex)\n&#x5B;321416 - 628513] (in decimal) \n\n<\/pre><\/div>\n\n\n<p>Making total combinations as: 628513 &#8211; 321416 = 307097<\/p>\n\n\n\n<p>These many combinations can easily be brute-forced. I have written a Python script below that can generate all the possible hash-2 values given the static value, the range of random values, and the sequence that was guessed.<\/p>\n\n\n\n<p>The python script is as below:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nimport os\n\ndirectory = os.getcwd()\n\nstatic_value = input(&quot;What is the static value in the hash?\\nYour Answer:&quot;)\nstart_hex = input(&quot;What is the lowest or starting number of the range?\\nYour Answer:&quot;)\nend_hex = input(&quot;What is the highest or ending number of the range?\\nYour Answer:&quot;)\nsequence = input(&quot;What is the ending sequence of the hash?\\nYour Answer:&quot;)\n\nstart_decimal = int(start_hex, 16)\nend_decimal = int(end_hex, 16)\n\nprint(&quot;Generating all the possible hex value...&quot;)\n\nwith open(directory+'\/hash-2','w') as file:\n    for decimal in range(start_decimal, end_decimal+1):\n        hex_value = hex(decimal)\n        file.write(static_value + hex_value.replace('0x', '') + sequence)\n        file.write(&quot;\\n&quot;)\n\nprint(&quot;File Generated and saved at &quot; + directory + &quot;\/hash-2&quot;)\n\n<\/pre><\/div>\n\n\n<p>saved this file as\u00a0<code>hash_2_generator.py<\/code> and ran it with:\u00a0<code>python3 hash_2_generator.py<\/code><\/p>\n\n\n\n<p>The output (All the inputs are of hex values):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nWhat is the static value in the hash?\nYour Answer:65c\nWhat is the lowest or starting number of the range?\nYour Answer:4e788\nWhat is the highest or ending number of the range?\nYour Answer:99721\nWhat is the ending sequence of the hash?\nYour Answer:12fd5467\nGenerating all the possible hex value...\nFile Generated and saved at ....\/hash-2\n\n<\/pre><\/div>\n\n\n<p>The output file:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/pentestpulse.wordpress.com\/wp-content\/uploads\/2024\/07\/1.png?w=913\" alt=\"\" class=\"wp-image-9\"\/><\/figure>\n\n\n\n<p>A quick search with the value\u00a0<code>65c5a68912fd5467<\/code>\u00a0(which is the actual hash of the victim&#8217;s tracking link) was found at 48898 positions, meaning that after 48898 attempts, the attacker could have found the victim&#8217;s reset password link.<\/p>\n\n\n\n<p>To make this attack worse and compromise more than one victim account, an attacker can place many other victims&#8217; email addresses between the attacker&#8217;s emails. While brute-forcing, an attacker will find the password reset links of all the victim accounts they want to target.<\/p>\n\n\n\n<p>I reported this finding to the specific vulnerable site, and I am pleased to share that they have since resolved the issue.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this blog post, I detail how I uncovered a critical vulnerability in a popular online service&#8217;s password reset feature. Due to poor password reset implementation practices, I was able to gain unauthorized access to user accounts. In this research, I will lay out the thought process from a hacker&#8217;s perspective to achieve an account [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-35","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/infosecin.com\/index.php?rest_route=\/wp\/v2\/posts\/35","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/infosecin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/infosecin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/infosecin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/infosecin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=35"}],"version-history":[{"count":3,"href":"https:\/\/infosecin.com\/index.php?rest_route=\/wp\/v2\/posts\/35\/revisions"}],"predecessor-version":[{"id":45,"href":"https:\/\/infosecin.com\/index.php?rest_route=\/wp\/v2\/posts\/35\/revisions\/45"}],"wp:attachment":[{"href":"https:\/\/infosecin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=35"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/infosecin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=35"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/infosecin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=35"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}