We get presented with the following challenge:
John bets nobody can find the passphrase to login!
GPG key: viphHowrirOmbugTudIbavMeuhacyet'
The file extracts to an apk (Android package file). Running strings on it doesn't give us any insight into what's going on. Let's load it into Genymotion:
Ok, so we have to guess a password. Let's pop this open in JD-GUI:
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130968599);
if ((a(getApplicationContext(), 2)) || (a(getApplicationContext(), "flagging{It_cannot_be_easier_than_this}")) || (a(getApplicationContext(), false)) || (a(getApplicationContext(), 2.78D))) {
Toast.makeText(getApplicationContext(), getString(2131492925), 1).show();
}
for (;;)
{
this.a = ((EditText)findViewById(2131361877));
((Button)findViewById(2131361878)).setOnClickListener(new a(this));
this.b = findViewById(2131361875);
return;
Toast.makeText(getApplicationContext(), getString(2131492922), 1).show();
}
}
The "flagging{It_cannot_be_easier_than_this}" looked interesting, but didn't turn out to be anything. However, the following snippet of code performs the validation check:
private boolean a(String paramString)
{
if (paramString.equals(c.a(b.a(b.b(b.c(b.d(b.g(b.h(b.e(b.f(b.i(c.c(c.b(c.d(getString(2131492920))))))))))))))))
{
Toast.makeText(getApplicationContext(), getString(2131492924), 1).show();
return true;
}
return false;
}
That's interesting. So we could theoretically use those functions to compute the answer. However, it doesn't appear to be dynamic in nature, it just builds the string to test against each time. I decided to just run the apk in a debugger. Looking through the smali output for this apk, we find the following:
.method private a(Ljava/lang/String;)Z
.locals 3
const/4 v0, 0x1
const v1, 0x7f0c0038
invoke-virtual {p0, v1}, Lit/polictf2015/LoginActivity;->getString(I)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/c;->d(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/c;->b(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/c;->c(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->i(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->f(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->e(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->h(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->g(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->d(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->b(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/b;->a(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-static {v1}, Lit/polictf2015/c;->a(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-virtual {p1, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v1
if-eqz v1, :cond_0
invoke-virtual {p0}, Lit/polictf2015/LoginActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v1
const v2, 0x7f0c003c
invoke-virtual {p0, v2}, Lit/polictf2015/LoginActivity;->getString(I)Ljava/lang/String;
move-result-object v2
invoke-static {v1, v2, v0}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v1
invoke-virtual {v1}, Landroid/widget/Toast;->show()V
:goto_0
return v0
:cond_0
const/4 v0, 0x0
goto :goto_0
.end method
My guess is the " invoke-virtual {p1, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z" would be a good place to pause since we're checking if our input is correct. Sure enough, out pops the flag.
flag{Maybe_This_Obfuscation_Was_Not_That_Good_As_We_Thought}