• [Flare-on 2019] Flarebear

    2020. 1. 9.

    by. ugonfor

    flarebear.apk (.apk 파일 분석)

    문제설명

    주어진 파일은 다음과 같다.

    1. apk파일

    2. 메세지

      메세지에는 곰돌이를 alive, happy 하게 하면 flag를 보여줄거다 라고 쓰여있음

      힌트인거 같다.


    필요한 툴
    1. jd-gui (자바 디컴파일러)
    2. dex2jar (class.dex파일을 jar파일로 변환시켜야 함)

    Solution

    먼저 apk 압출을 해제하고, classes.dex 파일을 .jar 파일로 변환시켰다.

    이후 jd-gui를 통해서 classes-dex2jar.jar를 열어보았다.

    파일 목록을 보면 다음과 같다.

    이중 다른 나머지는 자잘한 코드들인 거 같고, 주가 되는 코드들은 com.fireeye.flarebear에 있었다.

    처음에는 MainActivity가 주된 코드인것 같아, 이 코드만 열심히 확인해 보았다.

    하지만, 별다른 소득이 없어 실제로 apk파일을 핸드폰에 설치해 실행시켜보았다.

    메인화면 곰돌이 화면 똥이 없으면 나오는 문구

    실제 실행되는 모습과 코드를 비교해 보니, MainActivity는 처음 메인화면에서 나오는 continue, new flare bear 등을 보여주고, new activity는 new flare bear를 눌렀을 때 나오는 것들을 보여주었다.

    그래서, 메인이 되는 코드가 FlareBearActivity.class 임을 알고 이 코드의 목차를 알아보았다.

    눈에 띄는 것은 getPassword() 코드와 그 위쪽에 있는 danceWithFlag() 함수이다.

    시행착오를 겪음(넘어가도 됨)

    danceWithFlag() 함수에서 getPassword가 있고, decrypt 함수도 아래에 나와 있기에 처음에는 직접 flag를 찾아내려고 하였다.

    하지만, 이 코드에서 정의해놓은 함수들을 다 알아야 하고 decrypt과정도 간단한 것이 아니고 여러 복잡한 과정을 거친다. 그래서 한참 고민을 하다가 오히려 조건을 맞춰서 danceWithFlag()함수를 실행시키는 것이 편할 거라 생각함

    그래서 danceWithFlag()가 포함된 곳을 보니 setMood()함수가 있었다.

    그리고 그 setmood함수는 FlareBearActivity 부분에 있었다. (다음 코드의 맨 아래서 7번째 줄)

    @Metadata(bv={1, 0, 3}, d1={""}, d2={"Lcom/fireeye/flarebear/FlareBearActivity;", "Landroid/support/v7/app/AppCompatActivity;", "()V", "handler", "Landroid/os/Handler;", "getHandler", "()Landroid/os/Handler;", "setHandler", "(Landroid/os/Handler;)V", "activityUi", "", "drawable", "Landroid/graphics/drawable/Drawable;", "drawable2", "addPooUi", "Landroid/widget/ImageView;", "addPoos", "changeClean", "clean", "", "changeFlareBearImage", "changeHappy", "happy", "changeImageAndTag", "changeMass", "mass", "view", "Landroid/view/View;", "cleanUi", "dance", "danceWithFlag", "decrypt", "", "password", "", "data", "feed", "feedUi", "getPassword", "getStat", "activity", "", "getState", "", "key", "defValue", "incrementPooCount", "isEcstatic", "", "isHappy", "onCreate", "savedInstanceState", "Landroid/os/Bundle;", "play", "playUi", "removePoo", "restorePoo", "saveActivity", "activityType", "setFlareBearName", "setMood", "setState", "value", "rotN", "n", "Companion", "app_release"}, k=1, mv={1, 1, 15})
    public final class FlareBearActivity extends AppCompatActivity
    {
      public static final Companion Companion = new Companion(null);
    
      @NotNull
      public static final String FLARE_BEAR_NAME = "";
      private HashMap _$_findViewCache;
    
      @NotNull
      private Handler handler = new Handler();
    
      private final void activityUi(final Drawable paramDrawable1, final Drawable paramDrawable2)
      {
        this.handler.removeCallbacksAndMessages(null);
        ((ImageView)_$_findCachedViewById(R.id.flareBearImageView)).setImageDrawable(paramDrawable1);
        ((ImageView)_$_findCachedViewById(R.id.flareBearImageView)).setTag("activity1");
        this.handler.postDelayed((Runnable)new Runnable()
        {
          public final void run()
          {
            ((ImageView)this.this$0._$_findCachedViewById(R.id.flareBearImageView)).setImageDrawable(paramDrawable2);
            ((ImageView)this.this$0._$_findCachedViewById(R.id.flareBearImageView)).setTag("activity2");
          }
        }
        , 800L);
        this.handler.postDelayed((Runnable)new Runnable()
        {
          public final void run()
          {
            ((ImageView)this.this$0._$_findCachedViewById(R.id.flareBearImageView)).setImageDrawable(paramDrawable1);
            ((ImageView)this.this$0._$_findCachedViewById(R.id.flareBearImageView)).setTag("activity1");
          }
        }
        , 1600L);
        this.handler.postDelayed((Runnable)new Runnable()
        {
          public final void run()
          {
            this.this$0.setMood();
            FlareBearActivity.access$addPoos(this.this$0);
            FlareBearActivity.access$changeFlareBearImage(this.this$0);
          }
        }
        , 2400L);
      }
    

    여기서 알 수 있는 것은 setMood()를 isHappy() 조건과 isEcstatic()를 만족시키면 곰돌이가 Flag와 함께 춤을 출 것이다.


    isHappy() , isEcstatic()

    isHappy() isEcstatic()

    이제 각 함수들을 true로 만들어 줘야 하는 데, 'f'는 아마 feed인 거 같고, 'p'도 play인 것 같아서 각 함수를 찾아봤다. 또, 활동 가능한 것중 clean도 있기에 셋다 찾아보았다.

      public final void play(@NotNull View paramView)
      {
        Intrinsics.checkParameterIsNotNull(paramView, "view");
        saveActivity("p");
        changeMass(-2);
        changeHappy(4);
        changeClean(-1);
        playUi();
      }
      public final void clean(@NotNull View paramView)
      {
        Intrinsics.checkParameterIsNotNull(paramView, "view");
        saveActivity("c");
        removePoo();
        cleanUi();
        changeMass(0);
        changeHappy(-1);
        changeClean(6);
        setMood();
      }
      public final void feed(@NotNull View paramView)
      {
        Intrinsics.checkParameterIsNotNull(paramView, "view");
        saveActivity("f");
        changeMass(10);
        changeHappy(2);
        changeClean(-1);
        incrementPooCount();
        feedUi();
      }

    isHappy를 만족하려면 'f' 'p' 의 비율이 2이상 2.5이하여야 한다. 또, isEcstatic()을 만족하려면 mass가 72, happy가 30, clean이 0이어야 한다.

    #for isHappy()
    2.0 <= float(feed/play) <= 2.5
    
    #for isEcstatic()
    #mass
    72 = play*(-2) + clean*(0)+feed*(10)
    #happy
    30 = play*(4) + clean*(-1)+feed*(2)
    #clean
    0 = play*(-1) + clean*(6)+feed*(-1)

    아래 3차 방정식의 해를 구하면 다음과 같다.

    play = 4 , clean = 2, feed = 8

    그리고 이는 happy함수도 만족한다.


    fin.

    핸드폰으로 8번 밥주고 4번 놀아주고 2번 청소해줬다.

    그랬더니 다음과 같은 화면과 함께 flag를 보여줌

    flag : th4t_was_be4rly_a_chall3nge@flare-on.com

    'Writeup > Wargame_Writeup' 카테고리의 다른 글

    [Flare-on 2019] Snake  (0) 2020.01.21
    [Flare-on 2019] Demo  (0) 2020.01.21
    [Flare-on 2019] DnsChess  (0) 2020.01.21
    [pwnable.kr] flag  (0) 2020.01.12
    [Flare-on 2019] Memecat Battlestation Write-up  (0) 2020.01.09

    댓글