---
# System prepended metadata

title: MiniCTF Write-ups

---

```
 __      ____________._________________________          ____ _____________  _________
/  \    /  \______   \   \__    ___/\_   _____/         |    |   \______   \/   _____/
\   \/\/   /|       _/   | |    |    |    __)_   ______ |    |   /|     ___/\_____  \ 
 \        / |    |   \   | |    |    |        \ /_____/ |    |  / |    |    /        \
  \__/\  /  |____|_  /___| |____|   /_______  /         |______/  |____|   /_______  /
       \/          \/                       \/                                     \/ 
```
# 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}`

```
.________________________________  ___________ _______   ____  __.___ 
|   \__    ___/\____    /\______ \ \_   _____/ \      \ |    |/ _|   |
|   | |    |     /     /  |    |  \ |    __)_  /   |   \|      < |   |
|   | |    |    /     /_  |    `   \|        \/    |    \    |  \|   |
|___| |____|   /_______ \/_______  /_______  /\____|__  /____|__ \___|
                       \/        \/        \/         \/        \/    
```
