Đây là tổng hợp lời giải chi tiết cho các bài tập thuộc nhiều thể loại (Crypto, Misc, Pwn, RE, Web) trong tuần đầu tiên của cuộc thi CTF 0xGame 2021. Nội dung được trình bày lại một cách kỹ thuật, rõ ràng và độc lập — không phụ thuộc vào kinh nghiệm cá nhân hay phong cách viết blog.
Mã Hóa & Giải Mã RSA Cơ Bản
Bài ABC Of RSA cung cấp hai số nguyên tố p = 1435756429 và q = 1175078221, cùng khóa công khai e = 10007. Để phục hồi khóa riêng d, cần tính nghịch đảo mô-đun của e trên φ(n) = (p−1)(q−1):
#include <iostream>
#include <cmath>
using namespace std;
int main() {
long long p = 1435756429LL;
long long q = 1175078221LL;
long long phi = (p - 1) * (q - 1);
long long e = 10007;
long long d = 1;
while ((e * d) % phi != 1) d++;
cout << d << endl;
return 0;
}
Kết quả thu được được bao quanh bởi 0xGame{...} để nộp bài.
Phục Hồi Thông Điệp Từ Nhiều Bản Mã RSA
Bài BlackGiveRSA chia cờ thành 6 khối, mỗi khối 7 ký tự → chuyển sang dạng số nguyên m, sau đó mã hóa bằng c ≡ mᵉ mod n. Với n đã biết, phân tích thừa số để tìm p, q, rồi tính d như trên. Dùng pow(c, d, n) để giải mã từng khối:
from Crypto.Util.number import long_to_bytes, inverse
n = 1687126110378632809
p, q = 1175078221, 1435756429
e = 10007
d = inverse(e, (p-1)*(q-1))
ciphertexts = [ /* danh sách các giá trị c */ ]
flag_parts = [long_to_bytes(pow(c, d, n)).decode() for c in ciphertexts]
print(''.join(flag_parts))
Giải Mã Các Hệ Thống Cổ Điển
Bài Class8 kết hợp nhiều phương pháp mã hóa: mật mã Braille, Pigpen, Morse, và mã bàn phím QWERTY. Một phần sử dụng quy tắc "số lặp tương ứng vị trí chữ cái trên phím số 9" (ví dụ: 222 → C). Phần còn lại là mật mã "Little Man", trong đó các tư thế chân biểu thị chữ cái — yêu cầu phân tích hình ảnh minh họa chính xác.
Chuỗi Mã Hóa Lồng Nhau
Bài manycode gồm chuỗi mã hóa theo thứ tự: Emoji → Base32 → Hex → Base64. Chuỗi đầu vào:
4A5645475153435A4B345957595A4A524B4A3347454D4A5A4F5249554F4E4A564C415A453235323249524844533D3D3D
Sau khi giải mã lần lượt, ta thu được:
- Hex →
JVEGQSCZK4YWYZJRKJ3GEMJZORIUONJVLAZE2522IRHDS=== - Base32 →
MHhHYW1le1Rvb19tQG55X2MwZDN9 - Base64 →
0xGame{Too_m@ny_c0d3}
Giải Mã Vigenère Không Biết Khóa
Với bản mã có dạng yIang{iovesgsu1pg_Wkgfpesg}, so sánh với mẫu 0xGame{... cho thấy độ lệch ký tự ban đầu là b, c, a → khóa dự đoán là "bca". Kiểm tra bằng công cụ vigeneresolver hoặc viết hàm giải mã tuần tự xác nhận khóa đúng.
Hàm Toán Học Nghịch Đảo
Bài MyFunction định nghĩa phép biến đổi: f(x) = x × ln(x), với x là mã ASCII của từng ký tự cờ. Để phục hồi, duyệt x ∈ [32, 126], so sánh giá trị tính được với đầu ra thực tế (cho sai số ±0.5):
#include <cstdio>
#include <cmath>
#include <vector>
int main() {
std::vector<double> inputs;
double val;
while (scanf("%lf", &val) == 1) inputs.push_back(val);
for (double v : inputs) {
for (int x = 32; x < 127; ++x) {
if (std::abs(v - x * std::log(x)) < 0.5) {
putchar(x);
break;
}
}
}
puts("");
return 0;
}
Xử Lý Tập Tin NTFS và Dòng Ẩn
Bài "Nghiêm Túc Máu" yêu cầu khai thác tính năng Alternate Data Streams (ADS) trên hệ thống NTFS. Sử dụng công cụ streams.exe hoặc PowerShell (Get-Item -Stream *) để liệt kê dòng ẩn. Phát hiện tệp flag.txt trong stream tên HiddenBy13, nội dung được mã hóa ROT13 — giải mã để lấy nửa cờ còn thiếu.
Giải Thuật Đệ Quy Với Rào Cản
Bài "Thuật Toán Đơn Giản" mô phỏng bài toán đi bậc thang với điều kiện: từ bậc i có thể nhảy tới i+1, i+2, i+3; một số bậc bị hỏng (không được đặt chân). Gọi dp[i] là số cách đến bậc i, ta có:
- Nếu bậc
ibị hỏng →dp[i] = 0 - Ngược lại →
dp[i] = dp[i−1] + dp[i−2] + dp[i−3]
Dùng mảng trượt 4 phần tử để tối ưu bộ nhớ và áp dụng modulo 1435756429 trong suốt quá trình tính toán.
Thám Mã Web Qua Tệp robots.txt
Bài robots kiểm tra kiến thức về tiêu chuẩn robots.txt. Truy cập /robots.txt trên máy chủ sẽ trả về đường dẫn /411.html. Nội dung trang này chứa bình luận HTML ẩn chứa cờ dưới dạng base64 hoặc trực tiếp — cần kiểm tra phần <!-- ... --> trong source code.
Đảo Ngược Chương Trình Thực Thi
Bài our Compilation story thực hiện phép XOR tuần tự giữa các byte cờ và một mảng khóa dịch chuyển. Bằng cách so sánh cặp giá trị đầu ra a[i] và b[i], suy ra flag[i] = a[i] ^ b[i]. Ví dụ: nếu a[0] = 113, b[0] = 65, thì flag[0] = 113 ^ 65 = 48 → '0'.