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
NL>hc*gOTl|{|T!c04x9i
literal 0
HcmV?d00001
diff --git a/receiver/flags.old/.gitkeep b/receiver/flags.old/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/receiver/flags.old/art.txt b/receiver/flags.old/art.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/art.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/back-to-basic.txt b/receiver/flags.old/back-to-basic.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/back-to-basic.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/burvesigner.txt b/receiver/flags.old/burvesigner.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/burvesigner.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/crawlback.txt b/receiver/flags.old/crawlback.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/crawlback.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/gemas-fetcher.txt b/receiver/flags.old/gemas-fetcher.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/gemas-fetcher.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/hirnfick.txt b/receiver/flags.old/hirnfick.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/hirnfick.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/pasta.txt b/receiver/flags.old/pasta.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/pasta.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/s3.txt b/receiver/flags.old/s3.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/s3.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags.old/xl.txt b/receiver/flags.old/xl.txt
new file mode 100644
index 0000000..801ef9b
--- /dev/null
+++ b/receiver/flags.old/xl.txt
@@ -0,0 +1 @@
+WRECKIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags/.gitkeep b/receiver/flags/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/receiver/flags/blinkpdf.txt b/receiver/flags/blinkpdf.txt
new file mode 100644
index 0000000..ac24d5d
--- /dev/null
+++ b/receiver/flags/blinkpdf.txt
@@ -0,0 +1 @@
+WreckIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags/naraka.txt b/receiver/flags/naraka.txt
new file mode 100644
index 0000000..ac24d5d
--- /dev/null
+++ b/receiver/flags/naraka.txt
@@ -0,0 +1 @@
+WreckIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags/niko.txt b/receiver/flags/niko.txt
new file mode 100644
index 0000000..ac24d5d
--- /dev/null
+++ b/receiver/flags/niko.txt
@@ -0,0 +1 @@
+WreckIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags/poke.txt b/receiver/flags/poke.txt
new file mode 100644
index 0000000..ac24d5d
--- /dev/null
+++ b/receiver/flags/poke.txt
@@ -0,0 +1 @@
+WreckIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/flags/wanderer.txt b/receiver/flags/wanderer.txt
new file mode 100644
index 0000000..ac24d5d
--- /dev/null
+++ b/receiver/flags/wanderer.txt
@@ -0,0 +1 @@
+WreckIT50{PLACEHOLDER}
\ No newline at end of file
diff --git a/receiver/history/.gitkeep b/receiver/history/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/receiver/main.py b/receiver/main.py
new file mode 100644
index 0000000..4e661f3
--- /dev/null
+++ b/receiver/main.py
@@ -0,0 +1,105 @@
+from fastapi import Depends, FastAPI, HTTPException
+from pydantic import BaseModel
+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
+
+app = FastAPI()
+security = HTTPBasic()
+settings = get_settings()
+
+challenges = {
+ "poke": Poke(10000),
+ "blinkpdf": BlinkPDF(11000),
+ "naraka": Naraka(12000),
+ "wanderer": Wanderer(13000),
+ "niko": Niko(15000),
+}
+
+class Flag(BaseModel):
+ flag: str
+ challenge: str
+
+class History(BaseModel):
+ log: str
+
+
+@app.get("/")
+def read_root():
+ return {"service": "receiver-service"}
+
+
+@app.get("/restart/{challenge}")
+def restart(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} restart {challenge}")
+ return {"message": "Challenge restarted"}
+
+
+@app.get("/rollback/{challenge}")
+def rollback(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} up -d --force-recreate {challenge}")
+ return {"message": "Challenge restarted"}
+
+
+@app.get("/activate/{challenge}")
+def activate(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} up -d {challenge}")
+ return {"message": "Challenge activated"}
+
+
+@app.get("/deactivate/{challenge}")
+def deactive(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} down {challenge}")
+ return {"message": "Challenge deactivated"}
+
+
+@app.get("/credential/{challenge}")
+def credential(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ return challenges[challenge].credentials()
+
+
+@app.post("/flag")
+def receive(data: Flag, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, data.challenge)
+ challenge = challenges[data.challenge]
+ if challenge.distribute(data.flag):
+ return {"message": "Flag received"}
+
+ raise HTTPException(status_code=500, detail="Error receiving flag")
+
+
+@app.get("/check/{challenge}")
+def check(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ return {"success": challenges[challenge].check()}
+
+@app.post("/history")
+def history(data: History):
+ with open('history/command.txt', 'a') as f:
+ f.write(data.log + '\n')
+ return {"message": "Command received"}
+
+def is_admin(credentials):
+ if credentials.username != settings.ADMIN_USERNAME or credentials.password != settings.ADMIN_PASSWORD:
+ return False
+ return True
+
+
+def validate(credentials, challenge):
+ if not is_admin(credentials):
+ raise HTTPException(status_code=401, detail="Invalid credentials")
+
+ if challenge not in challenges:
+ raise HTTPException(status_code=400, detail="Invalid challenge")
diff --git a/receiver/main.py.old b/receiver/main.py.old
new file mode 100644
index 0000000..3299093
--- /dev/null
+++ b/receiver/main.py.old
@@ -0,0 +1,116 @@
+from fastapi import Depends, FastAPI, HTTPException
+from pydantic import BaseModel
+from fastapi.security import HTTPBasic, HTTPBasicCredentials
+from config import get_settings
+
+from challenges.Art import Art
+from challenges.XL import XL
+from challenges.GemasNotes import GemasNotes
+from challenges.Pasta import Pasta
+from challenges.Burvesigner import Burvesigner
+from challenges.S3 import S3
+from challenges.Crawlback import Crawlback
+from challenges.BackToBasic import BackToBasic
+from challenges.GemasFetcher import GemasFetcher
+from challenges.Hirnfick import Hirnfick
+
+import os
+
+app = FastAPI()
+security = HTTPBasic()
+settings = get_settings()
+
+challenges = {
+ "art": Art(10000),
+ "xl": XL(11000),
+ "gemas-notes": GemasNotes(12000),
+ "pasta": Pasta(13000),
+ "burvesigner": Burvesigner(14000),
+ "hirnfick": Hirnfick(15000),
+ "gemas-fetcher": GemasFetcher(16000),
+ "s3": S3(20000),
+ "crawlback": Crawlback(21000),
+ "back-to-basic": BackToBasic(22000),
+}
+
+
+class Flag(BaseModel):
+ flag: str
+ challenge: str
+
+class History(BaseModel):
+ log: str
+
+
+@app.get("/")
+def read_root():
+ return {"service": "receiver-service"}
+
+
+@app.get("/restart/{challenge}")
+def restart(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} restart {challenge}")
+ return {"message": "Challenge restarted"}
+
+
+@app.get("/rollback/{challenge}")
+def rollback(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} up -d --force-recreate {challenge}")
+ return {"message": "Challenge restarted"}
+
+
+@app.get("/activate/{challenge}")
+def activate(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} up -d {challenge}")
+ return {"message": "Challenge activated"}
+
+
+@app.get("/deactivate/{challenge}")
+def deactive(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ os.system(f"docker compose -f {settings.COMPOSE_LOCATION} down {challenge}")
+ return {"message": "Challenge deactivated"}
+
+
+@app.get("/credential/{challenge}")
+def credential(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ return challenges[challenge].credentials()
+
+
+@app.post("/flag")
+def receive(data: Flag, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, data.challenge)
+ challenge = challenges[data.challenge]
+ if challenge.distribute(data.flag):
+ return {"message": "Flag received"}
+
+ raise HTTPException(status_code=500, detail="Error receiving flag")
+
+
+@app.get("/check/{challenge}")
+def check(challenge: str, credentials: HTTPBasicCredentials = Depends(security)):
+ validate(credentials, challenge)
+ return {"success": challenges[challenge].check()}
+
+@app.post("/history")
+def history(data: History):
+ with open('history/command.txt', 'a') as f:
+ f.write(data.log + '\n')
+ return {"message": "Command received"}
+
+def is_admin(credentials):
+ if credentials.username != settings.ADMIN_USERNAME or credentials.password != settings.ADMIN_PASSWORD:
+ return False
+ return True
+
+
+def validate(credentials, challenge):
+ if not is_admin(credentials):
+ raise HTTPException(status_code=401, detail="Invalid credentials")
+
+ if challenge not in challenges:
+ raise HTTPException(status_code=400, detail="Invalid challenge")
diff --git a/receiver/modules/blinkpdf/__init__.py b/receiver/modules/blinkpdf/__init__.py
new file mode 100644
index 0000000..c969087
--- /dev/null
+++ b/receiver/modules/blinkpdf/__init__.py
@@ -0,0 +1,2 @@
+from .signature import *
+from .cipher import *
diff --git a/receiver/modules/blinkpdf/cipher.py b/receiver/modules/blinkpdf/cipher.py
new file mode 100644
index 0000000..6384f54
--- /dev/null
+++ b/receiver/modules/blinkpdf/cipher.py
@@ -0,0 +1,43 @@
+from Crypto.Cipher import AES
+from Crypto.Util.Padding import pad, unpad
+from Crypto.Util.number import long_to_bytes, bytes_to_long
+import hashlib
+import random
+import os
+
+# This will be the base dont change it
+q = 135589091449528481388008471290289910812753186702167314685052586130282290721619
+p = 271178182899056962776016942580579821625506373404334629370105172260564581443239
+g = pow(2, (p-1)//p, p)
+
+def encryptMessage(message, PRIVATE_KEY):
+ key = hashlib.sha256(bytes.fromhex(PRIVATE_KEY)).digest()[:16]
+ cipher = AES.new(key, AES.MODE_CBC, iv=os.urandom(16))
+ ciphertext = cipher.iv + cipher.encrypt(pad(message,16))
+ digest_message = int(hashlib.sha256(message).hexdigest(), 16)
+ x = int(PRIVATE_KEY, 16)
+ rand = random.Random()
+ rand.seed(bytes_to_long(message))
+ k = rand.getrandbits(216)
+ r = pow(g, k, p) % q
+ s = (pow(k, -1, q) * (digest_message + r * x)) % q
+ y = pow(g, x, q)
+ signature = long_to_bytes(y).zfill(32).hex() + long_to_bytes(digest_message).zfill(32).hex() + long_to_bytes(r).zfill(32).hex() + long_to_bytes(s).zfill(32).hex()
+ return ciphertext.hex() + signature
+
+def decryptMessage(ciphertext, PRIVATE_KEY):
+ cps = bytes.fromhex(ciphertext)
+ y = bytes_to_long(cps[-128:-96].lstrip(b'0'))
+ dig = bytes_to_long(cps[-96:-64].lstrip(b'0'))
+ r = bytes_to_long(cps[-64:-32].lstrip(b'0'))
+ s = bytes_to_long(cps[-32:].lstrip(b'0'))
+ u = pow(s, -1, q)
+ v = pow(g, (dig * u) % q, p) * pow(y, (r * u)%q, p) % p % q
+ ciphertext = cps[:-128]
+ iv = ciphertext[:16]
+ ct = ciphertext[16:]
+ key = hashlib.sha256(bytes.fromhex(PRIVATE_KEY)).digest()[:16]
+ cipher = AES.new(key, AES.MODE_CBC, iv=iv)
+ plaintext = cipher.decrypt(ct)
+ plain = unpad(plaintext, 16)
+ return v == r, plain
\ No newline at end of file
diff --git a/receiver/modules/blinkpdf/decdsa.py b/receiver/modules/blinkpdf/decdsa.py
new file mode 100644
index 0000000..7ec4989
--- /dev/null
+++ b/receiver/modules/blinkpdf/decdsa.py
@@ -0,0 +1,104 @@
+import hashlib
+import random
+from ecdsa import NIST256p, ellipticcurve
+
+class DECDSA:
+ def __init__(self, privateKey):
+ self.curve = NIST256p
+ self.order = self.curve.order
+ self.generator = self.curve.generator
+ self.private_key = int(privateKey, 16) % self.order
+ self.public_key = self.private_key * self.generator
+ # self.generate_keypair()
+
+ # def generate_keypair(self):
+ # test
+ # self.private_key = 68643326375728294502573326707893599968874260096336631364679496614035223206444
+ # self.private_key = random.randint(1, self.order - 1)
+ # self.public_key = self.private_key * self.generator
+
+ def lift_x(self, x):
+ p = self.curve.curve._CurveFp__p
+ a = self.curve.curve._CurveFp__a
+ b = self.curve.curve._CurveFp__b
+ y_squared = (x**3 + a*x + b) % p
+ y = pow(y_squared, (p + 1) // 4, p)
+ if (y * y) % p != y_squared:
+ raise ValueError(f"No valid point found for x={x}")
+
+ point1 = ellipticcurve.Point(self.curve.curve, x, y)
+ point2 = ellipticcurve.Point(self.curve.curve, x, p - y)
+ if y > p - y:
+ return point2
+ else:
+ return point1
+
+ def sign(self, message):
+ m1, m2 = message[:len(message)//2], message[len(message)//2:]
+ h1 = hashlib.sha256(m1).digest()[1:]
+ h2 = hashlib.sha256(m2).digest()[1:]
+ z1 = int.from_bytes(h1, byteorder='big') % self.order
+ z2 = int.from_bytes(h2, byteorder='big') % self.order
+ while True:
+ k1 = random.randint(z1, z1*4)
+ k2 = random.randint(z2, z2*4)
+ R1 = k1 * self.generator
+ R2 = k2 * self.generator
+ r1 = R1.x() % self.order
+ r2 = R2.x() % self.order
+ R_att_x = (self.lift_x(r1) + self.lift_x(r2)).x() % self.order
+
+ # assert for checking valid points
+ if(R_att_x!=(R1+R2).x() % self.order):
+ continue
+
+ if r1 == 0 or r2 == 0:
+ continue
+
+ ks = pow(k1, -1, self.order) + pow(k2, -1, self.order)
+ s = (pow(k1*k2, -1, self.order) * (z1 + r1 * self.private_key + z2 + r2 * self.private_key) * pow(ks, -1, self.order)) % self.order
+
+ if s == 0:
+ continue
+
+ r1, r2, s = int(r1), int(r2), int(s)
+ return self.sign_to_bytes(r1, r2, s)
+
+ def verify(self, message, signature):
+ r1, r2, s = self.bytes_to_sign(signature)
+ if not (1 <= r1 < self.order and 1 <= r2 < self.order and 1 <= s < self.order):
+ return False
+
+ m1, m2 = message[:len(message)//2], message[len(message)//2:]
+ h1 = hashlib.sha256(m1).digest()[1:]
+ h2 = hashlib.sha256(m2).digest()[1:]
+ z1 = int.from_bytes(h1, byteorder='big') % self.order
+ z2 = int.from_bytes(h2, byteorder='big') % self.order
+ s_inv = pow(s, -1, self.order)
+ u1 = (z1 * s_inv) % self.order
+ u2 = (z2 * s_inv) % self.order
+ u3 = (r1 * s_inv) % self.order
+ u4 = (r2 * s_inv) % self.order
+ R = u1 * self.generator + u3 * self.public_key + u2 * self.generator + u4 * self.public_key
+ R_x = R.x() % self.order
+ R_att_x = (self.lift_x(r1) + self.lift_x(r2)).x() % self.order
+ return R_x == R_att_x
+
+ def long_to_bytes(self, x):
+ return x.to_bytes(32, "big")
+
+ def bytes_to_long(self, x):
+ return int.from_bytes(x, "big")
+
+ def sign_to_bytes(self, r1, r2, s):
+ first_part = self.long_to_bytes(r1)
+ second_part = self.long_to_bytes(r2)
+ third_part = self.long_to_bytes(s)
+ return first_part + second_part + third_part
+
+ def bytes_to_sign(self, x):
+ r1 = self.bytes_to_long(x[:32])
+ r2 = self.bytes_to_long(x[32:64])
+ s = self.bytes_to_long(x[64:])
+ return r1, r2, s
+
\ No newline at end of file
diff --git a/receiver/modules/blinkpdf/signature.py b/receiver/modules/blinkpdf/signature.py
new file mode 100644
index 0000000..d6a4f8d
--- /dev/null
+++ b/receiver/modules/blinkpdf/signature.py
@@ -0,0 +1,43 @@
+from .decdsa import DECDSA
+import PyPDF2
+import io
+
+def sign_pdf(file, PRIVATE_KEY):
+ try:
+ decdsa = DECDSA(privateKey=PRIVATE_KEY)
+ pdf_reader = PyPDF2.PdfReader(file)
+ pdf_writer = PyPDF2.PdfWriter()
+ for page in pdf_reader.pages:
+ pdf_writer.add_page(page)
+
+ pdf_data = io.BytesIO()
+ pdf_writer.write(pdf_data)
+ pdf_data.seek(0)
+ pdf_content = pdf_data.read()
+ signature = decdsa.sign(pdf_content)
+ pdf_writer.add_metadata({'/Signature': signature.hex()})
+ signed_pdf = io.BytesIO()
+ pdf_writer.write(signed_pdf)
+ signed_pdf.seek(0)
+ return signed_pdf
+ except:
+ return False
+
+def verify_signature(file, PRIVATE_KEY):
+ try:
+ decdsa = DECDSA(privateKey=PRIVATE_KEY)
+ pdf_reader = PyPDF2.PdfReader(file)
+ signature_text = pdf_reader.metadata.get('/Signature', '')
+ signature = bytes.fromhex(signature_text)
+ pdf_data = io.BytesIO()
+ pdf_writer = PyPDF2.PdfWriter()
+ for page in pdf_reader.pages:
+ pdf_writer.add_page(page)
+
+ pdf_writer.write(pdf_data)
+ pdf_data.seek(0)
+ pdf_content = pdf_data.read()
+ return decdsa.verify(pdf_content,signature)
+ except Exception as e:
+ print(e)
+ return False
\ No newline at end of file
diff --git a/receiver/pyvenv.cfg b/receiver/pyvenv.cfg
new file mode 100644
index 0000000..10481c5
--- /dev/null
+++ b/receiver/pyvenv.cfg
@@ -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 /root/wreckitattdef-node/receiver
diff --git a/receiver/requirements.txt b/receiver/requirements.txt
new file mode 100644
index 0000000..3aab0c6
--- /dev/null
+++ b/receiver/requirements.txt
@@ -0,0 +1,14 @@
+fastapi==0.68.2
+fastapi-utils==0.2.1
+pydantic==1.10.2
+pydantic[dotenv]
+uvicorn
+requests
+pycryptodome
+pandas
+openpyxl
+fastecdsa
+pwntools
+pyelftools==0.29
+ecdsa==0.19.0
+PyPDF2==3.0.1
\ No newline at end of file
diff --git a/receiver/test.py b/receiver/test.py
new file mode 100644
index 0000000..042ad34
--- /dev/null
+++ b/receiver/test.py
@@ -0,0 +1,77 @@
+from modules.blinkpdf import *
+
+import io
+import requests
+import subprocess
+import re
+
+
+def check():
+ try:
+ # Getting private key
+ container_env = '''
+SECRET_KEY="a3a591c71b14fa8837dca5eb61ec8b14"
+PRIVATE_KEY="ceb4fd7181f056b048412a2bef7402991fdf3b8300f07e6617889cb7064cc529"
+ '''.strip()
+ private_key = re.search(r'PRIVATE_KEY="(.+?)"', container_env).group(1)
+
+ sess = requests.Session()
+
+ # Checking C1: Login as user
+ url = 'http://52.221.251.25:11000/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 = 'receiver/files/blinkpdf_hellodocs.pdf'
+ pdfbytes = open(pdfpath, 'rb').read()
+
+ # Checking C2: Sign pdf as user
+ sign_url = 'http://52.221.251.25:11000/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 = 'http://52.221.251.25:11000/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 valid.' in r.text, 'Verify function not working or algoritm verify process is changed'
+
+ # Checking C3: Verify invalid pdf as user
+ verify_url = 'http://52.221.251.25:11000/verify'
+ sendata = ('main_signed.pdf', pdfbytes, 'application/pdf')
+ filedata = {'file': sendata}
+ r = sess.post(verify_url, files=filedata, timeout=5)
+ assert 'The signature is invalid' in r.text, 'Verify function not working or algoritm verify process is changed for invalid signature'
+
+ host_flag = 'WreckIT50{1D3rZiz8CrMuO2b2QGsQVQqvk8dLjDCBnmTHV6dGzQ0zZXOScP69b2LxKV2cj7Ut}'
+
+ # Checking C5: Login as admin and enc_flag checking
+ url = 'http://52.221.251.25:11000/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://52.221.251.25:11000/admin_panel'
+ r = sess.get(url, timeout=5)
+ enc_flag = r.text.split('encrypted flag: ')[1].split('
')[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'
+
+ print('Check passed for blinkpdf')
+ return True
+
+ except Exception as e:
+ print(e)
+ return False
+
+check()
\ No newline at end of file
diff --git a/services.old/.gitignore b/services.old/.gitignore
new file mode 100644
index 0000000..2eea525
--- /dev/null
+++ b/services.old/.gitignore
@@ -0,0 +1 @@
+.env
\ No newline at end of file
diff --git a/services.old/art/Dockerfile b/services.old/art/Dockerfile
new file mode 100644
index 0000000..a322555
--- /dev/null
+++ b/services.old/art/Dockerfile
@@ -0,0 +1,24 @@
+FROM public.ecr.aws/docker/library/ruby:2.7.2
+
+ARG PASSWORD
+
+RUN echo root:${PASSWORD} | chpasswd
+RUN apt-get update && apt-get install -y openssh-server curl
+RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
+RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
+RUN service ssh start
+
+WORKDIR /ctf/art/
+
+RUN useradd -m ctf
+RUN chown -R root:root /ctf/art/
+
+COPY Gemfile .
+COPY app.rb .
+COPY start.sh .
+
+RUN bundle install
+RUN touch /flag.txt
+
+RUN chmod +x start.sh
+CMD ./start.sh
\ No newline at end of file
diff --git a/services.old/art/Gemfile b/services.old/art/Gemfile
new file mode 100644
index 0000000..0cdef2e
--- /dev/null
+++ b/services.old/art/Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+gem "sinatra"
+gem "slim"
\ No newline at end of file
diff --git a/services.old/art/app.rb b/services.old/art/app.rb
new file mode 100644
index 0000000..7abf453
--- /dev/null
+++ b/services.old/art/app.rb
@@ -0,0 +1,14 @@
+require "sinatra"
+require "slim"
+
+set :port, 8080
+set :bind, '0.0.0.0'
+set :environment, :production
+
+get '/' do
+ redirect '/art/gemastik'
+end
+
+get '/art/:word' do
+ return Slim::Template.new{ '' }.render
+end
\ No newline at end of file
diff --git a/services.old/art/docker-compose.yml b/services.old/art/docker-compose.yml
new file mode 100644
index 0000000..99ab2bd
--- /dev/null
+++ b/services.old/art/docker-compose.yml
@@ -0,0 +1,11 @@
+version: '3'
+services:
+ art:
+ restart: always
+ build:
+ context: .
+ args:
+ - PASSWORD=root
+ ports:
+ - "10000:8080"
+ - "10022:22"
diff --git a/services.old/art/start.sh b/services.old/art/start.sh
new file mode 100644
index 0000000..6402760
--- /dev/null
+++ b/services.old/art/start.sh
@@ -0,0 +1,5 @@
+# run sshd
+/usr/sbin/sshd -D &
+# run the command
+su ctf -c "bundle install"
+su ctf -c "ruby /ctf/art/app.rb"
\ No newline at end of file
diff --git a/services.old/back-to-basic/Dockerfile b/services.old/back-to-basic/Dockerfile
new file mode 100644
index 0000000..48934f2
--- /dev/null
+++ b/services.old/back-to-basic/Dockerfile
@@ -0,0 +1,29 @@
+FROM public.ecr.aws/docker/library/ubuntu:20.04
+
+ARG PASSWORD
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN echo root:${PASSWORD} | chpasswd
+RUN apt-get update && apt-get install -y openssh-server lib32z1 xinetd cmake gcc curl
+RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
+RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
+RUN service ssh start
+
+RUN useradd -m ctf
+
+WORKDIR /ctf
+
+RUN echo "Connection blocked" > /etc/banner_fail
+COPY ctf.xinetd /etc/xinetd.d/ctf
+COPY ./src/main.c ./
+COPY ./start.sh ./
+RUN gcc /ctf/main.c -no-pie -fno-stack-protector -Wl,-z,relro,-z,now -o /ctf/main
+RUN touch /flag.txt
+RUN chmod -R 755 /ctf
+RUN chmod +x /ctf/start.sh
+
+ENTRYPOINT []
+CMD ["/usr/sbin/xinetd", "-dontfork"]
+
+EXPOSE 8000
\ No newline at end of file
diff --git a/services.old/back-to-basic/ctf.xinetd b/services.old/back-to-basic/ctf.xinetd
new file mode 100644
index 0000000..2fdfb03
--- /dev/null
+++ b/services.old/back-to-basic/ctf.xinetd
@@ -0,0 +1,18 @@
+service ctf
+{
+ disable = no
+ socket_type = stream
+ protocol = tcp
+ wait = no
+ user = root
+ type = UNLISTED
+ port = 8000
+ bind = 0.0.0.0
+ server = /bin/sh
+ server_args = /ctf/start.sh
+ banner_fail = /etc/banner_fail
+ # safety options
+ per_source = 10 # the maximum instances of this service per source IP address
+ rlimit_cpu = 1 # the maximum number of CPU seconds that the service may use
+ #rlimit_as = 1024M # the Address Space resource limit for the service
+}
\ No newline at end of file
diff --git a/services.old/back-to-basic/src/main.c b/services.old/back-to-basic/src/main.c
new file mode 100644
index 0000000..ac33d45
--- /dev/null
+++ b/services.old/back-to-basic/src/main.c
@@ -0,0 +1,15 @@
+#include
+#include
+
+void give_me_idea() {
+ char buf[64];
+ puts("Do you have a good idea?");
+ fgets(buf, 200, stdin);
+}
+
+void main() {
+ setbuf(stdout, NULL);
+ give_me_idea();
+ puts("hmm... I'm also think the same thing...");
+ return 0;
+}
\ No newline at end of file
diff --git a/services.old/back-to-basic/start.sh b/services.old/back-to-basic/start.sh
new file mode 100644
index 0000000..327cc56
--- /dev/null
+++ b/services.old/back-to-basic/start.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+/usr/sbin/sshd -D &
+su ctf -c "/ctf/main"
\ No newline at end of file
diff --git a/services.old/burvesigner/Dockerfile b/services.old/burvesigner/Dockerfile
new file mode 100644
index 0000000..d0b6a4b
--- /dev/null
+++ b/services.old/burvesigner/Dockerfile
@@ -0,0 +1,27 @@
+FROM public.ecr.aws/docker/library/python:3.11-slim-buster
+
+ARG PASSWORD
+
+WORKDIR /opt
+
+RUN apt-get update
+RUN apt-get install -y nano openssh-server \
+ gcc python-dev python3-dev libgmp3-dev curl
+
+RUN echo root:${PASSWORD} | chpasswd
+RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
+RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
+RUN service ssh start
+
+COPY src/ .
+RUN touch /flag.txt
+RUN touch /priv.data
+
+RUN pip install -r requirements.txt
+RUN python3 database/init.py merricx_number_1_fans ${PASSWORD}
+RUN chmod +x ./start.sh
+
+EXPOSE 80
+EXPOSE 22
+
+CMD ./start.sh
diff --git a/services.old/burvesigner/docker-compose.yml b/services.old/burvesigner/docker-compose.yml
new file mode 100644
index 0000000..0b0d562
--- /dev/null
+++ b/services.old/burvesigner/docker-compose.yml
@@ -0,0 +1,16 @@
+version: "3"
+
+services:
+ burvesigner:
+ restart: always
+ container_name: burvesigner
+ build:
+ context: ./
+ args:
+ - PASSWORD=PASSWORD_14000
+ ports:
+ - "14000:80"
+ - "14022:22"
+ volumes:
+ - ./flag.txt:/flag.txt:ro
+ - ./priv.data:/priv.data:ro
diff --git a/services.old/burvesigner/flag.txt b/services.old/burvesigner/flag.txt
new file mode 100644
index 0000000..311c8dd
--- /dev/null
+++ b/services.old/burvesigner/flag.txt
@@ -0,0 +1 @@
+PLACEHOLDER
\ No newline at end of file
diff --git a/services.old/burvesigner/priv.data b/services.old/burvesigner/priv.data
new file mode 100644
index 0000000..d87b2ac
--- /dev/null
+++ b/services.old/burvesigner/priv.data
@@ -0,0 +1 @@
+PLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDERPLACEHOLDER
\ No newline at end of file
diff --git a/services.old/burvesigner/src/burvesigner.py b/services.old/burvesigner/src/burvesigner.py
new file mode 100644
index 0000000..dc1dcd3
--- /dev/null
+++ b/services.old/burvesigner/src/burvesigner.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+from Crypto.Util.number import *
+from fastecdsa.curve import Curve
+from fastecdsa.point import Point
+from base64 import urlsafe_b64decode, urlsafe_b64encode
+from time import time
+from zlib import crc32
+
+
+class BurveSigner:
+ def __init__(self):
+ self.C = Curve(
+ "burvesigner",
+ 0xDB7C2ABF62E35E668076BEAD208B,
+ 0xDB7C2ABF62E35E668076BEAD2088,
+ 0x659EF8BA043916EEDE8911702B22,
+ 0xDB7C2ABF62E35E7628DFAC6561C5,
+ 0x0E27CD305696E88F38F7EB1FBECE,
+ 0xCAA9A6F90944FAD41FBE02B8FD77,
+ )
+ self.t = self.C.p.bit_length() // 8
+ self.u = self.C.p.bit_length() - 64
+ self.v = getRandomNBitInteger(self.u)
+
+ def from_bytes(self, data):
+ return int.from_bytes(data, "little")
+
+ def to_bytes(self, num):
+ return int.to_bytes(num, self.t, "little")
+
+ def hash(self, msg):
+ return crc32(msg)
+
+ def set_keys(self):
+ priv = open("/priv.data", "rb").read()[:self.t]
+ self.x = self.from_bytes(priv)
+ self.Y = self.x * self.C.G
+
+ def get_params(self):
+ self.set_keys()
+ return {
+ "p": self.C.p,
+ "a": self.C.a,
+ "b": self.C.b,
+ "n": self.C.q,
+ "G": (self.C.gx, self.C.gy),
+ "Y": (self.Y.x, self.Y.y),
+ }
+
+ def sign(self, msg):
+ self.set_keys()
+ k = (int(time() * 1337) << self.u) + self.v
+ R = k * self.C.G
+ s = (self.hash(msg) - self.x * R.x) * pow(k, -1, self.C.q) % self.C.q
+ sig = b"".join(map(self.to_bytes, [R.x, R.y, s]))
+ return urlsafe_b64encode(sig)
+
+ def verify(self, msg, sig):
+ self.set_keys()
+ try:
+ assert len(sig) == 4 * self.t
+ sig = urlsafe_b64decode(sig)
+ arr = [sig[self.t * i : self.t * (i + 1)] for i in range(3)]
+ Rx, Ry, s = map(self.from_bytes, arr)
+ R = Point(Rx, Ry, self.C)
+ return self.hash(msg) * self.C.G == s * R + self.Y * R.x
+ except:
+ return False
diff --git a/services.old/burvesigner/src/database/check.py b/services.old/burvesigner/src/database/check.py
new file mode 100644
index 0000000..4dffb54
--- /dev/null
+++ b/services.old/burvesigner/src/database/check.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python3
+import sqlite3
+
+db = "database.db"
+con = sqlite3.connect(db)
+cur = con.cursor()
+cur.execute("SELECT * FROM users")
+
+rows = cur.fetchall()
+for row in rows:
+ print(row)
diff --git a/services.old/burvesigner/src/database/init.py b/services.old/burvesigner/src/database/init.py
new file mode 100644
index 0000000..0d12f94
--- /dev/null
+++ b/services.old/burvesigner/src/database/init.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+import hashlib
+import os
+import sqlite3
+import sys
+
+db = "database.db"
+
+
+class User:
+ def __init__(self, username, password, role):
+ self.username: str = username
+ self.password: str = password
+ self.role: str = role
+
+
+def main():
+ try:
+ os.remove(db)
+ except:
+ pass
+
+ con = sqlite3.connect(db)
+ con.execute("CREATE TABLE users (username TEXT, password TEXT, role TEXT)")
+
+ def insert_person(person: User):
+ cur.execute(
+ "INSERT INTO users (username, password, role) VALUES (?, ?, ?)",
+ (
+ person.username,
+ hashlib.sha256(
+ b"gema" + person.password.encode() + b"steg"
+ ).hexdigest(),
+ person.role,
+ ),
+ )
+ con.commit()
+
+ cur = con.cursor()
+ admin = User(sys.argv[1], sys.argv[2], "admin")
+ guest = User("guest", "guest", "guest")
+ insert_person(admin)
+ insert_person(guest)
+ con.close()
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 3:
+ print(f"Usage: {sys.argv[0]} ")
+ exit()
+ main()
diff --git a/services.old/burvesigner/src/main.py b/services.old/burvesigner/src/main.py
new file mode 100644
index 0000000..3b7d5b9
--- /dev/null
+++ b/services.old/burvesigner/src/main.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+from burvesigner import BurveSigner
+from flask import Flask, make_response, render_template, request, flash
+from hashlib import sha256
+from time import time
+import base64
+import json
+import secrets
+import sqlite3
+
+
+app = Flask(__name__)
+app.config["SECRET_KEY"] = secrets.token_hex()
+db = "database.db"
+signer = BurveSigner()
+
+
+b64p = lambda x: x + b"=" * (-len(x) % 4)
+b64u = lambda x: x.rstrip(b"=")
+b64e = lambda x: b64u(base64.urlsafe_b64encode(x))
+b64d = lambda x: base64.urlsafe_b64decode(b64p(x))
+
+
+def generate_token(user, role):
+ payload = b64e(json.dumps({
+ "user": user,
+ "role": role,
+ "exp": int(time()) + 300
+ }).encode())
+ signature = signer.sign(payload)
+ token = payload + b"." + signature
+ return token.decode()
+
+
+def validate_token(token):
+ assert token.count(".") == 1
+ payload, signature = token.split(".")
+ assert signer.verify(payload.encode(), signature.encode())
+ payload = json.loads(b64d(payload.encode()).decode())
+ assert payload.get("user") and payload.get("role") and payload.get("exp")
+ user, role, exp = payload.get("user"), payload.get("role"), payload.get("exp")
+ assert int(time()) < exp
+ return [user, role, exp]
+
+
+def create_response_data(user, role):
+ data = f"Welcome, {user}! "
+ if role == "admin":
+ try:
+ data += open("/flag.txt").read()
+ except:
+ data += "Flag is missing, please contact probset"
+ else:
+ data += "Only admin can access the flag"
+ return data
+
+
+def get_role(username, password):
+ connection = sqlite3.connect(db)
+ cursor = connection.cursor()
+ cursor.execute(
+ "SELECT role FROM users WHERE username = ? AND password = ?",
+ (username, sha256(b"gema" + password.encode() + b"steg").hexdigest()),
+ )
+ role = cursor.fetchone()
+ connection.close()
+ assert role
+ return role[0]
+
+
+@app.route("/params", methods=["GET"])
+def params():
+ return f"{json.dumps(signer.get_params(), indent=2)}"
+
+
+@app.route("/", methods=["POST", "GET"])
+def index():
+ if request.method == "POST":
+ username = request.form.get("username")
+ password = request.form.get("password")
+ try:
+ assert 2 <= len(username) <= 32
+ assert 2 <= len(password) <= 32
+ role = get_role(username, password)
+ token = generate_token(username, role)
+ validate_token(token)
+ response = make_response(render_template(
+ "index.html",
+ data=create_response_data(username, role)
+ ))
+ response.set_cookie("token", token)
+ return response
+ except:
+ flash("Invalid username and/or password")
+ return render_template("index.html")
+ else:
+ token = request.cookies.get("token")
+ if token:
+ try:
+ username, role, _ = validate_token(token)
+ return render_template(
+ "index.html",
+ data=create_response_data(username, role)
+ )
+ except:
+ flash("Invalid token")
+ return render_template("index.html")
+ else:
+ return render_template("index.html")
+
+
+if __name__ == "__main__":
+ app.run(host="0.0.0.0", port=80, debug=False)
diff --git a/services.old/burvesigner/src/requirements.txt b/services.old/burvesigner/src/requirements.txt
new file mode 100644
index 0000000..3875a9f
--- /dev/null
+++ b/services.old/burvesigner/src/requirements.txt
@@ -0,0 +1,3 @@
+fastecdsa
+Flask
+pycryptodome
\ No newline at end of file
diff --git a/services.old/burvesigner/src/start.sh b/services.old/burvesigner/src/start.sh
new file mode 100644
index 0000000..cd306f9
--- /dev/null
+++ b/services.old/burvesigner/src/start.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+/usr/sbin/sshd -D &
+chmod +x ./main.py
+./main.py
diff --git a/services.old/burvesigner/src/static/favicon.ico b/services.old/burvesigner/src/static/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..9f38a1bea5b88cab6687f8e9f4892467d45f88a8
GIT binary patch
literal 20192
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