Skip to content

Commit 4cbdfc8

Browse files
committed
mwcc challenge writeups are now added
1 parent 786a02e commit 4cbdfc8

15 files changed

+397
-1
lines changed

_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pageviews:
8888
# light — Use the light color scheme
8989
# dark — Use the dark color scheme
9090
#
91-
theme_mode: # [light | dark]
91+
theme_mode: dark # [light | dark]
9292

9393
# The CDN endpoint for media resources.
9494
# Notice that once it is assigned, the CDN url
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: Broken Access Control
3+
date: 2024-12-23 19:47:00 +/-0600
4+
categories: [Capture The Flags, Mountain West Cyber Challenge]
5+
tags: [ctf, mountain west cyber challenge, web, writeups]
6+
description: Mountain West Cyber Challenge Broken Access Control Challenge
7+
---
8+
9+
> Challenge description:
10+
>
11+
> You are John, a student at the school. There is a flag hidden somewhere in the website. You might need to gain access to admin privileges for the information you seek. Here are your credentials for the website: username: John password: johndoe123
12+
{: prompt-info }
13+
14+
![the login page](/assets/img/mwcc-2024/broken-access-control/image0.png)
15+
16+
Hm, lets try something before we log in. In the browser we can see that we're at `https://dockeridgohere.challenge.hackazon.org/login`, what if we went to `https://doesanyonereadthese.challenge.hackazon.org/admin`.
17+
18+
![holy shit](/assets/img/mwcc-2024/broken-access-control/image1.png)
19+
20+
Holy shit that actually worked.
21+
22+
FLAG: `CTF{5468697365626f6f6b6973666f727468}`
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
title: Crack That Zip
3+
date: 2024-12-23 19:47:00 +/-0600
4+
categories: [Capture The Flags, Mountain West Cyber Challenge]
5+
tags: [ctf, mountain west cyber challenge, password cracking, writeups]
6+
description: Mountain West Cyber Challenge Crack That Zip Challenge
7+
---
8+
9+
> Challenge description:
10+
>
11+
> Can you decrypt this zip file? The password should occur in standard wordlists such as "rockyou".
12+
> Flag format: CTF{32-hex}
13+
{: .prompt-info }
14+
15+
Given that the password is in `rockyou.txt` this should be pretty easy. We have a zip file that is password protected, so lets extract the hash from the file.
16+
17+
```terminal
18+
19+
┌─[slavetomints@parrot]─[~/ctfs/mwcc/pass]
20+
└──╼ $zip2john challenge.zip > hash.txt
21+
ver 2.0 efh 5455 efh 7875 challenge.zip/input.txt PKZIP Encr: TS_chk, cmplen=132, decmplen=140, crc=DF7C40D5 ts=88E4 cs=88e4 type=8
22+
23+
```
24+
25+
Lets verify that the command worked
26+
27+
```terminal
28+
29+
┌─[slavetomints@parrot]─[~/ctfs/mwcc/pass]
30+
└──╼ $cat hash.txt
31+
challenge.zip/input.txt:$pkzip$1*1*2*0*84*8c*df7c40d5*0*43*8*84*88e4*24413e2fce7f1526f78a0334ec84517c97fa4968ef7b9c06b84bf81bd9ca47f30e4ae788f36e22ca2b6c34af270fff123cd06e1c490d0f6763ad00a630c26870871814e4a74cd7c37f9a02a3ee1281a3363d7f98686fc241829fe845e45dbce000fc878f32ec3089856c830f38195c451f5391ad5b78a074ad497b904a048e2f6bdc833a*$/pkzip$:input.txt:challenge.zip::challenge.zip
32+
33+
```
34+
35+
ALright, now that we have the hash from the pdf, lets dump it into `John the Ripper` to get the password from it. We're going to use `rockyou.txt`, from the infamous rockyou.com breach.
36+
37+
```terminal
38+
39+
┌─[slavetomints@parrot]─[~/ctfs/mwcc/pass]
40+
└──╼ $john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
41+
Using default input encoding: UTF-8
42+
Loaded 1 password hash (PKZIP [32/64])
43+
Will run 4 OpenMP threads
44+
Press 'q' or Ctrl-C to abort, almost any other key for status
45+
budweiser (challenge.zip/input.txt)
46+
1g 0:00:00:00 DONE (2024-12-08 20:02) 33.33g/s 273066p/s 273066c/s 273066C/s 123456..whitetiger
47+
Use the "--show" option to display all of the cracked passwords reliably
48+
Session completed.
49+
50+
```
51+
52+
Now, armed with the password, lets inflate the zip and see what we can get.
53+
54+
```terminal
55+
56+
┌─[slavetomints@parrot]─[~/ctfs/mwcc/pass]
57+
└──╼ $unzip challenge.zip
58+
Archive: challenge.zip
59+
[challenge.zip] input.txt password:
60+
inflating: input.txt
61+
┌─[slavetomints@parrot]─[~/ctfs/mwcc/pass]
62+
└──╼ $cat input.txt
63+
If you can read this you managed to crack the password of the zip file.
64+
65+
Here is a nice flag for you:
66+
CTF{1aa4c84b1ff9f21ce476ff50c7d0fe74}
67+
68+
```
69+
70+
FLAG: `CTF{1aa4c84b1ff9f21ce476ff50c7d0fe74}`

_posts/2024-12-23-crypto2.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
title: Crypto 2
3+
date: 2024-12-23 19:47:00 +/-0600
4+
categories: [Capture The Flags, Hacker Conclvae v2]
5+
tags: [ctf, hacker conclave v2, crypto, writeups]
6+
description: Hacker Conclave v2 Crpyto 2 Challenge
7+
---
8+
9+
> Challenge description:
10+
>
11+
> In this challenge, we have access to a program that will encrypt the flag we want to obtain. When connecting to port **[redacted for privacy]** at the address **[redacted for privacy]**, it will return the program's output. Will you be able to retrieve the flag?
12+
{: .prompt-info }
13+
14+
Alright, so lets look at this challenge, when we connect to the port, it spits out two things at us. First, the source code, and then an encrypted message. Lets look at the source code.
15+
16+
17+
```python
18+
19+
import os
20+
import random
21+
import string
22+
from Cryptodome.Cipher import AES
23+
from Cryptodome.Protocol.KDF import PBKDF2
24+
from Cryptodome.Random import get_random_bytes
25+
from Cryptodome.Util.number import bytes_to_long
26+
27+
characters = string.ascii_letters + string.digits
28+
29+
if os.path.exists("/flag/flag.txt"):
30+
flag=(open("/flag/flag.txt","r").read()).encode("utf-8");
31+
else:
32+
flag=(open("flag.txt","r").read()).encode("utf-8");
33+
34+
key = ((""+random.choice(characters))*16).encode("utf-8");
35+
36+
cipher = AES.new(key, AES.MODE_ECB);
37+
38+
padded_content = flag.ljust((len(flag) // 16 + 1) * 16, b'\x00');
39+
encrypted_content = cipher.encrypt(padded_content);
40+
encrypted_content = bytes_to_long(encrypted_content);
41+
42+
print(open("cifra.py","r").read());
43+
print("encrypted_content="+str(encrypted_content)+"\n");
44+
45+
```
46+
{: file="cifra.py" }
47+
48+
Okay, so this ran when we connected, and thats why it printed the entire program and the encrypted content to the terminal, as that is the last thing that this program `cifra.py` does. Lets break down this program line by line.
49+
50+
```python
51+
52+
import os
53+
import random
54+
import string
55+
from Cryptodome.Cipher import AES
56+
from Cryptodome.Protocol.KDF import PBKDF2
57+
from Cryptodome.Random import get_random_bytes
58+
from Cryptodome.Util.number import bytes_to_long
59+
60+
```
61+
{: file="cifra.py" }
62+
63+
The `os` module is commonly used for using the functionality of the operating system. In this program its used to check if the flag is at `flag/flag.txt`.
64+
65+
The `random` module is used for randomness, as the name suggests. Its used in the program with `random.choice()`, where it makes a random selection based on hat is passed to `choice()`.
66+
67+
The `string` module is used here for the `string.ascii_letters` and `string.digits`, which is used to make the `characters` variable.
68+
69+
Next up is `Cryptodome`. It is another module that gives python some expanded cryptographic functionalities. Here, we they are importing `AES`, `PBKDF2`, `get_random_bytes`, and `bytes_to_long`. We'll go over their functionality as we get to them.
70+
71+
Moving away from the import statements, lets get into the meat and potatoes of the code. First up, we have:
72+
73+
```python
74+
75+
characters = string.ascii_letters + string.digits
76+
77+
```
78+
{: file="cifra.py" }
79+
80+
This is a pretty simple line. It takes all uppercase and lowercase ascii letters, and all digits 0-9 and concatenates them, seting characters value to `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`
81+
82+
So, moving on, we are confronted with this:
83+
84+
```python
85+
86+
if os.path.exists("/flag/flag.txt"):
87+
flag=(open("/flag/flag.txt","r").read()).encode("utf-8");
88+
else:
89+
flag=(open("flag.txt","r").read()).encode("utf-8");
90+
91+
```
92+
{: file="cifra.py" }
93+
94+
This `if...else` block first checks to see if the path to `/flag/flag.txt` exists, and if it does then it opens the file in read mode with UTF-8 encoding. However, if the path doesn't exist, it would open `./flag.txt` in read mode with UTF-8 encoding.
95+
96+
This next line is where the vulnerability in the code lies.
97+
98+
```python
99+
100+
key = ((""+random.choice(characters))*16).encode("utf-8");
101+
102+
```
103+
{: file="cifra.py" }
104+
105+
Usually, a 16 character key can be pretty secure, knowing that there are 62 possible choices for each character, leaving a whopping `47,672,401,706,823,533,450,263,330,816`, or `Forty-seven octillion, six hundred seventy-two septillion, four hundred one sextillion, seven hundred six quintillion, eight hundred twenty-three quadrillion, five hundred thirty-three trillion, four hundred fifty billion, two hundred sixty-three million, three hundred thirty thousand, eight hundred sixteen`. However, the arrow in the knee is that they randomly generate one character and repeat it 16 times, taking that huge number of possilities and turning it into `62`. This, is easily brute-forcable.
106+
107+
```python
108+
109+
padded_content = flag.ljust((len(flag) // 16 + 1) * 16, b'\x00');
110+
encrypted_content = cipher.encrypt(padded_content);
111+
encrypted_content = bytes_to_long(encrypted_content);
112+
113+
print(open("cifra.py","r").read());
114+
print("encrypted_content="+str(encrypted_content)+"\n");
115+
116+
```
117+
{: file="cifra.py" }
118+
119+
The rest of the code covers encrypting the flag, and then printing the program and the encrypted flag to the terminal.
120+
121+
The encrypted flag I got was `33184633452588628947694484591780825103796687823569131220950080094742922022993114204314814746054128940142933245107995`
122+
123+
Lets now review my decryption program.
124+
125+
```python
126+
127+
from Cryptodome.Cipher import AES
128+
from Cryptodome.Util.number import long_to_bytes
129+
import string
130+
131+
# Given encrypted content (ciphertext as a long integer)
132+
encrypted_content = 33184633452588628947694484591780825103796687823569131220950080094742922022993114204314814746054128940142933245107995
133+
134+
# Convert encrypted content back to bytes
135+
ciphertext = long_to_bytes(encrypted_content)
136+
137+
# Character set used to generate the key
138+
characters = string.ascii_letters + string.digits
139+
140+
# Brute-force all possible single-character keys repeated 16 times
141+
for char in characters:
142+
key = (char * 16).encode("utf-8") # Key is one character repeated 16 times
143+
cipher = AES.new(key, AES.MODE_ECB) # Initialize cipher with key
144+
145+
try:
146+
decrypted_content = cipher.decrypt(ciphertext).rstrip(b'\x00') # Remove padding
147+
if decrypted_content.startswith(b"conclave{"): # Check if it starts with "conclave{"
148+
print(f"Key: {key.decode()} | Flag: {decrypted_content.decode()}")
149+
except Exception:
150+
continue # Skip invalid decryptions
151+
152+
```
153+
{: file="bruteforce.py" }
154+
155+
To quickly cover the program, we import some of the same modules as they did originally in order to reverse the encrypted content to bytes, and to recreate the `characters` variable. Then for each character in `characters` we are going to make an AES key the same way they did and attempt to decrypt it. If it starts with `conclave{`, which is the flag format for the CTF, then we know we have the correct key. And it's all wrapped in a `try...except` in order to skip past invalid decryptions that might cause the program to error out.
156+
157+
```terminal
158+
159+
┌─[slavetomints@parrot]─[~/ctfs/hacker-conclave-v2/crypto/crypto2]
160+
└──╼ $python bruteforce.py
161+
Key: HHHHHHHHHHHHHHHH | Flag: conclave{40e9222eee660a0c1cd2e736d613a7e1}
162+
163+
```
164+
165+
FLAG: `conclave{40e9222eee660a0c1cd2e736d613a7e1}`
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
title: Introduction Challenge
3+
date: 2024-12-23 19:47:00 +/-0600
4+
categories: [Capture The Flags, Mountain West Cyber Challenge]
5+
tags: [ctf, mountain west cyber challenge, web, writeups]
6+
description: Mountain West Cyber Challenge Introduction Challenge
7+
---
8+
9+
> Challenge description:
10+
>
11+
> There is a flag hidden on the web page of this challenge
12+
{: .prompt-info }
13+
14+
Cryptic, love it, to keep it simple, someone must've just learned what a <br> is and went to town on the site. Once you've scrolled long enough to witness the heat death of the universe, youll find the flag.
15+
16+
![the flag, and too many <br>'s](/assets/img/mwcc-2024/introduction-challenge/image0.png)
17+
18+
FLAG: `CTF{c96f130f}`
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: Missile Command
3+
date: 2024-12-23 19:47:00 +/-0600
4+
categories: [Capture The Flags, Mountain West Cyber Challenge]
5+
tags: [ctf, mountain west cyber challenge, misc, writeups]
6+
description: Mountain West Cyber Challenge Missile Command Challenge
7+
---
8+
9+
This challenge was simple, play the Missile Command clone, blow up 5 missiles, and you get the flag. A nice break from the other challenges.
10+
11+
FLAG: `CTF{classified_code_restricted_voice_only}`

_posts/2024-12-23-weak-crypto.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
title: Weak Crypto
3+
date: 2024-12-23 19:47:00 +/-0600
4+
categories: [Capture The Flags, Mountain West Cyber Challenge]
5+
tags: [ctf, mountain west cyber challenge, web, writeups]
6+
description: Mountain West Cyber Challenge Weak Crypto Challenge
7+
---
8+
9+
> Challenge description:
10+
>
11+
> We were recently hacked, so we needed to take our entire login application offline. All users who access the site are forced to be anonymous users only based on their session token.
12+
>
13+
> Can you verify that our fix is effective?
14+
>
15+
> Note: The login functionality and form are actually disabled, there is no brute forcing required to solve this challenge; focus on the session token
16+
{: .prompt-info }
17+
18+
Hm, that sounds no great, lets take quick look at the html to verify that we cant enter anything in.
19+
20+
```html
21+
22+
<!DOCTYPE html>
23+
<html>
24+
<head>
25+
<meta charset="utf-8">
26+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
27+
<meta name="viewport" content="width=device-width, initial-scale=1">
28+
<title>Admin Panel</title>
29+
<link href="static/bootstrap.min.css" rel="stylesheet">
30+
<link href="static/admin.css" rel="stylesheet">
31+
<script src="static/bootstrap.min.js"></script>
32+
</head>
33+
<body>
34+
<div class="container">
35+
<div class="row vertical-center">
36+
<div class="col-md-4 col-md-offset-4">
37+
<div class="panel panel-default">
38+
<div class="panel-heading">
39+
<h3 class="panel-title text-center"><b>Admin Panel</b></h3>
40+
</div>
41+
<div class="panel-body">
42+
<form id="login_form" handled="true" method="#" action="#">
43+
<fieldset>
44+
<div class="form-group">
45+
<input class="form-control" placeholder="Username" name="username" id="username" type="text" disabled="">
46+
</div>
47+
<div class="form-group">
48+
<input class="form-control" placeholder="Password" name="password" id="password" type="password" disabled="">
49+
</div>
50+
<input type="submit" name="submit" id="submit" class="btn btn-block btn-primary" disabled="">
51+
</fieldset>
52+
</form>
53+
</div>
54+
</div>
55+
<div class="text-center"><a href="api/user">You can verify your user status here</a></div>
56+
</div>
57+
</div>
58+
</div>
59+
</body>
60+
</html>
61+
62+
```
63+
64+
Yep, seems like we cant access anything, lets go take a look at those cookies.
65+
66+
![finding the cookies](/assets/img/mwcc-2024/weak-crypto/image0.png)
67+
68+
That cookie looks like it might be encoded, and Cyber Chef think so too. Usin a Base64 decoder we can turn `eyJ1c2VyIjogImFub255bW91cyJ9` into `{"user": "anonymous"}`.
69+
70+
Let's verify who we are by clicking on the link. We see that we go to `/api/user`
71+
72+
Now, this might seem insecure, and yep that's right. Lets attempt some privilage escalation by making out own little cookie. We cant forget to encode it too. `{"user": "admin"}` becomes `eyJ1c2VyIjogImFkbWluIn0=`
73+
74+
And now if we revisit the verification api...
75+
76+
```json
77+
78+
{
79+
"flag": "CTF{c75b3e039425dfcf0c5ef0dfdd5b00a3}"
80+
}
81+
82+
```
83+
84+
Gottem
85+
86+
FLAG: `CTF{c75b3e039425dfcf0c5ef0dfdd5b00a3}`

0 commit comments

Comments
 (0)