When sailors get lost, sometimes they will put a m...
تم الإنشاء في: ٤ أبريل ٢٠٢٦
تمت الإجابة باستخدام GPT-5.4 Thinking بواسطة Chat01
تم الإنشاء في: ٤ أبريل ٢٠٢٦
تمت الإجابة باستخدام GPT-5.4 Thinking بواسطة Chat01
When sailors get lost, sometimes they will put a message in a bottle and set it off to sea in search of help. You sailor, have come across one of these bottles. Only thing is- it's broken. Something might be hidden here, if you can figure it out...
Unless otherwise noted, all flags match the regular expression RS{[\w\d!?:]*} (letters, digits, !, ?, :, and _ are allowed).
tìm flag rồi viết write up chi tiết bằng tiếng Việt
Flag: RS{D4vy_J0nes_Sp3aks_1n_Sil3nce}
Đề cho một file davy_jones_message.pcap, nên hướng đầu tiên là phân tích network traffic.
Khi xem nhanh payload, mình thấy rất nhiều gói UDP có magic:
text52 54 50 53 R T P S
Tức là đây là traffic RTPS (Real-Time Publish Subscribe), thường gặp trong DDS / ROS2.
Ngoài ra còn thấy các string như:
vehicle_sensorcomputer_control=> rất giống một hệ thống ROS2/DDS đang publish/subscribe dữ liệu cảm biến.
Khi parse các gói RTPS nhỏ lẻ, có submessage DATA_FRAG với length nhìn rất vô lý nếu chỉ xét từng packet riêng lẻ.
Ví dụ:
0x34a0 = 13472 bytesĐiều này là dấu hiệu mạnh cho thấy:
Đây cũng rất khớp với hint của đề: “bottle is broken”.
Vậy bước đúng là:
Sau khi nhóm fragment theo:
srcdstIP IDprotocolvà ghép lại đúng offset, mình thu được các UDP datagram lớn.
Lúc này các độ dài bắt đầu hợp lý:
và RTPS submessage parse ngon.
DATA_FRAGSau khi parse RTPS từ các datagram đã ghép, mình thấy các mẫu dữ liệu lớn được gửi bằng DATA_FRAG:
fragmentStartingNum = 1, rồi 11, rồi 21fragmentsInSubmessage = 10, 10, 8fragmentSize = 1344sampleSize = 36628=> rõ ràng mỗi sample bị chia ra nhiều fragment RTPS, và phải ghép tiếp theo:
writerSN (sequence number)fragmentStartingNumfragmentSizeđể reconstruct full sample.
Sau khi ghép xong một sample 36628 bytes, phần metadata đầu sample trông như sau:
xyzrgbpoint_step = 16row_step = 36496data_len = 36496Đây là format rất điển hình của ROS2 sensor_msgs/PointCloud2.
Nói ngắn gọn:
Mỗi point gồm 4 float:
xyzrgbKhi vẽ:
Đặc biệt, nếu tách riêng các điểm màu vàng (rgb = 0xffd600) và plot theo x-z, thì message hiện ra rất rõ.
Message đọc được là:
textRS{D4vy_J0nes_Sp3aks_1n_Sil3nce}
DATA_FRAGPointCloud2python# nhóm theo (src, dst, ip_id, proto), ghép theo fragment offset
python# parse RTPS header # lấy DATA_FRAG: # writerSN # fragmentStartingNum # fragmentSize # sampleSize # rồi đặt fragment data vào đúng offset trong sample buffer
python# sau metadata, data point cloud bắt đầu ở offset 128 # mỗi point 16 bytes = 4 float32: x, y, z, rgb arr = np.frombuffer(data, dtype='<f4').reshape(-1, 4)
pythonplt.scatter(arr[:,0], arr[:,2], s=1)
Hoặc lọc điểm vàng:
pythonrgbu = np.frombuffer(arr[:,3].astype('<f4').tobytes(), dtype=np.uint32) mask = (rgbu == 0xffd600) plt.scatter(arr[mask,0], arr[mask,2], s=20)
Flag cuối cùng là:
textRS{D4vy_J0nes_Sp3aks_1n_Sil3nce}
Nếu muốn, mình có thể viết thêm một script solve hoàn chỉnh để bạn chạy lại từ file .pcap và tự động in ra flag.
flag RS{D4vy_J0nes_Sp3aks_1n_Sil3nce} này sai, hãy làm lại cẩn thận hơn
Đúng, flag trước mình đọc sai do nhìn trực tiếp scatter plot nên bị nhầm chữ.
Mình đã làm lại từ đầu và dựng dữ liệu cẩn thận hơn.
Flag đúng là:
textRS{D4vy_J0nes_Sp3aks_1n_5il3nce}
.pcapFile đề bài là:
textdavy_jones_message.pcap
Khi soi nhanh payload trong pcap, thấy rất nhiều packet có magic:
textRTPS
Đây là giao thức RTPS (Real-Time Publish-Subscribe), thường đi cùng DDS / ROS2.
Ngoài ra còn lộ ra một số string như:
vehicle_sensorcomputer_control=> rất giống traffic của một hệ thống robot/xe mô phỏng đang publish-subscribe dữ liệu cảm biến.
Nếu chỉ đọc từng packet riêng lẻ thì nhiều gói RTPS có độ dài rất “vô lý”, ví dụ submessage báo dài hơn rất nhiều so với payload thực tế trong một frame Ethernet.
Khi kiểm tra header IP thì thấy:
Đây chính là chỗ “broken bottle” của đề:
Nếu không reassemble IP trước, phần RTPS bên trên sẽ bị đọc sai hoặc thiếu.
Mình ghép fragment theo bộ khóa:
src IPdst IPIP IDprotocolSau khi ghép xong, các UDP datagram trở nên hợp lý và parse được RTPS bình thường.
Traffic chủ yếu là UDP giữa:
10.42.0.1010.42.0.11với RTPS chạy trên cổng đích 17911.
Sau khi reassemble, parse RTPS cho thấy các loại submessage chính:
DATADATA_FRAGHEARTBEATACKNACKINFO_TSINFO_DSTTrong đó phần quan trọng nhất là DATA_FRAG.
Mỗi sample lớn được chia fragment như sau:
fragmentStartingNum = 1, 11, 21fragmentsInSubmessage = 10, 10, 8fragmentSize = 1344sampleSize = 36628=> tức là mỗi sample hoàn chỉnh có kích thước 36628 bytes, bị chia thành nhiều RTPS fragment.
Ghép các DATA_FRAG theo:
writer idsequence numberfragmentStartingNumthì reconstruct được full sample.
Khi xem đầu sample đã ghép xong, cấu trúc rất giống message ROS2:
xyzrgbpoint_step = 16row_step = 36496data_len = 36496Đây chính là format của:
textsensor_msgs/PointCloud2
Tức là dữ liệu ẩn không phải text thường, mà là point cloud 3D.
Mỗi point gồm 16 byte:
float32 xfloat32 yfloat32 zfloat32 rgbKhi lấy sample đầu tiên và plot theo các mặt phẳng:
x-yy-zx-zthì mặt phẳng x-z lộ ra một dòng chữ phía trên.
Nhưng nếu chỉ nhìn scatter plot thường thì rất dễ đọc sai, vì:
5 / S, l / 1, v / y rất dễ nhầmĐây chính là lý do lần trước mình đọc sai flag.
Trong point cloud có một nhóm điểm màu vàng nổi bật:
textrgb = 0xffd600
Khi lọc riêng các điểm này rồi chiếu lên mặt phẳng x-z, dòng chữ hiện ra rõ hơn nhiều.
Sau đó mình quantize tọa độ lên lưới đều (bước 0.4), chuyển thành ảnh nhị phân 7 hàng kiểu bitmap.
Lúc này text đọc được rõ ràng là:
textRS{D4vy_J0nes_Sp3aks_1n_5il3nce}
Flag cũ mình đưa là:
textRS{D4vy_J0nes_Sp3aks_1n_Sil3nce}
Sai ở phần:
text5il3nce
mình đã đọc nhầm thành:
textSil3nce
Sau khi dựng bitmap chuẩn từ các điểm vàng, ký tự đầu của từ cuối cùng là 5, không phải S.
DATA_FRAGPointCloud20xffd600python# group by (src, dst, ip_id, proto) # sort by fragment offset # place each fragment payload into the correct offset # truncate at final fragment end
python# parse RTPS header # for each DATA_FRAG: # read writerId, sequenceNumber # read fragmentStartingNum, fragmentsInSubmessage # read fragmentSize, sampleSize # place fragment bytes into sample buffer
pythonpts = np.frombuffer(sample[data_offset:data_offset+data_len], dtype='<f4') pts = pts.reshape(-1, 4) # x, y, z, rgb
pythonrgbu = np.frombuffer(pts[:,3].astype('<f4').tobytes(), dtype=np.uint32) mask = (rgbu == 0xffd600) yellow = pts[mask]
python# quantize x,z lên lưới qx = np.round(yellow[:,0] / 0.4).astype(int) qz = np.round((yellow[:,2] - 4.05) / 0.4).astype(int) # fill occupancy grid, rồi in ra ASCII / save ảnh
Flag chuẩn là:
textRS{D4vy_J0nes_Sp3aks_1n_5il3nce}
Nếu bạn muốn, mình có thể viết tiếp một script solve hoàn chỉnh chạy thẳng từ file .pcap để tự động in ra flag.