読者です 読者をやめる 読者になる 読者になる

SECCON 2015 Online CTF write up

2015/12/5~6の間SECCON 2015 Online CTFにチーム名BiPhoneでAkashi_SNとして5人で参加しました。CTFは初めてまだ1ヶ月ほどにしてはまぁ解けた方かな・・・?

ちなみに順位は240位でした。

僕が解いた問題のwrite upをします。

問題はgithubにあります

Write up

SECCON WARS 2015

https://youtu.be/8SFsln4VyEk

Youtubeのリンクがあるだけ・・・

とりあえず見てみるとStarWarsをもじったみたいな動画があり途中からQRコードが見え始める。

とにかくダウンロードしてフレームごとに分けてみる

背景だけ動いているのでもしやこれは比較明合成が使えるのではと思い

僕がよく星の写真を撮るときに使う

SiriusCompを使ってみたら

はっきりと出てくるので読み取って終わり

flag:SECCON{TH3F0RC3AVVAK3N53P7}

Unzip the file

後で他の人のwrite upを見て解きました

unzipというファイルが渡されUnzip the fileとのこと

普通に解凍しようとするとパスワードがかかっているみたい・・・

$ unzip unzip.zip 
Archive:  unzip.zip
[unzip.zip] backnumber08.txt password: 
   skipping: backnumber08.txt        incorrect password
   skipping: backnumber09.txt        incorrect password
   skipping: flag                    incorrect password

]

backnumber08.txtをとりあえず検索してみると

backnumber08.txt

このようにヒットする

ファイルの中身がわかっているので既知平文攻撃だとわかる

これはpkcrackというツールで解析できるみたい

とりあえずソースをダウンロードして

$ cd pkcrack-1.2.2/test
pkcrack-1.2.2/test/$ sudo make

testディレクトリに移動してmakeすると/srcに実行ファイルが生成される

pkcrackの使い方は、
   -C [暗号化されたzipファイル]
   -c [暗号化されたzipファイルの中で平文がわかるファイル]
   -P [平文のファイルが入っている暗号化されていないzip]
   -p [平文のファイル]
   -d [出力先(復号したzipファイルの名前)]

backnumber08.zipbacknumber08.txtを普通にZIP圧縮したもの

/pkcrack-1.2.2/src/$ pkcrack -C ./unzip.zip -c backnumber08.txt -P backnumber08.zip -p backnumber08.txt -d unzip_1.zip
Files read. Starting stage 1 on Mon Dec  7 14:35:35 2015
Generating 1st generation of possible key2_5299 values...done.
Found 4194304 possible key2-values.
Now we're trying to reduce these...
Lowest number: 984 values at offset 970
Lowest number: 932 values at offset 969
Lowest number: 931 values at offset 967
Lowest number: 911 values at offset 966
Lowest number: 906 values at offset 965
Lowest number: 904 values at offset 959
Lowest number: 896 values at offset 955
Lowest number: 826 values at offset 954
Lowest number: 784 values at offset 606
Lowest number: 753 values at offset 206
Done. Left with 753 possible Values. bestOffset is 206.
Stage 1 completed. Starting stage 2 on Mon Dec  7 14:35:46 2015
Ta-daaaaa! key0=270293cd, key1=b1496a17, key2=8fd0945a
Probabilistic test succeeded for 5098 bytes.
Ta-daaaaa! key0=270293cd, key1=b1496a17, key2=8fd0945a
Probabilistic test succeeded for 5098 bytes.
Stage 2 completed. Starting zipdecrypt on Mon Dec  7 14:36:14 2015
Decrypting backnumber08.txt (5315a01322ab296c211eecba)... OK!
Decrypting backnumber09.txt (83e6640cbec32aeaf10ed1ba)... OK!
Decrypting flag (34e4d2ab7fe1e2421808bab2)... OK!
Finished on Mon Dec  7 14:36:14 2015

暗号化されていないunzip_1.zipが生成される

$ unzip unzip_1.zip 
Archive:  unzip_1.zip
replace backnumber08.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
 inflating: backnumber08.txt        
 inflating: backnumber09.txt        
 inflating: flag                    
$ ls
 backnumber08.zip
 unzip.zip
 backnumber08.txt
 backnumber09.txt
 flag
 unzip_1.zip
$ file flag 
 flag: Microsoft Word 2007+

flagはwordファイルだとわかる

LibreOfficeで開いてみる

何も見えないがフォントの色を変えると出てくる

flag:SECCON{1s_th1s_passw0rd_ weak?}

Reverse-Engineering Android APK 1

これはチームメイトが前半を解いてくれ最後は僕がflagをみつけました。

なんかいいとこ取りみたい・・・

APKファイルが渡され「じゃんけんに1000回連続で勝ち続けよ」とありました

さすがにごり押しは厳しそうなのでapkファイルについて調べてたらただの圧縮ファイルであるとわかり解凍してみる。

するとclasses.dexというファイルがあってこれがアプリの本体だとわかった。

これは、dex2jarというソフトで変換できるそうなのでやってみる

~/dex2jar-2.0$ ./d2j-dex2jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar
~/dex2jar-2.0$ ls
classes-dex2jar.jar            d2j-dex2smali.bat   d2j-smali.bat
classes.dex                     d2j-dex2smali.sh    d2j-smali.sh
d2j-baksmali.bat                d2j-jar2dex.bat     d2j-std-apk.bat
d2j-baksmali.sh                 d2j-jar2dex.sh      d2j-std-apk.sh
d2j-dex-recompute-checksum.bat  d2j-jar2jasmin.bat  d2j_invoke.bat
d2j-dex-recompute-checksum.sh   d2j-jar2jasmin.sh   d2j_invoke.sh
d2j-dex2jar.bat                 d2j-jasmin2jar.bat  lib
d2j-dex2jar.sh                  d2j-jasmin2jar.sh

するとclasses-dex2jar.jarというファイルができる

これはただのZIPファイルなので展開してみる

すると/com,/androidの2つのフォルダが出てくる

/com/example/seccon2015/rock_paper_scissors/MainActivity.classJava Decompilerというソフトで見てみると

package com.example.seccon2015.rock_paper_scissors;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import java.util.Random;

public class MainActivity
  extends Activity
  implements View.OnClickListener
{
  Button P;
  Button S;
  int cnt = 0;
  int flag;
  private final Handler handler = new Handler();
  int m;
  int n;
  Button r;
  private final Runnable showMessageTask = new Runnable()
  {
    public void run()
    {
      TextView localTextView = (TextView)MainActivity.this.findViewById(2131492946);
      MainActivity localMainActivity;
      if (MainActivity.this.n - MainActivity.this.m == 1)
      {
        localMainActivity = MainActivity.this;
        localMainActivity.cnt += 1;
        localTextView.setText("WIN! +" + String.valueOf(MainActivity.this.cnt));
      }
      for (;;)
      {
        if (1000 == MainActivity.this.cnt) {
          localTextView.setText("SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}");
        }
        MainActivity.this.flag = 0;
        return;
        if (MainActivity.this.m - MainActivity.this.n == 1)
        {
          MainActivity.this.cnt = 0;
          localTextView.setText("LOSE +0");
        }
        else if (MainActivity.this.m == MainActivity.this.n)
        {
          localTextView.setText("DRAW +" + String.valueOf(MainActivity.this.cnt));
        }
        else if (MainActivity.this.m < MainActivity.this.n)
        {
          MainActivity.this.cnt = 0;
          localTextView.setText("LOSE +0");
        }
        else
        {
          localMainActivity = MainActivity.this;
          localMainActivity.cnt += 1;
          localTextView.setText("WIN! +" + String.valueOf(MainActivity.this.cnt));
        }
      }
    }
  };
  
  static
  {
    System.loadLibrary("calc");
  }
  
  public native int calc();
  
  public void onClick(View paramView)
  {
    if (this.flag == 1) {
      return;
    }
    this.flag = 1;
    ((TextView)findViewById(2131492946)).setText("");
    TextView localTextView1 = (TextView)findViewById(2131492944);
    TextView localTextView2 = (TextView)findViewById(2131492945);
    this.m = 0;
    this.n = new Random().nextInt(3);
    int i = this.n;
    localTextView2.setText(new String[] { "CPU: Paper", "CPU: Rock", "CPU: Scissors" }[i]);
    if (paramView == this.P)
    {
      localTextView1.setText("YOU: Paper");
      this.m = 0;
    }
    if (paramView == this.r)
    {
      localTextView1.setText("YOU: Rock");
      this.m = 1;
    }
    if (paramView == this.S)
    {
      localTextView1.setText("YOU: Scissors");
      this.m = 2;
    }
    this.handler.postDelayed(this.showMessageTask, 1000L);
  }
  
  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130968600);
    this.P = ((Button)findViewById(2131492941));
    this.S = ((Button)findViewById(2131492943));
    this.r = ((Button)findViewById(2131492942));
    this.P.setOnClickListener(this);
    this.r.setOnClickListener(this);
    this.S.setOnClickListener(this);
    this.flag = 0;
  }
}

39行目にflagらしきものが・・・

$cntはカウンターだとわかった

どうやらcalc()はライブラリーから読み込んでいるらしい・・・

そういえばライブラリファイルがあったなー

/rps/lib/x86/libcalc.sofileコマンドでたたくと

/CTF/seccon2015/Reverse-Engineering 
Android APK 1/rps/lib/x86/libcalc.so: ELF 32-bit LSB shared object, 
Intel 80386, version 1 (SYSV),dynamically linked, 
BuildID[sha1]=841ccd3a86c678914a4dc1a17bd80647ca290916, stripped

32bitのelfだとわかったので、

Retargetable Decompilerというオンラインのデコンパイラーでデコンパイルしてみると

//
// This file was generated by the Retargetable Decompiler
// Website: https://retdec.com
// Copyright (c) 2015 Retargetable Decompiler <info@retdec.com>

// Address range: 0x400 - 0x405
int32_t Java_com_example_seccon2015_rock_1paper_1scissors_MainActivity_calc(void) {
    // 0x400
    return 7;
}

calc()はいつでも7を返すということがわかり

SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}"

ここを計算して

flag:SECCON{107749}

Command-Line Quiz


telnet caitsith.pwn.seccon.jp
   User:root
   Password:seccon
   すべての *.txt ファイルを読め

接続しクイズの答えもわかったが回答の仕方がわからず断念・・・

Steganography 1

MrFusion.gpjb

というファイルが渡され「Find image files in the file」とのこと

このファイルはgifファイルだとわかったので拡張子を変え表示してみるとSECCON{}としか描かれてないのに8Mはでかすぎるので

バイナリエディタで見てみるといろんなファイルフォーマットのヘッダーが含まれていたのでそれを仕様に沿って分けてみるてそれを合成すると

flagかと思ってsubmitしたがincorrectばっかりだった

結局できず断念

//gpjbはg(mp)p(ng)j(peg)b(mp)の略だったようでbmpが足らなかったから不正解だったみたい

—-後で解いてみた

binwalkという便利なコマンドがあるらしい

なんでもファイルを解析してくれるみたいfileコマンドの強いやつって感じかな?

aptでgetできるものはバージョンが古いみたいなのでここからホームディレクトリにダウンロードして

# cd binwalk-2.0.0.tar/binwalk-2.0.0
binwalk-2.0.0.tar/binwalk-2.0.0 $ ./configure
binwalk-2.0.0.tar/binwalk-2.0.0 $ make
binwalk-2.0.0.tar/binwalk-2.0.0 $ sudo make install

これでインストールできbinwalkで実行できる

binwalkMrFusion.gpjbを解析してみると

$ binwalk MrFusion.gif 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             GIF image data, version "89a", 1280 x 720
6943          0x1B1F          PNG image, 1280 x 720, 8-bit colormap, interlaced
7194          0x1C1A          Zlib SECCON_2015_Online_CTF data, SECCON_2015_Online_CTF, unSECCON_2015_Online_CTF size >= 922950
9727          0x25FF          JPEG image data, JFIF standard  1.01
26632         0x6808          PC bitmap, Windows 3.x format, 1280 x 720 x 24
2791486       0x2A983E        GIF image data, version "89a", 1280 x 720
2794240       0x2AA300        PNG image, 1280 x 720, 8-bit colormap, interlaced
2794491       0x2AA3FB        Zlib SECCON_2015_Online_CTF data, SECCON_2015_Online_CTF, unSECCON_2015_Online_CTF size >= 922950
2796217       0x2AAAB9        JPEG image data, JFIF standard  1.01
2813627       0x2AEEBB        PC bitmap, Windows 3.x format, 1280 x 720 x 24
5578481       0x551EF1        GIF image data, version "89a", 1280 x 720
5580896       0x552860        PNG image, 1280 x 720, 8-bit colormap, interlaced
5581147       0x55295B        Zlib SECCON_2015_Online_CTF data, SECCON_2015_Online_CTF, unSECCON_2015_Online_CTF size >= 922950
5583378       0x553212        JPEG image data, JFIF standard  1.01
5601221       0x5577C5        PC bitmap, Windows 3.x format, 1280 x 720 x 24
8366075       0x7FA7FB        GIF image data, version "89a", 1280 x 720
8368830       0x7FB2BE        PNG image, 1280 x 720, 8-bit colormap, interlaced
8369081       0x7FB3B9        Zlib SECCON_2015_Online_CTF data, SECCON_2015_Online_CTF, unSECCON_2015_Online_CTF size >= 922950
8371932       0x7FBEDC        JPEG image data, JFIF standard  1.01

このようにファイルフォーマットごとに表示してくれます

(ZlibはPNGに使われている圧縮方法なのでZlibのところは無視する)

gif→pngjpegbmp→gif→png→・・・のように繰り返している

#coding: UTF-8

a = [0,6943,9727,26632,2791486,2794240,2796217,2813627,5578481,5580896,5583378,5601221,8366075,8368830,8371932,8388384]
#アドレス
b = ['gif','png','jpg','bmp']
#拡張子
f = open("MrFusion.gif", "rb")
#ファイルの読み込み
for x in range(len(a)-1):
  open('result{:02d}.{}'.format(x, b[x % 4]), 'wb').write(f.read(a[x + 1] - a[x]))

pythonのコードを書いて実行

~/ctf/a4 ls
MrFusion.gif  split.py
~/ctf/a$ python split.py 
~/ctf/a$ ls
MrFusion.gif  result03.bmp  result07.bmp  result11.bmp  split.py
result00.gif  result04.gif  result08.gif  result12.gif
result01.png  result05.png  result09.png  result13.png
result02.jpg  result06.jpg  result10.jpg  result14.jpg
~/ctf/a$ 

分割されたファイルが出力される

最初と最後のファイルをネガポジ変換しすべてjpgに変換して

SiriusCompで比較明合成

Please input flag like this format–>SECCON{ * **}

形式が指定されていたので

flag:SECCON{OCT 21 2015 0728}

4042


謎の文章が2005年に古代遺跡から発見された。

これは何を意味している?

no-network.txt

4042?2005?古代遺跡?ということでとりあえずググってみた

東ソー?

おっなんかヒットした

https://www.ietf.org/rfc/rfc4042.txt

RFC4042・・・?

とりあえずググってみた

RFC4042–Google検索

RFC4042 2005 年のエープリルフール発行のジョーRFC です。 UCS4 や UCS2(Unicode) のエンコード方法である UTF-9 や UTF-18 を規定しています。 PDP-10 などの8bitベースではないマシンのためのエンコーディング…?

・・・http://www.imasy.or.jp/~yotti/rfc-joke.htmlより転載

ジョーク・・・

このファイルはUTF-9で書かれているのか・・・

rfc4042.txtにUTF-9からUCS-4に変換するコードが書かれている

/* Return UCS-4 value from UTF-9 string (C version)
* Accepts: pointer to pointer to UTF-9 string
* Returns: UCS-4 character, nonet pointer updated
*/

UINT31 UTF9_to_UCS4 (UINT9 **utf9PP)
{
  UINT9 nonet;
  UINT31 ucs4;
  for (ucs4 = (nonet = *(*utf9PP)++) & 0xff;
  nonet & 0x100;
  ucs4 |= (nonet = *(*utf9PP)++) & 0xff)
  ucs4 <<= 8;
  return ucs4;
}

Last Challenge (Thank you for playing)

規則性にのっとて変換するだけ

flag:SECCON{SEEYOUNEXTYEAR}

終わり

初めてにしてはできたかな~

でも疲れた

視野は広くしないと