All Articles

KnightCTF CTF 2022 Writeup

This page has been machine-translated from the original page.

I participated in KnightCTF 2023, which was held in January 2023.

The difficulty didn’t look too high, so I thought I might be able to sweep all the Rev challenges, but I had the start time wrong and ran out of time — finishing with 4 solves.

image-20230121235349947

I’ll write a short writeup for each problem I solved.

The Rev binaries in this contest required glibc 2.3.4, so I used a Docker image built from the following Dockerfile.

# glibc2_3_4-ubuntu20_10
FROM ubuntu:21.10
ENV TZ=Europe/London
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN sed -i -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
RUN apt update && apt upgrade -y
RUN apt install gdb strace ltrace vim unzip zip git -y

RUN mkdir -p /app
ENV HOME=/app
WORKDIR $HOME

KrackMe 1.0 (Rev)

image-20230121115824664

This was a fairly straightforward ELF challenge.

To run the binary you needed to supply exactly 5 command-line arguments (the actual values didn’t matter).

After that, you also had to provide input of exactly 0x27 characters.

image-20230122175857705

After successful string input, the input string is split into 4 parts of 9 characters each and validated separately.

image-20230122175041238

I wrote the following script to decrypt the flag from the decompiled validation logic for each part.

# -0x9
unknown = [0x6d,0x65,0x72,0x60,0x5d,0x4d,0x74,0x47,0x65]
c100 = 100
# 0x6142202c 776f6e6b
c60 = 
for u in unknown:
    print(chr(
        u ^ c100 ^ c60
    ), end="")

>> KCTF{kRaC

# 0x1b -
unknown = [0x76,0x7d,0x51,0x6c,0x4f,0x74,0x6f,0x7c,0x73]
c108 = 0x6f
c130 = 0x61
for u in unknown:
    print(chr(
        u ^ c108 ^ c130
    ), end="")

>> xs_bAzar}

# 0x9-0x11
unknown = [0x61,0x55,0x47,0x39,0x55,0x65,0x44,0x6f,0x55]
u1 = 0x65
c138 = 0x6f
for u in unknown:
    print(chr(
        u ^ u1 ^ c138
    ), end="")

>> k_M3_oNe_


# 0x12-0x19
unknown = [0x28,0x47,0x7e,0x54,0x79,0x5f,0x47,0x7b,0x28]
u1 = 0x65
c152 = 0x7d
for u in unknown:
    print(chr(
        u ^ u1 ^ c152
    ), end="")

>> 0_fLaG_c0

Help Jimmy (Rev)

image-20230121153544694

Jimmy faces an impossible situation — either he gets eaten by a tiger or attacked by pirates — and we need to change his fate.

image-20230122182305522

Analyzing the file, even though the decompiled output doesn’t reflect it, the disassembly clearly shows a code path that jumps to a mysterious function.

image-20230122182342802

I set a breakpoint at this location with gdb, patched ZF to redirect execution to that function, and obtained the flag.

The Activator (Rev)

image-20230121192130229

Looking at the decompiled output, the input validation logic was quite complex, but it was clear that the function executed when the correct password is entered outputs the flag.

So I used gdb to jump directly to that address and obtained the flag.

The Defuser (Rev)

image-20230121192032089

Following the decompiled output carefully, I found that the binary checks whether a specific string is set as an environment variable.

So I set the correct environment variable and ran the binary to obtain the flag.

Hello (Network)

image-20230122182944951

This looked easy, so I solved it as a bonus.

Expanding the challenge’s packet capture, I found several DNS queries being issued to a mysterious subdomain.

image-20230122183101094

The last characters in chronological order were ==, which identified it as Base64. I extracted the string VVBCTHtvMV9tcjNhX2VuMF9oazNfaTBofQ==.

Decoding it from Base64 yields UPBL{o1_mr3a_en0_hk3_i0h}.

However, the flag format is different, so some encryption must have been applied.

I thought it might be ROT13, but no matching key existed.

Since the real flag format is KCTF, and KCTF encrypted to UPBL, I guessed it was a substitution cipher.

I systematically identified the characters: K→U, C→P, and so on, using the Vigenère cipher table.

image-20230122183758082

Reference: Vigenère cipher - Wikipedia

Using the key KNIG converted UPBL to KCTF, but since this is KniGT CTF, the key should be KNIGT. The decrypted result with that key, KCTF{v1_ce3s_yu0_xx3_a0b}, is the flag.

image-20230122184001932