How To Simply Solve reCaptcha V2 using Java and 2Captcha

How To Simply Solve reCaptcha V2 using Java and 2Captcha

·

9 min read

Table of contents

No heading

No headings in the article.

Have you ever seen something like this?

reCaptcha.png

This is a type of Captcha, precisely a reCaptcha.

A Captcha is a challenge-response that many websites use to check whether the user is a human or a robot.

2Captcha is one of the many services that provide CAPTCHA recognition.

2Captcha solves several challenges, and the reCaptcha V2 is one of them.

Recaptcha V2 is probably the most popular one adopted by many sites.

This article will show you how to solve ReCaptcha V2 using SDK easily.

2Captcha-register.png

Once you sign up, you’ll need to pay a small amount to process a set amount of requests (it’s probably 0.5$).

You should see an API key, which is a long alphanumeric word.

apiKey.png

Let’s see a simple way to bypass Captcha on a website.

We’ll use the Java code in the 2Captcha Github repository.

This is the package structure that I created:

This is the pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>org.example</groupId>
   <artifactId>TwoCaptchaJava</artifactId>
   <version>1.0-SNAPSHOT</version>

   <properties>
       <maven.compiler.source>17</maven.compiler.source>
       <maven.compiler.target>17</maven.compiler.target>
   </properties>

   <licenses>
       <license>
           <name>MIT License</name>
           <url>http://www.opensource.org/licenses/mit-license.php</url>
           <distribution>repo</distribution>
       </license>
   </licenses>

   <developers>
       <developer>
           <email>info@2captcha.com</email>
           <name>2captcha</name>
           <url>https://github.com/2captcha</url>
           <id>2captcha</id>
       </developer>
   </developers>

   <scm>
       <connection>scm:git:git://github.com/2captcha/2captcha-java.git</connection>
       <developerConnection>scm:git:git@github.com:2captcha/2captcha-java.git</developerConnection>
       <url>https://github.com/2captcha/2captcha-java</url>
   </scm>

   <distributionManagement>
       <repository>
           <id>ossrh</id>
           <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
       </repository>
       <snapshotRepository>
           <id>ossrh</id>
           <url>https://oss.sonatype.org/content/repositories/snapshots</url>
       </snapshotRepository>
   </distributionManagement>

   <dependencies>
       <dependency>
           <groupId>com.squareup.okhttp3</groupId>
           <artifactId>okhttp</artifactId>
           <version>4.7.2</version>
       </dependency>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.13.1</version>
           <scope>test</scope>
       </dependency>
       <dependency>
           <groupId>org.mockito</groupId>
           <artifactId>mockito-core</artifactId>
           <version>3.3.3</version>
           <scope>test</scope>
       </dependency>
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <version>3.0.1</version>
           <scope>provided</scope>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-source-plugin</artifactId>
               <executions>
                   <execution>
                       <id>attach-sources</id>
                       <goals>
                           <goal>jar-no-fork</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>

           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-javadoc-plugin</artifactId>
               <executions>
                   <execution>
                       <id>attach-javadocs</id>
                       <goals>
                           <goal>jar</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>

           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-gpg-plugin</artifactId>
               <executions>
                   <execution>
                       <id>sign-artifacts</id>
                       <phase>verify</phase>
                       <goals>
                           <goal>sign</goal>
                       </goals>
                       <configuration>
                           <keyname>F8F9DA2268324C45C3A46015C273F1F4693E6F11</keyname>
                               <skip>true</skip>
                       </configuration>
                   </execution>
               </executions>
           </plugin>

       </plugins>
   </build>

</project>

The ApiClient is a class that addresses everything related to connectivity.

package com.techwithmaddy.captcha;

import com.techwithmaddy.exception.ApiException;
import com.techwithmaddy.exception.NetworkException;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.File;
import java.nio.file.Files;
import java.util.Map;

public class ApiClient {

    /**
     * API server
     */
    private final String host = "2captcha.com";

    /**
     * Network client
     */
    private final OkHttpClient client = new OkHttpClient();

    /**
     * Sends captcha to /in.php
     *
     * @param params
     * @param files
     * @return
     * @throws Exception
     */
    public String in(Map<String, String> params, Map<String, File> files) throws Exception {
        HttpUrl.Builder url = new HttpUrl.Builder()
                .scheme("https")
                .host(host)
                .addPathSegment("in.php");

        RequestBody body;

        if (files.size() == 0) {
            FormBody.Builder form = new FormBody.Builder();
            params.forEach(form::add);
            body = form.build();
        } else {
            MultipartBody.Builder form = new MultipartBody.Builder();
            form.setType(MultipartBody.FORM);
            params.forEach(form::addFormDataPart);
            for (Map.Entry<String, File> entry : files.entrySet()) {
                byte[] fileBytes = Files.readAllBytes(entry.getValue().toPath());
                form.addFormDataPart(entry.getKey(), entry.getValue().getName(), RequestBody.create(fileBytes));
            }
            body = form.build();
        }

        Request request = new Request.Builder()
                .url(url.build())
                .post(body)
                .build();

        return execute(request);
    }

    /**
     * Does request to /res.php
     *
     * @param params
     * @return
     * @throws Exception
     */
    public String res(Map<String, String> params) throws Exception {
        HttpUrl.Builder url = new HttpUrl.Builder()
                .scheme("https")
                .host(host)
                .addPathSegment("res.php");

        params.forEach(url::addQueryParameter);

        Request request = new Request.Builder()
                .url(url.build())
                .build();

        return execute(request);
    }

    /**
     * Executes http request to api
     *
     * @param request
     * @return
     * @throws Exception
     */
    private String execute(Request request) throws Exception {
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new NetworkException("Unexpected code " + response);
            }

            String body = response.body().string();

            if (body.startsWith("ERROR_")) {
                throw new ApiException(body);
            }

            return body;
        }
    }

}

Let's add the Captcha class to our application. This class has different fields which describe the features that shape captchas, such as id, code, params, and file.

package com.techwithmaddy.captcha;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public abstract class Captcha {

   protected String id;
   protected String code;

   protected Map<String, String> params;
   protected Map<String, File> files;

   public Captcha() {
       params = new HashMap<>();
       files = new HashMap<>();
   }

   public String getId() {
       return id;
   }

   public void setId(String id) {
       this.id = id;
   }

   public String getCode() {
       return code;
   }

   public void setCode(String code) {
       this.code = code;
   }

   public void setProxy(String type, String uri) {
       params.put("proxy", uri);
       params.put("proxytype", type);
   }

   public void setSoftId(int softId) {
       params.put("soft_id", String.valueOf(softId));
   }

   public void setCallback(String callback) {
       params.put("pingback", callback);
   }

   public Map<String, String> getParams() {
       Map<String, String> params = new HashMap<String, String>(this.params);

       if (!params.containsKey("method")) {
           if (params.containsKey("body")) {
               params.put("method", "base64");
           } else {
               params.put("method", "post");
           }
       }

       return params;
   }

   public Map<String, File> getFiles() {
       return new HashMap<>(files);
   }

}

Then, we have the Recaptcha class, which extends the Captcha class.

This class has three essential parameters to solve the reCaptcha:

  • site key: this parameter is the one you can find in the developer's settings.

  • action: this can be challenging to find, but it's something such as {action: do_something}

  • pageurl: the complete URL of the page where you see reCaptcha.

package com.techwithmaddy.captcha;

public class ReCaptcha extends Captcha {

   public ReCaptcha() {
       super();
       params.put("method", "userrecaptcha");
   }

   public void setSiteKey(String siteKey) {
       params.put("googlekey", siteKey);
   }

   public void setUrl(String url) {
       params.put("pageurl", url);
   }

   public void setInvisible(boolean invisible) {
       params.put("invisible", invisible ? "1" : "0");
   }

   public void setVersion(String version) {
       params.put("version", version);
   }

   public void setAction(String action) {
       params.put("action", action);
   }

   public void setScore(Double score) {
       params.put("min_score", String.valueOf(score));
   }

}

The TwoCaptcha class is the class that we'll use to instantiate on the application class, and we're going to parse the API key (indeed, it's not an abstract class).

package com.techwithmaddy.captcha;

import com.techwithmaddy.exception.ApiException;
import com.techwithmaddy.exception.NetworkException;
import com.techwithmaddy.exception.ValidationException;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

/**
* Class com.techwithmaddy.captcha.TwoCaptcha
*/
public class TwoCaptcha {

   /**
    * API KEY
    */
   private String apiKey;

   /**
    * ID of software developer. Developers who integrated their software
    * with our service get reward: 10% of spendings of their software users.
    */
   private int softId;

   /**
    * URL to which the result will be sent
    */
   private String callback;

   /**
    * How long should wait for captcha result (in seconds)
    */
   private int defaultTimeout = 120;

   /**
    * How long should wait for recaptcha result (in seconds)
    */
   private int recaptchaTimeout = 600;

   /**
    * How often do requests to `/res.php` should be made
    * in order to check if a result is ready (in seconds)
    */
   private int pollingInterval = 10;

   /**
    * Helps to understand if there is need of waiting
    * for result or not (because callback was used)
    */
   private boolean lastCaptchaHasCallback;

   /**
    * Network client
    */
   private ApiClient apiClient;

   /**
    * com.techwithmaddy.captcha.TwoCaptcha constructor
    */
   public TwoCaptcha() {
       this.apiClient = new ApiClient();
   }

   /**
    * com.techwithmaddy.captcha.TwoCaptcha constructor
    *
    * @param apiKey
    */
   public TwoCaptcha(String apiKey) {
       this();
       setApiKey(apiKey);
   }

   /**
    * @param apiKey
    */
   public void setApiKey(String apiKey) {
       this.apiKey = apiKey;
   }

   /**
    * @param softId
    */
   public void setSoftId(int softId) {
       this.softId = softId;
   }

   /**
    * @param callback
    */
   public void setCallback(String callback) {
       this.callback = callback;
   }

   /**
    * @param timeout
    */
   public void setDefaultTimeout(int timeout) {
       this.defaultTimeout = timeout;
   }

   /**
    * @param timeout
    */
   public void setRecaptchaTimeout(int timeout) {
       this.recaptchaTimeout = timeout;
   }

   /**
    * @param interval
    */
   public void setPollingInterval(int interval) {
       this.pollingInterval = interval;
   }

   /**
    * @param apiClient
    */
   public void setHttpClient(ApiClient apiClient) {
       this.apiClient = apiClient;
   }

   /**
    * Sends captcha to `/in.php` and waits for it's result.
    * This helper can be used instead of manual using of `send` and `getResult` functions.
    *
    * @param captcha
    * @throws Exception
    */
   public void solve(Captcha captcha) throws Exception {
       Map<String, Integer> waitOptions = new HashMap<>();

       if (captcha instanceof ReCaptcha) {
           waitOptions.put("timeout", recaptchaTimeout);
       }

       solve(captcha, waitOptions);
   }

   /**
    * Sends captcha to `/in.php` and waits for it's result.
    * This helper can be used instead of manual using of `send` and `getResult` functions.
    *
    * @param captcha
    * @param waitOptions
    * @throws Exception
    */
   public void solve(Captcha captcha, Map<String, Integer> waitOptions) throws Exception {
       captcha.setId(send(captcha));

       if (!lastCaptchaHasCallback) {
           waitForResult(captcha, waitOptions);
       }
   }

   /**
    * This helper waits for captcha result, and when result is ready, returns it
    *
    * @param captcha
    * @param waitOptions
    * @throws Exception
    */
   public void waitForResult(Captcha captcha, Map<String, Integer> waitOptions) throws Exception {
       long startedAt = (long)(System.currentTimeMillis() / 1000);

       int timeout = waitOptions.getOrDefault("timeout", this.defaultTimeout);
       int pollingInterval = waitOptions.getOrDefault("pollingInterval", this.pollingInterval);

       while (true) {
           long now = (long)(System.currentTimeMillis() / 1000);

           if (now - startedAt < timeout) {
               Thread.sleep(pollingInterval * 1000);
           } else {
               break;
           }

           try {
               String result = getResult(captcha.getId());
               if (result != null) {
                   captcha.setCode(result);
                   return;
               }
           } catch (NetworkException e) {
               // ignore network errors
           }
       }

       throw new TimeoutException("Timeout " + timeout + " seconds reached");
   }

   /**
    * Sends captcha to '/in.php', and returns its `id`
    *
    * @param captcha
    * @return
    * @throws Exception
    */
   public String send(Captcha captcha) throws Exception {
       Map<String, String> params = captcha.getParams();
       Map<String, File> files = captcha.getFiles();

       sendAttachDefaultParams(params);

       validateFiles(files);

       String response = apiClient.in(params, files);

       if (!response.startsWith("OK|")) {
           throw new ApiException("Cannot recognise api response (" + response + ")");
       }

       return response.substring(3);
   }

   /**
    * Returns result of captcha if it was solved or `null`, if result is not ready
    *
    * @param id
    * @return
    * @throws Exception
    */
   public String getResult(String id) throws Exception {
       Map<String, String> params = new HashMap<>();
       params.put("action", "get");
       params.put("id", id);

       String response = res(params);

       if (response.equals("CAPCHA_NOT_READY")) {
           return null;
       }

       if (!response.startsWith("OK|")) {
           throw new ApiException("Cannot recognise api response (" + response + ")");
       }

       return response.substring(3);
   }

   /**
    * Gets account's balance
    *
    * @return
    * @throws Exception
    */
   public double balance() throws Exception {
       String response = res("getbalance");
       return Double.parseDouble(response);
   }

   /**
    * Reports if captcha was solved correctly (sends `reportbad` or `reportgood` to `/res.php`)
    *
    * @param id
    * @param correct
    * @throws Exception
    */
   public void report(String id, boolean correct) throws Exception {
       Map<String, String> params = new HashMap<>();
       params.put("id", id);

       if (correct) {
           params.put("action", "reportgood");
       } else {
           params.put("action", "reportbad");
       }

       res(params);
   }

   /**
    * Makes request to `/res.php`
    *
    * @param action
    * @return
    * @throws Exception
    */
   private String res(String action) throws Exception {
       Map<String, String> params = new HashMap<>();
       params.put("action", action);
       return res(params);
   }

   /**
    * Makes request to `/res.php`
    *
    * @param params
    * @return
    * @throws Exception
    */
   private String res(Map<String, String> params) throws Exception {
       params.put("key", apiKey);
       return apiClient.res(params);
   }

   /**
    * Attaches default parameters to request
    *
    * @param params
    */
   private void sendAttachDefaultParams(Map<String, String> params) {
       params.put("key", apiKey);

       if (callback != null) {
           if (!params.containsKey("pingback")) {
               params.put("pingback", callback);
           } else if (params.get("pingback").length() == 0) {
               params.remove("pingback");
           }
       }

       lastCaptchaHasCallback = params.containsKey("pingback");

       if (softId != 0 && !params.containsKey("soft_id")) {
           params.put("soft_id", String.valueOf(softId));
       }
   }

   /**
    * Validates if files parameters are correct
    *
    * @param files
    * @throws ValidationException
    */
   private void validateFiles(Map<String, File> files) throws ValidationException {
       for (Map.Entry<String, File> entry : files.entrySet()) {
           File file = entry.getValue();

           if (!file.exists()) {
               throw new ValidationException("File not found: " + file.getAbsolutePath());
           }

           if (!file.isFile()) {
               throw new ValidationException("Resource is not a file: " + file.getAbsolutePath());
           }
       }
   }

}

Finally, we have the Captcha Application. In this class, we:

  • Instantiate a TwoCaptcha object and parse the API_KEY.
  • Instantiate a ReCaptcha object which we'll use to set the site key and URL.
  • A try/catch block to handle exceptions.
package com.techwithmaddy.example;

import com.techwithmaddy.captcha.ReCaptcha;
import com.techwithmaddy.captcha.TwoCaptcha;

import java.io.IOException;

public class CaptchaApplication {

       public static void main(String[] args) throws IOException {

       TwoCaptcha solver = new TwoCaptcha("ENTER_YOUR_API_KEY");
       ReCaptcha reCaptcha = new ReCaptcha();

       reCaptcha.setSiteKey("6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u");
       reCaptcha.setUrl("https://2captcha.com/demo/recaptcha-v2");


       try {
           solver.solve(reCaptcha);
           System.out.println("Captcha solved: " + reCaptcha.getCode() + "\nId is: " + reCaptcha.getId());
       } catch (Exception e) {
           System.out.println("Error occurred: " + e.getMessage());
       }
       }

}

Start the application. ▶️

In the console, you should see something like this:

Captcha solved: 03AGdBq26z-vSsF2bZpq3hq_07mebsEJZJ_vqGE_nIsPacCZpXm6CAt6aEtrPoq9P40cldWH7YPuUHmRWn2povnPwwwMeZH114_frx4Zgz8OtLEtbVTT1yTv76tJpDyTmdvVyiAvZwWMkY6iBb5eR4nK9acXZyeNs_GYipI4Vxf9yuHbTWJy1CtBpOLjQKEgkgQpvpdjFim1syb0N_9gMzmzFSXbTLeG2izcaxd3VIrXKa9pjZkSRJJoEbkGeF5NHgQ9Qo4UEuenkg6xo9dLkiVqPbY5ht4f34ZCXalTVTUNLdyVy4zcKwdbmjhpvMCqRQR-LAMt7IYPyn7-a3Pnpsg0sakhoQE4E5FsZ-_ux2-2XCWjy37pXuIPrutZbaytQpNoB01oSJeFrSFynqO8wmfn3pNMIVXTI7DoiFFpuly18gMhyAV-JDX9W_cWu9Gu6OemviVubXMCrFJhl2a8Gm7Lk6ggezqJHMGCYG-hcKKFajRxmuV3pDoTZhlsHPONuz_MT71-isQ0P6AUsVss2LTtIoa38-JRZdpw

That's it for this article!

You've learned how to solve reCaptcha using SDK.

For more information, have a look at these links:

Until next time! 👋🏾

Did you find this article valuable?

Support Maddy by becoming a sponsor. Any amount is appreciated!