Alfa Surfing
Team: Pepekai
Rank: 6 / 346
The following UI view, challenge description and other things are google translated from Russian to English
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.
Sources: chillguy_6b73b5f.tar.gz
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

Flag: alfa{ChILLGuY_caN_BYp45S_EV3RYthIng_And_Do_aNyThIng}
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!
Flag in a file
/flag.txtin 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:
using a Local File Inclusion
/api/content?path=/etc/passwdoverwriting .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

Flag: alfa{whA7_th3_HeCk__yOu_HaVe_b3En_SpAMm3d}
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.pcap
The flag is located along the path
/app/FLAG.txton 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)

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:
writes at addr 0x7034b0 (fflush@GOT) = 0x409670 (system@PLT)
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:
Flag: alfa{0n3_7eNT4CL3_DOe5N7_know_WHA7_0TH3R_doEs}
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/
Sources: youtroopers_6ea8819.tar.gz
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

Flag: alfa{oLD_70kEns_ArE_s71LL_VAL1d}
Last updated
