Friday, 21 October 2016

Correct CAPTCHA Re-Use Attacks

Hello all,
In a previous life, I was asked by a friend to test a web form he was developing for vulnerabilities. The form was protected via a CAPTCHA to thwart automated posting and ensure a human is actually submitting the form. Apart from that, the CAPTCHA was also messing with my ability to run a scanner to get the low hanging fruit. This moved testing of the CAPTCHA implementation up as the first item of my list.
The first thing I did, was google around the bitmap returning URL to find the make/type of CAPTCHA used and see if I can gain some insight as to its specifics. The actual CAPTCHA being called to produce the bitmap was JpegImage.aspx, which after a bit of googling turned out to be the CAPTCHA challenge producing component of http://www.codeproject.com/KB/aspnet/CaptchaImage.aspx, a 5-star rated codeproject. What it does,  essentially, is generate the CAPTCHA, store its value to a session variable, generate the resulting image and return it to the caller.
Next thing I did, was take a look at the example code and usage suggestions on the codeproject site. What caught my attention was the following snippet.
1private void Page_Load(object sender, System.EventArgs e)
2{
3  if (!this.IsPostBack)
4 
5    // Create a random code and store it in the Session object.
6 
7    this.Session["CaptchaImageText"] = GenerateRandomCode();
8 
9  else
10  {
11    // On a postback, check the user input.
12 
13    if (this.CodeNumberTextBox.Text ==
14      this.Session["CaptchaImageText"].ToString())
15    {
16      // Display an informational message.
17 
18      this.MessageLabel.CssClass = "info";
19      this.MessageLabel.Text = "Correct!";
20    }
21    else
22    {
23      // Display an error message.
24 
25      this.MessageLabel.CssClass = "error";
26      this.MessageLabel.Text = "ERROR: Incorrect, try again.";
27 
28      // Clear the input and create a new random code.
29 
30      this.CodeNumberTextBox.Text = "";
31      this.Session["CaptchaImageText"] = GenerateRandomCode();
32    }
33  }
34}
Do you see it? If not have a closer look at the highlighted portion on the below snippet.
1private void Page_Load(object sender, System.EventArgs e)
2{
3  if (!this.IsPostBack)
4 
5    // Create a random code and store it in the Session object.
6 
7    this.Session["CaptchaImageText"] = GenerateRandomCode();
8 
9  else
10  {
11    // On a postback, check the user input.
12 
13    if (this.CodeNumberTextBox.Text ==
14      this.Session["CaptchaImageText"].ToString())
15    {
16      // Display an informational message.
17 
18      this.MessageLabel.CssClass = "info";
19      this.MessageLabel.Text = "Correct!";
20    }
21    else
22    {
23      // Display an error message.
24 
25      this.MessageLabel.CssClass = "error";
26      this.MessageLabel.Text = "ERROR: Incorrect, try again.";
27 
28      // Clear the input and create a new random code.
29 
30      this.CodeNumberTextBox.Text = "";
31      this.Session["CaptchaImageText"] = GenerateRandomCode();
32    }
33  }
34}
The “CaptchaImageText” session variable is cleared and reset only on an unsuccessful post of the page. So, after a successful POST of the CATCHA challenge, and as long as the JpegImage.aspx is not called and given that the session is not reset, subsequent re-posts of information with the same CAPTCHA value will always pass. You can prevent the JpegImage.aspx from being called again by using an automatic drop or rewrite rule (to a non-existent page) to an in line proxy such as Burp. Whats more, you can chain an automatic scanner such as IBM AppScan through that inline proxy up to the point where a correct CAPTCHA is entered and enable the cancelling out rule afterwards thereby ensuring that the correct CAPTCHA code will always be used.
At this point you may think, OK so what, you still need a human to enter the first value however its significance depends on what is protected by the CAPTCHA challenge.
From a penetration testers’ perspective though, abusing this issue makes a huge difference in the quality and depth of the performed test. Cancelling out the CAPTCHA form protection greatly improves the visibility of an automated scanner on the low hanging fruits of the form and doesn’t require full manual inspection.
A week later I came across another CAPTCHA control in another form, “A CAPTCHA Control for ASP.NET 2” from http://www.codeproject.com/KB/custom-controls/CaptchaNET_2.aspx using the Captcha.ashx to generate the code and image. Again, the same issue popped up as you can see below form the sample source code provided at the project page.
1protected void btnSubmit_Click(object s, EventArgs e)
2{
3    if (Session["captcha"] != null && txtCaptcha.Text.ToLower() ==
4    Session["captcha"].ToString())
5    {
6        if (success != null)
7        {
8            success();
9        }
10    }
11    else
12    {
13        txtCaptcha.Text = "";
14        SetCaptcha();
15 
16        if (failure != null)
17        {
18            failure();
19        }
20    }
21}
It is worth noting that the above is not an issue with the CAPTCHA itself – and its strength or susceptibility to withstand automated OCR-like attacks – but rather with the CAPTCHA integration in the logic of the form that it is  protecting. This is another example why people should not blindly copy-paste sample code found on the internet.
./Z

No comments:

Post a comment

Note: only a member of this blog may post a comment.