Droid Flag - KnightCTF 2022

Posted on by NomanProdhan

Hello hacker

I hope you are doing lots of hacking đŸ¤Ÿ

"DroidFlag" is a straightforward reverse engineering challenge that involves analyzing an APK file to discover a hidden flag. The APK can be decompiled using tools like the online service at javadecompilers.com or the Jadx decompiler locally.

Upon decompiling and extracting the contents of the APK, the Java source code is located in /sources/org/knightsquad/droidflag/. This directory contains several Java classes including BuildConfig.java, KnightCTF.java, MainActivity.java, R.java, and StringHandler.java.

The initial examination of KnightCTF.java, revealed a decoy flag.

package org.knightsquad.droidflag;

public class KnightCTF {
    public static String flag = "KCTF{its_a_fake_flag}";

Further investigation into MainActivity.java showed that it compares two strings and displays an alert based on the comparison. One string is user-input while the other is generated by StringHandler.

package org.knightsquad.droidflag;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    /* access modifiers changed from: private */
    public EditText inputFlagET;
    private TextView kctfTitle;
    private Button validateButton;

    /* access modifiers changed from: protected */
    public void onCreate(Bundle bundle) {
        setContentView((int) R.layout.activity_main);
        TextView textView = (TextView) findViewById(R.id.kctf2022Title);
        this.kctfTitle = textView;
        textView.setText("|| KnightCTF 2022 || Organized by Knight Squad ||");
        this.inputFlagET = (EditText) findViewById(R.id.inputFlag);
        Button button = (Button) findViewById(R.id.checkButton);
        this.validateButton = button;
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                String obj = MainActivity.this.inputFlagET.getText().toString();
                if (obj.isEmpty()) {
                    Toast.makeText(MainActivity.this, "Please enter a flag", 0).show();
                } else if (obj.length() <= 10) {
                    Toast.makeText(MainActivity.this, KnightCTF.flag, 0).show();
                } else {
                    StringHandler stringHandler = new StringHandler();
                    if ((stringHandler.getS1(MainActivity.this) + "{" + stringHandler.getS3(MainActivity.this) + "_" + stringHandler.getS2(MainActivity.this) + "_" + stringHandler.getS4(MainActivity.this) + "}").equals(obj)) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.AlertDialog);
                        builder.setTitle((CharSequence) MainActivity.this.getResources().getString(R.string.s9));
                        builder.setMessage((CharSequence) MainActivity.this.getResources().getString(R.string.s10));
                    AlertDialog.Builder builder2 = new AlertDialog.Builder(MainActivity.this, R.style.AlertDialog);
                    builder2.setTitle((CharSequence) MainActivity.this.getResources().getString(R.string.s11));
                    builder2.setMessage((CharSequence) MainActivity.this.getResources().getString(R.string.s12));

The StringHandler class appears to manipulate strings fetched using the R class, including reversing some strings before returning them. To understand the manipulation, the strings of interest—s5, s6, s7, and s8—are located in /resources/res/values/strings.xml. These specific strings are crucial to understanding the generation of the correct flag within the application.

package org.knightsquad.droidflag;

import android.content.Context;
import androidx.appcompat.app.AppCompatActivity;

public class StringHandler extends AppCompatActivity {
    public String getS1(Context context) {
        return context.getResources().getString(R.string.s5);

    public String getS2(Context context) {
        return new StringBuilder(context.getResources().getString(R.string.s6)).reverse().toString();

    public String getS3(Context context) {
        return new StringBuilder(context.getResources().getString(R.string.s7)).reverse().toString();

    public String getS4(Context context) {
        return new StringBuilder(context.getResources().getString(R.string.s8)).reverse().toString();

    <string name="s1">KCTF{}</string>
    <string name="s10">You have entered the right flag. Good Job.</string>
    <string name="s11">Warning</string>
    <string name="s12">You have entered a wrong flag. Pleae try again.</string>
    <string name="s2">KCTF2022{}</string>
    <string name="s3">KnightCTF{}</string>
    <string name="s4">KnightCTF200{}</string>
    <string name="s5">KCTF</string>
    <string name="s6">3LpM1s</string>
    <string name="s7">D10RdNa</string>
    <string name="s8">3Sr3V3r</string>
    <string name="s9">Congratulations</string>

Generating the actual flag from the DroidFlag challenge is straightforward. Start with the string s5, which is "KCTF", and append "{" to it. Next, take the string s7, "D10RdNa", reverse it to "aNdR01D", and concatenate this to the existing string followed by an underscore. Continue by reversing string s6, "3LpM1s", to "s1MpL3", and append this with another underscore . Finally, reverse string s8, "3Sr3V3r", to "r3V3rS3", and add this to the string. Close with "}" to complete the format. This sequence constructs the flag correctly.

Its an easy challenge and I hope you enjoyed the writeup.