Google CTF Quals 2018 – Shall We Play A Game?

Description: Win the game 1,000,000 times to get the flag.

Google CTF Shall We play a Game?

Install the apk with adb

				
					adb install -r shallweplayagame.apk
				
			

It seems to be the game of tic-tac-toe, and we need 1,000,000 games won to get the flag.
If we lose, the application closes and the counter will return to 0.

Our intention will not be to win 1,000,000 games in a row. But it will be winning 1,000,000 times in one play.
So let’s read the source code to understand how the app works using jadx (GUI version)
But before, let’s decompile the source code with APKTool

				
					apktool d shallweplayagame.apk
				
			

The package name is com.google.ctf.shallweplayagame and there are just one activity, which is com.google.ctf.shallweplayagame.GameActivity.
But, there are three classes in the app, which is: a()b() and N().

Anyways, the most valuable code is here

				
					void m() {
    Object _ = N._(0, N.a, 0);
    N._(0, N.c, _, 2, N._(1, N.b, this.q, 1));
    ((TextView) findViewById(R.id.score)).setText(new String((byte[]) N._(0, N.d, _, this.r)));
    o();
}

void n() {
    for (int i = 0; i < 3; i++) {
        for (int i2 = 0; i2 < 3; i2++) {
            this.l[i2][i].a(a.EnumC0032a.EMPTY, 25);
        }
    }
    k();
    this.o++;
    Object _ = N._(2, N.e, 2);
    N._(2, N.f, _, this.q);
    this.q = (byte[]) N._(2, N.g, _);
    
    if (this.o == 1000000) {
        m();
    } else {
        ((TextView) findViewById(R.id.score)).setText(String.format("%d / %d", Integer.valueOf(this.o), 1000000));
    }
}
				
			

Where m() show the flag.
this.o++; increase the win counter.
if (this.o == 1000000) checks if the counter is 1,000,000.

I have tried several ways, for example, that the counter starts directly at 1,000,000 and I also tried to patch the code by modifying that if we win, we jump to the m() function.

But there was no success because it makes a series of extra validations.
And that’s because there is some sort of loop that must be executed 1,000,000 times.

Then, we will need to create a loop that tells the application that we have already won 1,000,000 times.

We need modify the .smali file of GameActivity.
We can found that in
shallweplayagame/smali/com/google/ctf/shallweplayagame

And the code that we’ll introduce/modify is in the n() method.
Which start in the line code number 666.

Looking this value 0xf4240 in hex, which mean 1,000,000.
Here’s the win counter

And here the win check

In these section, we need add a loop which runs 1,000,000 times.

Go to the .locals 10 line in the top of the method and change 10 by 11. This is for add a new local variable unit.
Under const/4 v6, 0x2 we need initialize the new variable (v10) to 0 (0x0).

After add-int/lit8 v0, v0, 0x1 which is in :cond_1, add this instruction move v0, v9. This will move 1,000,000 in win counter.

Under the iput that is follow from the new code write, add

				
					:goto_3 # New loop

if-ge v10, v9, :cond_3 # break condition, if v10 is 1,000,000

add-int/lit8 v10, v10, 0x1 # Increase counter of our new variable by 1
				
			

Must looks like

Finally, let’s go further down in the code, search for iput-object v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B.

We’ll add this two code lines

				
					goto :goto_3

:cond_3
				
			

Look like

The entire n() methods is here

				
					.method n()V
    .locals 11 # Add new local variable

    const v9, 0xf4240

    const/4 v8, 0x1

    const/4 v7, 0x3

    const/4 v1, 0x0

    const/4 v6, 0x2

    const v10, 0x0 # Add new variable initialize to 0

    move v2, v1

    :goto_0
    if-ge v2, v7, :cond_1

    move v0, v1

    :goto_1
    if-ge v0, v7, :cond_0

    iget-object v3, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a;

    aget-object v3, v3, v0

    aget-object v3, v3, v2

    sget-object v4, Lcom/google/ctf/shallweplayagame/a$a;->a:Lcom/google/ctf/shallweplayagame/a$a;

    const/16 v5, 0x19

    invoke-virtual {v3, v4, v5}, Lcom/google/ctf/shallweplayagame/a;->a(Lcom/google/ctf/shallweplayagame/a$a;I)V

    add-int/lit8 v0, v0, 0x1

    goto :goto_1

    :cond_0
    add-int/lit8 v0, v2, 0x1

    move v2, v0

    goto :goto_0

    :cond_1
    invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->k()V

    iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I

    add-int/lit8 v0, v0, 0x1

    move v0, v9 # Move 1,000,000 in win counter

    iput v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I

    :goto_3 # New loop

    if-ge v10, v9, :cond_3 # break condition, if v10 is 1,000,000

    add-int/lit8 v10, v10, 0x1 # Increase counter of our new variable by 1

    new-array v0, v7, [Ljava/lang/Object;

    invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v2

    aput-object v2, v0, v1

    sget-object v2, Lcom/google/ctf/shallweplayagame/N;->e:[I

    aput-object v2, v0, v8

    invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v2

    aput-object v2, v0, v6

    invoke-static {v0}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object;

    move-result-object v0

    const/4 v2, 0x4

    new-array v2, v2, [Ljava/lang/Object;

    invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v3

    aput-object v3, v2, v1

    sget-object v3, Lcom/google/ctf/shallweplayagame/N;->f:[I

    aput-object v3, v2, v8

    aput-object v0, v2, v6

    iget-object v3, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B

    aput-object v3, v2, v7

    invoke-static {v2}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object;

    new-array v2, v7, [Ljava/lang/Object;

    invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v3

    aput-object v3, v2, v1

    sget-object v3, Lcom/google/ctf/shallweplayagame/N;->g:[I

    aput-object v3, v2, v8

    aput-object v0, v2, v6

    invoke-static {v2}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object;

    move-result-object v0

    check-cast v0, [B

    check-cast v0, [B

    iput-object v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B

    goto :goto_3 # End of the loop, then, come back to goto_3 until v10 is 1,000,000

    :cond_3 # Break, come here if v10 is 1,000,000

    iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I

    if-ne v0, v9, :cond_2

    invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->m()V

    :goto_2
    return-void

    :cond_2
    const v0, 0x7f070055

    invoke-virtual {p0, v0}, Lcom/google/ctf/shallweplayagame/GameActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/TextView;

    const-string v2, "%d / %d"

    new-array v3, v6, [Ljava/lang/Object;

    iget v4, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I

    invoke-static {v4}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v4

    aput-object v4, v3, v1

    invoke-static {v9}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v1

    aput-object v1, v3, v8

    invoke-static {v2, v3}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

    move-result-object v1

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V

    goto :goto_2
.end method
				
			

Now it’s rebuild time!
With APKTool, build

				
					apktool b shallweplayagame
				
			

Generate a new key

				
					keytool -genkey -v -keystore name.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias alias
				
			

Align with zipalign

				
					zipalign -v -p 4 shallweplayagame/dist/shallweplayagame.apk shallweplayagame-aligned.apk
				
			

Then, sign the apk

				
					jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore name.keystore shallweplayagame-aligned.apk alias
				
			

Uninstall the original apk and then, install the new

				
					adb install -r shallweplayagame-aligned.apk
				
			

When we run the application, and win the first game, it will appear that the application has crashed.
But don’t worry, remember that it must be run 1,000,000 times to win.

If Android tells us that the application is not responding, we will tell it to wait.

We wait a few moments and we will get the flag.

This flag is encoded with a technique that removes secondary vowels from words. Interpreting the content, it looks something like this:
CTF{TheLossOfInnocenceIsThePriceOfApplause}
That is:
The Loss Of Innocence Is The Price Of Applause”.

I hope you found it useful (:

Leave a Reply

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