Skip to content

Latest commit

 

History

History
 
 

Water Color

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Water Color

Well we think this puzzle is hard!

Attachments:

Solution

Used Bluestacks to emulate and jadx-gui to decompile the apk.

This is the obfuscated MainActivity of the app.

public class bj9704hd extends Fragment {
    String sf8352oh;
    String tq9643uf;

    public native void extraInfo();

    public native byte[] initFromJNI();

    static {
        System.loadLibrary("native-lib");
    }

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
    }

    public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle bundle) {
        View inflate = layoutInflater.inflate(R.layout.fragment_login, viewGroup, false);
        final EditText editText = (EditText) inflate.findViewById(R.id.usernametext);
        final EditText editText2 = (EditText) inflate.findViewById(R.id.passwordtext);
        ((Button) inflate.findViewById(R.id.loginbtn)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                bj9704hd.this.sf8352oh = editText.getText().toString();
                bj9704hd.this.tq9643uf = editText2.getText().toString();
                if (bj9704hd.this.sf8352oh.matches("") || bj9704hd.this.tq9643uf.matches("")) {
                    Toast.makeText(bj9704hd.this.getActivity().getApplicationContext(), "Username and Password are required", 0).show();
                } else if (new dh7645oi().wr9452tb(bj9704hd.this.tq9643uf)) {
                    bj9704hd.this.extraInfo();
                    SharedPreferences.Editor edit = bj9704hd.this.getActivity().getSharedPreferences("watercolor", 0).edit();
                    edit.putString("password", bj9704hd.this.tq9643uf);
                    edit.commit();
                    bj9704hd.this.getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new nt9923ng()).addToBackStack("main").commit();
                } else {
                    Toast.makeText(bj9704hd.this.getActivity().getApplicationContext(), "Incorrect Username or Password", 0).show();
                }
            }
        });
        return inflate;
    }

    class dh7645oi {
        Cipher uh7608nh;

        public dh7645oi() {
            try {
                MessageDigest instance = MessageDigest.getInstance("SHA-256");
                byte[] initFromJNI = bj9704hd.this.initFromJNI();
                instance.update(initFromJNI, 0, initFromJNI.length);
                SecretKeySpec secretKeySpec = new SecretKeySpec(instance.digest(), "AES");
                Cipher instance2 = Cipher.getInstance("AES");
                this.uh7608nh = instance2;
                instance2.init(1, secretKeySpec);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e2) {
                e2.printStackTrace();
            } catch (NoSuchPaddingException e3) {
                e3.printStackTrace();
            }
        }

        public String fg8461gs(String str) {
            try {
                return Base64.encodeToString(this.uh7608nh.doFinal(str.getBytes()), 2);
            } catch (BadPaddingException e) {
                e.printStackTrace();
                return "";
            } catch (IllegalBlockSizeException e2) {
                e2.printStackTrace();
                return "";
            }
        }

        public boolean wr9452tb(String str) {
            try {
                String readLine = new BufferedReader(new InputStreamReader(bj9704hd.this.getActivity().getAssets().open("uw7289nv"))).readLine();
                if (readLine == null || !readLine.equals(fg8461gs(str))) {
                    return false;
                }
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
    }
}

It checks only the password when we try to login. The check is as follows...

  • initFromJNI from native library gives a string. The SHA-256 digest of the string is used as a key for AES ECB (No IV was used)
  • The password is encryped using the above AES instance
  • The encrypted password is base64 encoded and checked with asset uw7289nv which is...

r6EszdHWf8TkNrC+BLfiXILGelpBPHw7koCOeLpBv8w= --> afa12ccdd1d67fc4e436b0be04b7e25c82c67a5a413c7c3b92808e78ba41bfcc

Password

I have Bluestacks rooted and ADB with Frida Server... So I used JNItrace to log the JNI and tried to login...

Key is MfVLoHWWiAyeaTl SHA-256 --> 770f97bc76546a3508b6be54adac963af6e86f67551993904f56feccb0ce2388

AES decrypting it gives the password Th!$_!$_$uPp0s3d_t0_63_$3cur3

There is another function that is called from native library when we succesfully login extraInfo. I used IDA to decompile the native library, at the end of the function we see this...

  __android_log_print(4LL, "watercolor", "valid username is: %s", v3);

So instead of reversing the function I used ADB logcat to see the logs, then logged in the apk with the valid password.

Username

04-26 19:35:23.335  1520  1577 I audio_hw_primary: choose pcmC0D0p for 0
04-26 19:35:23.337  5105  5105 I watercolor: valid username is: !@W@t3RC^l3r&$3cr3t!U53R#
04-26 19:35:23.352  5105  5114 I art     : Do partial code cache collection, code=24KB, data=29KB
04-26 19:35:23.354  5105  5114 I art     : After code cache collection, code=24KB, data=29KB
04-26 19:35:23.354  5105  5114 I art     : Increasing code cache capacity to 128KB
04-26 19:35:23.362  1846  1877 D InputMethodManagerService: packageName=io.peykar.watercolor, activityName=.kg8923um

So username is !@W@t3RC^l3r&$3cr3t!U53R#

Now we got the username and password but where is flag??? There is a solvedPuzzleUI function in another class...

solvedPuzzleUI

    private void solvedPuzzleUI() {
        LinearLayout linearLayout = (LinearLayout) this.hd9863mm.findViewById(R.id.pauseContainer);
        getLayoutInflater().inflate(R.layout.puzzle_solved_ui, linearLayout, true);
        linearLayout.setClickable(true);
        linearLayout.setVisibility(0);
        ((Button) this.hd9863mm.findViewById(R.id.okBtn)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                new Thread(new Runnable() {
                    public void run() {
                        try {
                            URI uri = new URI(kj0762ey.this.ss2176jv.getResources().getString(R.string.hostname));
                            OkHttpClient unused = kj0762ey.this.fs9572kw = se5498vr.ab3702ns(uri.getHost(), kj0762ey.this.ss2176jv.getResources().getString(R.string.fingerprint));
                            String string = kj0762ey.this.gs0973mt.getString("password", "");
                            JSONObject jSONObject = new JSONObject();
                            try {
                                jSONObject.put("username", "find a valid username to capture the flag");
                                jSONObject.put("password", string);
                                kj0762ey.this.fs9572kw.newCall(new Request.Builder().url(uri.toURL()).post(RequestBody.create(kj0762ey.this.yh5921pa, jSONObject.toString())).build()).enqueue(new Callback() {
                                    public void onFailure(Call call, IOException iOException) {
                                        Log.i("watercolor", "Failure in sending request");
                                        iOException.printStackTrace();
                                    }

                                    public void onResponse(Call call, Response response) {
                                        Log.i("watercolor", "Response received successfully");
                                    }
                                });
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        } catch (Exception e2) {
                            e2.printStackTrace();
                        }
                    }
                }).start();
                kj0762ey.this.getActivity().getSupportFragmentManager().popBackStack();
                kj0762ey.this.getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new bj9704hd()).addToBackStack("login").commit();
            }
        });

On solving the puzzle it sends a JSON with username and password to the address in hostname.

In Resources/resources.arsc/res/values/strings.xml we get the hostname https://water-color.peykar.io

I made a post request with curl to get the flag

curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"username":"!@W@t3RC^l3r&$3cr3t!U53R#","password":"Th!$_!$_$uPp0s3d_t0_63_$3cur3"}' \
  https://water-color.peykar.io

{"flag":"S4CTF{W4t3r_C0l0r_is_Beaut1fu1_But_Danger0u5_B3_C4r3ful!!}"}

Flag

S4CTF{W4t3r_C0l0r_is_Beaut1fu1_But_Danger0u5_B3_C4r3ful!!}