suanの備忘録

SUANが色々行ったこと等を書いていきます。

SECCON Beginners CTF 2020 WriteUpのようなもの

SECCON Beginners CTF 2020に参加しました!

SECCON Beginners CTF 2020にチームMIS.Wとしてsoeki、musaprg、shiro、tommi、aoiと参加しました。結果は700点、144位(/1009)でした。

f:id:SUAN:20200524153620p:plain
SCORE

WriteUpとか言っていますが、力業のごり押しなので悪しからず。想定解は私以外の人の方がもっと詳しくわかりやすく書いてあると思います。

SECCON Challenges

解けたもの

[MISC] emoemoencode

emoemoencodeをとりあえず

$ cat emoemoencode.txt

してみました。すると絵文字がたくさん現れてきました。

🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽

栗が多い。ま、当然なんのこっちゃわからないのでバイナリエディタを使ってバイナリを見てみました。

f:id:SUAN:20200524160025p:plain
バイナリ
ここで私は最初、全角英数を変換してこのバイナリにしていると思い、ctf4b{のバイナリと比較してみましたが、変換法則が見つからなかったので断念。
次に半角英数を見てみました。ctf4b{のバイナリと比べてみると、いい感じに規則が見つかりました。

F0 9F 8D XXの場合は
XXA0なら全体を60に置き換え、B0なら全体を70に置き換え、のように行いました。
例: F0 9F 8D A060

F0 9F 8C XXの場合は
XXB0なら全体を30に置き換え。
例: F0 9F 8C B030

これによって得られたフラッグは

ctf4b{stegan0graphy_by_em000000ji}

[REVERSING] mask

とりあえず./maskをして実行すると、

$ ./mask 
Usage: ./mask [FLAG]

と返ってくる。ので今度は[FLAG]に文字を入れて実行。

$ ./mask ctf4b{}
Putting on masks...
atd4`qu
c`b bki
Wrong FLAG. Try again.

と返ってきた。何かしら変換を行って2つの何かと比較しているようだ。

$ strings mask

を実行し、文字列を見てみると怪しい2つの文字列を発見。

atd4`qdedtUpetepqeUdaaeUeaqau
c`b bk`kj`KbababcaKbacaKiacki

あとはこれに合うように[FLAG]を決めれば良いと思い、すべてのアルファベットを探索。 ← これを力業という。

元の文字 変化後A 変化後B
a a a
b ` b
c a c
d d `
e e a
f d b
g e c
h ` h
i a i
j ` j
k a k
l d h
m e i
n d j
o e k
p p `
q q a
r p b
s q c
t t `
u u a
v t b
w u c
x p h
y q i
z p j

ここから存在しないものは記号をテキトーに入れていき、得られたフラッグは

ctf4b{dont_reverse_face_mask}

[REVERSING] yakisoba

とりあえず./yakisobaをして実行すると、

$ ./yakisoba 
FLAG: ctf4b{}
Wrong!

FLAGにはとりあえずテキトーに入れてみた。
今回はmaskとは違い、何も出てこない。strings yakisobaをしても出てこない。
何も出てこないのは困るので逆アセンブルしました。
IDAを使用して逆アセンブルをし、グラフとして出てきたもの見ていたら、1文字ずつ正誤判定しているようでした。
ので、グラフをたどっていって無事フラグが手に入りました。
ctf4b{sp4gh3tt1_r1pp3r1n0}

f:id:SUAN:20200524155122p:plain
IDA
↑これをちまちま一つずつ見ていったという、どう考えても時間かかる力業で解きました。

え?アセンブリを読めって?嫌だよ面倒くさい()

解けなかったもの

[REVERSING] siblangs

siblangs.apkだったのでとりあえず手持ちのXperia XZ Premiumにインストール、実行。

f:id:SUAN:20200524162234p:plain
siblangs1
f:id:SUAN:20200524162308p:plain
siblangs2
f:id:SUAN:20200524162333p:plain
siblangs3
ここでVALIDATE AはiOSで動かせと言われる。apkなので当然動くわけない。(持ってないから動かせないしね)
とりあえず動くVALIDATE Bの方を使うことにした。

.apkファイルはただの.zipなので解凍して、そして中のソースファイル(classes.dex)を.jarに変換後、デコンパイルしてみた。
そこで死ぬほど怪しいものを発見。

package es.o0i.challengeapp.nativemodule;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class ValidateFlagModule extends ReactContextBaseJavaModule {
  private static final int GCM_IV_LENGTH = 12;
  
  private final ReactApplicationContext reactContext;
  
  private final SecretKey secretKey = new SecretKeySpec("IncrediblySecure".getBytes(), 0, 16, "AES");
  
  private final SecureRandom secureRandom = new SecureRandom();
  
  public ValidateFlagModule(ReactApplicationContext paramReactApplicationContext) {
    super(paramReactApplicationContext);
    this.reactContext = paramReactApplicationContext;
  }
  
  public String getName() {
    return "ValidateFlagModule";
  }
  
  @ReactMethod
  public void validate(String paramString, Callback paramCallback) {
    byte[] arrayOfByte = new byte[43];
    arrayOfByte[0] = 95;
    arrayOfByte[1] = -59;
    arrayOfByte[2] = -20;
    arrayOfByte[3] = -93;
    arrayOfByte[4] = -70;
    arrayOfByte[5] = 0;
    arrayOfByte[6] = -32;
    arrayOfByte[7] = -93;
    arrayOfByte[8] = -23;
    arrayOfByte[9] = 63;
    arrayOfByte[10] = -9;
    arrayOfByte[11] = 60;
    arrayOfByte[12] = 86;
    arrayOfByte[13] = 123;
    arrayOfByte[14] = -61;
    arrayOfByte[15] = -8;
    arrayOfByte[16] = 17;
    arrayOfByte[17] = -113;
    arrayOfByte[18] = -106;
    arrayOfByte[19] = 28;
    arrayOfByte[20] = 99;
    arrayOfByte[21] = -72;
    arrayOfByte[22] = -3;
    arrayOfByte[23] = 1;
    arrayOfByte[24] = -41;
    arrayOfByte[25] = -123;
    arrayOfByte[26] = 17;
    arrayOfByte[27] = 93;
    arrayOfByte[28] = -36;
    arrayOfByte[29] = 45;
    arrayOfByte[30] = 18;
    arrayOfByte[31] = 71;
    arrayOfByte[32] = 61;
    arrayOfByte[33] = 70;
    arrayOfByte[34] = -117;
    arrayOfByte[35] = -55;
    arrayOfByte[36] = 107;
    arrayOfByte[37] = -75;
    arrayOfByte[38] = -89;
    arrayOfByte[39] = 3;
    arrayOfByte[40] = 94;
    arrayOfByte[41] = -71;
    arrayOfByte[42] = 30;
    try {
      Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
      GCMParameterSpec gCMParameterSpec = new GCMParameterSpec(128, arrayOfByte, 0, 12);
      cipher.init(2, this.secretKey, gCMParameterSpec);
      arrayOfByte = cipher.doFinal(arrayOfByte, 12, arrayOfByte.length - 12);
      byte[] arrayOfByte1 = paramString.getBytes();
      for (int i = 0;; i++) {
        if (i < arrayOfByte.length) {
          if (arrayOfByte1[i + 22] != arrayOfByte[i]) {
            paramCallback.invoke(new Object[] { Boolean.valueOf(false) });
            return;
          } 
        } else {
          paramCallback.invoke(new Object[] { Boolean.valueOf(true) });
          return;
        } 
      } 
    } catch (Exception exception) {
      paramCallback.invoke(new Object[] { Boolean.valueOf(false) });
      return;
    } 
  }
}

どうもエンコードされたByte列をデコードしてから入力文字列の23文字目から比較しているらしい。
ということで自前でJavaでその部分だけ実行してみた。

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

class cyph {

    private static final SecretKey secretKey = new SecretKeySpec("IncrediblySecure".getBytes(), 0, 16, "AES");

    public static void main(String args[]) throws Exception {
        byte[] arrayOfByte = new byte[43];
        arrayOfByte[0] = 95;
        arrayOfByte[1] = -59;
        arrayOfByte[2] = -20;
        arrayOfByte[3] = -93;
        arrayOfByte[4] = -70;
        arrayOfByte[5] = 0;
        arrayOfByte[6] = -32;
        arrayOfByte[7] = -93;
        arrayOfByte[8] = -23;
        arrayOfByte[9] = 63;
        arrayOfByte[10] = -9;
        arrayOfByte[11] = 60;
        arrayOfByte[12] = 86;
        arrayOfByte[13] = 123;
        arrayOfByte[14] = -61;
        arrayOfByte[15] = -8;
        arrayOfByte[16] = 17;
        arrayOfByte[17] = -113;
        arrayOfByte[18] = -106;
        arrayOfByte[19] = 28;
        arrayOfByte[20] = 99;
        arrayOfByte[21] = -72;
        arrayOfByte[22] = -3;
        arrayOfByte[23] = 1;
        arrayOfByte[24] = -41;
        arrayOfByte[25] = -123;
        arrayOfByte[26] = 17;
        arrayOfByte[27] = 93;
        arrayOfByte[28] = -36;
        arrayOfByte[29] = 45;
        arrayOfByte[30] = 18;
        arrayOfByte[31] = 71;
        arrayOfByte[32] = 61;
        arrayOfByte[33] = 70;
        arrayOfByte[34] = -117;
        arrayOfByte[35] = -55;
        arrayOfByte[36] = 107;
        arrayOfByte[37] = -75;
        arrayOfByte[38] = -89;
        arrayOfByte[39] = 3;
        arrayOfByte[40] = 94;
        arrayOfByte[41] = -71;
        arrayOfByte[42] = 30;
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gCMParameterSpec = new GCMParameterSpec(128, arrayOfByte, 0, 12);
        cipher.init(2, secretKey, gCMParameterSpec);
        arrayOfByte = cipher.doFinal(arrayOfByte, 12, arrayOfByte.length - 12);
        String text = new String(arrayOfByte);
        System.out.println(text);
    }
}

すると
1pt_3verywhere}
とだけ出てきた。後ろ半分はこれでわかった。

f:id:SUAN:20200524164041p:plain
siblangs4
ただ、前半分はわかりませんでした。他の人のWriteUpを見るとReact Nativeがーーーって書いてあってたしかにそんな文字見たなとなりました。

くやちーーーーー

SECCONを終えて

REVERSING、MISCが解けてちょっと嬉しい。前回はMISCしか解けなかったので…
PWNやりたいって言っていた気がするけどまだ実力不足で何もわかりませんでした(´;ω;`)