[MOBISEC] babyrev

silver35·2023년 8월 14일

Mobisec

목록 보기
1/1

문제개요

문제 apk 파일을 다운받으면 flag를 입력하는 textView가 있으며 CHECK FLAG 버튼을 누르면 flag가 참인지 거짓인지 비교해준다.

WriteUP

[MainActivity.java]
CHECK FLAG 버튼을 누르면 onClick() 함수가 호출되며 FlagChecker 클래스의 checkFlag가 호출되고 있다. result가 참이면 valid flag이며 거짓이면 Invalid flag를 출력한다.

public void onClick(View v) {
                int color;
                String msg;
                String flag = flagWidget.getText().toString();
                boolean result = FlagChecker.checkFlag(MainActivity.this, flag);
                if (result) {
                    msg = "Valid flag!";
                    color = -16737536;
                } else {
                    msg = "Invalid flag";
                    color = SupportMenu.CATEGORY_MASK;
                }
                resultWidget.setText(msg);
                resultWidget.setTextColor(color);
            }
        });

[FlagChecker.java]
checkFlag 함수를 확인해보면 if안의 조건절이 모두 거짓이며 flag의 8번째 글자부터 마지막-1 글자가 r과 일치해야 True를 반환한다.

public static boolean checkFlag(Context ctx, String flag) {
        if (!flag.startsWith("MOBISEC{") || new StringBuilder(flag).reverse().toString().charAt(0) != '}' || flag.length() != 35 || !flag.toLowerCase().substring(8).startsWith("this_is_") || !new StringBuilder(flag).reverse().toString().toLowerCase().substring(1).startsWith(ctx.getString(C0267R.string.last_part)) || flag.charAt(17) != '_' || flag.charAt((int) (getY() * Math.pow(getX(), getY()))) != flag.charAt(((int) Math.pow(Math.pow(2.0d, 2.0d), 2.0d)) + 1) || !bam(flag.toUpperCase().substring(getY() * getX() * getY(), (int) (Math.pow(getZ(), getX()) - 1.0d))).equals("ERNYYL") || flag.toLowerCase().charAt(16) != 'a' || flag.charAt(16) != flag.charAt(26) || flag.toUpperCase().charAt(25) != flag.toUpperCase().charAt(26) + 1) {
            return false;
        }
        String r = getR();
        return flag.substring(8, flag.length() - 1).matches(r);
    }

먼저, if문 안에 조건을 분석하면 아래와 같다.

!flag.startsWith("MOBISEC{") 
-> 먼저 MOBISEC{으로 flag가 시작되야한다 
-> flag = MOBISEC{ 

|| new StringBuilder(flag).reverse().toString().charAt(0) != '}'
-> flag의 마지막 글자가 } 여야한다
-> flag = MOBISEC{}

|| flag.length() != 35
-> flag의 길이는 35이다.

|| !flag.toLowerCase().substring(8).startsWith("this_is_") 
-> flag의 인덱스 8부터 this_is_로 구성되야한다.
-> flag = MOBISEC{this_is_}

|| !new StringBuilder(flag).reverse().toString().toLowerCase().substring(1).startsWith(ctx.getString(C0267R.string.last_part))
-> public static final int last_part = 2131427368;로 C0267R 클래스에 ID가 정의되어 있다. 
-> res → strings.xml에 보면 last_part의 값이 ver_cis인 것을 알 수 있다.이를 역순으로 뒤집으로 sic_rev가 된다.
-> flag = MOBISEC{this_is_ooooooooooosic_rev}

|| flag.charAt(17) != '_'
-> 인덱스 17의 문자가 _
-> flag = MOBISEC{this_is_o_ooooooooosic_rev}

|| flag.charAt((int) (getY() * Math.pow(getX(), getY()))) != flag.charAt(((int) Math.pow(Math.pow(2.0d, 2.0d), 2.0d)) + 1) 
-> getX() = 2, getY() = 3을 반환한다.
-> flag.charAt(24) != flag.charAt(17) 
-> 인덱스 24의 문자가 _
-> flag = MOBISEC{this_is_o_oooooo_oosic_rev}

|| !bam(flag.toUpperCase().substring(getY() * getX() * getY(), (int) (Math.pow(getZ(), getX()) - 1.0d))).equals("ERNYYL") 
-> getX() = 2, getY() = 3, getZ() = 5를 반환한다.
-> !bam(flag.toUpperCase().substring(18, (24)).equals("ERNYYL")
-> bam 함수를 분석하면 ‘\r’(carriage retrun)은 아스키코드 13을 의미한다. 현재 전달된 flag는 모두 대문자이며 out =”ERNYYN”이다. 따라서, 입력값은 REALLY이다
-> flag = MOBISEC{this_is_o_REALLY_oosic_rev}

|| flag.toLowerCase().charAt(16) != 'a' 
-> 인덱스 16은 a
-> flag = MOBISEC{this_is_a_REALLY_oosic_rev}

|| flag.charAt(16) != flag.charAt(26) 
-> flag = MOBISEC{this_is_a_REALLY_oasic_rev}

|| flag.toUpperCase().charAt(25) != flag.toUpperCase().charAt(26) + 1
-> flag = MOBISEC{this_is_a_REALLY_basic_rev}

즉, if문을 통과하려면 flag = MOBISEC{this_is_a_REALLY_basic_rev}로 설정되어야 한다. 이후 String r = getR();이 호출되 r에 저장된다. getR 함수는 r변수에 upper이 true일때 [A-Z_]을 false일때 [a-z_]을 저장한다.

public static String getR() {
        String r = BuildConfig.FLAVOR;
        boolean upper = true;
        for (int i = 0; i < 26; i++) {
            r = upper ? r + "[A-Z_]" : r + "[a-z_]";
            upper = !upper;
        }
        return r;
    }

즉, r 변수에는 다음과 같이 정규표현식이 저장된다.

r = "[A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_]"

이후 flag.substring(8, flag.length() - 1).matches(r);가 실행되는데 flag의 값이 8번째부터 마지막-1까지 정규표현식 r과 매치되는지 확인하여 일치하면 True를 일치하지 않으면 False를 반환한다. 즉, flag는 번갈아서 대문자,소문자의 형식으로 저장되어야 하며 최종 flags는 아래와 같다.

MOBISEC{ThIs_iS_A_ReAlLy_bAsIc_rEv}

1개의 댓글

comment-user-thumbnail
2023년 8월 14일

큰 도움이 되었습니다, 감사합니다.

답글 달기