การใช้ setuid bit อย่างถูกต้อง


45

ฉันมีกระบวนการที่ต้องการสิทธิ์รูทเมื่อรันโดยผู้ใช้ปกติ เห็นได้ชัดว่าฉันสามารถใช้ "setuid bit" เพื่อทำสิ่งนี้ให้สำเร็จ วิธีที่เหมาะสมในการทำเช่นนี้ในระบบ POSIX คืออะไร?

นอกจากนี้ฉันจะทำสิ่งนี้กับสคริปต์ที่ใช้ล่าม (bash, perl, python, php, ฯลฯ ) ได้อย่างไร

คำตอบ:


53

บิต setuidสามารถตั้งค่าในแฟ้มที่ปฏิบัติการเพื่อที่ว่าเมื่อเรียกใช้โปรแกรมจะมีสิทธิ์ของเจ้าของไฟล์แทนของผู้ใช้จริงที่ว่าพวกเขาจะแตกต่างกัน นี่คือความแตกต่างระหว่างuid ที่มีประสิทธิภาพ (id ผู้ใช้) และuid จริง

ยูทิลิตี้ทั่วไปบางอย่างเช่นเป็นpasswdเจ้าของรูทและกำหนดค่าด้วยวิธีนี้ไม่จำเป็น (จำเป็นpasswdต้องเข้าถึง/etc/shadowซึ่งรูทสามารถอ่านได้เท่านั้น)

กลยุทธ์ที่ดีที่สุดเมื่อทำสิ่งนี้คือทำสิ่งที่คุณต้องทำในฐานะผู้ใช้ระดับสูงทันทีจากนั้นลดสิทธิพิเศษเพื่อให้บั๊กหรือการใช้งานในทางที่ผิดมีโอกาสเกิดขึ้นน้อยลงในขณะที่รันรูท เมื่อต้องการทำเช่นนี้คุณตั้งค่าuid ที่มีประสิทธิภาพของกระบวนการเป็นuid ที่แท้จริง ใน POSIX C:

#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        real,
        geteuid()
    );
}

int main (void) {
    uid_t real = getuid();
    report(real);
    seteuid(real);
    report(real);
    return 0;
}

ฟังก์ชั่นที่เกี่ยวข้องซึ่งควรจะมีความเทียบเท่าในภาษาระดับสูงกว่ามากที่สุดหากใช้โดยทั่วไปในระบบ POSIX:

  • getuid(): รับuid จริง
  • geteuid(): รับที่มีประสิทธิภาพ UID
  • seteuid(): ตั้งค่าที่มีประสิทธิภาพ UID

คุณไม่สามารถทำอะไรกับอันสุดท้ายที่ไม่เหมาะสมกับ uid จริงยกเว้นในกรณีที่บิต setuid ถูกตั้งค่าไว้ในไฟล์ที่เรียกใช้งานได้ gcc test.c -o testuidดังนั้นลองนี้รวบรวม จากนั้นคุณต้องมีสิทธิ์:

chown root testuid
chmod u+s testuid

อันสุดท้ายตั้งค่าบิต setuid หากตอนนี้คุณทำงาน./testuidในฐานะผู้ใช้ปกติคุณจะเห็นกระบวนการโดยค่าเริ่มต้นทำงานกับ uid 0 ที่มีประสิทธิภาพ, รูท

แล้วสคริปต์ล่ะ

สิ่งนี้แตกต่างจากแพลตฟอร์มหนึ่งไปอีกแพลตฟอร์มแต่บน Linux สิ่งที่ต้องใช้ล่ามรวมถึง bytecode ไม่สามารถใช้บิต setuid ได้จนกว่าจะมีการตั้งค่าไว้ที่ล่าม นี่เป็นสคริปต์ Perl ง่าย ๆ ที่เลียนแบบรหัส C ด้านบน:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n"; 

True เป็น * ราก nixy, perl มีการสร้างในตัวแปรพิเศษสำหรับ uid ที่มีประสิทธิภาพ ( $>) และ uid จริง ( $<) แต่ถ้าคุณลองแบบเดียวกันchownและchmodใช้กับไฟล์ที่คอมไพล์แล้ว (จาก C ตัวอย่างก่อนหน้า) มันจะไม่สร้างความแตกต่างเลย สคริปต์ไม่สามารถรับสิทธิ์ได้

คำตอบนี้คือการใช้ setuid binary เพื่อรันสคริปต์:

#include <stdio.h>
#include <unistd.h> 

int main (int argc, char *argv[]) {
    if (argc < 2) {
        puts("Path to perl script required.");
        return 1;
    }
    const char *perl = "perl";
    argv[0] = (char*)perl;
    return execv("/usr/bin/perl", argv);
}

คอมไพล์นี้แล้วgcc --std=c99 whatever.c -o perlsuid chown root perlsuid && chmod u+s perlsuidตอนนี้คุณสามารถรันสคริปต์ perl ใด ๆด้วย uid ที่มีประสิทธิภาพของ 0 ไม่ว่าใครจะเป็นเจ้าของ

กลยุทธ์ที่คล้ายกันจะทำงานกับ php, python และอื่น ๆ แต่ ...

# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face

ได้โปรดได้โปรดอย่าปล่อยชนิดของสิ่งนี้โกหกรอบ เป็นไปได้ว่าคุณต้องการรวบรวมในชื่อของสคริปต์เป็นเส้นทางที่แน่นอนเช่นแทนที่รหัสทั้งหมดmain()ด้วย:

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

พวกเขาทำให้แน่ใจ/opt/suid_scriptsและทุกอย่างในนั้นเป็นแบบอ่านอย่างเดียวสำหรับผู้ใช้ที่ไม่ใช่รูท whatever.plมิฉะนั้นคนที่สามารถแลกเปลี่ยนในสิ่งที่สำหรับ

นอกจากนี้ระวังว่าภาษาสคริปต์ช่วยให้หลายตัวแปรสภาพแวดล้อมที่มีการเปลี่ยนแปลงวิธีที่พวกเขาดำเนินการสคริปต์ ตัวอย่างเช่นตัวแปรสภาพแวดล้อมอาจทำให้ไลบรารีที่จัดหาโดยผู้เรียกถูกโหลดซึ่งอนุญาตให้ผู้เรียกใช้เรียกใช้โค้ดโดยพลการในฐานะรูท ดังนั้นถ้าคุณไม่ทราบว่าทั้งล่ามและสคริปต์ที่ตัวเองมีประสิทธิภาพกับทุกตัวแปรสภาพแวดล้อมที่เป็นไปได้ไม่ทำเช่นนี้

ดังนั้นฉันควรทำอย่างไรดี

เป็นวิธีที่ปลอดภัยเพื่อให้ผู้ใช้ที่ไม่ใช่รากเพื่อเรียกใช้สคริปต์เป็นรากคือการเพิ่มsudosudo /path/to/scriptกฎและมีการทำงานของผู้ใช้ Sudo รวบรวมตัวแปรสภาพแวดล้อมส่วนใหญ่และยังช่วยให้ผู้ดูแลระบบสามารถเลือกผู้ที่สามารถเรียกใช้คำสั่งและสิ่งที่ขัดแย้ง ดูวิธีรันโปรแกรมเฉพาะในฐานะรูทโดยไม่ต้องใส่รหัสผ่าน? สำหรับตัวอย่าง


1
เชลล์จำนวนมากจะทำงานแตกต่างกันหากถูกเรียกว่าเป็น-privilegedเชลล์และหากเรียกจากสคริปต์ที่รับผิดชอบสามารถเรียกใช้suid rootวิธีนั้นได้อย่างปลอดภัย ฉันกลัวว่าความคิดของสคริปที่รับผิดชอบนั้นเป็นเรื่องเล็ก ๆ น้อย ๆ ในโลกแห่งความเป็นจริง อย่างไรก็ตามมันอาจจะคุ้มค่าที่จะกล่าวถึงความสำคัญของการหลีกเลี่ยงการเขียนทุกชนิดถ้าเป็นไปได้ทั้งหมดในขณะที่สิทธิ์จะเพิ่มขึ้น ...
mikeserv

@mikeserv ฉันไม่คุ้นเคยกับแนวคิดของ " -privilegedเชลล์" และ "สคริปต์ที่รับผิดชอบ" คุณต้องการคำตอบอื่นเกี่ยวกับเชลล์ไหม? ฉันไม่สามารถรับประกันได้ว่าคุณติ๊กแน่นอน) ไม่มีเหตุผลที่ดีในการทำเช่นนี้ - sudoเป็นตัวเลือกที่ดีกว่าถ้าเป็นไปได้ - ฉันเขียนมันเป็นคำตอบทั่วไปที่เกิดจากคำถามแรก ๆ เกี่ยวกับการพิสูจน์ตัวตนด้วยรหัสผ่านระบบใน php .
goldilocks

1
ฉันไม่ต้องการที่จะทำเช่นนั้น - สคริปต์ที่มีความรับผิดชอบนั้นยากจริง ๆ - อาจเป็นเพราะฉัน แต่ผมต้องบอกว่าผมไม่เห็นด้วยกับการวิเคราะห์ของsudo- ฉันถือว่าsudoเป็นโรคจิตในการที่จะอ้างว่าไม่rootว่าจะเป็น คุณสามารถใส่เปลือกไว้ในตัวคุณsudoersได้ suในความคิดของฉันดีกว่าสำหรับวัตถุประสงค์ในการเขียนสคริปต์ แต่sudoจะดีสำหรับแอปพลิเคชันสด แต่ไม่ชัดเจนเหมือนสคริปต์ที่มี#!/bin/ksh -pbangline หรือสิ่งที่suid kshอยู่ใน$PATHนั้น
mikeserv

ในขณะที่คำตอบนี้ตอบคำถาม แต่สคริปต์ setuid เป็นสูตรสำหรับการเปิดตัวเองสู่ช่องโหว่การเพิ่มระดับสิทธิ์ในท้องถิ่น มีเหตุผลที่สคริปต์ไม่สามารถตั้งค่าได้อย่างง่ายดาย คำตอบที่ถูกต้องที่นี่คือ sudo
mdadm

ฉันใช้เสรีภาพในการแก้ไขคำตอบของคุณอย่างน้อยก็พูดถึงช่องโหว่ขนาดใหญ่ที่มีตัวแปรสภาพแวดล้อม ฉันไม่ชอบคำตอบที่เป็นที่ยอมรับมากนักเนื่องจากมันได้รับความนิยมอย่างมากในวิธีการที่ไม่ปลอดภัย (setuid wrapper สำหรับสคริปต์) มันจะเป็นการดีที่สุดที่จะแนะนำ sudo และปล่อยให้การอภิปรายเพิ่มเติมสำหรับหัวข้ออื่น (ฉันครอบคลุมไว้ที่นั่นโดยวิธี)
Gilles 'หยุดชั่วร้าย'

11

ใช่คุณสามารถ แต่มันก็อาจจะเป็นความคิดที่ดีมาก โดยปกติแล้วคุณจะไม่ตั้งค่าบิต SUID โดยตรงบนปฏิบัติการของคุณ แต่ใช้sudo (8)หรือsu (1)เพื่อดำเนินการ (และ จำกัด ผู้ที่สามารถดำเนินการได้)

โปรดทราบว่ามีปัญหาด้านความปลอดภัยมากมายที่ทำให้ผู้ใช้ทั่วไปสามารถรันโปรแกรม (และโดยเฉพาะอย่างยิ่งสคริปต์!) เป็นรูท พวกเขาส่วนใหญ่ต้องทำเช่นนั้นเว้นแต่โปรแกรมนั้นเขียนขึ้นมาเป็นพิเศษและระมัดระวังเป็นพิเศษเพื่อให้ผู้ใช้ที่ประสงค์ร้ายสามารถใช้รูทเชลล์ได้อย่างง่ายดาย

ดังนั้นแม้ว่าคุณจะต้องทำมันก็เป็นความคิดที่ดีที่จะทำความสะอาดอินพุตทั้งหมดให้กับโปรแกรมที่คุณต้องการรันในฐานะ root รวมถึงสภาพแวดล้อม, พารามิเตอร์บรรทัดคำสั่ง, STDIN และไฟล์ใด ๆ ที่ประมวลผลข้อมูลที่มาจากการเชื่อมต่อเครือข่าย มันเปิด ฯลฯ ฯลฯ มันยากมากที่จะทำและยิ่งยากที่จะทำ

ตัวอย่างเช่นคุณอาจอนุญาตให้ผู้ใช้ที่แก้ไขไฟล์ด้วยเครื่องมือแก้ไขเท่านั้น แต่เอดิเตอร์อนุญาตให้เรียกใช้งานคำสั่งของผู้ช่วยเหลือภายนอกหรือระงับดังนั้นจึงให้ผู้ใช้รูทเชลล์ทำตามที่ต้องการ หรือคุณอาจกำลังเรียกใช้งานเชลล์สคริปต์ซึ่งมีความปลอดภัยสูงสำหรับคุณ แต่ผู้ใช้สามารถรันด้วยการ $ IFS ที่แก้ไขแล้วหรือตัวแปรสภาพแวดล้อมอื่น ๆ ที่เปลี่ยนแปลงพฤติกรรมของมันอย่างสมบูรณ์ หรืออาจเป็นสคริปต์ PHP ที่ควรเรียกใช้งาน "ls" แต่ผู้ใช้เรียกใช้โดยมีการแก้ไข $ PATH และทำให้มันใช้เชลล์ซึ่งเขาใช้ชื่อว่า "ls" หรือปัญหาความปลอดภัยอื่น ๆ อีกมากมาย

ถ้าโปรแกรมนั้นต้องทำงานเป็นส่วนหนึ่งของรูทจริงๆมันน่าจะดีที่สุดที่จะแก้ไขมันเพื่อให้มันทำงานได้เหมือนผู้ใช้ทั่วไป แต่เพียงแค่รัน (หลังจากฆ่าเชื้อให้ได้มากที่สุด) ส่วนที่ต้องการรูทผ่านโปรแกรมตัวช่วย suid

โปรดทราบว่าแม้ว่าคุณจะไว้วางใจผู้ใช้ในพื้นที่ทั้งหมดของคุณ (เช่นคุณให้รหัสผ่านรูทแก่พวกเขา) มันเป็นความคิดที่ไม่ดีเนื่องจากการกำกับดูแลความปลอดภัยโดยไม่ตั้งใจใด ๆ ของพวกเขา (เช่นมีสคริปต์ PHP ออนไลน์หรือรหัสผ่านอ่อนแอหรือ อะไรก็ตาม) ก็ทำให้ผู้โจมตีจากระยะไกลสามารถประนีประนอมเครื่องทั้งหมดได้อย่างสมบูรณ์


1
หนุน ดูความปลอดภัยเช่นการภาวนาในชุดโปรแกรมเมอร์ POSIX สำหรับman sh- command -p env -i ...และแม้กระทั่งการที่ล้มเหลวที่จะทำ มันค่อนข้างยากที่จะทำหลังจากทั้งหมด ถึงกระนั้นหากมีการทำอย่างถูกต้องเพื่อวัตถุประสงค์ที่ชัดเจนsuidนักล่ามที่มีความสามารถจะดีกว่าการใช้suหรือsudo- เนื่องจากพฤติกรรมของผู้ใดคนหนึ่งจะอยู่นอกการควบคุมของสคริปต์เสมอ(แม้ว่าsuจะมีโอกาสน้อยกว่าที่จะโยนลูกบอลโค้งตามที่มีแนวโน้ม จะเป็นคนบ้าน้อยเล็ก ๆ น้อย ๆ ) Bundling แพคเกจทั้งหมดเป็นทางออกเดียวที่แน่ใจว่า - มันเพิ่งดีกว่าจะแน่ใจว่าเป็นสิ่งที่
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.