Angstromctf-2023 Ctf Writeup - filestore

Overview

Challenge cho source code: https://drive.google.com/file/d/1udkCu6axKKX6KhXgP4X3W_ejeIew0HNF/view?usp=sharing

index.php:

<?php
    if($_SERVER['REQUEST_METHOD'] == "POST"){
        if ($_FILES["f"]["size"] > 1000) {
            echo "file too large";
            return;
        }
    
        $i = uniqid();

        if (empty($_FILES["f"])){
            return;
        }

        if (move_uploaded_file($_FILES["f"]["tmp_name"], "./uploads/" . $i . "_" . hash('sha256', $_FILES["f"]["name"]) . "_" . $_FILES["f"]["name"])){
            echo "upload success";
        } else {
            echo "upload error";
        }
    } else {
        if (isset($_GET["f"])) {
            include "./uploads/" . $_GET["f"];
        }

        highlight_file("index.php");

        // this doesn't work, so I'm commenting it out 😛
        // system("/list_uploads");
    }
?>

Dockerfile:

FROM php:8.1.18-apache-bullseye

RUN groupadd -r admin && useradd -r -g admin admin
RUN groupadd -r ctf && useradd -r -g ctf ctf

RUN sed -i "s/Listen 80/Listen 8080/" /etc/apache2/ports.conf

RUN chmod -R 755 /etc/apache2 &&\
    chmod -R 755 /var/www/

COPY flag.txt /flag.txt
RUN chown admin:admin /flag.txt &&\
    chmod 440 /flag.txt

COPY make_abyss_entry /make_abyss_entry
RUN chown root:root /make_abyss_entry &&\
    chmod 111 /make_abyss_entry &&\
    chmod g+s /make_abyss_entry

COPY list_uploads /list_uploads
RUN chown admin:admin /list_uploads &&\
    chmod 111 /list_uploads &&\
    chmod g+s /list_uploads

COPY src /var/www/html

RUN mkdir /abyss &&\
    chown -R root:root /abyss &&\
    chmod -R 331 /abyss

RUN chown -R root:root /var/www/html &&\
    chmod -R 555 /var/www/html

RUN rm -rf /var/www/html/uploads

RUn mkdir /var/www/html/uploads &&\
    chmod -R 333 /var/www/html/uploads

RUN rm -f /bin/chmod /usr/bin/chmod /bin/chown /usr/bin/chown

USER ctf

EXPOSE 8080

Nói sơ sơ qua thì index.php cho upload file nhưng tên file random không truy cập được + có bug LFI. Dockerfile setup 2 file binary đáng nghi là make_abyss_entry (set guid root) và list_uploads (set guid admin), user chạy web php là ctf còn để đọc được flag thì cần quyền admin

Exploitation

Ngồi loay hoay tính năng upload file chẳng ra gì do tên file thì không đoán được, path traversal thì không thể do khi espace đoạn random string được prefix phía trước sẽ tạo ra 1 thư mục không tồn tại => không thể upload!!

Ngồi chơi 1 lúc mới nhớ đến với bug LFI có thể dùng pearcmd.php, 1 lib trong php để tạo webshell. Kĩ thuật này cũng không có gì mới, mọi người có thể tham khảo tại đây (update reference)

Include file pearcmd.php viết webshell vào webroot: pearcmd

Shell nè: Shell

RCE rồi nhưng chưa có flag ngay do còn cần phải leo thang đặc quyền mới đọc được flag, mình có tự chạy docker và có 1 vài ý tưởng rồi, mà trước tiên phải lấy được reverse shell đã: reverseshell

Nội dung pastebin nè: pastebin

Khi lấy reverse shell đôi khi do một số ký tự đặc biệt như <, $, hay & mà mắc lỗi encoding, server không chạy được command nên mình thường đi kiểu đường vòng như này - curl webshell rồi load vào bash, đỡ phải nghĩ nhiều hihi.

Privilege Escalation

Binary file /make_abyss_entry tạo ra 1 thư mục random riêng cho user ctf ở thư mục /abyss còn /list_uploads thì chạy command ls ở thư mục /var/www/html/uploads. Cái này mình chạy command thì biết thôi chứ không biết reverse gì đâu :))

Ai biết qua các kĩ thuật leo thang đặc quyền trên linux thì cũng chắc đoán được đây là leo thang qua PATH enviroment variable.

Do file /list_uploads gọi command ls - relative path nên khi thực thi, linux sẽ lookup binary file của command này thông qua PATH env. Bằng cách tạo 1 file ls giả chứa command cat /flag.txt, chỉnh sửa PATH env để ưu tiên file binary giả này sau đó thực thi list_uploads (được set guid là admin), ta có thể đọc được flag. Mà thế nên nó mới cho /make_abyss_entry để tạo thư mục riêng tạo file ls, không thì tranh nhau tạo ở /tmp thì lộ hết bài :))

Mình cũng quền nhắc là ở file Docker có dòng này:

RUN rm -f /bin/chmod /usr/bin/chmod /bin/chown /usr/bin/chown

Thành ra không cấp quyền thực thi cho file ls “giả” được, google thì mình tìm được có thể dùng perl để thay thế lệnh chmod sau

Nói đủ rồi, end game thôi. Tạo thư mục riêng bằng /make_abyss_entry: make_abyss_entry

Tạo file ls trong thư mục vừa rồi, cấp quyền exec bằng perl, sửa biến PATH env sau đó chạy /list_uploads là được flag: list_uploads

flag: actf{w4tch_y0ur_p4th_724248b559281824}