문제 apk 파일을 다운받으면 flag를 입력하는 textView가 있으며 CHECK FLAG 버튼을 누르면 flag가 참인지 거짓인지 비교해준다.
[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}

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