Initial commit

This commit is contained in:
Rayhan Hanaputra
2025-10-10 22:18:06 +07:00
committed by GitHub
commit ea02892f14
1246 changed files with 289342 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
from .Challenge import Challenge
from modules.blinkpdf import *
import io
import requests
import subprocess
import re
class BlinkPDF(Challenge):
flag_location = 'flags/blinkpdf.txt'
history_location = 'history/blinkpdf.txt'
def distribute(self, flag):
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f'Flag {flag} written to {self.flag_location}')
return True
except Exception as e:
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
return False
def check(self):
try:
# Getting private key
container_env = subprocess.run(
["docker", "exec", "blinkpdf_container", "cat", "/opt/.env"],
capture_output=True,
text=True
).stdout.strip()
private_key = re.search(r'PRIVATE_KEY="(.+?)"', container_env).group(1)
assert len(private_key) > 0, 'Missing PRIVATE_KEY on .env'
sess = requests.Session()
# Checking C1: Login as user
url = f'http://localhost:{self.port}/login'
data = {"username": "user", "password": "user"}
r = sess.post(url, data=data, timeout=5)
assert 'Welcome to the PDF Signature App'.lower() in r.text.lower(), 'Cannot login as user'
pdfpath = 'files/blinkpdf_hellodocs.pdf'
pdfbytes = open(pdfpath, 'rb').read()
# Checking C2: Sign pdf as user
sign_url = f'http://localhost:{self.port}/sign'
r = sess.post(sign_url, timeout=5)
sendata = ('main.pdf', pdfbytes, 'application/pdf')
filedata = {'file': sendata}
r = sess.post(sign_url, files=filedata, timeout=5)
signed_pdf = r.content
assert r.status_code == 200 and 'application/pdf' in r.headers['Content-Type'], 'Signed PDF not available or incorrect content type'
signed_pdf_stream = io.BytesIO(signed_pdf)
assert verify_signature(signed_pdf_stream, private_key), 'Algorithm for signature process is changed'
# Checking C3: Verify valid pdf as user
pdf_bytes_stream = io.BytesIO(pdfbytes)
signed_pdf_stream = sign_pdf(pdf_bytes_stream, private_key)
verify_url = f'http://localhost:{self.port}/verify'
sendata = ('main_signed.pdf', signed_pdf_stream, 'application/pdf')
filedata = {'file': sendata}
r = sess.post(verify_url, files=filedata, timeout=5)
assert 'The signature is <strong>valid</strong>.' in r.text, 'Verify function not working or algoritm verify process is changed'
# Checking C3: Verify invalid pdf as user
verify_url = f'http://localhost:{self.port}/verify'
sendata = ('main_signed.pdf', pdfbytes, 'application/pdf')
filedata = {'file': sendata}
r = sess.post(verify_url, files=filedata, timeout=5)
assert 'The signature is <strong>invalid' in r.text, 'Verify function not working or algoritm verify process is changed for invalid signature'
# Checking C4: Checking flag on container
with open(self.flag_location, 'r') as f:
host_flag = f.read().strip()
container_flag = subprocess.run(
["docker", "exec", "blinkpdf_container", "cat", "/flag.txt"],
capture_output=True,
text=True
).stdout.strip()
assert host_flag == container_flag, 'Flag mismatch between host and container'
# Checking C5: Login as admin and enc_flag checking
url = f'http://localhost:{self.port}/login'
data = {'username': "admin", "password": f'{private_key}'}
r = sess.post(url, data=data, timeout=5)
assert 'Welcome to the PDF Signature App'.lower() in r.text.lower(), 'Cannot login as admin'
url = f'http://localhost:{self.port}/admin_panel'
r = sess.get(url, timeout=5)
enc_flag = r.text.split('encrypted flag: ')[1].split('</p>')[0]
cek, dec = decryptMessage(enc_flag, private_key)
assert dec.decode() == host_flag, 'Change algorithm for encryption flag'
assert cek == True, 'Change signature algorithm for encryption flag'
self.logger.info('Check passed for blinkpdf')
return True
except Exception as e:
self.logger.error(f'Could not check blinkpdf: {e}')
return False
+34
View File
@@ -0,0 +1,34 @@
import logging
import random
import string
from config import get_settings
class Challenge(object):
name = __name__
settings = get_settings()
port = 0
def __init__(self, port):
self.port = port
self.add_logger()
def add_logger(self):
self.logger = logging.getLogger()
def random_string(self, length):
charset = string.ascii_uppercase + string.ascii_lowercase + string.digits
return ''.join(random.choice(charset) for i in range(length))
def distribute(self, flag):
raise NotImplementedError
def check(self):
raise NotImplementedError
def credentials(self):
return {
'username': 'ctfuser',
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
}
+99
View File
@@ -0,0 +1,99 @@
from .Challenge import Challenge
import io
import requests
import random
import subprocess
import requests
import re
class Naraka(Challenge):
flag_location = 'flags/naraka.txt'
history_location = 'history/naraka.txt'
def distribute(self, flag):
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f'Flag {flag} written to {self.flag_location}')
return True
except Exception as e:
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
return False
def check(self):
random_name = [
"nael", "mufat", "rayhan", "wicak", "Kingggg",
"osdie", "udin", "memet", "entong", "krabby",
"mew", "charizard", "pidgey", "rattata", "vulpix",
"eevee", "snorlax", "magikarp", "growlithe", "abra"
]
math_expression = "+-*"
python_programs = [
"a = 10;b = 5;print(a+b)", "a = 'add'; b='string';print(a+b)","x = [1,2,3,4,5,6,7,8];print(max(x))"
]
programs_result = ["b&#39;15","b&#39;addstring","b&#39;8"]
try:
# Step 1: Chekc Greetings return name
name = random.choice(random_name)
url = f'http://localhost:{self.port}/render?name={name}'
r = requests.get(url, timeout=5)
assert name in r.text, 'Greetings is not greetings my name :('
# Step 2: Check eval calculator using random math expression
math = f'{random.randint(1,100)}{random.choice(math_expression)}{random.randint(1,100)}'
math_result = eval(math)
data = {'chall': '2','input':math}
url = f'http://localhost:{self.port}/chall'
r = requests.post(url, data=data, timeout=5)
assert str(math_result) in r.text, 'Eval calculator is not working :('
# Step 3: Check exec that execute python program
ind = random.randint(0,2)
pythononeline = python_programs[ind]
pythonresult = programs_result[ind]
data = {'chall': '1','input':pythononeline}
url = f'http://localhost:{self.port}/chall'
r = requests.post(url, data=data, timeout=5)
assert pythonresult in r.text, 'My code line is not execute :('
# Step 4: Check execute FLAG declaration execute
url = f'http://localhost:{self.port}/sourcecode/1'
r = requests.get(url, timeout=5)
flag_assignments = re.findall(r'^\s*FLAG\s*=\s*sys\.argv\[2\]\s*$', r.text, re.MULTILINE)
flag_assignments_num = flag_assignments = re.findall(r'^\s*FLAG\s*=\s*.+$', r.text, re.MULTILINE)
assert flag_assignments != 1 or flag_assignments_num != 1, "execute FLAG declared incorrectly"
# Step 5: Check evaluate FLAG declaration execute
url = f'http://localhost:{self.port}/sourcecode/1'
r = requests.get(url, timeout=5)
flag_assignments = re.findall(r'^\s*FLAG\s*=\s*sys\.argv\[2\]\s*$', r.text, re.MULTILINE)
flag_assignments_num = flag_assignments = re.findall(r'^\s*FLAG\s*=\s*.+$', r.text, re.MULTILINE)
assert flag_assignments != 1 or flag_assignments_num != 1, "evaluate FLAG declared incorrectly"
# Step 6: Check flag
with open(self.flag_location, 'r') as f:
host_flag = f.read().strip()
container_flag = subprocess.run(
["docker", "exec", "naraka_container", "cat", "/flag.txt"],
capture_output=True,
text=True
).stdout.strip()
assert host_flag == container_flag, 'Flag mismatch between host and container'
self.logger.info('Check passed for naraka')
return True
except Exception as e:
self.logger.error(f'Could not check naraka: {e}')
return False
+93
View File
@@ -0,0 +1,93 @@
from .Challenge import Challenge
import io
import requests
import random
import subprocess
import json
class Niko(Challenge):
flag_location = 'flags/niko.txt'
history_location = 'history/niko.txt'
def distribute(self, flag):
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f'Flag {flag} written to {self.flag_location}')
return True
except Exception as e:
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
return False
def check(self):
error_messages = [
"あなたはどんなオタクですか",
"冗談じゃないよ!",
"tch なんだよ こいつ",
"どうしてそんなことが可能でしょうか…不可能です",
"本当のあなたは何ですか?",
"うわー、ごめんなさい",
"御心のままに、主よ",
"もういいよ、やめて!",
"時間です",
"悪くないよ。"
]
try:
# Step 1: Check if the flag still exists and matches the one in the container
with open(self.flag_location, 'r') as f:
host_flag = f.read().strip()
container_flag = subprocess.run(
["docker", "exec", "niko_container", "cat", "/flag.txt"],
capture_output=True,
text=True
).stdout.strip()
assert host_flag == container_flag, 'Flag mismatch between host and container'
self.logger.info('Flag check passed for niko')
# Step 2: Check if can access flag
container_key = subprocess.run(
["docker", "exec", "niko_container", "cat", "/opt/flag"],
capture_output=True,
text=True
).stdout.strip()
urlFlag = f'http://localhost:{self.port}/api/getFlag?flag={container_key}'
expected_output = container_flag
response2 = requests.get(urlFlag)
assert response2.text.strip() == expected_output, 'Get Flag check does not work'
self.logger.info('Get flag endpoint check passed for niko')
# Step 3: Check if the webpage can be accessed
url = f'http://localhost:{self.port}/'
response = requests.get(url)
status_code = response.status_code
assert status_code == 200, 'Webpage is not accessible'
self.logger.info('Webpage accessibility check passed for niko')
# Step 4: Check if the output of the specific URL equals the expected string
urlFlag = f'http://localhost:{self.port}/api/getFlag?flag=1'
expected_output = "(⋟﹏⋞) 私をバカにしようとしているのか (´ ͡༎ຶ ͜ʖ ͡༎ຶ `)︵‿︵"
response2 = requests.get(urlFlag)
assert response2.text.strip() == expected_output, 'Webpage output does not match expected output'
self.logger.info('Webpage output check passed for niko')
# Step 5: Check if the chat endpoint is working
urlChat = f'http://localhost:{self.port}/api/chat'
data = 'test'
response3 = requests.post(urlChat, data=data)
assert json.loads(response3.text.strip()).get("output") in error_messages, 'Api endpoint is not accessible'
self.logger.info('Webpage delay check passed for niko')
return True
except Exception as e:
self.logger.error(f'Could not check niko: {e}')
return False
+65
View File
@@ -0,0 +1,65 @@
from .Challenge import Challenge
import io
import requests
import random
import subprocess
class Poke(Challenge):
flag_location = 'flags/poke.txt'
history_location = 'history/poke.txt'
def distribute(self, flag):
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f'Flag {flag} written to {self.flag_location}')
return True
except Exception as e:
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
return False
def check(self):
pokemon_names = [
"pikachu", "bulbasaur", "charmander", "squirtle", "jigglypuff",
"meowth", "psyduck", "machop", "gastly", "krabby",
"mew", "charizard", "pidgey", "rattata", "vulpix",
"eevee", "snorlax", "magikarp", "growlithe", "abra"
]
try:
# Step 1: Randomize the Pokémon name
pokemon_name = random.choice(pokemon_names)
url = f'http://localhost:{self.port}/'
data = {'pokemon_name': pokemon_name}
r = requests.post(url, data=data, timeout=5)
assert pokemon_name.lower() in r.text.lower(), 'Pokémon data not available'
# Step 2: Check if the image is available
image_url = f'http://localhost:{self.port}/?image={pokemon_name.lower()}.png'
r = requests.get(image_url, timeout=5)
assert r.status_code == 200 and 'image/png' in r.headers['Content-Type'], 'Pokémon image not available or incorrect content type'
# Step 3: Check if the flag still exists and matches the one in the container
with open(self.flag_location, 'r') as f:
host_flag = f.read().strip()
container_flag = subprocess.run(
["docker", "exec", "poke_container", "cat", "/flag.txt"],
capture_output=True,
text=True
).stdout.strip()
assert host_flag == container_flag, 'Flag mismatch between host and container'
self.logger.info('Check passed for poke')
return True
except Exception as e:
self.logger.error(f'Could not check poke: {e}')
return False
+88
View File
@@ -0,0 +1,88 @@
from .Challenge import Challenge
import io
import requests
import random
import subprocess
import requests
import re
class Wanderer(Challenge):
flag_location = 'flags/wanderer.txt'
history_location = 'history/wanderer.txt'
def distribute(self, flag):
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f'Flag {flag} written to {self.flag_location}')
return True
except Exception as e:
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
return False
def check(self):
try:
session = requests.Session()
# Step 1: Check Register
register_url = f'http://localhost:{self.port}/index.php?module=user&action=register'
registration_data = {'username': 'adminwreckitchecker','password': 'adminwreckitchecker'}
response = session.post(register_url, data=registration_data)
assert "username already exists" in response.text or "Registration Success" in response.text or response.status_code == 200 , 'Function Register Failed'
# Step 2: Check Login
login_url = f'http://localhost:{self.port}/index.php?module=user&action=login'
login_data = {'username': 'adminwreckitchecker', 'password': 'adminwreckitchecker'}
response = session.post(login_url, data=login_data)
assert "Login Success" in response.text or response.status_code == 200, 'Login function failed'
# Step 3: Check UUID Not hidden
view_page_url = f'http://localhost:{self.port}/index.php?module=page&action=viewPage'
response = session.get(view_page_url)
uuid_pattern = re.compile(r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}')
found_uuids = uuid_pattern.findall(response.text)
assert found_uuids, 'UUID not found'
# Step 4: Check create sample page
sample_page_url = f'http://localhost:{self.port}/index.php?module=page&action=samplePage'
response = session.get(sample_page_url)
assert "Create Success" in response.text or response.status_code == 200, 'Create Sample Failed'
# Step 5: Check Edit HTML Functionality
edit_url_html = f'http://localhost:{self.port}/index.php?module=user&action=edit&type=html'
response = session.get(edit_url_html)
assert response.status_code == 200, 'Edit html page not accessible'
# Step 6: Check Edit JS Functionality
edit_url_js = f'http://localhost:{self.port}/index.php?module=user&action=edit&type=js'
response = session.get(edit_url_js)
assert response.status_code == 200, 'Edit js page not accessible'
# Step 7: Check Edit CSS Functionality
edit_url_css = f'http://localhost:{self.port}/index.php?module=user&action=edit&type=css'
response = session.get(edit_url_css)
assert response.status_code == 200, 'Edit css page not accessible'
# Step 8: Check flag
with open(self.flag_location, 'r') as f:
host_flag = f.read().strip()
container_flag = subprocess.run(
["docker", "exec", "wanderer_container", "cat", "/flag.txt"],
capture_output=True,
text=True
).stdout.strip()
assert host_flag == container_flag, 'Flag mismatch between host and container'
self.logger.info('Check passed for wanderer')
return True
except Exception as e:
self.logger.error(f'Could not check wanderer: {e}')
return False