
comparing
-
Tóm tắt
comparing.cpp.- Chia flag thành các tuple theo cặp:
{ flag[i * 2], flag[i * 2 + 1], i }và đẩy vào priority_queue (comparator sắp xếp theo char1 + char2). - Mỗi lần lặp nó pop 2 tuple (gọi là A và B), rồi in 2 dòng:
- Dòng 1:
even/odd(val1, val3, i1)với val1 = ascii(A.first), val3 = ascii(B.first), i1 = A.index. - Dòng 2:
even/odd(val2, val4, i2)với val2 = ascii(A.second), val4 = ascii(B.second), i2 = B.index. even(val1,val3,ii)trả về chuỗi: to_string(val1) + to_string(val3) + to_string(ii) + reverse(to_string(val1) + to_string(val3)).odd(val1,val3,ii)trả về chuỗi: to_string(val1) + to_string(val3) + to_string(ii) (hàm addend trong code = 0).
- Chia flag thành các tuple theo cặp:
-
Decode:
- Ta chỉ cần viết 2 hàm even() và odd() để thử parse chuỗi số theo 2 cách trên. Nếu parse thành công thì thu được (val1, val3, idx) với val1 và val3 là ASCII code của ký tự.
- Sau đó ta sắp xếp lại theo logic encode.
-
Dưới đây là code giải flag của tôi.
def even(s): for v1_len in (2, 3): for v3_len in (2, 3): for idx_len in (1, 2): prefix_len = v1_len + v3_len + idx_len if prefix_len >= len(s): continue v1_str, v3_str, idx_str = s[:v1_len], s[v1_len:v1_len+v3_len], s[v1_len+v3_len:prefix_len] if s[prefix_len:] != (v1_str + v3_str)[::-1]: continue if v1_str.isdigit() and v3_str.isdigit() and idx_str.isdigit(): v1, v3, idx = map(int, (v1_str, v3_str, idx_str)) if 32 <= v1 <= 126 and 32 <= v3 <= 126: return v1, v3, idx
def odd(s): for v1_len in (2, 3): for v3_len in (2, 3): for idx_len in (1, 2): if v1_len + v3_len + idx_len != len(s): continue v1_str, v3_str, idx_str = s[:v1_len], s[v1_len:v1_len+v3_len], s[v1_len+v3_len:] if v1_str.isdigit() and v3_str.isdigit() and idx_str.isdigit(): v1, v3, idx = map(int, (v1_str, v3_str, idx_str)) if 32 <= v1 <= 126 and 32 <= v3 <= 126: return v1, v3, idx
def decode(s): return even(s) or odd(s)
output = [ "9548128459", "491095", "1014813", "561097", "10211614611201", "5748108475", "1171123", "516484615", "114959", "649969946", "1051160611501", "991021", "1231012101321", "9912515", "11411511", "1151164611511"]
tuples = []for a, b in zip(output[::2], output[1::2]): v1, v3, i1 = decode(a) v2, v4, i2 = decode(b) tuples += [(chr(v1), chr(v2), i1), (chr(v3), chr(v4), i2)]
flag_parts = {i: c1 + c2 for (c1, c2, i) in sorted(tuples, key=lambda t: (ord(t[0]) + ord(t[1]), t[2]))}flag = "".join(flag_parts[i] for i in sorted(flag_parts))print(flag)- Tôi chạy script và đã có được flag.
Flag
ictf{cu3st0m_c0mp@r@t0rs_1e8f9e}nimrod
- Đầu tiên tôi chạy thử file để xem nó hoạt động như nào.

- Khi tôi nhập tạm một input thay cho flag thì nó sẽ báo sai.
- Tiếp đến tôi mở IDA để xem code của chương trình.
- Tìm kiểm thử xem liệu có string
Enter the flag:không.

- Tuyệt vời, nó đây rồi.
- Nhưng khi tôi mở ra thì nó chỉ trỏ tới địa chỉ chứ không phải biến cố định.
- Vậy nên tôi thử debug và xem liệu ở đâu sẽ gọi đến nó.

- Sau một lúc debug thì tôi đến được hàm này, khả năng nó sẽ là hàm xử lý chính của chương trình.
- Đầu tiên là đọc input vào
v1. - Sau đó đi qua một hàm
xorEncryptvà gán kết quả vàov2. - Cuối cùng là so sánh
v2vớiencryptedFlag. - Bên trong hàm
xorEncryptcòn phụ thuộc vào cả hàmkeystreamở bên trong nó nữa. - Từ đó tôi sẽ viết một đoạn script python để tái tạo lại hàm
xorEncryptvàkeystream, sau đó choencryptedFlagđể decode xor và ta sẽ có được flag. - Dưới đây là script python của tôi.
def keystream(seed, length): result = bytearray() for _ in range(length): seed = (1664525 * seed + 1013904223) & 0xFFFFFFFFFFFFFFFF result.append((seed >> 16) & 0xFF) return bytes(result)
def xorEncrypt(data, seed): ks = keystream(seed, len(data)) return bytes([b ^ k for b, k in zip(data, ks)])
encryptedFlag = bytearray([ 0x28, 0xF8, 0x3E, 0xE6, 0x3E, 0x2F, 0x43, 0x0C, 0xB9, 0x96, 0xD1, 0x5C, 0xD6, 0xBF, 0x36, 0xD8, 0x20, 0x79, 0x0E, 0x8E, 0x52, 0x21, 0xB2, 0x50, 0xE3, 0x98, 0xB5, 0xC9, 0xB8, 0xA0, 0x88, 0x30, 0xD9, 0x0A])seed = 0x13371337
cipher = xorEncrypt(encryptedFlag, seed)print(cipher.decode('UTF-8', errors='ignore'))- Sau khi chạy script thì tôi đã có được flag cho bài này.
Flag
ictf{a_mighty_hunter_bfc16cce9dc8}stacked
- Bài này chỉ đơn giản là lấy từng ký tự trong flag bạn đầu ra rồi đi qua từng hàm theo file chall, cứ thế 3 char được xử lý 3 lần.
- Vậy nên ta chỉ cần đi qua các hàm đó theo thứ tự ngược lại tương ứng với char được mã hóa thì ta sẽ giải được flag của bài này.
- Dưới đây là code của tôi đi ngược lại từng char qua các hàm theo thứ tự ngược lại để giải được bài này.
def eor(a1: int) -> int: return (a1 ^ 0x69) & 0xFF
def rtl(a1: int) -> int: return ((a1 << 1) & 0xFF | (a1 >> 7)) & 0xFF
def inc(a1: int) -> int: global globalvar, flag flag[globalvar] = a1 & 0xFF globalvar += 1 return flag[globalvar - 1]
def off(a1: int) -> int: return (a1 - 15) & 0xFF
flag = [0x94, 0x7, 0xd4, 0x64, 0x7, 0x54, 0x63, 0x24, 0xad, 0x98, 0x45, 0x72, 0x35]
flag[0] = off(eor(rtl(flag[0])))flag[1] = eor(eor(eor(flag[1])))flag[2] = rtl(off(rtl(flag[2])))flag[3] = rtl(rtl(eor(flag[3])))flag[4] = eor(eor(eor(flag[4])))flag[5] = rtl(off(rtl(flag[5])))flag[6] = rtl(eor(rtl(flag[6])))flag[7] = rtl(rtl(eor(flag[7])))flag[8] = rtl(off(eor(flag[8])))flag[9] = eor(eor(rtl(flag[9])))flag[10] = off(off(rtl(flag[10])))flag[11] = rtl(rtl(eor(flag[11])))flag[12] = eor(off(rtl(flag[12])))
print("ictf{" + "".join(chr(i) for i in flag) + "}")- Sau khi chạy code thì tôi đã có được flag cho bài này.
Flag
ictf{1n54n3_5k1ll2}weird-app
- Tôi mở file apk của chall trong giả lập android để xem chương trình sẽ làm những gì.

- App apk này khi mở lên chỉ hiển thị một dòng ký tự kỳ lạ chứ không có tương tác gì cả.
- Tiếp đến tôi dùng JADX để xem code file apk của chall này.
- Tìm kiếm ký tự
Transformed flag: idvi+1{s6e3{)arg2zv[moqa905+xem nó có xuất hiện ở đâu không.

- Sau khi tìm kiếm thì tôi được dẫn đến file này.
- Thì ra đây chỉ là một dòng được in cố định.
- Tôi tiếp tục phân tích qua để hiểu hơn về file này.
public static final String transformFlag(String flag) { Intrinsics.checkNotNullParameter(flag, "flag"); String res = ""; int length = flag.length(); for (int i = 0; i < length; i++) { int length2 = "abcdefghijklmnopqrstuvwxyz".length(); for (int c = 0; c < length2; c++) { if ("abcdefghijklmnopqrstuvwxyz".charAt(c) == flag.charAt(i)) { int ind = c + i; res = res + "abcdefghijklmnopqrstuvwxyz".charAt(ind % "abcdefghijklmnopqrstuvwxyz".length()); } } int length3 = "0123456789".length(); for (int c2 = 0; c2 < length3; c2++) { if ("0123456789".charAt(c2) == flag.charAt(i)) { int ind2 = (i * 2) + c2; res = res + "0123456789".charAt(ind2 % "0123456789".length()); } } int length4 = "!@#$%^&*()_+{}[]|".length(); for (int c3 = 0; c3 < length4; c3++) { if ("!@#$%^&*()_+{}[]|".charAt(c3) == flag.charAt(i)) { int ind3 = (i * i) + c3; res = res + "!@#$%^&*()_+{}[]|".charAt(ind3 % "!@#$%^&*()_+{}[]|".length()); } } } return res;}- Tôi tìm được một hàm, có lẽ nó dùng để mã hóa flag theo từng nhóm (ký tự, chữ số, ký hiệu).
- Nghi ngờ là flag được mã hóa bởi hàm này nên tôi tiến hành decode ngược lại.
- Sau đây là script python decode của tôi.
alphabet = "abcdefghijklmnopqrstuvwxyz"digits = "0123456789"symbols = "!@#$%^&*()_+{}[]|"
cipher = "idvi+1{s6e3{)arg2zv[moqa905+"flag = ""
for i, ch in enumerate(cipher): if ch in alphabet: ind = alphabet.index(ch) flag += alphabet[(ind - i) % 26] elif ch in digits: ind = digits.index(ch) flag += digits[(ind - 2*i) % 10] elif ch in symbols: ind = symbols.index(ch) flag += symbols[(ind - i*i) % len(symbols)]
print(res)- Tôi chạy script và đã có flag cho bài này.
Flag
ictf{1_l0v3_@ndr0id_stud103}whoami
- Chall này chỉ đơn giản là gọi các AI model từ openrouter về đưa cho chúng một vai cố định là root, khi người dùng nhập vào sẽ đoán xem đó là AI gì.
- Dựa vào đó, tôi lên thử openrouter và test trước qua các AI này và thấy với cùng một câu chào “hello” thì chúng sẽ trả lời lại theo cách riêng của mỗi model.
- Dựa vào đấy tôi cũng test lại với server nc thì thấy đúng là chúng sẽ có những cách trả lời riêng.
- Cần một chút thời gian để làm quen với từng giọng điệu của các model và có một xíu may mắn thì tôi đã giải được flag cho bài này.
Flag
ictf{i_guess_u_uncovered_my_identity_b1f914a9}