All Articles

n00bz CTF 2024 Writeup

8/3 から開催されていた n00bz CTF 2024 に 0nePadding で参加して最終順位 28 位でした。

img

超優秀なチームメンバーのおかげでほぼ全完できました(惜しかった!

image-20240805223646681

今回は解いた問題数が多いのでざっくり Writeup を書きます。

もくじ

Vacation(Rev)

My friend told me they were going on vacation, but they sent me this weird PowerShell script instead of a postcard! Author: 0xBlue

問題バイナリとして以下のスクリプトと暗号文が与えられます。

$bytes = [System.Text.Encoding]::ASCII.GetBytes((cat .\flag.txt))
[System.Collections.Generic.List[byte]]$newBytes = @()
$bytes.ForEach({
    $newBytes.Add($_ -bxor 3)
    })
$newString =  [System.Text.Encoding]::ASCII.GetString($newBytes)
echo $newString | Out-File -Encoding ascii .\output.txt
m33ayxeqln\sbqjp\twk\{lq~

コードを見てわかる通り 3 で XOR すると復号できます。

image-20240803121729441

Brain(Rev)

Help! A hacker said that this “language” has a flag but I can’t find it! Author: NoobMaster

問題バイナリとして以下の BrainFuck コードが与えられます。

>+++++++++++[<++++++++++>-]<[-]>++++++++[<++++++>-]<[-]>++++++++[<++++++>-]<[-]>++++++++++++++[<+++++++>-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<++>-]<[-]>+++++++++++++++++++++++++++++++++++++++++[<+++>-]<[-]>+++++++[<+++++++>-]<[-]>+++++++++++++++++++[<+++++>-]<[-]>+++++++++++[<+++++++++>-]<[-]>+++++++++++++[<++++>-]<[-]>+++++++++++[<++++++++++>-]<[-]>+++++++++++++++++++[<+++++>-]<[-]>+++++++++++[<+++++++++>-]<[-]>++++++++[<++++++>-]<[-]>++++++++++[<++++++++++>-]<[-]>+++++++++++++++++[<+++>-]<[-]>+++++++++++++++++++[<+++++>-]<[-]>+++++++[<+++++++>-]<[-]>+++++++++++[<++++++++++>-]<[-]>+++++++++++++++++++[<+++++>-]<[-]>++++++++++++++[<+++++++>-]<[-]>+++++++++++++++++++[<++++++>-]<[-]>+++++++++++++[<++++>-]<[-]>+++++++[<+++++++>-]<[-]>+++++++++++[<++++++++++>-]<[-]>+++++++++++++++++[<++++++>-]<[-]>+++++++[<++++++>-]<[-]>+++++++++++[<+++++++++>-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-]<[-]>+++++++++++[<+++>-]<[-]>+++++++++++++++++++++++++[<+++++>-]<[-]

例によって以下のスクリプトで C コードに置き換えます。

参考:bftoc/bftoc.py at master · paulkaefer/bftoc · GitHub

結果は以下のようになりました。

/* This is a translation of bf.txt, generated by bftoc.py (by Paul Kaefer)
 * It was generated on Saturday, August 03, 2024 at 03:20AM
 */

#include <stdio.h>

void main(void)
{
    int size = 1000;
    int tape[size];
    int i = 0;

    /* Clearing the tape (array) */
    for (i=0; i<size; i++)
        tape[i] = 0;

    int ptr = 0;

    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 10;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 8;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 6;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 8;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 6;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 14;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 7;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 61;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 2;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 41;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 3;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 7;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 7;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 19;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 5;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 9;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 13;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 4;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 10;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 19;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 5;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 9;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 8;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 6;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 10;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 10;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 17;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 3;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 19;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 5;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 7;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 7;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 10;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 19;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 5;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 14;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 7;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 19;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 6;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 13;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 4;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 7;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 7;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 10;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 17;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 6;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 7;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 6;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 9;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 107;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 1;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 11;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 3;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
    ptr += 1;
    tape[ptr] += 25;
    while (tape[ptr] != 0)
    {
        ptr -= 1;
        tape[ptr] += 5;
        ptr += 1;
        tape[ptr] -= 1;
    }
    ptr -= 1;
    while (tape[ptr] != 0)
    {
        tape[ptr] -= 1;
    }
}

このコードは、主に以下のパートで成り立っています。

ptr += 1;
tape[ptr] += 11;
while (tape[ptr] != 0)
{
    ptr -= 1;
    tape[ptr] += 10;
    ptr += 1;
    tape[ptr] -= 1;
}

これは Flag の各文字の検証を行っています。

なので以下の Solver で Flag を取得しました。

case = """tape[ptr] += 11
tape[ptr] += 10
tape[ptr] += 8
tape[ptr] += 6
tape[ptr] += 8
tape[ptr] += 6
tape[ptr] += 14
tape[ptr] += 7
tape[ptr] += 61
tape[ptr] += 2
tape[ptr] += 41
tape[ptr] += 3
tape[ptr] += 7
tape[ptr] += 7
tape[ptr] += 19
tape[ptr] += 5
tape[ptr] += 11
tape[ptr] += 9
tape[ptr] += 13
tape[ptr] += 4
tape[ptr] += 11
tape[ptr] += 10
tape[ptr] += 19
tape[ptr] += 5
tape[ptr] += 11
tape[ptr] += 9
tape[ptr] += 8
tape[ptr] += 6
tape[ptr] += 10
tape[ptr] += 10
tape[ptr] += 17
tape[ptr] += 3
tape[ptr] += 19
tape[ptr] += 5
tape[ptr] += 7
tape[ptr] += 7
tape[ptr] += 11
tape[ptr] += 10
tape[ptr] += 19
tape[ptr] += 5
tape[ptr] += 14
tape[ptr] += 7
tape[ptr] += 19
tape[ptr] += 6
tape[ptr] += 13
tape[ptr] += 4
tape[ptr] += 7
tape[ptr] += 7
tape[ptr] += 11
tape[ptr] += 10
tape[ptr] += 17
tape[ptr] += 6
tape[ptr] += 7
tape[ptr] += 6
tape[ptr] += 11
tape[ptr] += 9
tape[ptr] += 107
tape[ptr] += 1
tape[ptr] += 11
tape[ptr] += 3
tape[ptr] += 25
tape[ptr] += 5"""

flag = ""
arr = case.splitlines()
for i,a in enumerate(arr):
    w = int(a.split(" ")[-1])
    if i % 2 == 0:
        count = w
    else:
        flag += chr(count*w)

# n00bz{1_c4n_c0d3_1n_br41nf*ck!}

FlagChecker(Rev)

Why did the macros hide its knowledge? Because it didn’t want anyone to “excel”! Note: char21 is the SAME as char22 Note 2: The correct flag has ALL LOWERCASE, NUMBERS, n00bz{] AND UNDERSCORES (There’s two underscores in the entire flag) Author: NoobMaster

問題バイナリとして与えられた FlagChecker.xlsm から olevba でスクリプトを抽出します。

olevba FlagChecker.xlsm > out.txt

抽出したスクリプトは以下の通りでした。

Sub FlagChecker()

    Dim chars(1 To 24) As String
    guess = InputBox("Enter the flag:")
    If Len(guess) <> 24 Then
        MsgBox "Nope"
    End If
    char_1 = Mid(guess, 1, 1)
    char_2 = Mid(guess, 2, 1)
    char_3 = Mid(guess, 3, 1)
    char_4 = Mid(guess, 4, 1)
    char_5 = Mid(guess, 5, 1)
    char_6 = Mid(guess, 6, 1)
    char_7 = Mid(guess, 7, 1)
    char_8 = Mid(guess, 8, 1)
    char_9 = Mid(guess, 9, 1)
    char_10 = Mid(guess, 10, 1)
    char_11 = Mid(guess, 11, 1)
    char_12 = Mid(guess, 12, 1)
    char_13 = Mid(guess, 13, 1)
    char_14 = Mid(guess, 14, 1)
    char_15 = Mid(guess, 15, 1)
    char_16 = Mid(guess, 16, 1)
    char_17 = Mid(guess, 17, 1)
    char_18 = Mid(guess, 18, 1)
    char_19 = Mid(guess, 19, 1)
    char_20 = Mid(guess, 20, 1)
    char_21 = Mid(guess, 21, 1)
    char_22 = Mid(guess, 22, 1)
    char_23 = Mid(guess, 23, 1)
    char_24 = Mid(guess, 24, 1)
    If Asc(char_1) Xor Asc(char_8) = 22 Then
        If Asc(char_10) + Asc(char_24) = 176 Then
            If Asc(char_9) - Asc(char_22) = -9 Then
                If Asc(char_22) Xor Asc(char_6) = 23 Then
                    If (Asc(char_12) / 5) ^ (Asc(char_3) / 12) = 130321 Then
                        If char_22 = char_11 Then
                            If Asc(char_15) * Asc(char_8) = 14040 Then
                                If Asc(char_12) Xor (Asc(char_17) - 5) = 5 Then
                                    If Asc(char_18) = Asc(char_23) Then
                                        If Asc(char_13) Xor Asc(char_14) Xor Asc(char_2) = 121 Then
                                            If Asc(char_14) Xor Asc(char_24) = 77 Then
                                                If 1365 = Asc(char_22) Xor 1337 Then
                                                    If Asc(char_10) = Asc(char_7) Then
                                                        If Asc(char_23) + Asc(char_8) = 235 Then
                                                            If Asc(char_16) = Asc(char_17) + 19 Then
                                                                If Asc(char_19) = 107 Then
                                                                    If Asc(char_20) + 501 = (Asc(char_1) * 5) Then
                                                                        If Asc(char_21) = Asc(char_22) Then
                                                                            MsgBox "you got the flag!"
                                                                        End If
                                                                    End If
                                                                End If
                                                            End If
                                                        End If
                                                    End If
                                                End If
                                            End If
                                        End If
                                    End If
                                End If
                            End If
                        End If
                    End If
                End If
            End If
        End If
    End If
End Sub

24 文字の Flag 文字列から各文字を取り出し、様々な検証を行っているようです。

これを Z3Py で書き起こすと以下のようになりました。

from z3 import *
flag = [BitVec(f"flag[{i}]", 64) for i in range(25)]
s = Solver()

for i in range(25):
    s.add(And(
        (flag[i] >= 0x21),
        (flag[i] <= 0x7e)
    ))

s.add(flag[1] == ord("n"))
s.add(flag[2] == ord("0"))
s.add(flag[3] == ord("0"))
s.add(flag[4] == ord("b"))
s.add(flag[5] == ord("z"))
s.add(flag[6] == ord("{"))
s.add(flag[24] == ord("}"))

s.add(flag[1] ^ flag[8] == 22)
s.add(flag[10] + flag[24] == 176)
s.add(flag[9] - flag[22] == -9)
s.add(flag[22] ^ flag[6] == 23)
s.add(flag[22] == flag[11])
# s.add(UDiv(flag[12],5) ** 4 == 130321)
s.add(flag[12] == 95)
s.add(flag[15] * flag[8] == 14040)
s.add(flag[12] ^ (flag[17] - 5) == 5)
s.add(flag[18] == flag[23])
s.add(flag[13] ^ flag[14] ^ flag[2] == 121)
s.add(flag[14] ^ flag[24] == 77)
s.add(flag[22] ^ 1337 == 1365)
s.add(flag[10] == flag[7])
s.add(flag[23] + flag[8] == 235)
s.add(flag[16] == flag[17] + 19)
s.add(flag[19] == 107)
s.add(flag[20] + 501 == flag[1] * 5)
s.add(flag[21]  == flag[22])

while s.check() == sat:
    m = s.model()
    for i in range(1,25):
        c = flag[i]
        print(chr(m[c].as_long()),end="")
    print("")

# n00bz{3xc3l_y0ur_sk1lls}

VBA で ^ が XOR じゃなくて累乗を意味するということに気づかず少しハマりました。

Plane(Forensic)

So many plane-related challenges! Why not another one? The flag is the latitude, longitude of the place this picture is taken from, rounded upto two decimal places. Example: n00bz{55.51,-20.27} Author: NoobMaster

exif に座標が埋まってました。

image-20240803125338587

Flag は以下。

n00bz{13.37,-13.37}

Wave(Forensic)

The Wave is not audible, perhaps corrupted? Note: Wrap the flag in n00bz{}. There are no spaces in the flag and it is all lowercase. Author: NoobMaster

破損した WAV ファイルが与えられます。

マジックナンバーなどが 0 に置き換えられてるようです。

image-20240803130300252

適当に取得したサンプルの WAV ファイルは以下の通りでした。

image-20240803130746367

これを参考に破損した WAV ファイルを修復します。

image-20240803131304018

修復した WAV ファイルを再生するとモールス信号でしたので、以下のサイトで解読しました。

image-20240803131420529

参考:Morse Code Adaptive Audio Decoder | Morse Code World

正しい Flag は以下でした。

n00bz{beepbopmorsecode}

Vinegar(Crypto)

Can you decode this message? Note: Wrap the decrypted text in n00bz{}

以下のテキストが与えられます。

Encrypted flag: nmivrxbiaatjvvbcjsf
Key: secretkey

問題名の通りヴィジュネル暗号(Vigenere cipher)でしたので、以下のサイトで解析しました。

image-20240804152233865

正しい Flag は以下でした。

n00bz{vigenerecipherisfun}

Vinegar 2(Crypto)

Never limit yourself to only alphabets! Author: NoobMaster

問題バイナリとして以下の Solver が与えられます。

今回も換字式暗号のようですが、変換テーブルが独自に実装されているようです。

alphanumerical = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}_?'
matrix = []
for i in alphanumerical:
	matrix.append([i])

idx=0
for i in alphanumerical:
	matrix[idx][0] = (alphanumerical[idx:len(alphanumerical)]+alphanumerical[0:idx])
	idx += 1

flag=open('../src/flag.txt').read().strip()
key='5up3r_s3cr3t_k3y_f0r_1337h4x0rs_r1gh7?'
assert len(key)==len(flag)
flag_arr = []
key_arr = []
enc_arr=[]
for y in flag:
	for i in range(len(alphanumerical)):
		if matrix[i][0][0]==y:
			flag_arr.append(i)

for y in key:
	for i in range(len(alphanumerical)):
		if matrix[i][0][0]==y:
			key_arr.append(i)

for i in range(len(flag)):
	enc_arr.append(matrix[flag_arr[i]][0][key_arr[i]])
encrypted=''.join(enc_arr)
f = open('enc.txt','w')
f.write(encrypted) # *fa4Q(}$ryHGswGPYhOC{C{1)&_vOpHpc2r0({

そこで、これを復号する以下の Solver を作成しました。

alphanumerical = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}_?'
matrix = []
for i in alphanumerical:
	matrix.append([i])

idx=0
for i in alphanumerical:
	matrix[idx][0] = (alphanumerical[idx:len(alphanumerical)]+alphanumerical[0:idx])
	idx += 1
	
flag=r"*fa4Q(}$ryHGswGPYhOC{C{1)&_vOpHpc2r0({"
key='5up3r_s3cr3t_k3y_f0r_1337h4x0rs_r1gh7?'
assert len(key)==len(flag)

flag_arr = []
key_arr = []
resolve_arr = []
for y in key:
	for i in range(len(alphanumerical)):
		if matrix[i][0][0]==y:
			key_arr.append(i)

for i,f in enumerate(flag):
	for j,arr in enumerate(matrix):
		if arr[0][key_arr[i]] == f:
			resolve_arr.append(j)
			break

for n in resolve_arr:
	print(alphanumerical[n],end="")
print("")

# n00bz{4lph4num3r1c4l_1s_n0t_4_pr0bl3m}

RSA(Crypto)

The cryptography category is incomplete without RSA. So here is a simple RSA challenge. Have fun! Author: noob_abhinav

問題バイナリとして以下が与えられます。

e = 3
n = 135112325288715136727832177735512070625083219670480717841817583343851445454356579794543601926517886432778754079508684454122465776544049537510760149616899986522216930847357907483054348419798542025184280105958211364798924985051999921354369017984140216806642244876998054533895072842602131552047667500910960834243
c = 13037717184940851534440408074902031173938827302834506159512256813794613267487160058287930781080450199371859916605839773796744179698270340378901298046506802163106509143441799583051647999737073025726173300915916758770511497524353491642840238968166849681827669150543335788616727518429916536945395813

e が 3 であり、c が n より小さいため、c の 3 乗根をとることにしました。

import gmpy2
from Crypto.Util.number import long_to_bytes, bytes_to_long

e = 3
n = 135112325288715136727832177735512070625083219670480717841817583343851445454356579794543601926517886432778754079508684454122465776544049537510760149616899986522216930847357907483054348419798542025184280105958211364798924985051999921354369017984140216806642244876998054533895072842602131552047667500910960834243
c = 13037717184940851534440408074902031173938827302834506159512256813794613267487160058287930781080450199371859916605839773796744179698270340378901298046506802163106509143441799583051647999737073025726173300915916758770511497524353491642840238968166849681827669150543335788616727518429916536945395813
assert c < n

a,b=gmpy2.iroot(c,e)
print(long_to_bytes(a))

# n00bz{crypt0_1s_1nc0mpl3t3_w1th0ut_rs4!!}

上記の Solver で Flag を取得できます。

Random(Crypto)

I hid my password behind an impressive sorting machine. The machine is very luck based, or is it?!?!?!? Author: Connor Chang

問題バイナリとして以下のスクリプトが与えられます。

リモートサーバーではこのコードをコンパイルしたプログラムが稼働しているようです。

#include<chrono>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<string>
#include<fstream>
#include<thread>
#include<map>
using namespace std;

bool amazingcustomsortingalgorithm(string s) {
    int n = s.size();
    for (int i = 0; i < 69; i++) {
        cout << s << endl;
        bool good = true;
        for (int i = 0; i < n - 1; i++)
            good &= s[i] <= s[i + 1];
        
        if (good)
            return true;

        random_shuffle(s.begin(), s.end());

        this_thread::sleep_for(chrono::milliseconds(500));
    }

    return false;
}

int main() {
    string s;
    getline(cin, s);

    map<char, int> counts;
    for (char c : s) {
        if (counts[c]) {
            cout << "no repeating letters allowed passed this machine" << endl;
            return 1;
        }
        counts[c]++;
    }

    if (s.size() < 10) {
        cout << "this machine will only process worthy strings" << endl;
        return 1;
    }

    if (s.size() == 69) {
        cout << "a very worthy string" << endl;
        cout << "i'll give you a clue'" << endl;
        cout << "just because something says it's random mean it actually is" << endl;
        return 69;
    }

    random_shuffle(s.begin(), s.end());
    
    if (amazingcustomsortingalgorithm(s)) {
        ifstream fin("flag.txt");
        string flag;
        fin >> flag;
        cout << flag << endl;
    }
    else {
        cout << "UNWORTHY USER DETECTED" << endl;
    }
}

amazingcustomsortingalgorithm 関数では、入力した文字列を random_shuffle 関数でシャッフルし、ソートされた文字列が生成されれば Flag を取得できます。

何度か操作を行うと、random_shuffle 関数によるシャッフルのパターンが固定化されていることに気づきます。

そこで、ABCDEFGHIJK をシャッフルした文字列と対応する位置の文字を手動で置き換え、1 回目のシャッフル結果が ABCDEFGHIJK となる文字列を用意しました。

img

これを入力すると Flag を取得できます。

img

Tail(OSINT)

Here’s a picture of a plane’s tail. Can you find the airline’s hub (the airport where they mostly operate from). Use the three letter airport IATA code and wrap it in n00bz{}. Example: n00bz{SFO}

以下の画像が与えられます。

tail

airplain blue flower などで検索するとこの模様の飛行機が見つかります。

image-20240803134618787

正しい Flag は n00bz{PPT} でした。

The Gang(OSINT)

John Doe has been suspected of creating a gang. The members of team n00bzUnit3d also seem associated with it. Can you find out if John Doe has recently joined the team? You might find what you are looking for ;) P.S.: The team website might help

n00bzUnit3d のチームサイトから以下のユーザページを見つけます。

参考:Authors

image-20240803134754779

The Gang 2(OSINT)

You may have gotten your first flag, but it’s just the beginning! John Doe, as overconfident as he is, has left you with a riddle. Maybe it hides some secrets? Continue where you left off last time. Flag will already by wrapped in n00bz{} Author: NoobMaster

以下の文を縦読みすると、ユーザ名が JOHN HACKER DOE であることがわかります。

image-20240804165619478

これを X で探すと、Flag が見つかりました。

image-20240804203238241

Flag は n00bz{5t0p_ch4s1ng_m3_4f2d1a7d} です。

The Gang 3(OSINT)

Can you find out where the OG meetup point is? The flag is in the format n00bz{lat,long} with upto 3 decimal places, rounded. Continue where you left off… Note: Wikipedia can be wrong sometimes ;) Author: NoobMaster

ツイートを詳しく読むと、AES 暗号文と復号のためのヒントが与えられます。

image-20240804203524274

過去の n00bzCTF の John Doe の関連問題を探すと、AES の復号に必要な情報が見つかります。

参考:n00bzCTF2023-OfficalWriteups/OSINT/John Doe Strikes Again at master · n00bzUnit3d/n00bzCTF2023-OfficalWriteups

参考:n00bzCTF-OfficialWriteups/osint/john_doe at main · n00bzUnit3d/n00bzCTF-OfficialWriteups

これを復号すると Discord サーバの URL がでてきました。

image-20240804220324149

このサーバに参加すると、インドの Bengaluru 周辺の 110 フィートの銅像で合流する予定であることがわかります。

この銅像の人物は空港の名前の由来にもなっているらしいです。

image-20240804230348552

これは、以下の銅像でした。

参考:Nadaprabhu Kempegowda Statue - Google Maps

この銅像の座標が正しい Flag n00bz{13.199,77.682} になります。

The Gang 4(OSINT)

Can you find more information about his flight? Time to close John Doe’s case one and for all! Author: NoobMaster Note: Flag format: n00bz{DateOfFlight(DD/MM/YYYY)FlightNumberIATAairportCodeOfDepartureIATAairportCodeofArrivalACTUALtimeOfDepartureACTUALtimeOfArrivalGateofDepartureAirplaneModel} Example: n00bz{26/06/2024AA1337DFWSFO13:3715:5132AAirbusA319-400} Please use the above format to format your flag.

Discord サーバ内の会話から、John Doe が乗った飛行機に関する情報を収集します。

image-20240804233208113

image-20240804233158161

これらの情報から以下のサイトにたどり着くことができます。

参考:AI506 (AIC506) Air India Flight Tracking and History 03-Aug-2024 (DEL / VIDP-BLR / VOBL) - FlightAware

正しい Flag は n00bz{03/08/2024_AI506_DEL_BLR_10:03_12:44_30_AirbusA350-900} でした。

Pastebin(OSINT)

Just go to my pastebin which was created long time back. Username: abhinav654321

与えられたユーザ名で Pastebin を検索します。

image-20240803142834930

参考:Nothing here - Pastebin.com

この投稿の過去のコンテンツを Wayback Machine で探すと Flag を特定できます。

image-20240803142814491

参考:n00bz{l0ngt1m3ag0m34nsw4yb4ck} - Pastebin.com

PastebinX(OSINT)

All I need is the User ID said the boss. Help the poor employee to find the id. Flag format - n00bz{1d}. If id is “hhjkh876897” then flag will be n00bz{1dhhjkh876897}

Pastebin の投稿から、@Abhinav78082932 という X アカウントの ID?を特定する問題であると予想できます。

しかし、ここで完全にハマりました。

最終的にチームメンバーが Twitter アカウントの内部 ID?のようなものが存在することに気づき、正しい Flag を特定できました。

参考:Twitter ID Finder and Converter (100% Working ✅)

Passwordless(Web)

Tired of storing passwords? No worries! This super secure website is passwordless! Author: NoobMaster

問題バイナリとして以下のコードが与えられます。

#!/usr/bin/env python3
from flask import Flask, request, redirect, render_template, render_template_string
import subprocess
import urllib
import uuid
global leet

app = Flask(__name__)
flag = open('/flag.txt').read()
leet=uuid.UUID('13371337-1337-1337-1337-133713371337')

@app.route('/',methods=['GET','POST'])
def main():
    global username
    if request.method == 'GET':
        return render_template('index.html')
    elif request.method == 'POST':
        username = request.values['username']
        if username == 'admin123':
            return 'Stop trying to act like you are the admin!'
        uid = uuid.uuid5(leet,username) # super secure!
        return redirect(f'/{uid}')

@app.route('/<uid>')
def user_page(uid):
    if uid != str(uuid.uuid5(leet,'admin123')):
        return f'Welcome! No flag for you :('
    else:
        return flag

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=1337)

ユーザ名 admin123 の UUID を URL に含む場合に Flag が返されるため、手動で生成した UUID を URL に付与して直接アクセスしました。

image-20240803152508854

image-20240803152459680

Addition(Misc)

My little brother is learning math, can you show him how to do some addition problems? Author: Connor Chang

なんか足し算の計算をするらしいです。

import time
import random

questions = int(input("how many questions do you want to answer? "))

for i in range(questions):
    a = random.randint(0, 10)
    b = random.randint(0, 10)

    yourans = int(input("what is " + str(a) + ' + ' + str(b) + ' = '))

    print("calculating")

    totaltime = pow(2, i)

    print('.')
    time.sleep(totaltime / 3)
    print('.')
    time.sleep(totaltime / 3)
    print('.')
    time.sleep(totaltime / 3)

    if yourans != a + b:
        print("You made my little brother cry 😭")
        exit(69)

f = open('/flag.txt', 'r')
flag = f.read()
print(flag[:questions])

totaltime = pow(2, i) で Sleep させられる時間が膨大過ぎるので、正攻法では解けません。

最終的に print(flag[:questions]) で Flag を表示できればいいため、単に -1 を入力して Flag を取得しました。

from pwn import *

# Set context
# context.log_level = "debug"
target = remote("24.199.110.35", 42189, ssl=False)

# Exploit
q = -1
target.recvuntil(b"how many questions do you want to answer? ")
target.sendline(str(q))
print(target.recvline())

# Finish exploit
target.interactive()
target.clean()

image-20240803153342246

Subtraction(Misc)

My little brother is learning math, can you show him how to do some subtraction problems? Author: Connor Chang

今度は引き算らしいです。

696969 個のランダムに生成された偶数の配列を任意の整数 c から引いた結果の絶対値をとる操作を最大 20 回繰り返した結果、配列内のすべての値を同じ値にすることができれば Flag を取得できます。

import random

n = 696969

a = []
for i in range(n):
    a.append(random.randint(0, n))
    a[i] -= a[i] % 2

print(' '.join(list(map(str, a))))

for turns in range(20):
    c = int(input())
    for i in range(n):
        a[i] = abs(c - a[i])

    if len(set(a)) == 1:
        print(open('/flag.txt', 'r').read())
        break

少し悩んだ末に、最終的に配列内の要素の最大値を 1 に近づけていく操作ができればよいことに気づきました。

そこで、以下の Solver で最大値と最小値の平均をとる操作を繰り返していくことで Flag を取得できました。

from pwn import *

# context.log_level = "debug"
# target = process(TARGET_PATH)
target = remote("challs.n00bzunit3d.xyz", 10274, ssl=False)

r = target.recvline().decode()
arr = []
for n in r.split(" "):
    arr.append(int(n))

# Exploit
n = len(arr)
for turns in range(20):
    min_a = min(arr)
    max_a = max(arr)
    c = (min_a + max_a) // 2
    for i in range(n):
        arr[i] = abs(c - arr[i])
    target.sendline(str(c).encode())
    if len(set(arr)) == 1:
        print("found")
        break

print(target.recvline())

# Finish exploit
target.interactive()
target.clean()

image-20240805020414145

Think Outside the Box(Pwn)

You cannot beat my Tic-Tac-Toe bot! If you do, you get a flag! Author: NoobMaster

○× ゲームの後攻でゲームに勝利すると Flag を得られます。

しかし、普通にプレイしても後攻だと引き分けにしかできません。

また、入力をとる箇所も 1 箇所しかないため、Attack Surface が一切ありません。

最終的に、チームメンバーが -1 を入力すると board_limit_0x3 s> y && board_limit_0x3 s> x の検証をバイパスするとともに、Bot の操作をバグらせることができることに気づきました。

int32_t main(int32_t argc, char** argv, char** envp)
{
    void* fsbase
    int64_t rax = *(fsbase + 0x28)
    int32_t board_limit_0x3 = 3
    int16_t board
    __builtin_strncpy(dest: &board, src: "____X____", n: 9)
    puts(str: "Welcome to Tic-Tac-Toe! In order…")
    printBoard(&board)
    int32_t N
    int32_t M
    move(&board, board_limit_0x3, stage_count: 1, &N, &M, 0, 1)
    printBoard(&board)
    int32_t K
    int32_t L
    move(&board, board_limit_0x3, stage_count: 2, &K, &L, N, M)
    printBoard(&board)
    int32_t var_30
    int32_t var_2c
    move(&board, board_limit_0x3, stage_count: 3, &var_30, &var_2c, K, L)
    printBoard(&board)
    void var_28
    void var_24
    move(&board, board_limit_0x3, stage_count: 4, &var_28, &var_24, var_30, var_2c)
    
    if (rax == *(fsbase + 0x28))
        return rax.d - (*(fsbase + 0x28)).d
    
    __stack_chk_fail()
    noreturn
}
        

int64_t move(char* board, int32_t board_limit_0x3, int32_t stage_count, int32_t* arg4, int32_t* arg5, int32_t arg6, int32_t arg7)
{
    void* fsbase
    int64_t rax = *(fsbase + 0x28)
    printf(format: "Move: ")
    int32_t y  // カンマ区切りでy,x
    int32_t x
    __isoc99_scanf(format: "%d,%d", &y, &x)
    
    if (board_limit_0x3 s> y && board_limit_0x3 s> x && board[sx.q(y) * 3 + sx.q(x)] != 'X' && board[sx.q(y) * 3 + sx.q(x)] != 'O')
        board[sx.q(y) * 3 + sx.q(x)] = 0x4f
        int32_t is_win
        is_win.b = checkWin(board, board_limit_0x3) != 0
        
        if (is_win.b != 0)
            printBoard(board)
            printf(format: "You won! Flag: ")
            void buf
            fgets(&buf, n: 0x26, fp: fopen(filename: "flag.txt", mode: &data_3080))
            printf(format: &data_308b, &buf)
            exit(status: 0)
            noreturn
        
        printBoard(board)
        
        if (stage_count == 1)
            puts(str: "Bot turn!")
            int32_t var_68
            int32_t var_64
            firstmove(board, y, x, &var_68, &var_64)
            *arg4 = var_68
            *arg5 = var_64
        
        if (stage_count == 2)
            puts(str: "Bot turn!")
            int32_t var_60
            int32_t var_5c
            secondmove(board, y, x, &var_60, &var_5c, arg6, arg7, board_limit_0x3)
            *arg4 = var_60
            *arg5 = var_5c
        
        if (stage_count == 3)
            puts(str: "Bot turn!")
            uint64_t board_limit_0x3_1 = zx.q(board_limit_0x3)
            uint64_t var_b8_2 = zx.q(arg7)
            void var_58
            void var_54
            thirdmove(board, y, x, &var_58, &var_54, arg6)
        
        if (stage_count == 4)
            puts(str: "Bot turn!")
            uint64_t board_limit_0x3_2 = zx.q(board_limit_0x3)
            uint64_t var_b8_3 = zx.q(arg7)
            fourthmove(board, y)
        
        if (rax == *(fsbase + 0x28))
            return rax - *(fsbase + 0x28)
        
        __stack_chk_fail()
        noreturn
    
    puts(str: "Invalid move!")
    exit(status: 0)
    noreturn
}

○× ゲームのテーブルは線形の文字列で管理されているため、-1 を入力に使用することで Bot の動きを止めつつ複数のマスを埋めることでゲームに勝利し、Flag を取得しました。

image-20240804235837469

まとめ

たくさんの問題を解いた。

全体的に難易度は易しめでサクサク解けたので楽しかったです。