SECCON2014 オンライン予選(en)

12/6 9:00 ~ 12/7 17:00 で、SECCON2014 が開催されたため、チームctpmとして参加してきた。
結果は3,200点で25位。上位チームとは2倍以上も差があるのが恐ろしい。

f:id:f_rin:20141208002435p:plain

今回は自分では大して仕事をしていない感が強かったけど、感想も兼ねてWriteupを書く。課題はexploit.

Reverse it(bin 100)

fileコマンドを打つと、ただのデータの模様。
中身を見てみる。

[rintaro@rintaro_PC] $ hexdump Reverseit
0000000 9dff 700d b6da fc93 7263 2822 22bd d218
0000010 b425 d47b 8c00 a1ab 609e e707 bb04 3156
0000020 7b5a d684 bbd4 b97e 00a0 ab6d 36cd 31c9
0000030 a4d1 012d 5872 a9b5 3acf c104 1b51 4700
0000040 dd70 0814 e965 9377 b431 3776 96f8 1049

(snip)

0001de0 1000 1000 0000 3000 2110 7000 8000 0000
0001df0 a200 d4d4 0000 6696 8754 2d00 1eff 0000
0001e00 8400 8400 1010 1000 6494 64a4 0100 0eff
0001e10 8dff
0001e12

始まりが 9dff, 終わりが 8dff なので、jpgファイルが4bitごとに反転されているのがわかる。
大会中はCybozuLive + Chat でやりとりしてるんだけど、
「4bitずつに分割して反転すればOK」と書き込んだ4分後にチームメイトが flag 書き込んでくれてた。正直すごいと思った。

Decrypt it(Easy)(crypt 200)

以下3ファイルをダウンロードできる。

  • readme.txt : 中に $ ./rnd crypt1.png ecrypt1.bin と書いてある
  • rnd : 32bit ELF実行ファイル
  • ecrypt1.bin : dataファイル

引数を2つとってrndを実行するというものらしいが、第1引数のファイルがない。
rnd を逆アセしてみると、次のようになっている。
f:id:f_rin:20141208004835p:plain

srandにUNIX epoch time を詰め込んで、その後のrand値をもとにビット演算, ADD, ANDといった計算をする。
さらに、第1引数のファイルから読み込んだの値と XOR をしてファイル出力している。
第1引数に該当するファイルがないため、これを復元するためにUNIX epoch timeを算出できればよい。
この実行ファイルでは、rand関数の値をもとに

sar      edx, 31
shr      edx, 24

といった計算がされているが、C言語のstdlib.hでは最大 0x7fffffff までの値しか出力されないため、これらは実質意味がない計算となる(すべて0になる)。
これらを端折り、pngの先頭4バイトからecrypt1.binの4バイトを生成する epochtime を求める。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

int encrypt(unsigned int rnd, unsigned int input){
    unsigned int edx,enc;
    edx = rnd;

    enc = input ^ edx;
    enc = enc & 0xFF;

    return enc;
}

bool encryptable(int src, int dst){
    int rnd = rand();
    int enc = (encrypt(rnd, src));
    return dst == enc;
}

int main(int argc, char *argv[]){
    unsigned int eax,edx;
    int time = 0;
    int i;

    for(i=1400000000;i<1418310000;i++){
        srand(i);

        // 1st byte
        if(!encryptable(0x89, 0x34)) continue;

        // 2nd byte
        if(!encryptable(0x50, 0x70)) continue;

        // 3rd byte
        if(!encryptable(0x4e, 0xf0)) continue;

        // 4th byte
        if(encryptable(0x47, 0x2d)){
            printf("time: %d\n", i);
        }
    }
    return 0;
}

これで epochtime が"1416667590"であることがわかる。これをもとに逆算してやればよい。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    FILE *fp1, *fp2;
    char *filename1 = "ecrypt1.bin";
    char *filename2 = "decrypted.png";

    int time = 1416667590;
    int src, random;
    unsigned int edx;

    char buf[20];
    fp1 = fopen(filename1, "rb");
    fp2 = fopen(filename2, "wb");

    srand(time);

    while(fread(&src, 1, 1, fp1)==1){
        edx = rand();
        src = src ^ edx;

        fwrite(&src, 1, 1, fp2);
    }
    fclose(fp1);
    fclose(fp2);

    return 0;
}

pngファイルが出力されたので、この先はチームメイトに任せて自分は他の問題に移った。
f:id:f_rin:20141208012155p:plain

version2(nw 200)

チームメイトから、h2oとversion2という言葉から連想できることない?
と聞かれ、「http/2じゃね?」といったら解決した。持つべきものはチームメイトです。
この前HTTP/2カンファレンス行っといて良かったw

choose the number(pg 100)

サーバにnc接続すると、いくつかの数字とmax, minを聞かれるため、ひたすらそれに答えていく問題。

#!/usr/bin/env python
import socket

hostname = "153.120.128.155"
port = 31337

bufsize=1024 * 10

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((hostname,port))

data = s.recv(bufsize)
cnt = 0

while 1:
    print cnt
    cnt += 1
    print data

    lineList = data.split('\n')
    nums = lineList[0].split(', ')
    nums = map(long,nums)
    #print 'numlist:', nums

    if 'maximum' in lineList[1]:
        senddata = max(nums)
    elif 'minimum' in lineList[1]:
        senddata = min(nums)
    else:
        print 'error.'
        break

    print 'send:', senddata
    s.sendall(str(senddata))
    data = s.recv(bufsize)

s.close()

途中からmin, max以外のものも聞かれるかと思っていたが、そんなことなかった。

[root@kali] # python solve.py
0
-6, -8
The maximum number? 
send: -6
1
-8, 2, 1
The maximum number? 
send: 2

(snip)

99
3026579565, -4268653171, 824594082, -1216800427, -2942059119, 2734824164, 2378867278, -3312616937, -1450788809, 2609446196, 933229918, -525031883, 2658713277, -3424197581, 2714002200, 961679001, -1918141388, 2384073056, -2349209667, -3795758126, -4053382186, -4128329931, -1220975135, -2445923040, -646890171, -3105229879, -3785136266, 2150927192, 666171246, 2339568430, 1457151652, -3789764701, 2958693042, 2949695341, 1215289247, 1945421978, 3719865041, -1827448426, -560498694, -1501020665, -4125652107, 2095140397, -1209597803, -1380665248, 2024078738, -3669945851, -932304006, -3662055663, -3345496903, 3422310749, -308751840, 3836085859, 3783586083, 209757048, -2013587456, -556615597, 2817625014, 1000219239, -3647092256, -2153377637, -2060011630, 414266117, 2987383496, 3356743747, 2037768595, 167324123, 1894461082, 3785190578, 3719997868, -3896365555, -3023028888, 1736304958, -143009352, 383457339, -1341908584, 2597950776, -2942290071, -940979268, 237865581, -473747952, 4104067010, -95495622, -473772437, 1573676099, -1344741287, -3961922306, -2691292645, 2955392562, -871606715, -1068266030, 1024089948, -1371364589, -245319363, -1352852338, 3445587717, -148004269, 2246597267, 1982576407, -373017230, -1649620257, -1985458588
The minimum number? 
send: -4268653171
100
Congratulations!
The flag is SECCON{Programming is so fun!}

Advanced RISC Machine(exploit 300)

ARMの実行ファイルのExploit.

[root@kali] # checksec.sh --file passcheck-arm
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   passcheck-arm

スタック上でシェルは実行はできない。
Raspberry Piが手元にあったがうまく実行できず、静的解析のみで読み進めていった。
しかし、いつもデバッガ+静的で読んでいるのが裏目に出てうまく読めなかった。
sys_readの長さチェックをやっていないため、SVCでsys_open呼んでsys_read→sys_writeさせればいけるなぁと思いつつも、攻略できず撃沈。

The Golden Gate(pg 400)

エンコーダーボードの写真と暗号文をもとに、デコードする問題。
これは回路に起こして復号するところは自分はやっていなかったけど、とても惜しかったため紹介。
エンコーダーボードから回路を書きおこしてもらった。
f:id:f_rin:20141208185516p:plain

まず与えられている文字列をbase64デコードする。文字数的に"==" は余分なため、削除した状態でデコードすると良い。

[root@kali] # echo "BQDykmgZ0I6SaQnq4o/iEONudetXdPJdpl1UVSlU69oZOtvqnHfinOpcEfIjXy9okkVpsuw2kpKS" | base64 -d > tmp_file
[root@kali] # hexdump tmp_file
0000000 0005 92f2 1968 8ed0 6992 ea09 8fe2 10e2
0000010 6ee3 eb75 7457 5df2 5da6 5554 5429 daeb
0000020 3a19 eadb 779c 9ce2 5cea f211 5f23 682f
0000030 4592 b269 36ec 9292 0092               
0000039

これを、回路をトレースしたコードに食わせると、文字列が得られる。

8b 1f 00 08 4b 02 54 73 03 00 c9 0b 55 48 cb 48 4c 49 c8 57 56 2c 76 08 76 75 f7 f6 f6
ab 29 c8 31 4b c9 28 8c cf cf 48 77 c9 08 ca ad f1 02 e5 b7 00 a0 03 25 87 00 00 8b 00

実はこれ、競技時間中にはチャットから流れていってしまって気付けなかった。
本当にもったいない&申し訳ないことをした。ちゃんと見ていれば良かったorz
8b 1f なので、gzipが逆転しているため、大会後に戻してみたら速攻でflag取得できた。
しかも後から気付くと、hexdump で見てたけど、xxd で見た文字列を入力値に使っていれば反転すらしてないgzipの値が得られるではないか。
回路を復元してデコードすれば良いだけの問題であることがわかる。

[root@kali] # echo "1f8b0800024b735400030bc9485548cb494c57c82c5608767576f6f7abf6c8294b3128c9cf8c48cfc977ca08f1ade50200b703a087250000009f"|xxd -r -p > out.gz
[root@kali] # gzip -dc out.gz
The flag is SECCON{Hlvd0toiXgloBhTM}

gzip: out.gz: unexpected end of file

その他

Holy shellcode(exploit 400)は、x86上で普通に動くシェルは作成したものの、ヘブライ語化は手付かずのままとなってしまったため進捗ダメです状態で終了。
XSSも、他メンバがやっていたものにあーだこーだ言っていたぐらいでメインでは攻略に参加せず。

ひとつ驚いたのはBleeding "Heartbleed" Test Web(web 300)だ。
チームメイトが自宅に公開サーバを作ってきたけどIPアドレスを忘れてしまい困った事態に陥ったが、Cloudnに詳しいメンバの手により一瞬で外部アクセス可能なサーバを用意してもらいました。無料ポイントを使えたので、無料にて攻略ができたのが感動した。
その後はすぐ解約してましたw

持つべきは仲間ですね。本当にそう思った。
それと、他の問題の状況も確認する。これ大事。
当たり前だけどポイント落とさないためにもコミュニケーションほんとに重要だった。

この順位だと本戦にいけるかはわからないけど、しばらくは海外勢の様子を観察しながらExploitの練習を続けることにする。