↩ home



Android Reverse Engineering use case : bypass SSL certificate pinning on Prisma

27 Jul 2016

I've read that SSL certificate pinning is a protection against web API reverse engineering. But some ressources show how it's not and how we can bypass it like in Bypassing SSL Pinning on Android via Reverse Engineering.

And when I read the good Unbundling Pokémon Go article, I was surprised to see they pushed certificate pinning as an advice against web API reverse engineering.

But seriously, is it even a try ?

 

What's the point ? Your web API will always be visible to someone who really want to understand it. So you should probably focus your efforts on building a good REST API and consider it public. You even should publish it with a documentation. Transparency leads to more security.

I want to show you how easy we can reverse API with Prisma Android app.

 

Prisma Use Case

1. Get APK and decompile it.

I'm root so I can get APK file. And with help of this stackoverflow tutorial, I've been able to see what's inside the code :

We can see they use :

  • Retrofit
  • OkHttp
  • API interface :
public abstract interface PrismaAPI
{
  @GET("/config")
  public abstract a getConfig();

  @POST("/styles")
  public abstract StylesResponse getStyles(@Body CodesObject paramCodesObject);

  @POST("/process")
  public abstract UploadResponse process(@Body ProcessRequest paramProcessRequest);

  @GET("/status/{id}")
  public abstract UploadResponse status(@Path("id") String paramString);

  @POST("/upload/image")
  public abstract UploadResponse upload(@Body TypedInput paramTypedInput, @Header("prisma-image-sign") String paramString);
}
  • SSL certificate pinning :
  private OkHttpClient s()
  {
    OkHttpClient localOkHttpClient = new OkHttpClient();
    localOkHttpClient.setCertificatePinner(new CertificatePinner.Builder().add("api2.neuralprisma.com", new String[] { "sha1/yJUTaAGXKAosVcP805D1OgU7yfs=" }).build());
    try
    {
      SSLContext localSSLContext = SSLContext.getInstance("TLSv1.2");
      localSSLContext.init(null, null, null);
      localOkHttpClient.setSslSocketFactory(new f(localSSLContext.getSocketFactory()));
      ConnectionSpec.Builder localBuilder = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS);
      TlsVersion[] arrayOfTlsVersion = new TlsVersion[1];
      arrayOfTlsVersion[0] = TlsVersion.TLS_1_2;
      localOkHttpClient.setConnectionSpecs(Collections.singletonList(localBuilder.tlsVersions(arrayOfTlsVersion).build()));
      return localOkHttpClient;
    }
    catch (Exception localException) {}
    return localOkHttpClient;
  }

 

2.We already have detailed API interface... but we continue.

I want to print on logcat everything API requests and receive from server. I could have sniff network but because of certificate pinning.. it could have been difficult and I want to go deeper on my possibilities.

I will directly modify the code and put some Log.d(). This way I have also the possiblity to modify what we send to API and maybe try to hack it ?

Because they use Retrofit (version 1) and OkHttp (version 1 or 2) I have access to sources and I can figure out where to insert logging code. But even obfuscated, network calls are easy to find.

In retrofit/client/OkClient.class there are two interesting methods :

static com.squareup.okhttp.Request createRequest(Request paramRequest)
static Response parseResponse(com.squareup.okhttp.Response paramResponse)

First one translates Retrofit request to OkHttp. Second one translates OkHttp response to Retrofit.

This is where I will put my logs.

 

3. How to modify APK ?

After some research, I find APK Studio which allows to modify code and rebuild APK. Problem is it uses Smali format which is as user-friendly as the assembler. But I get the point : there are registers and there are operations (opcodes).

I find my two interesting methods in Smali :

.method static createRequest(Lretrofit/client/Request;)Lcom/squareup/okhttp/Request;
    .locals 6

    .prologue
    .line 57

    new-instance v0, Lcom/squareup/okhttp/Request$Builder;

    invoke-direct {v0}, Lcom/squareup/okhttp/Request$Builder;->()V

    ....

And 

.method static parseResponse(Lcom/squareup/okhttp/Response;)Lretrofit/client/Response;
    .locals 6

    .prologue
    .line 73

    new-instance v0, Lretrofit/client/Response;

    invoke-virtual {p0}, Lcom/squareup/okhttp/Response;->request()Lcom/squareup/okhttp/Request;

    ...

 

I just have to Log.d() Request and Response objects. Because it's waayyyy too difficult to code it in Smali, I develop the code in Java in a sandbox app :

    private static void logMyRequest(retrofit.client.Request test) {
        Log.d("DBG", test.getMethod() + " " + test.getUrl());
        for (retrofit.client.Header h : test.getHeaders()) {
            Log.d("DBG", "Header " + h.getName() + " " + h.getValue());
        }
    }

    private static void logMyResponse(com.squareup.okhttp.Response response) {
        Log.d("DBGR", response.code() + " " + response.message());
        for (String hn : response.headers().names()) {
            Log.d("DBGR", "Header " + hn + " " + response.headers().get(hn));
        }

        byte[] reqBuffer = new byte[1024];
        int read = -1;
        try {
            while ((read = response.body().byteStream().read(reqBuffer, 0, 1024)) >= 0) {
                Log.d("DBGR", new String(reqBuffer, 0, read));
            }
        } catch (IOException e) {
            Log.e("DBGR", e.getMessage());
        }
    }

 

Then I decompile my own sandbox app and voilà, Smali code generated.

I just need to call these two methods in Smali (which is something I could have done in Java... but I learned a little bit Smali so I can do it by myself) :

.method static parseResponse(Lcom/squareup/okhttp/Response;)Lretrofit/client/Response;
    .locals 6

    .prologue
    .line 73

# Start hack
    move-object v0, p0

    invoke-static {v0}, Lretrofit/client/OkClient;->logMyResponse(Lcom/squareup/okhttp/Response;)V
# End hack

 

4. Test this new APK.

Here the steps to test my own version of Prisma :

  • Uninstall Prisma
  • Build new APK
  • Sign new APK (with debug keystore, does not matter)
  • Install new APK 

At app launch, we get the first request which retrieve available Prisma styles :

Every API call is logged now. 

 

5. Conclusion.

  • SSL certificate pinning is useless.
  • Your REST API is visible whatever how you try to hide (it includes SECRET API / ID / PASSWORD...). Deal with it.
  • Hey, it's also a cool tutorial for APK hacking no ? ;).

 



Top