Page cover

Alfa Surfing

circle-info

Team: Pepekai

Rank: 6 / 346

circle-info

The following UI view, challenge description and other things are google translated from Russian to English

Challenge
Category
Points
Solves

Chill Day / Чилловый день

Easy, Infra, Linux, Cloud

478 pts

165

Spam Protection / Защита от спама

Easy, Infra, Web

716 pts

32

A tangled story / Запутанная история

Hard, Pwn, Network, Forensics

892 pts

7

Landing of bloggers / Десант блогеров

Easy, Web, Logic

743 pts

26

Chill Day

Description

You met a chill guy on the ocean shore. He is 90% chill, and the other 56% vibe. Your task is to surpass him. Turn on the maximum chill mode.

The secret of chillness is kept on the server where the chill guy navibcoded a service for archiving web pages.

chillguy-1llrkbd3.alfactf.ru/arrow-up-right

Sources: chillguy_6b73b5f.tar.gzarrow-up-right

Solution

visiting the website, it present itself as a website fetcher and archiver.

in the source code, there this sus looking function and comment

the IP hinted at an internal IP of AWS metadata service. although it is blacklisted, it can be easily bypassed using wildcard DNS such *.nip.io as shown below

after a bit of enumerating, we found the machine credential at /latest/user-data

next step is to fetch the public hostname of the ec2 instance to be able to ssh into it

with a valid credential and public hostname, we can then just ssh into it and get the flag

circle-check

Spam Protection

Description

In the distance, a huge pile of garbage can be seen in the ocean. You swim closer and realize: it's endless spam.

Inside the pile, buried under promises of quick money and fake stocks, a flag shines. We'll have to dig deeper!

xz1t3vao.spambox.alfactf.ru/arrow-up-right

Flag in a file /flag.txt in the root of the file system

Solution

blackbox web challenge as there is no source code.

we can upload files, view the files, create directories and so on. In one of the requests that lists files, we can see that there's a hidden .config.yaml file

we can retrieve the content of the file as follows

it describes some of the behaviour and the permission of the application towards the filesystem. Here are some of the ideas that I tried but failed:

  1. using a Local File Inclusion /api/content?path=/etc/passwd

  2. overwriting .config.yaml and authorized_keys but it was blacklisted

upon searching there's a legacy authorized_keys2 that is not blacklisted that could possibly also allows authentication and it did worked.

first, I uploaded my ssh public key to the .ssh path with the name of authorized_keys2

next, I authenticate to the system using ssh and my private key and it did worked

circle-check

A tangled story

Description

You're lying on your surfboard, basking in the sun, when suddenly you hear someone calling for help. You approach the source of the sound and see a strange picture: right at the surface of the water, covered in wires, an octopus is swimming. He downloaded something from the Internet again, started to figure it out, and got confused.

Now his tentacles live their own lives and do not obey. Help the ocean dweller to untangle himself, and at the same time warn him in the future so that he does not think of using wired headphones...

Octopus computer traffic recording: octopus.pcaparrow-up-right

The flag is located along the path /app/FLAG.txt on this computer.

Solution

this is a combined network forensic and pwn challenge, I personally didn't involved in the forensics at all. all I know is that after I woke up, my teammates mentioned a need for pwn help and they provided everything I need to know.

anyway, I was given a run.elf by my teammates and the task was on. as I was still asleep my teammates already found the vulnerability by logging into the user melloy.

first, as shown below we can see the binary has minimal protections

decompiling the binary, we can see some LLM shenanigans happening

running the binary will shows the following error

and so I download the 1.2 GB Qwen3-0.6B-F16.gguf from huggingface to satisfy the dependencies. I ran it again and it works, however it seems we only have one shot (and I downloaded a different model but it doesn't matter)

circle-info

note that the remote requires some sort of pow which was done by my teammate, I just received the code 👍

with the format string, overwriting one of the function entry in GOT to system@PLT is easy.

as this is a one shot pwn, we can't possibly leak libc and the binary has no "/bin/sh" string, I then search if there's a string that ends in "sh" and there was, this will simplifly the exploit

next I need to find what function to overwrite and how to control its argument.

as can be seen above, after printf is called, the binary calls fflush with stdout in RDI.

in this solution I chose fflush as the target function to be overwritten with system and stdout to contain the address of "sh"

so we need two writes in our one shot payload:

  1. writes at addr 0x7034b0 (fflush@GOT) = 0x409670 (system@PLT)

  2. writes at addr 0x7047c0 (stdout) = 0x671c49 ◂— 0x6e6f617865006873 /* 'sh' */

as the lower nibble of the 32 bytes value 0x671c49 is lower than 0x409670, we will start writing the bytes that has the lower value first.

and thus 0x1c49 will be written first and so our payload will start as

next we'll calculate the remaining bytes that we need to write such that the value will be 0x9670

and thus our next payload will be

next, we will write the higher nibble of the value to target_address+2.

since 0x67 is higher than 0x40 we will start writing the bytes that has the lower value first, that 0x40. we need to find the next number that has 0x40 in is lower address after 0x9670 and its offset.

and thus our next payload will be

this means after 0x9670, we will write additional 208 bytes and the total bytes written will be 0x9740. since we're using the $hhn modifier, we will only write 1 bytes of 0x9740 which corresponds to 0x40.

using the same concept we can find the next offset to write 0x67.

and thus our next payload will be

finally, we just need to pad it with 8 bytes and append the payload with the addresses of the to write and update its offsets.

here's the exploit being ran againts the remote server

here's the full exploit script:

circle-check

Landing of bloggers

Description

New vacationers have arrived on the island — bloggers. Their right hand has become fused with a smartphone, they are constantly filming something and admiring the local beauty

The bloggers are planning a large-scale stream, but a slow internet connection is preventing this. One of the stores has the necessary router, but it is too expensive

Help them buy a high-speed router so that as many people as possible can learn about this beautiful island.

Service for joint purchases: youtroopers-4me3mn03.alfactf.ru/arrow-up-right

Sources: youtroopers_6ea8819.tar.gzarrow-up-right

Solution

in the source code we can see the goal of this challenge is to buy a product named Высокоскоростной роутер SeaLink X20

however, after registering we can see we start with 0 money and there's no way to topup or gain money.

in the app, there's this mechanism of "clubbing", where multiple user can jointly group and buy an item together. there's a flaw in the logic to calculate how much a person should pay

as there's no check to how much the participant_count can be, if its value is much greater than the product price, the the rounding from int() will return 0.

in the config.py, we can see the price for the item we need to buy is 333

this means we can just register 334 users right? not really, the user registration in the app implements a recaptcha, I tried to find if we can bypass it but found nothing.

another thing to note is that the app stores data in in files:

  • users in /app/users/<username>

  • clubbings in /app/clubbings/<item>/<id>/<username>

by sheer luck when testing dynamically, I found a strange behaviour.

I had saved the request to join clubbing in the repeater as my username session was still testtesttesttesttest. after some time I renamed my user via the web to blablablablablabla which prompted the application to generate a new session token.

at the time I didn't think this was relevant, and as I hit the join clubbing in the repeater using the old session token, I noticed the app didn't invalidate the token and thus a valid old username.

this is because the app relies on the username provided in the jwt token instead of an id that then links to a username in most common applications.

with this in mind we can then craft fake users to lower the price_per_person value to 0 and buy the item to get the flag.

below is the script to automate the creation of fake users and make them join the clubbing

after the script is done running, in the web view join the clubbing using the printed clubbing_id and buy the item to get the flag

circle-check

Last updated