17-bo‘lim
Foydalanuvchilar, huquqlar va xavfsizlik
UID va GID, haqiqiy va amaldagi identifikator, setuid dasturlar, imkoniyatlar (capabilities), sudo, SELinux va AppArmor.
Ushbu bo‘lim mundarijasi
Operatsion tizim uchun foydalanuvchi - bu son. Barcha tekshiruvlar shu sonni taqqoslashga borib taqaladi.
Kim kim? #
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
uid_t haqiqiy = getuid();
uid_t amaldagi = geteuid();
gid_t guruh = getgid();
gid_t amaldagi_guruh = getegid();
printf("Haqiqiy UID (real) : %s\n",
haqiqiy == 0 ? "0 - root" : "0 emas - oddiy foydalanuvchi");
printf("Haqiqiy va amaldagi teng? %s\n",
haqiqiy == amaldagi ? "ha" : "yo'q - setuid dastur");
printf("Guruh va amaldagi teng? %s\n",
guruh == amaldagi_guruh ? "ha" : "yo'q");
/* Qo'shimcha guruhlar */
gid_t guruhlar[64];
int soni = getgroups(64, guruhlar);
printf("Qo'shimcha guruhlar bormi? %s\n", soni > 0 ? "ha" : "yo'q");
return 0;
}
Haqiqiy UID (real) : 0 emas - oddiy foydalanuvchi
Haqiqiy va amaldagi teng? ha
Guruh va amaldagi teng? ha
Qo'shimcha guruhlar bormi? ha
id
uid=1000(husanboy) gid=1000(husanboy) groups=1000(husanboy),27(sudo),100(users)
Uch xil UID #
/* getresuid POSIX emas - GNU kengaytmasi */
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
int main(void) {
uid_t haqiqiy, amaldagi, saqlangan;
getresuid(&haqiqiy, &amaldagi, &saqlangan);
printf("Uchala UID teng bo'lishi kerak (oddiy dastur):\n");
printf(" Haqiqiy = amaldagi ? %s\n", haqiqiy == amaldagi ? "ha" : "yo'q");
printf(" Amaldagi = saqlangan ? %s\n",
amaldagi == saqlangan ? "ha" : "yo'q");
gid_t hg, ag, sg;
getresgid(&hg, &ag, &sg);
printf("\nGuruhlar uchun ham shunday:\n");
printf(" Uchalasi teng ? %s\n",
hg == ag && ag == sg ? "ha" : "yo'q");
return 0;
}
Uchala UID teng bo'lishi kerak (oddiy dastur):
Haqiqiy = amaldagi ? ha
Amaldagi = saqlangan ? ha
Guruhlar uchun ham shunday:
Uchalasi teng ? ha
setuid dasturlar #
ls -l /usr/bin/passwd /usr/bin/sudo
-rwsr-xr-x 1 root root 68208 may 30 2024 /usr/bin/passwd
-rwsr-xr-x 1 root root 277936 apr 4 2024 /usr/bin/sudo
Egasining x o'rnida s turibdi - bu setuid biti.
find /usr/bin -perm -4000 -type f 2>/dev/null | head
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/su
/usr/bin/chsh
/usr/bin/newgrp
/usr/bin/gpasswd
setuid biti qo'yilgan dastur egasining huquqlari bilan
ishlaydi. passwd root egaligida bo'lgani uchun oddiy
foydalanuvchi ham /etc/shadow ni o'zgartira oladi.
Muammo: dasturda bitta xato bo'lsa, hujumchi root oladi.
Xavfli joylar:
| Xavf | Nima bo'ladi |
|---|---|
| Bufer to'lib ketishi | Ixtiyoriy kod root sifatida |
system() chaqiruvi | PATH orqali hujum |
| Muhit o'zgaruvchilari | LD_PRELOAD, IFS bilan hujum |
| Fayl yo'llarida TOCTOU | Ramziy havola bilan almashtirish |
Shuning uchun zamonaviy Linux imkoniyatlar (capabilities) ga o'tmoqda - keyingi bo'limchada.
O'z dasturingizga setuid qo'yish kerak bo'lsa, avval o'ylab
ko'ring: haqiqatan boshqa yo'l yo'qmi? Ko'p hollarda alohida
xizmat va soket orqali aloqa ancha xavfsiz yechim.
Huquqni tushirish #
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
int main(void) {
uid_t boshlangich = geteuid();
printf("Boshlang'ich amaldagi UID root mi? %s\n",
boshlangich == 0 ? "ha" : "yo'q");
/* root emasmiz - root bo'lishga urinish rad etiladi */
errno = 0;
int natija = setuid(0);
if (boshlangich != 0) {
printf("Root emasmiz, setuid(0) natijasi: %s\n",
natija == 0 ? "o'tdi" : "rad etildi");
printf("Bu kutilgan holat - oddiy foydalanuvchi root bo'la olmaydi\n");
}
printf("\nTo'g'ri tartib (root uchun):\n");
printf(" 1. setgroups(0, NULL) - qo'shimcha guruhlarni tashlash\n");
printf(" 2. setgid(yangi_gid) - avval guruh\n");
printf(" 3. setuid(yangi_uid) - keyin foydalanuvchi\n");
printf(" 4. Natijani TEKSHIRISH - setuid xato qaytarishi mumkin\n");
return 0;
}
Boshlang'ich amaldagi UID root mi? yo'q
Root emasmiz, setuid(0) natijasi: rad etildi
Bu kutilgan holat - oddiy foydalanuvchi root bo'la olmaydi
To'g'ri tartib (root uchun):
1. setgroups(0, NULL) - qo'shimcha guruhlarni tashlash
2. setgid(yangi_gid) - avval guruh
3. setuid(yangi_uid) - keyin foydalanuvchi
4. Natijani TEKSHIRISH - setuid xato qaytarishi mumkin
1-xato: tartibni chalkashtirish
setuid(1000); /* endi root emassiz */
setgid(1000); /* BU ISHLAMAYDI - huquq yo'q */
setgid ni avval chaqiring, chunki setuid dan keyin uni
o'zgartirish huquqi yo'qoladi.
2-xato: qo'shimcha guruhlarni unutish
setgid(1000);
setuid(1000);
/* lekin jarayon hali "sudo" guruhida! */
setgroups(0, NULL) ni birinchi chaqiring.
3-xato: natijani tekshirmaslik
setuid(1000); /* xato qaytarsa-chi? */
xavfli_ishni_bajarish();
Agar setuid muvaffaqiyatsiz bo'lsa (masalan RLIMIT_NPROC
chegarasi sabab), dastur root sifatida davom etadi.
To'g'ri:
if (setgroups(0, NULL) != 0) { _exit(1); }
if (setgid(gid) != 0) { _exit(1); }
if (setuid(uid) != 0) { _exit(1); }
if (setuid(0) == 0) { _exit(1); } /* qayta ko'tarilmasin */
Oxirgi qator muhim: setuid(0) muvaffaqiyat qaytarsa, demak
huquq to'liq tushmagan.
Imkoniyatlar (capabilities) #
Linux root huquqlarini ~40 ta mustaqil imkoniyatga ajratgan:
| Imkoniyat | Nimaga ruxsat |
|---|---|
CAP_NET_BIND_SERVICE | 1024 dan kichik portni ochish |
CAP_NET_RAW | Xom soket (ping uchun) |
CAP_CHOWN | Fayl egasini o'zgartirish |
CAP_KILL | Boshqa foydalanuvchi jarayoniga signal |
CAP_SYS_TIME | Tizim vaqtini o'zgartirish |
CAP_DAC_OVERRIDE | Huquq tekshiruvini chetlab o'tish |
CAP_SYS_ADMIN | Juda ko'p narsa - "yangi root" |
getcap /usr/bin/ping
/usr/bin/ping cap_net_raw=ep
sudo setcap cap_net_bind_service=+ep /usr/local/bin/mening_serverim
grep Cap /proc/self/status
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
capsh --decode=000001ffffffffff | head -3
0x000001ffffffffff=cap_chown,cap_dac_override,cap_dac_read_search,
cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,...
Ilgari ping setuid root edi - unga xom soket kerak.
Bu esa ping dagi har qanday xato to'liq root berishini
anglatardi.
Hozir:
getcap /usr/bin/ping
/usr/bin/ping cap_net_raw=ep
Endi ping buzilsa ham hujumchi faqat xom soket oladi -
fayl o'chira olmaydi, foydalanuvchi qo'sha olmaydi.
Bu eng kam imtiyoz (least privilege) prinsipining amaliy qo'llanishi.
O'z serveringiz 80-portni ochishi kerak bo'lsa, uni root ostida ishlatish shart emas:
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/server
Yoki systemd orqali:
[Service]
User=www-data
AmbientCapabilities=CAP_NET_BIND_SERVICE
CAP_SYS_ADMIN esa istisno - u shunchalik keng qamrovliki, uni
berish deyarli root berish bilan teng.
Muhit o'zgaruvchilari xavfi #
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
/* Xavfli: PATH ga tayanish */
setenv("PATH", "/tmp/yolg'on:/usr/bin", 1);
const char *yol = getenv("PATH");
printf("PATH qiymati: %s\n", yol);
printf("Birinchi katalog /tmp da mi? %s\n",
strncmp(yol, "/tmp", 4) == 0 ? "ha - XAVFLI" : "yo'q");
printf("\nAgar dastur system(\"ls\") chaqirsa:\n");
printf(" /tmp/yolg'on/ls topilsa - o'sha bajariladi\n");
printf(" setuid dasturda bu to'liq root demakdir\n");
printf("\nYechim: to'liq yo'l ishlatish\n");
printf(" execv(\"/bin/ls\", argv) - PATH ishlatilmaydi\n");
return 0;
}
PATH qiymati: /tmp/yolg'on:/usr/bin
Birinchi katalog /tmp da mi? ha - XAVFLI
Agar dastur system("ls") chaqirsa:
/tmp/yolg'on/ls topilsa - o'sha bajariladi
setuid dasturda bu to'liq root demakdir
Yechim: to'liq yo'l ishlatish
execv("/bin/ls", argv) - PATH ishlatilmaydi
system() ishlatmangsystem("ls") ichida /bin/sh -c "ls" bajariladi. Qobiq esa:
PATHni o'qiydi;IFS(maydon ajratuvchi) ni o'qiydi;ENV,BASH_ENVorqali skript yuklashi mumkin.
Hujumchi bularning istalganini o'zgartirib, o'z kodini root sifatida ishga tushira oladi.
Qoida:
| Yomon | Yaxshi |
|---|---|
system("ls /tmp") | execv("/bin/ls", argv) |
popen("grep ...") | pipe + fork + execv |
execvp("ls", argv) | execv("/bin/ls", argv) |
Yadro setuid dasturlarda LD_PRELOAD va LD_LIBRARY_PATH ni
avtomatik tozalaydi, lekin PATH ni tozalamaydi - bu sizning
mas'uliyatingiz.
Eng ishonchli yondashuv - imtiyozli dasturda butun muhitni qayta qurish:
extern char **environ;
environ = NULL;
setenv("PATH", "/usr/bin:/bin", 1);
Parol saqlanishi #
sudo cat /etc/shadow | head -2
root:$6$xyzABC$1kLm...:19845:0:99999:7:::
husanboy:$y$j9T$Qs8p...:19902:0:99999:7:::
| Prefiks | Algoritm |
|---|---|
$1$ | MD5 - ishlatmang |
$5$ | SHA-256 |
$6$ | SHA-512 |
$y$ | yescrypt - zamonaviy odatiy |
$2b$ | bcrypt |
ls -l /etc/passwd /etc/shadow
-rw-r--r-- 1 root root 2841 sen 1 09:12 /etc/passwd
-rw-r----- 1 root shadow 1524 sen 1 09:12 /etc/shadow
/etc/passwd hamma o'qiy oladi - dasturlar UID dan nom
topishi kerak (ls -l shuni qiladi).
Ilgari parol xeshlari ham shu yerda edi. Hujumchi faylni o'qib, xeshlarni oflayn buzardi.
Yechim: xeshlarni /etc/shadow ga ko'chirish - u faqat root va
shadow guruhiga o'qiladi.
Muhim tamoyillar:
- Parol hech qachon ochiq saqlanmaydi - faqat xeshi;
- Har bir xeshda tuz (salt) bor - bir xil parollar turli xesh beradi;
- Algoritm ataylab sekin -
bcrypt,yescryptminglab iteratsiya bajaradi.
Uchinchi nuqta muhim: SHA-256 o'zi juda tez - GPU sekundiga
milliardlab xesh hisoblaydi. Parol uchun maxsus sekin
algoritm kerak.
Veb-ilovangizda ham xuddi shu qoida: md5(parol) yoki
sha256(parol) hech qachon yaramaydi. bcrypt, argon2 yoki
scrypt ishlating.
Majburiy kirish nazorati #
| Mexanizm | Qayerda | G'oyasi |
|---|---|---|
| DAC | Hamma joyda | Fayl egasi huquq beradi |
| SELinux | RHEL, Fedora | Har obyektga yorliq, siyosat qat'iy |
| AppArmor | Ubuntu, SUSE | Dastur yo'li bo'yicha profil |
getenforce
Enforcing
sudo aa-status | head -5
apparmor module is loaded.
42 profiles are loaded.
30 profiles are in enforce mode.
/usr/bin/man
/usr/sbin/cupsd
ls -Z /etc/passwd
system_u:object_r:passwd_file_t:s0 /etc/passwd
DAC (ixtiyoriy nazorat): fayl egasi o'zi hal qiladi:
chmod 777 maxfiy.txt # egasi shunday qila oladi
MAC (majburiy nazorat): siyosatni administrator belgilaydi, egasi uni buza olmaydi.
Amaliy misol: nginx buzilgan bo'lsin. DAC bo'yicha u
www-data huquqlari bilan istalgan o'qiladigan faylni ko'ra
oladi - shu jumladan boshqa saytlarning ma'lumotlarini.
AppArmor profili bilan esa:
/usr/sbin/nginx {
/var/www/** r,
/var/log/nginx/*.log w,
# boshqa hech nima
}
Endi buzilgan nginx /home ni ham, /etc/shadow ni ham o'qiy
olmaydi - hatto huquqlar ruxsat bersa ham.
Bu "chuqurlikda himoya" (defense in depth) prinsipi: bir qatlam buzilsa, ikkinchisi ushlab qoladi.
Xavfsiz dastur yozish qoidalari #
| Qoida | Sabab |
|---|---|
| Eng kam imtiyoz bilan ishlang | Buzilsa zarar kam |
| Huquqni imkon qadar erta tushiring | Xato oynasi qisqaradi |
| Barcha tizim chaqiruvi natijasini tekshiring | Sokin xato eng xavflisi |
system/popen o'rniga execv | Qobiq hujumlari yo'qoladi |
| Kirish ma'lumotini uzunligini tekshiring | Bufer to'lishi |
Vaqtinchalik faylga mkstemp | TOCTOU |
Fayl ochishda O_NOFOLLOW | Ramziy havola hujumi |
umask ga tayanmang | Rejimni aniq bering |
| Xato xabarida ichki yo'llarni ko'rsatmang | Ma'lumot sizishi |
| Kutubxonalarni yangilab turing | Ma'lum zaifliklar |
Kodni tekshirish vositalari:
gcc -Wall -Wextra -Wformat-security -fstack-protector-strong -D_FORTIFY_SOURCE=2
valgrind --leak-check=full ./dastur
gcc -fsanitize=address,undefined -g dastur.c -o dastur
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void) {
/* Xavfsiz vaqtinchalik fayl */
char shablon[] = "/tmp/xavfsiz_XXXXXX";
int fd = mkstemp(shablon);
if (fd < 0) {
perror("mkstemp");
return 1;
}
printf("Vaqtinchalik fayl yaratildi\n");
printf("Nomi tasodifiymi? %s\n",
strcmp(shablon, "/tmp/xavfsiz_XXXXXX") != 0 ? "ha" : "yo'q");
write(fd, "maxfiy", 6);
close(fd);
/* Darhol o'chiramiz - fayl deskriptori orqali ishlash mumkin edi */
unlink(shablon);
printf("Fayl o'chirildi : ha\n");
printf("\nNima uchun mkstemp:\n");
printf(" tmpnam/tempnam nomni qaytaradi, faylni yaratmaydi\n");
printf(" Oradagi vaqtda hujumchi o'sha nomda havola yaratadi\n");
printf(" mkstemp esa nomni tanlash va yaratishni birga bajaradi\n");
return 0;
}
Vaqtinchalik fayl yaratildi
Nomi tasodifiymi? ha
Fayl o'chirildi : ha
Nima uchun mkstemp:
tmpnam/tempnam nomni qaytaradi, faylni yaratmaydi
Oradagi vaqtda hujumchi o'sha nomda havola yaratadi
mkstemp esa nomni tanlash va yaratishni birga bajaradi
getuidvageteuidqiymatlarini chiqaring va farqini ayting.idbuyrug'i chiqishini tahlil qiling.find /usr/bin -perm -4000bilan setuid dasturlarni toping.getresuidbilan uchala UID ni ko'ring.- Huquq tushirishning to'g'ri tartibini yozing.
getcap /usr/bin/pingnatijasini tushuntiring.setuidvacapabilitiesyondashuvini solishtiring.system()ningPATHorqali qanday hujum qilinishini tushuntiring./etc/passwdva/etc/shadowhuquqlarini taqqoslang.mkstempbilan xavfsiz vaqtinchalik fayl yarating.
Xulosa #
- Foydalanuvchi OT uchun - son (UID), nom faqat qulaylik uchun.
- Har jarayonda uchta UID: haqiqiy, amaldagi, saqlangan.
- Huquqlar amaldagi UID bo'yicha tekshiriladi.
- setuid dastur egasining huquqlari bilan ishlaydi - juda xavfli.
- Huquq tushirish tartibi: setgroups → setgid → setuid.
setuidnatijasini albatta tekshiring.- Imkoniyatlar root huquqini mayda bo'laklarga ajratadi.
- setuid o'rniga
setcapishlating - eng kam imtiyoz. - setuid dasturda
system/popenishlatmang. - Parollar tuzlangan va sekin algoritm bilan xeshlanadi.
- SELinux/AppArmor egasining xohishidan qat'i nazar cheklaydi.
- Vaqtinchalik fayl uchun
mkstemp- TOCTOU dan himoya.
Keyingi bo'limda tizimning yuklanish jarayonini ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.