리버싱CTF/mobisec

[MOBISEC] BabyRev (10)

우와해커 2020. 7. 25. 23:09

MOBISEC

babyrev (10)

Jadx-gui를 이용해서 APK파일을 디스어셈블한다.

 

MainActivity

[그림-1] MainActivity

 

분석

다음과 같은 코드를 사용하면 ID를 지정하고 리소스에 매핑된 문자열을 불러올 수 있다.

아래 필드를 따라가보면 ID넘버가 존재하며 이 ID넘버와 매핑되는 값을 strings.xml에서 찾는다.

개발에 대한 자세한 내용은 아래에 정리한 부록1을 참고한다.

final EditText flagWidget = (EditText) findViewById(C0055R.C0057id.flag);
final TextView resultWidget = (TextView) findViewById(C0055R.C0057id.result);

 

아래의 조건문에 맞는 문자열이 이 문제의 Flag값이라는 것을 알 수 있다.

if (FlagChecker.checkFlag(MainActivity.this, flagWidget.getText().toString())) {
       msg = "Valid flag!";
       color = -16737536;
} else {
       msg = "Invalid flag";
       color = SupportMenu.CATEGORY_MASK;
}

 

Flagchecker의 checkFlag 매소드를 확인해보자

 

FlagChecker.checkFlag()

크게 조건문의 두가지가 존재하며 False값을 반환하면 올바른 flag가 아니라는 것을 알 수 있다.

[그림-2] FlagChecker.checkFlag()

조건문 1

첫번째, 조건문을 단계별로 쉽게 풀기 위하여 or연산(||) 단위로 나눠서 분석한다.

1. !flag.startsWith("MOBISEC{") || 
- MOBISEC{

2. new StringBuilder(flag).reverse().toString().charAt(0) != '}' || 
- MOBISEC{   }

3. flag.length() != 35 || 
- 총 문자열의 길이가 35가 되야한다.

4. !flag.toLowerCase().substring(8).startsWith("this_is_") ||
- MOBISEC{this_is_  }

5. !new StringBuilder(flag).reverse().toString().toLowerCase().substring(1).startsWith(ctx.getString(C0055R.string.last_part)) || 
- c0055R.string.last_part의 문자열 값은 "ver_cis"인데 이것을 reverse()한다. 
- substrings(1)로 인해 마지막 문자, }가 빠진 나머지 문자열임을 알 수 있다.
- MOBISEC{this_is_  sic_rev}

6. flag.charAt(17) != '_' ||
- MOBISEC{this_is_?_  sic_rev}
- 16번째는 아직 모르기 때문에 ?를 입력하였다.

7. flag.charAt((int) (((double) getY()) * Math.pow((double) getX(), (double) getY()))) != flag.charAt(((int) Math.pow(Math.pow(2.0d, 2.0d), 2.0d)) + 1) ||
- 아래에 있는 메소드가 실행하게 된다. 각 메소드가 반환하는 값을 넣고 계산하면 된다.
- 수식을 정리하면 다음과 같다. flag.charAt(24) != flag.charAt(17)
- 24번째 문자는 '_'이 와야한다.


8. !bam(flag.toUpperCase().substring(getY() * getX() * getY(), (int) (Math.pow((double) getZ(), (double) getX()) - 1.0d))).equals("ERNYYL") || 
- bam 매소드는 굉장히 복잡하기 때문에 이 매소드를 직접 실행해서 결과를 보는 것이 낫다.
- 해당 값을 계산하면 "REALLY"가 된다.
- MOBISEC{this_is_?_REALLY_  sic_rev}


9. flag.toLowerCase().charAt(16) != 'a' ||
- 16번째 문자에 a가 와야한다.
- MOBISEC{this_is_a_REALLY_  sic_rev}


10. flag.charAt(16) != flag.charAt(26) || 
- 16번째와 24번째 문자는 같다.
- MOBISEC{this_is_a_REALLY_?asic_rev}


11. flag.toUpperCase().charAt(25) != flag.toUpperCase().charAt(26) + 1) 
- 26번째 문자는 'a'이다. 이것을 대문자로 변경하여 +1을 더하면 B가 된다.
- String 문자열이 아닌 char 타입이라 숫자와 연산이 가능하다.
- MOBISEC{this_is_a_REALLY_Basic_rev}

 

조건문 2 

이 조건문은 간단하지만 집중이 필요하다.

if (!flag.substring(8, flag.length() - 1).matches(getR())) {
            return false;
}

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

 

 

먼저 flag.length()는 35글자이므로 flag.substring(8, 34)를 계산하면

양쪽 대괄호를 제외한 문자열을 추출하는 것을 알 수 있다.

this_is_a_REALLY_Basic_rev.matches(getR()))

 

getR() 매소드를 테스트 코드로 붙여넣어서 실행하면 다음과 같다.

this_is_a_REALLY_Basic_rev.matches([A-Z_][a-z_][A-Z_][a-z_][A-Z_][a-z_].......)

match 매소드는 정규표현식을 의미하며 임시 클래스를 생성하여 테스트 해보면 대문자,소문자 순으로 매칭이 되어야 한다는 것을 알 수 있다.

 

위 조건문에 해당하는 문자열로 치환하면 다음과 같다.

ThIs_iS_A_ReAlLy_bAsIc_rEv

 

하지만 1번 조건문도 동시에 만족해야 하므로 제외됬던 대괄호 밖의 문자열이 같이 존재해야 한다.

Flag: MOBISEC{ThIs_iS_A_ReAlLy_bAsIc_rEv}


부록 1) android - Resource string읽어오기

@strings.xml
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="string_id">This is a sample test</string>

@R.java
public static final class string {
public static final int string_id=0x7f050007;

 

"This is a smaple test" 라는 string을 resource로부 읽어오기 위해서는 아래와 같은 코드가 가능하다.

1.
getString(string_id), or getResource().getString(string_id);

2.
Context context = this;
context.getString(string_id);

 

부록 2) 클래스, 매소드 정리

StringBuilder: String 문자열 만드는데 사용하는 클래스

substring(1): 1번째 인덱스부터 시작, 그이전 0번째 인덱스는 포함되지 않는다.

 

'리버싱CTF > mobisec' 카테고리의 다른 글

[MOBISEC] Pincode (10)  (0) 2020.07.26