daq2712
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # BlitzCTF 2025 Writeup ## Web ### Middle of nowhere @daq ![image](https://hackmd.io/_uploads/Hyhlt7trll.png) Khi ấn vào URL, dưới đây là giao diện trang web ![image](https://hackmd.io/_uploads/B1V5FXYrgl.png) Ấn follow my lead, sẽ dẫn đến trang login ![image](https://hackmd.io/_uploads/rJTZcXKHgl.png) Sau một lúc mò và một vài lần nhập thử credentials sai thì thấy trên BurpSuite có API `/_next/static/chunks/pages/login-7593437a57f70c0b.js` Ở phần response thấy đoạn sau ![middle_of_no_where](https://hackmd.io/_uploads/rJC_jQtrgl.png) Có thể thấy username là `adm1n` và pass là `BlitZ123Hackzzzzzzzz` Thử đăng nhập với thông tin đó Nó kẹt ở trạng thái `Decrypting...` mãi nhưng thử reload lại page sẽ xuất hiện flag ![image](https://hackmd.io/_uploads/rkNb2XtSxg.png) ``` FLAG: Blitz{N3x7js_M1ddl3w4r3_Byyyyp4sssssss} ``` ### Fleg Store @Haizzzzzzzzz ![image](https://hackmd.io/_uploads/H1YMAvFHgx.png) Truy cập vào link trong ảnh thì ta được giao diện như sau ![image](https://hackmd.io/_uploads/SJeD0vKBex.png) Bỏ qua phần login vì nó không có gì đặc biệt cả ![image](https://hackmd.io/_uploads/HJ1KWOtHex.png) Sau khi đăng nhập xong thì đạp vào mặt chúng ta là các chức năng cơ bản của trang web bao gồm: - DashBoard(nơi để tạo coupon và hiện các coupon đã tạo) ![image](https://hackmd.io/_uploads/ByMOXOFrxg.png) - Shop(nơi trưng bày các vật phẩm) ![image](https://hackmd.io/_uploads/Bye9mutrgl.png) - Cart(nơi hiện các vật phẩm đã add và tổng giá trị của chúng đồng thời cũng là nơi thanh toán) ![image](https://hackmd.io/_uploads/Hyvsm_KHlg.png) - Redeem(chỗ để add coupon) ![image](https://hackmd.io/_uploads/Hy7pX_KSel.png) Sau khi dạo vòng quanh các chức năng cơ bản của trang web thì ta có thể cơ bản xác định rằng để giải được bài này ta tạo coupon và add coupon để mua được vật phẩm flag.txt Nghe có vẻ dễ đúng không!!! Nhưng khi ta Generate Coupon liên tục thì ta nhận thấy rằng dường như trang web chỉ cho chúng ta tạo 5 coupon ![image](https://hackmd.io/_uploads/SyPvSuKHll.png) Vậy chúng ta lấy đâu ra thêm coupon??? Sau khi thực hiện vài thao tác nghiệp vụ chuyên nghiệp(cụ thể là F12)thì mình tìm được hàm này ![image](https://hackmd.io/_uploads/rJ1LvOKrel.png) Sau khi tra vội bằng Gemini thì mình biết được hàm này hoạt động cơ bản như sau - Khi ta click vào Generate Coupon thì client sẽ gửi một POST request đến server sau sau đó server sẽ trả lại cho chúng ta một response - Sau đó hàm tiếp tục check nếu trong response có thuộc tính error thì không hiện coupon và ngược lại Sau khi hiểu được hàm này thì mình nhận ra được hai điểm bất thường - Thứ nhất là tại sao trong hàm ghi check thuộc tính error mà mình tìm trong response lại ko có TẠI SAO??? TẠI SAO??? TẠI TẠI SAO???(vị tiền bối nào đọc được wu này thì nhớ giải đáp cho vãn bối) - Thứ hai là như mình đã nói ở trên thì dường như web chỉ cho tạo ra 5 coupon nhưng trong hàm lại không có phần nào quy định như vậy cả --> Ta có thể thử spam request tạo coupon liên tục thông qua Burp để khi mà response chưa kịp trả về thì ta đã gửi tiếp request ***(hành vi này chống chỉ định dành cho những ai dùng BurpSuite Community)*** ![image](https://hackmd.io/_uploads/B1I-MYtBlx.png) ![image](https://hackmd.io/_uploads/HJwGztFBgg.png) Việc còn lại là mua cờ thôi!!! ``` Blitz{FLEG_L00T3R_SH0P} ``` ### Fleg Store 2.0 @daq ![image](https://hackmd.io/_uploads/SyjIT9cBex.png) Khi truy cập trang web thấy giao diện ![image](https://hackmd.io/_uploads/HJLhRc5ree.png) Có thể thấy trang web sẽ lưu số lần mình click chuột, thử vào mục Buy Flag ![image](https://hackmd.io/_uploads/S1kk1s5Sgl.png) Như vậy phải click chuột 9,999 lần mới có thể mua được flag Thấy có mục create backup, thử ấn và nó tải về 1 file json ![image](https://hackmd.io/_uploads/H1z_ei9Bxe.png) Sau khi mở file thấy đoạn sau, là số click của mình ![image](https://hackmd.io/_uploads/HyBAxsqrxx.png) Sửa thành 9,999 và chọn restore backup ![image](https://hackmd.io/_uploads/rJZIbj9Hle.png) Như vậy là số click của mình đã là 9999, bây giờ có thể vào mua flag ![image](https://hackmd.io/_uploads/B1SFZiqrlx.png) ``` FLAG: Blitz{FlEg_l00t3R_sh0p_Butt_w1th_cl1qu35} ``` ### Unstoppable force meets immovable object @daq ![image](https://hackmd.io/_uploads/ByjCQujSxx.png) Truy cập trang web thấy login page không có gì đặc biệt ![image](https://hackmd.io/_uploads/By98VCqrex.png) Quay sang file zip đã cho để phân tích, mở file `main.py` thấy đoạn sau ```python NOT_PASSWORD = "P@ssword@123" def immovable_object(data, block_size=32): if len(data) % block_size != 0: data += b"\0" * (block_size - (len(data) % block_size)) h = 0 for i in range(0, len(data), block_size): block = int.from_bytes(data[i : i + block_size], "big") h ^= block return h ``` Có thể thấy hàm `immovable_object` không phải là một hàm băm an toàn, đơn giản nó chỉ XOR các chunk 32 byte Và những data nhỏ hơn 32 byte được pad với NULL đến đúng 1 block Ví dụ có `B0 ⊕ B1 ⊕ B1 = B0` Vì những block giống nhau sẽ tự XOR chính nó đi, nên khi XOR `B0` với `B1` và `B1` vẫn chỉ tương tự như XOR mỗi `B0` Sau đó thấy đoạn code này ```python @app.route("/", methods=["GET", "POST"]) def home(): if request.method == "POST": password = request.form["password"] unstopabble_force = immovable_object(password.encode("utf-8")) if password != NOT_PASSWORD and unstopabble_force == immovable_object( NOT_PASSWORD.encode() ): return FLAG return redirect(url_for("home")) url_for("static", filename="style.css") return render_template("index.html") ``` Như vậy để bypass logic cần phải nhập khác với `NOT_PASSWORD` và 2 kết quả XOR giống nhau hay còn gọi là một collision Từ đó viết ra code python ```python import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) url = "https://ufmio-n1sj9nsb.blitzhack.xyz/" NOT_PASSWORD = "P@ssword@123" B0 = NOT_PASSWORD.encode() + b"\x00" * (32 - len(NOT_PASSWORD)) B1 = b"A" * 32 payload = (B0 + B1 + B1).decode("latin-1") # keep raw bytes intact res = requests.post(url, data={"password": payload}, verify=False) print(res.text) ``` Sau khi chạy code đã tìm ra được flag ![image](https://hackmd.io/_uploads/rJgzNh0cBxx.png) ``` FLAG: blitz{60nn4_b3_4_b16_c0ll1510n_wh3n_un570pp4bl3_f0rc3_m3375_1mm0v4bl3_0bj3c7} ``` ### Unstoppable force meets immovable object 2 @daq ![image](https://hackmd.io/_uploads/rJXzpAqHex.png) Tương tự như bài trước, khi truy cập URL vẫn chỉ là trang login ![image](https://hackmd.io/_uploads/rJHEa09Bxl.png) Ở trong file zip đã cho, ở file `main.py` thấy đoạn sau ```python @app.route("/", methods=["GET", "POST"]) def home(): if request.method == "POST": username = request.form["username"] password = request.form["password"] if username != password and complex_custom_hash( password ) == complex_custom_hash(username): return FLAG return redirect(url_for("home")) ``` Có thể thấy endpoint sẽ trả về flag khi 2 string khác nhau đều hash cùng một giá trị dưới hàm `complex_custom_hash` sau đây ```python def complex_custom_hash(data_string): if not isinstance(data_string, str): raise TypeError("Input must be a string.") data_bytes = data_string.encode("utf-8") P = 2**61 - 1 B = 101 hash_val = 0 for byte_val in data_bytes: hash_val = (hash_val * B + byte_val) % P length_mix = (len(data_bytes) * 123456789) % P hash_val = (hash_val + length_mix) % P chunk_size = 40 num_chunks = 64 // chunk_size folded_hash = 0 temp_hash = hash_val for _ in range(num_chunks): chunk = temp_hash & ((1 << chunk_size) - 1) folded_hash = (folded_hash + chunk) % (1 << chunk_size) temp_hash >>= chunk_size final_small_hash = folded_hash scrambled_hash = 0 for _ in range(3): scrambled_hash = ( final_small_hash ^ (final_small_hash >> 7) ^ (final_small_hash << 3) ) & ((1 << chunk_size) - 1) final_small_hash = scrambled_hash return f"{scrambled_hash:04x}" ``` Sau lần thực hiện đầu tiên chỉ còn 40 bit trạng thái nên có nhiều nhất $2^{40}$ output có thể Sử dụng birthday attack (hint dựa vào phần mô tả) cần khoảng $2^{20}$ lượt để tìm một collision Từ đó viết được code sau ```python import random, string import requests #PoC MASK = (1 << 40) - 1 P = 2**61 - 1 B = 101 SHIFT = lambda x: (x ^ (x>>7) ^ (x<<3)) & MASK def h(s: str) -> int: v = 0 for b in s.encode(): v = (v*B + b) % P v = (v + len(s)*123456789) % P v &= MASK for _ in range(3): v = SHIFT(v) return v def collision(max_trials=2_000_000): seen = {} alpha = string.ascii_letters + string.digits for _ in range(max_trials): s = ''.join(random.choice(alpha) for _ in range(random.randint(4,12))) k = h(s) if k in seen and seen[k] != s: return seen[k], s, k, seen seen[k] = s return None, None, None, seen u, p, k, seen = collision() if u and p: print(f"Collision after {len(set(seen))} trials:") print(f"Username = {u}") print(f"Password = {p}") print(f"Hash = {k:010x}") else: print("No collision found.") #Exploit url = "https://ufmiotwo-asdhwsad.blitzhack.xyz/" data = { "username": u, "password": p } r = requests.post(url, data=data, allow_redirects=False) if r.status_code == 200 and r.text: print("Flag:", r.text.strip()) else: print("Login failed or wrong endpoint") ``` ![image](https://hackmd.io/_uploads/SyodVyiHex.png) Như vậy sau 538655 lần đã tìm được username, pass, hash cùng với đó là flag ``` FLAG: Blitz{b1r7hd4y_p4r4d0x_3475_5h177y_h45h35_l1k3_7h15} ``` ## Hardware ### Redstone Logic 1 @daq ![image](https://hackmd.io/_uploads/HyBh37tBgg.png) Vì không tìm hiểu nhiều về hardware nên bài này mình chỉ còn cách đoán mò, để ý chỉ có 2 submit attempts Có thể thấy đáp án chỉ có thể là `11` hoặc `10` hoặc `01` hoặc `00` Và cực kì may mắn mình đã đoán bừa trúng là `00` trong ngay attempt đầu ``` FLAG: Blitz{00} ``` ### Redstone Logic 2 @daq ![image](https://hackmd.io/_uploads/ByLOaQYBlx.png) So với bài trước thì bài này không giới hạn lượt submit Có thể thấy đáp án chỉ có thể là một trong những số có 5 chữ số tạo thành bởi số 1 và số 0, mình quyết định brute force từng số một và ra kết quả ``` FLAG: Blitz{10010} ``` ### IOT Sucks @ansobad ![image](https://hackmd.io/_uploads/S1OvxD5Ble.png) Tôi thấy đèn LED nhấp nháy (hoặc dùng code/script để phân tích mức sáng từng frame) Mã Morse là điều tôi liên tưởng đến nó mỗi chu kỳ sáng → chấm hoặc gạch, phân theo thời gian - HIGH trong 500ms → . (chấm) - HIGH trong 700ms → - (gạch) - LOW 400ms → khoảng cách trong 1 ký tự - LOW 900ms → giữa các ký tự - LOW 500ms → sau 1 chữ cái Tôi đo độ ms bằng `VLC Media Player` ![image](https://hackmd.io/_uploads/H1PtGw9Bxx.png) Tải file `frame` đã trích xuất Tôi đã tách toàn bộ video `iot.mkv` thành các ảnh mỗi 10 frame (tức mỗi ảnh cách nhau ~167ms), tổng cộng 540 ảnh. ![image](https://hackmd.io/_uploads/HyMDmPcrge.png) Mỗi file là một ảnh chụp 1 thời điểm trong video Tôi không biết làm bài này theo cách khác nên mình sẽ lamf thủ công - Di chuyển từng ảnh bằng mũi tên → kiểm tra đèn sáng hay tắt - Ghi lại liên tiếp những ảnh nào có đèn sáng → đếm số ảnh → suy ra thời gian đèn sáng ![image](https://hackmd.io/_uploads/HkDZNvcree.png) `-- --- .-. ... . .. --- - .---- ..... -... .- -.. ` Ta sẽ được mã như này => `MORSE IOT 15 BAD` Giờ sẽ là ghép chữ thôi ``` Flag: Blitz{m0rs3i0t_15_b4d} ``` ## Forensics ### Essay @daq ![image](https://hackmd.io/_uploads/Syp9AQYBxx.png) Có thể thấy đó là file `.docm`, ngay lập tức nghĩ đến macro cùng với đó là olevba để phân tích Đầu tiên, dùng lệnh `olevba Essay.docm` để phân tích ![image](https://hackmd.io/_uploads/HJgV8y4YSeg.png) Decode đoạn đó ra được phrase `Sup3rS3cretPassW0RD` (tưởng sẽ là password để extract nhưng cuối cùng lại không cần) Tiếp tục dùng binwalk để extract ![image](https://hackmd.io/_uploads/rkgelEFSee.png) Có file `0.zip` ở đó rất đáng ngờ Dùng lệnh `unzip 0.zip -d second_doc` để giải nén sang một folder mới Vào folder word thấy có `vbaProject.bin` là file chứa những thứ liên quan đến macro ![image](https://hackmd.io/_uploads/By7f-EYSxg.png) Strings file đó và thấy dòng mã base64 (thực ra không cần giải nén `0.zip` mà file này ở file extracted gốc cũng sẽ thấy đoạn base64) ![image](https://hackmd.io/_uploads/ByKSW4YBgx.png) Cho lên CyberChef để giải mã ![image](https://hackmd.io/_uploads/ry5obEtHxe.png) ``` FLAG: Blitz{0l3_D3Mp_M3l10s} ``` ### The Warrior's Legacy #1 @hphuc204 ![image](https://hackmd.io/_uploads/Hy_3Rn5rxe.png) Trước hết ta tải file về có định dạng là warriors_legacy.zip Sau đó giải nén file : ```warriors_legacy.zip -d gear/``` Chúng ta sẽ kiểm tra file vừa giải nén có tên là gì: ![image](https://hackmd.io/_uploads/HklUZpcHle.png) Tiếp theo chuyển đến thư mục đó rồi xem bên trong nó có những gì. ![image](https://hackmd.io/_uploads/BJ2xWa9Blg.png) Ta có thể thấy đây là file save của Minecraft, một world save folder chứa: ``` advancements/ data/ datapacks/ DIM-1/ ← Nether DIM1/ ← The End entities/ icon.png ← Biểu tượng world level.dat ← File chính lưu metadata của thế giới level.dat_old ← Bản backup playerdata/ region/ ← Chunk world stats/ ``` Xem lại trong description ta có thể kiểm tra các file trong Minecraft world để tìm flag bị ẩn trong: + Tên nhân vật / advancement / statistic + Nội dung command block / lectern / sign / item lore / item name + File level.dat hoặc các file trong playerdata, entities, region Ta sẽ dùng tool đọc .dat và .nbt (định dạng của Minecraft). Cách nhanh nhất là dùng nbttools trong python để giải mã định dạng NBT (Named Binary Tag) Tiếp đến ta sẽ tạo một môi trường Python ảo và kích hoạt nó để cài nbtlib vào đó: ![image](https://hackmd.io/_uploads/S1z6NpqHgg.png) Sau đó chúng ta sẽ sử dụng lệnh để đọc nội dung của level.dat ```python -m nbtlib -r level.dat``` ![image](https://hackmd.io/_uploads/BJugPT9Sle.png) Nhìn kĩ ta sẽ thấy flag bị chia thành nhiều phần trong các item theo dạng base64. Ta sẽ giải mã lần lượt các phần ``` echo QmxpdHp7TkJUXw== | base64 -d echo Rm9yX0Jsb2Nrc30= | base64 -d echo SXNfTm90X0p1c3Rf | base64 -d ``` ![image](https://hackmd.io/_uploads/HJGvw65Sxg.png) Sau đó ta ghép chúng lại thành một flag đầy đủ là ``` Blitz{NBT_Is_Not_Just_For_Blocks} ``` ## OSINT ### The Lost Dog @daq ![image](https://hackmd.io/_uploads/Hkh7mVtrge.png) Dựa vào description, đoạn `On walks, he’d pull one unforgettable stunt involving a knothole, a fence, and a rival dog` ![image](https://hackmd.io/_uploads/SJ3FUEKBee.png) Đoạn `The dog belonged to someone else. While its owner was away for a day, a friend looked after it.` ![image](https://hackmd.io/_uploads/B1ZxBEtHxe.png) Đoạn `That friend — minimalist by design — built a fast, statically typed language with systems-level access and C-like syntax` nói về Walter Bright, cha đẻ ngôn ngữ lập trình D, mà syntax tương tự với ngôn ngữ C và cũng là bạn của chủ của chú chó ![image](https://hackmd.io/_uploads/SJDHB4trel.png) ![image](https://hackmd.io/_uploads/H1I3r4KSxl.png) Cả 2 dẫn chứng cuối đều support cho cái tên ở dẫn chững đầu tiên, đó là Rover ``` FLAG: Blitz{Rover} ``` Tất cả các thông tin trên được tìm thấy ở https://news.ycombinator.com/item?id=42834739 ### Hacked by Kids Part 1 @ansobad ![image](https://hackmd.io/_uploads/ByduD5cSgl.png) Bài này mới nhìn vào tôi gần như không biết tìm đó ở đâu nên ta sẽ phải đi từ từ - Xác định các từ khóa quan trọng như "hacked an officer’s security", "leaking private messages and files", "young group", và "embarrass powerful people". - Tìm hiểu về các nhóm hacker trẻ tuổi đã từng thực hiện các cuộc tấn công nhằm vào các quan chức hoặc tổ chức chính phủ. Tôi tìm được 1 trang uy tín để tìm vụ án này https://www.justice.gov/ Tại vì mấy vụ hack này thì Mĩ là ưu tiên hàng đầu để tìm https://www.justice.gov/usao-edva/pr/two-men-arrested-allegedly-hacking-senior-us-government-officials Đây là link của vụ án đó tôi đã tìm ra ![image](https://hackmd.io/_uploads/Sy5i6Mcrex.png) ``` Blitz{1:16-mj-406} ``` ## Crypto @ansobad ### Broken encoding or what? ![image](https://hackmd.io/_uploads/Hk5h-QcBxe.png) String cần giải mã: `voubz{nabay_ir_ut_jpf_mak_gdrwbj_euhs}` Sau khi thử nhiều cách (Dvorak, Azerty, đảo ngược…), chuỗi đã được giải mã bằng layout workman ↦ qwerty https://www.dcode.fr/keyboard-change-cipher ![image](https://hackmd.io/_uploads/rJcKP75Bel.png) `Blitz{catch_me_if_you_can_qwerty_kids}` ### Custom RSA? - Revenge ![image](https://hackmd.io/_uploads/r1rd-rqSlx.png) **Phân tích mã nguồn (crypto1.py)** `m = b"Blitz{REDACTED}"` Đây là flag plaintext, được lưu ở dạng bytes. Sẽ được mã hóa RSA. Đưa về dạng RSA thì tôi sẽ thay mã RSA trong challenge được sửa đổi một chút: $ϕ(n)=(p−1)(q−1)$ Chall này: `mod_phi=(p−1)(q−1)(e−1)` Tức là hàm được nhân thêm `(e-1) ` Đây là Custom RSA ```python mod_phi = 381679521901481226602014060495892168161810654344421566396411258375972593287031851626446898065545609421743932153327689119440405912 n = 1236102848705753437579242450812782858653671889829265508760569425093229541662967763302228061 c = 337624956533508120294617117960499986227311117648449203609049153277315646351029821010820258 ``` Tính khóa được giấu `d = pow(e, -1, mod_phi)` Ở đây vì có (e-1) nên tôi phải tìm đúng e mới tính được d Tìm e ```py r = mod_phi // n while True: if mod_phi % r == 0: e = r + 1 break r += 1 ``` Ta ra được e `e = 308776508606152118670230312260475727067` `r = mod_phi // n` Vì (p-1)(q-1) ≈ n, nên phép chia này cho ta gần đúng (e-1) Tìm d ```python from Crypto.Util.number import long_to_bytes d = pow(e, -1, mod_phi) ``` Tìm m để ra flag ```python m = pow(c, d, n) flag = long_to_bytes(m) print(flag.decode()) ``` Code ```python from Crypto.Util.number import long_to_bytes # Các giá trị đã cho mod_phi = 381679521901481226602014060495892168161810654344421566396411258375972593287031851626446898065545609421743932153327689119440405912 n = 1236102848705753437579242450812782858653671889829265508760569425093229541662967763302228061 c = 337624956533508120294617117960499986227311117648449203609049153277315646351029821010820258 # Bước 1: Tìm e r = mod_phi // n # Ước lượng e-1 print(f"Ước lượng ban đầu e-1 ≈ {r}") for delta in range(0, 10000): candidate = r + delta if mod_phi % candidate == 0: e = candidate + 1 print(f" Tìm được e = {e}") break else: print("Không tìm thấy e!") exit() # Bước 2: Tính d (khóa bí mật) d = pow(e, -1, mod_phi) print(f" Khóa bí mật d = {d}") # Bước 3: Giải mã m = pow(c, d, n) flag = long_to_bytes(m).decode() print(f" Flag giải mã được: {flag}") ``` ``` Flag: Blitz{Cust0m_RSA_OMGGG} ``` ### Randomized Chaos ![image](https://hackmd.io/_uploads/ByvOFS9Hgx.png) Phân tích Mã hóa trong `enc_3.py` ```python def encrypt(flag): res = [] for i, f in enumerate(flag): k = random.getrandbits(32) k = k & 0xFF # Lấy 8 bit cuối r = rol(((f ^ k) & (~k | f)), k % 8) res.append(r) return bytes(res) ``` Dễ thấy: - Mỗi byte f trong flag được mã hóa với một giá trị ngẫu nhiên k ∈ [0..255]. + Công thức mã hóa (chatGPT): r = rol( ((f ⊕ k) ∧ (¬k ∨ f)), k % 8 ) Với mỗi byte f, khi dùng k ngẫu nhiên thì byte mã hóa r sẽ phân bố theo một xác suất nhất định. Đây là dạng bài mà tôi thấy khá lạ ý tưởng bài này sẽ là: Khai thác phân bố xác suất: - Vì mỗi dòng trong output_4.txt là một bản mã khác nhau của flag với key khác nhau (random), nên: - Với một vị trí i, ta có 6.240 mẫu giá trị mã hóa khác nhau (vì flag mã hóa 6.240 lần). - Từ đó, ta tạo bảng tần suất các giá trị mã hóa r xuất hiện tại mỗi vị trí. → Với mỗi vị trí i, nếu thử toàn bộ f ∈ [0..255], ta tính phân bố lý thuyết của r khi mã hóa f. → So sánh phân bố thực tế (từ file) và phân bố lý thuyết (từ brute-force), ta tìm ra giá trị f phù hợp Trong enc_3.py, hàm encrypt(flag) sẽ mã hóa từng byte của flag như sau: ```python for i, f in enumerate(flag): k = random.getrandbits(32) # tạo số 32-bit ngẫu nhiên k = k & 0xFF # chỉ lấy 8 bit cuối → k ∈ [0, 255] r = rol(((f ^ k) & (~k | f)), k % 8) # mã hóa từng byte ``` File output_4.txt chứa 6.240 dòng, mỗi dòng là flag đã được mã hóa với random key khác nhau. - Với mỗi byte trong flag (giả sử flag dài 37 byte), ta sẽ có: 6.240 giá trị r ứng với 6.240 lần mã hóa. - Thu được bảng tần suất Counter[r] tại từng vị trí. Đến đây tôi cần gợi ý đến từ chat So sánh với phân phối thật C[r] trong dữ liệu Likelihood + brute-force: Duyệt toàn bộ f, chọn byte có phân bố phù hợp nhất ```python from collections import Counter import math def rol(x, n): return ((x << n) | (x >> (8 - n))) & 0xFF def encrypt_byte(f, k): return rol(((f ^ k) & ((~k & 0xFF) | f)), k % 8) # Tính phân bố lý thuyết cho mỗi f def build_theoretical_dist(f): dist = Counter() for k in range(256): r = encrypt_byte(f, k) dist[r] += 1 total = sum(dist.values()) for r in dist: dist[r] /= total return dist # Tính log-likelihood giữa phân bố thực và lý thuyết def score(candidate_dist, real_dist): s = 0 for r in real_dist: p = candidate_dist.get(r, 1e-6) s -= real_dist[r] * math.log(p) return s ``` Kết hợp với` output_4.txt` Code: ```python flag_len = len(lines[0]) recovered_flag = [] for j in range(flag_len): real_dist = Counter(line[j] for line in lines) best_score = float('inf') best_f = None for f in range(256): candidate_dist = build_theoretical_dist(f) sc = score(candidate_dist, real_dist) if sc < best_score: best_score = sc best_f = f recovered_flag.append(best_f) flag = bytes(recovered_flag) print("Recovered flag:", flag.decode()) ``` ``` FLAG: Blitz{RaND0m_KEY_GENeRateD_By_Zwique} ``` ### Fiboz-cryption ![image](https://hackmd.io/_uploads/rylyeUqHex.png) Từ file `Fiboz.py`, tôi biết: - Tạo dãy Fibonacci-like: bắt đầu từ hai seed a, b. ```python seq = [a, b] for _ in range(length - 2): seq.append(seq[-1] + seq[-2]) ``` - Tạo key: Mỗi số trong dãy sẽ được biến đổi bằng số bước Collatz(tham khảo chat), sau đó lấy `mod 256`. - Mã hóa: Dữ liệu được XOR từng byte với key ```python # Tính số bước của chuỗi Collatz từ n đến 1 def collatz_len(n): steps = 0 while n != 1: n = n // 2 if n % 2 == 0 else 3 * n + 1 steps += 1 return steps # Tạo dãy Fibonacci-like dài l phần tử, bắt đầu từ a, b def fib_like(l, a, b): seq = [a, b] for _ in range(l - 2): seq.append(seq[-1] + seq[-2]) return seq # Tạo key bằng cách tính collatz_len cho mỗi phần tử, sau đó lấy mod 256 def make_key(l, a, b): return [collatz_len(n) % 256 for n in fib_like(l, a, b)] ``` Ta thử đọc file mã hóa xem thế nào nhé: ```python from pathlib import Path cipher = Path("output.enc").read_bytes() ``` Tôi nhận ra được: - Flag bắt đầu bằng Blitz{ → dùng để xác định đúng key. - Key dài bằng độ dài ciphertext (64 bytes). - Ta sẽ thử nhiều giá trị a, b và kiểm tra.-> Brute tìm a, b, l Code: ```python target_prefix = b"Blitz{" valid_ascii = set(range(32, 127)) # Kiểm tra ký tự ASCII in được # Brute-force seed a, b for a in range(100_000, 200_000): if collatz_len(a) % 256 != 180: continue # tương ứng với key[0] for b in range(150_000, 250_000): if collatz_len(b) % 256 != 129: continue # tương ứng với key[1] if collatz_len(a + b) % 256 != 246: continue # tương ứng với key[2] key = make_key(len(cipher), a, b) plaintext = bytes(cipher[i] ^ key[i % len(key)] for i in range(len(cipher))) # Kiểm tra flag hợp lệ if plaintext.startswith(target_prefix) and plaintext.endswith(b"}") and all(c in valid_ascii for c in plaintext): print(f"a,b: a={a}, b={b}") print("Flag:", plaintext.decode()) exit() print("Không tìm thấy flag.") ``` Kết quả: ``` a,b: a=121393, b=196418 Flag: Blitz{So_You_Have_Studied_Fibonacci_And_Collatz_Conjecture_Now?} ``` ### Lightning Strike ![image](https://hackmd.io/_uploads/BkAErUqHlx.png) Khi mở file, tôi thấy các chữ cái như B, l, i, t, z được in theo kiểu dọc xiên ziczac như hình sét đánh. Một số chữ viết hoa, một số viết thường. Dữ liệu không hề chứa số hay ký hiệu — chỉ có chữ cái Blitz. → Điều bất thường duy nhất là chữ hoa vs chữ thường. Thế thì chỉ còn 1 kỹ thuật giấu tin phổ biến chính là: `Case-based Steganography` Ta sẽ thử quy ước: - CHữ hoa : bit 1 - Chữ thường: bit 0 Tiên hành mã hóa nó: ```python # Đọc file và lấy các ký tự chữ cái with open('ZZZ.txt', 'r', encoding='utf-8') as f: letters = [c for c in f.read() if c.isalpha()] # Chuyển đổi HOA/thường thành chuỗi bit bits = ''.join('1' if c.isupper() else '0' for c in letters) # Chia nhóm 7-bit và chuyển sang ký tự ASCII flag = ''.join(chr(int(bits[i:i+7], 2)) for i in range(0, len(bits), 7)) print(flag) ``` Toàn bộ file chứa 203 chữ cái → chia được thành 29 nhóm 7-bit thế nên mỗi nhóm tương ứng với 1 ký tự mã ASCII 7-bit ``` Flag: Blitz{17_h4pp3n5_50_f4s7_ZZZ} ``` ## Misc ### Stratogreet @hphuc204 ![image](https://hackmd.io/_uploads/BkvJOUcrex.png) Dạng bài này sẽ là: Trích xuất hình ảnh ẩn bên trong tín hiệu âm thanh từ file` stratogreet.wav` sử dụng kỹ thuật SSTV Công cụ cần thiết để giải mã: Windows-RX-SSTV Ta bắt đầu: - Mở file stratogreet.wav bằng trình phát nhạc (VLC, Windows Media Player,…). - Chờ → ảnh sẽ xuất hiện trong cửa sổ RX-SSTV tự động. - Lưu ảnh bằng nút “Save Image” hoặc chụp lại màn hình. ![image](https://hackmd.io/_uploads/SySb58qHgg.png) Ảnh toàn bộ: ![image](https://hackmd.io/_uploads/ryNXq85Hxg.png) Trong hình ảnh challenge tôi liên tưởng đến: `"Apollo Soyuz"` Tôi tra gg xem có thông tin nào về hình ảnh này và liên quan tới ngày: ![image](https://hackmd.io/_uploads/B1ZesI5Heg.png) Có lẽ đây chính là flag của bài này: ``` Blitz{1975_07_15} ``` ### Diff n' Rae @daq ![image](https://hackmd.io/_uploads/HJhM2s9Bll.png) Extract file zip thì được 2 file .jpg đều không mở được ![image](https://hackmd.io/_uploads/SkChTicreg.png) Trong mô tả có từ khóa `compare`, ngay lập tức nghĩ đến lệnh `diff` Đầu tiên strings cả 2 file và viết ra 2 file txt ![image](https://hackmd.io/_uploads/SyllAi5Bxe.png) `-n 1`: in ra cả những string chỉ dài 1 ký tự vì mặc định strings chỉ in những chuỗi có 4 kí tự trở lên, có thể sẽ bị sót thông tin quan trọng Với việc in cả những string chỉ dài 1 ký tự, có thể lấy được từng byte là những kí tự ASCII ẩn Tiếp theo dùng lệnh `diff` phân biệt 2 file txt ![image](https://hackmd.io/_uploads/S1p5Ro9rel.png) Ghép các dòng đó lại được `QmxpdHp7ZDFmRl8xU191NTNmdUx9` Đó là đoạn mã base64, cho lên CyberChef để giải ![image](https://hackmd.io/_uploads/ByI4J35Bee.png) ``` FLAG: Blitz{d1fF_1S_u53fuL} ``` ### Lazy Flag @hphuc204 ![image](https://hackmd.io/_uploads/ryEJta9Beg.png) Trước hết ta sẽ thử nhấp vào link xem có gì không ![image](https://hackmd.io/_uploads/rkLfY65Sle.png) Có thể thấy nó không có gì cả nhưng trong description có phần ```Guess the "Secret" I hide here and put the flag as Blitz{SECRET}```. Nó gợi ý có thể là ẩn trong file đính kèm hoặc chính nội dung đề Ta sẽ thử tải nó dưới dạng Microsoft Worf(.docx) Sau khi tải về nó sẽ có tên là ``` Lazy Flag.docx ``` ![image](https://hackmd.io/_uploads/B1cc5T9Sxl.png) Ta sẽ đồi file ```.docx``` thành ```.zip``` rồi mở ra xem ![image](https://hackmd.io/_uploads/r1NT1RcBle.png) ![image](https://hackmd.io/_uploads/H10AJC5Hxl.png) Ta có đoán ```word/document.xml``` là nơi gần như chắc chắn chứa flag nếu nó xuất hiện dưới dạng text. Sử dụng ``` less word/document.xml``` để xem nội dung bên trong. ![image](https://hackmd.io/_uploads/HyRwxC9reg.png) Thấy được kí tự đầu tiên là ```<w:t>L</w:t>```, đoạn sau là ```<w:t>l</w:t> ,<w:t>a</w:t>```. Sau đó ta có thể thấy được các ký tự đáng chú ý (mỗi <w:t> chứa đúng 1 ký tự) lần lượt là ```L a g u h```. Ghép lại ta được flag ``` FLAG: Blitz{LAUGH} ``` ### Hidden Signal in Noise @hphuc204 ![image](https://hackmd.io/_uploads/ByFt-0cSel.png) Dựa vào description ta có thể thấy 4 byte đầu là header (magic), mỗi ký tự của flag được tách thành 2 nửa byte (nibble) và mỗi nibble được lưu vào nửa cao (high nibble) của một byte, các byte này lặp lại sau mỗi X byte. Trước hết hãy xem trong file này có gì ```xxd -g1 -l 64 magic.mrf``` ![image](https://hackmd.io/_uploads/SJfozCqBex.png) Sau khi xem qua có thể có y tưởng mỗi byte cờ có thể thấy ```4 byte đầu: 5A A5 5A A5 → chính là header```và ```từ byte thứ 4 trở đi là phần dữ liệu ẩn, rất có thể chứa flag.``` Ta sẽ thử Brute-force bước nhảy X với các bước: + Mỗi ký tự cần 2 nibble ⇒ cần 2 bytes mang thông tin. + Các byte chứa nibble cách nhau mỗi X byte. + Tiến hành brute-force X trong khoảng 2 đến 64. Code: ```python def brute_force_flag(filename): with open(filename, 'rb') as f: data = f.read() payload = data[4:] # Bỏ 4 byte header đầu tiên print(f"[+] Tổng số byte sau header: {len(payload)}") for X in range(2, 64): # Thử các bước nhảy từ 2 đến 63 nibbles = [b >> 4 for b in payload[::X]] # lấy high nibble chars = [] for i in range(0, len(nibbles) - 1, 2): byte = (nibbles[i] << 4) | nibbles[i + 1] chars.append(byte) try: text = bytes(chars).decode('ascii', errors='ignore') except Exception as e: print(f"[!] Decode lỗi tại X={X}: {e}") continue if "Blitz{" in text: print(f"[✅] Tìm thấy stride X = {X}") print(f"[🧩] Flag đầy đủ: {text}") final = text.split("}")[0] + "}" print(f"[🏁] Final flag: {final}") with open("flag.txt", "w") as out: out.write(final) print("[💾] Flag đã được lưu vào flag.txt") return else: print(f"[ ] X = {X:02d} → Không có 'Blitz{{', thử: {text[:30]!r}") print("[❌] Không tìm thấy flag trong các bước nhảy từ 2 đến 63.") if __name__ == "__main__": brute_force_flag("magic.mrf") ``` ![image](https://hackmd.io/_uploads/S13Wu0qSle.png) Ta đã tìm thấy flag: ``` FLAG: Blitz{H1dd3n_4n4lyt1c_Ch4ll3ng3} ``` ### BlitzBot @hphuc204 ![image](https://hackmd.io/_uploads/HkuWYR9Ble.png) Dựa vào description ta có thể thấy có 3 loại robot: Blue, Yellow, và Red. Mỗi ngày, mỗi robot sẽ sinh ra các robot khác theo quy tắc sau: + Blue tạo: 2 Yellow + 3 Red + Yellow tạo: 2 Red + 3 Blue + Red tạo: 2 Blue + 3 Yellow ``` Đây là hệ phương trình tuyến tính => dùng ma trận và mũ ma trận để giải nhanh cho số ngày lớn. ``` Trước hết ta cần tạo môi trường thì cần có Python và pwntools để giao tiếp với server. ![image](https://hackmd.io/_uploads/BJ1I905Sex.png) Sau đó sẽ chạy lại script: ```python blitzbot_solver.py``` ![image](https://hackmd.io/_uploads/rylX20qHlx.png) Theo để bài vì ```Time Limit: 3 Seconds for all test cases``` nên rất lâu để tính toán và chắc chắn test case cuối cùng sẽ luôn bị chậm nên ta sẽ dùng script ```pwntools``` để tự động nhận + gửi kết quả tức thì ```python from pwn import * MOD = 10**9 + 7 def mat_mult(a, b): res = [[0]*3 for _ in range(3)] for i in range(3): for j in range(3): for k in range(3): res[i][j] = (res[i][j] + a[i][k]*b[k][j]) % MOD return res def mat_pow(mat, exp): res = [[int(i == j) for j in range(3)] for i in range(3)] while exp: if exp % 2 == 1: res = mat_mult(res, mat) mat = mat_mult(mat, mat) exp //= 2 return res def compute(B0, Y0, R0, N): T = [ [1, 3, 2], [2, 1, 3], [3, 2, 1], ] T_N = mat_pow(T, N) init = [B0, Y0, R0] result = [0, 0, 0] for i in range(3): for j in range(3): result[i] = (result[i] + T_N[i][j]*init[j]) % MOD return result def main(): r = remote("pwn.blitzhack.xyz", 1234) while True: try: line = r.readline().decode().strip() if not line: continue print(f"[INPUT] {line}") B0, Y0, R0, N = map(int, line.split()) BN, YN, RN = compute(B0, Y0, R0, N) answer = f"{BN} {YN} {RN}" print(f"[ANSWER] {answer}") r.sendline(answer.encode()) except: break r.interactive() if __name__ == "__main__": main() ``` ![image](https://hackmd.io/_uploads/HkDk6R9Sgg.png) Sau khi chạy xong ta có được flag ``` FLAG: Blitz{SUD0_M4K3_B07***} ``` ### Chamber of Secrets @ansobad ![image](https://hackmd.io/_uploads/rycT5korxx.png) → Đây là ảnh JPEG bình thường Mở ảnh bằng trình xem ảnh thấy đó là một trang sách – chương 1 của Harry Potter – “The Worst Birthday”. JPEG luôn kết thúc bằng FFD9. Nếu có dữ liệu sau đó ⇒ thường là dữ liệu ẩn. `xxd -p chamber.jpg | tail` Kết quả có dòng bắt đầu bằng `ffd9 504b0304...` ⇒ thấy chuỗi `504b0304` nghĩa là `"PK\x03\x04"` → mở đầu của file ZIP. Bài này tôi tra chatGPT xem những sự bất thường thì thấy có gắn thêm 1 file ZIP ở cuối. Cắt phần zip ẩn: `grep -aboUa -m1 -e $'\xff\xd9' chamber.jpg` `dd if=chamber.jpg bs=1 skip=57867 of=hidden.zip` Kiểm tra: `unzip -l hidden.zip` ![image](https://hackmd.io/_uploads/HyHh2yoBel.png) Có 1 file flag ở đây nhưng đang bị mã hóa bằng pass Tìm mật khẩu: `unzip -P exchanged hidden.zip` Ra được đoạn: `https://lastchamberofsecrect.com/url-decode/base?galf=QmxpdHp7aDFkZDNuXzFuXzdoM19kMzNwX3hEfQ%3D%3D` Thử decode bằng `base64` ``` Flag: Blitz{h1dd3n_1n_7h3_d33p_xD} ``` ### Language of Madness @daq ![image](https://hackmd.io/_uploads/H18THvjHxe.png) Mở file lên thấy có nhiều kí tự ![image](https://hackmd.io/_uploads/B1wDtDsBgg.png) ![image](https://hackmd.io/_uploads/SkVctwjBeg.png) Thấy mọi thứ đều in được dưới dạng ASCII (33–126) Có đúng 94 ký tự khác nhau xuất hiện và chúng lặp lại trong một chu kỳ cố định bắt đầu với `'&BA@?>~...` Hoán vị 94 ký tự đó là bảng mã `xlat1` của Malbolge. Dùng lệnh `git clone https://github.com/bipinu/malbolge.git` để lấy code Sau đó chạy code với text.txt sẽ ra flag ``` FLAG:Blitz{H4rd3st_Pr0gr4m1ng_L4ngu3s?} ``` ### Duck’s Revenge @daq ![image](https://hackmd.io/_uploads/B1HshDjSgl.png) Đầu tiên kiểm tra xem là loại file gì và một số thông tin ![image](https://hackmd.io/_uploads/Hyjs6wirlg.png) ![image](https://hackmd.io/_uploads/Hk-0avjSel.png) Về byte pattern, có thể thấy cứ mỗi 2 byte sẽ lặp lại là `00` hay `08` hay `02`, có thể là USB HID keystroke report (1 byte + 1 bộ sửa đổi byte) Thường thì một USB Rubber Ducky sẽ lưu trữ payload keystroke ở 1 file nhị phân tên là `inject.bin` Mỗi HID 8 byte report sẽ trông như sau ![image](https://hackmd.io/_uploads/HJ9dxOiBll.png) Tuy nhiên, DuckEncoder tối ưu hóa không gian, trong các script đơn giản, mỗi keypress được lưu dưới dạng `(usage ID, modifier)`, y hệt như những gì ta thấy ở đây Ví dụ như sau ![image](https://hackmd.io/_uploads/HJ7cXOoHgl.png) Sử dụng code python sau ```python MOD_SHIFT = 0x02 hid = { # letters **{0x04+i: chr(ord('a')+i) for i in range(26)}, # numbers 0x1e:'1',0x1f:'2',0x20:'3',0x21:'4',0x22:'5', 0x23:'6',0x24:'7',0x25:'8',0x26:'9',0x27:'0', 0x28:'\n', 0x2d:'-', 0x2e:'=', 0x2f:'[', 0x30:']', 0x31:'\\', 0x33:';', 0x34:"'", 0x36:',', 0x37:'.', 0x38:'/', } shift_map = {'1':'!', '2':'@', '3':'#', '4':'$', '5':'%', '6':'^', '7':'&', '8':'*', '9':'(', '0':')', '-':'_', '=':'+', '[':'{', ']':'}', '\\':'|', ';':':', "'":'"', ',':'<', '.':'>', '/':'?'} with open('naknak','rb') as f: blob = f.read() out = [] for usage, mod in zip(blob[0::2], blob[1::2]): if usage == 0: # delay/no-key continue ch = hid.get(usage, '?') if mod & MOD_SHIFT: ch = (shift_map.get(ch, ch.upper())) out.append(ch) print(''.join(out)) ``` Output giống như 1 đường link ![image](https://hackmd.io/_uploads/BkGmyuiHxg.png) Thử truy cập `https://justpaste.it/grp32` ![image](https://hackmd.io/_uploads/B1iS1usBxx.png) ``` FLAG: Blitz{1'm_4_nak} ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully