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.

    private void Page_Load(object sender, System.EventArgs e)
    {
      if (!this.IsPostBack)

        // Create a random code and store it in the Session object.

        this.Session["CaptchaImageText"] = GenerateRandomCode();

      else
      {
        // On a postback, check the user input.

        if (this.CodeNumberTextBox.Text ==
          this.Session["CaptchaImageText"].ToString())
        {
          // Display an informational message.

          this.MessageLabel.CssClass = "info";
          this.MessageLabel.Text = "Correct!";
        }
        else
        {
          // Display an error message.

          this.MessageLabel.CssClass = "error";
          this.MessageLabel.Text = "ERROR: Incorrect, try again.";

          // Clear the input and create a new random code.

          this.CodeNumberTextBox.Text = "";
          this.Session["CaptchaImageText"] = GenerateRandomCode();
        }
      }
    }

Do you see it? If not have a closer look at the highlighted portion on the below snippet.

    private void Page_Load(object sender, System.EventArgs e)
    {
      if (!this.IsPostBack)

        // Create a random code and store it in the Session object.

        this.Session["CaptchaImageText"] = GenerateRandomCode();

      else
      {
        // On a postback, check the user input.

        if (this.CodeNumberTextBox.Text ==
          this.Session["CaptchaImageText"].ToString())
        {
          // Display an informational message.

          this.MessageLabel.CssClass = "info";
          this.MessageLabel.Text = "Correct!";
        }
        else
        {
          // Display an error message.

          this.MessageLabel.CssClass = "error";
          this.MessageLabel.Text = "ERROR: Incorrect, try again.";

          // Clear the input and create a new random code.

          this.CodeNumberTextBox.Text = "";
          this.Session["CaptchaImageText"] = GenerateRandomCode();
        }
      }
    }

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.

protected void btnSubmit_Click(object s, EventArgs e)
{
    if (Session["captcha"] != null && txtCaptcha.Text.ToLower() ==
    Session["captcha"].ToString())
    {
        if (success != null)
        {
            success();
        }
    }
    else
    {
        txtCaptcha.Text = "";
        SetCaptcha();

        if (failure != null)
        {
            failure();
        }
    }
}

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

Tagged with:
 

One Response to Correct CAPTCHA Re-Use Attacks

  1. GG says:

    A very nice presentation of a business logic error. It would be rather interesting though if you could discover the flaw using some kind of automation. A relevant presentation in FRHACK 2009 about a tool called BLe comes in mind. Are you familiar with it? I have not heard anything about it since but you should try to contact that guy. He may have something really useful for you.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>