Super Serial - picoCTF Web Exploitation: Writeup

Bui Thanh TamBui Thanh Tam
4 min read

Giới thiệu

Trong writeup này, mình sẽ chia sẻ cách khai thác challenge Super Serial thuộc mảng Web Exploitation của picoCTF. Đây là một bài lab xoay quanh lỗi PHP Deserialization – một lỗ hổng khá phổ biến và nguy hiểm nếu lập trình viên không xử lý dữ liệu cẩn thận.

Vậy lỗi PHP Deserialization là gì?

Serialization là quá trình chuyển đổi dữ liệu phức tạp từ đối tượng, mảng (object, array) thành chuỗi (string) để lưu trữ hoặc truyền đi, sau đó có thể deserialize (khôi phục) về dạng ban đầu.

Trong PHP, serialization thường được dùng để:

  • Lưu session.

  • Truyền object giữa các hệ thống.

  • Lưu dữ liệu trong file.

Vấn đề xảy ra khi ứng dụng deserialize input không tin cậy từ user. Attacker có thể:

  • Inject object tùy chỉnh.

  • Gọi đến các magic method nguy hiểm (__wakeup(), __destruct(), __toString()…).

  • Thực hiện RCE, leo thang đặc quyền, hoặc đọc dữ liệu nhạy cảm.

.PHP vs .PHPS

  • .php → server sẽ chạy code và trả về output (người dùng sẽ không thấy source code).

  • .phps → server sẽ hiển thị source code kèm highlight thay vì thực thi.

  • Điều này hữu ích cho tutorial, demo, nhưng trong môi trường production thì cực kỳ nguy hiểm vì nó vô tình làm lộ source code.

Các bước thực hiện:

1. Khởi động

  • Link thử thách: http://mercury.picoctf.net:42449/

  • Challenge đưa ra hint: flag nằm ở ../flag.

    Khi truy cập, ta gặp một form đăng nhập.

    Trước tiên mình thử check robots.txt và thấy một entry gợi ý /admin.phps.

  • URL này trả về 404, nhưng .phps khiến mình nảy ra ý tưởng: thử đổi index.php thành index.phps.

    Kết quả, index.phps load thành công và lộ toàn bộ source code.

    2. Thu thập source code

    Bằng cách đổi những trang có đuôi .php sang .phps

    • index.phps → tham chiếu đến authentication.php

    • authentication.phps → tham chiếu đến cookie.php

Nhờ đó, mình đã lấy được logic xử lý cookie + unserialize.

3. Phân tích code

Tại endpoint authentication.phps có class:

  • Class này có một thuộc tính công khai là $log_file chúng ta có thể kiểm soát.

  • Hàm __toString() sẽ được gọi tự động khi đối tượng được dùng như một chuỗi. Trong trường hợp này nó gọi đến hàm read_log().

  • Hàm read_log() sẽ đọc nội dung của tệp tin được chỉ định bởi $log_file.

Trong cookie.phps, có đoạn:

  • Ở đây, nếu có cookie là login sẽ được urldecode rồi đưa vào base64_decode sau đó unserialize trực tiếp thành một object PHP

  • Một điều đặc biệt là khối catch khi có lỗi. Nếu quá trình unserialize thất bại, chương trình sẽ dừng lại và in ra thông báo "Deserialization error." kèm theo giá trị của biến $perm.

Đây chính là điểm yếu mà chúng ta cần khai thác. Khi một đối tượng được in ra (như trong trường hợp này), PHP sẽ tự động gọi phương thức __toString() của nó. Nếu chúng ta truyền một đối tượng của class access_log vào, phương thức này sẽ được kích hoạt, đọc nội dung của tệp tin mà chúng ta đã chỉ định.

4. Xây dựng payload

  • Thay vì xây dựng chuỗi serialized một cách thủ công, mình quyết định viết một đoạn mã PHP đơn giản để tạo đối tượng và serialize nó một cách chính xác.

  • Ta có thể dùng https://onlinephp.io/ để chạy code php.

  • Đầu tiên ta có thể tạo một class access_log giống với mã nguồn của thử thách. Sau đó gán giá trị ../flag cho biến $log_file

  • Ta được biết biến $perm giải mã hóa theo format unserialize(base64_decode(urlencode())) nên khi mã hóa thì ta phải làm ngược lại là urlencode(base64_encode(serialize())). Sau đó in nó ra màn hình.

    Sau khi thực thi thi đoạn code trên thì ta được đoạn mã dưới đây:

  • Quay trở lại api /authentication.php vào phần cookie trong Storage thêm trường name = login, value là đoạn mã trên sau đó load lại trang.

Vậy là xong rồi!

Thử thách đã được giải quyết

Ta đã lợi dụng:

  1. .phps leak → xem source code.

  2. PHP unserialize() xử lý input từ cookie không an toàn.

  3. Dùng object injection với __toString() để đọc flag.

Bài học rút ra

  • Không bao giờ dùng unserialize() với dữ liệu người dùng gửi lên.

  • Tránh để lộ file .phps trên production server.

  • Luôn sanitize/validate dữ liệu và xem xét dùng format an toàn hơn (JSON thay vì serialized object).

Kết luận

Challenge Super Serial minh họa rất rõ sự nguy hiểm của PHP deserialization. Đây là một lỗi nằm trong OWASP Top 10, có thể dẫn tới RCE hoặc lộ dữ liệu quan trọng.

Hy vọng bài writeup này giúp bạn hiểu rõ hơn cách khai thác và phòng tránh deserialization.

0
Subscribe to my newsletter

Read articles from Bui Thanh Tam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Bui Thanh Tam
Bui Thanh Tam