TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
คุณต้องถามระบบว่าผู้ใช้มีสิทธิ์ในการเขียนหรือไม่ วิธีที่น่าเชื่อถือเพียงวิธีเดียวคือการเปลี่ยน uid ที่มีประสิทธิภาพ, gid ที่มีประสิทธิภาพและการเสริม gids เป็นของผู้ใช้และใช้การaccess(W_OK)
เรียกของระบบ (แม้จะมีข้อ จำกัด บางอย่างในบางระบบ / การกำหนดค่า)
และจำไว้ว่าการไม่มีสิทธิ์ในการเขียนลงไฟล์ไม่ได้รับประกันว่าคุณจะไม่สามารถแก้ไขเนื้อหาของไฟล์ที่พา ธ นั้นได้
เรื่องราวอีกต่อไป
ลองพิจารณาสิ่งที่ต้องทำเพื่อให้ผู้ใช้ $ มีสิทธิ์เข้าถึงการเขียน/foo/file.txt
(สมมติว่าไม่มีเลย/foo
และ/foo/file.txt
เป็น symlink)
เขาต้องการ:
- ค้นหาการเข้าถึง
/
(ไม่จำเป็นread
)
- ค้นหาการเข้าถึง
/foo
(ไม่จำเป็นread
)
- การเข้าถึงเพื่อเขียน
/foo/file.txt
คุณสามารถมองเห็นแล้วว่าวิธีการ (เช่น@ lcd047 ของหรือ@ apaul ของ ) ที่ตรวจสอบเพียงได้รับอนุญาตจากfile.txt
จะไม่ทำงานเพราะพวกเขาอาจจะบอกว่าfile.txt
สามารถเขียนได้แม้ว่าผู้ใช้จะไม่ได้รับอนุญาตการค้นหาหรือ/
/foo
และวิธีการเช่น:
sudo -u "$user" find / -writeble
จะไม่ทำงานเพราะจะไม่รายงานไฟล์ในไดเรกทอรีที่ผู้ใช้ไม่มีการเข้าถึงแบบอ่าน ( find
ทำงานเป็น$user
ไม่สามารถแสดงรายการเนื้อหาของพวกเขา) แม้ว่าเขาจะสามารถเขียนถึงพวกเขา
หากเราลืมเกี่ยวกับ ACLs ระบบไฟล์แบบอ่านอย่างเดียวแฟล็ก FS (เช่นไม่เปลี่ยนรูป) มาตรการรักษาความปลอดภัยอื่น ๆ (apparmor, SELinux ซึ่งสามารถแยกแยะความแตกต่างระหว่างการเขียนประเภทต่าง ๆ ) และเน้นเฉพาะการอนุญาตแบบดั้งเดิมและคุณสมบัติการเป็นเจ้าของเท่านั้น ที่กำหนด (ค้นหาหรือเขียน) find
ได้รับอนุญาตที่มีอยู่แล้วค่อนข้างซับซ้อนและยากที่จะแสดงด้วย
คุณต้องการ:
- ถ้าคุณเป็นเจ้าของไฟล์คุณต้องได้รับอนุญาตจากเจ้าของ (หรือมี uid 0)
- หากคุณไม่ได้เป็นเจ้าของไฟล์ แต่เป็นกลุ่มของคุณคุณต้องได้รับอนุญาตจากกลุ่มนั้น (หรือมี uid 0)
- หากคุณไม่ได้เป็นเจ้าของและไม่อยู่ในกลุ่มใด ๆ ของคุณการอนุญาตอื่น ๆ จะมีผล (เว้นแต่ว่าคุณจะเป็น 0)
ในfind
ไวยากรณ์นี่เป็นตัวอย่างกับผู้ใช้ uid 1 และ gids 1 และ 2 นั่นคือ:
find / -type d \
\( \
-user 1 \( -perm -u=x -o -prune \) -o \
\( -group 1 -o -group 2 \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) -o -type l -o \
-user 1 \( ! -perm -u=w -o -print \) -o \
\( -group 1 -o -group 2 \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
หนึ่งในนั้นพรุนไดเรกทอรีที่ผู้ใช้ไม่มีสิทธิ์ในการค้นหาและสำหรับไฟล์ประเภทอื่น ๆ (ยกเว้น symlinks ที่ไม่เกี่ยวข้อง) ตรวจสอบการเข้าถึงการเขียน
หากคุณต้องการพิจารณาสิทธิ์ในการเขียนในไดเรกทอรี:
find / -type d \
\( \
-user 1 \( -perm -u=x -o -prune \) -o \
\( -group 1 -o -group 2 \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user 1 \( ! -perm -u=w -o -print \) -o \
\( -group 1 -o -group 2 \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
หรือโดยพลการ$user
และความเป็นสมาชิกกลุ่มที่ดึงมาจากฐานข้อมูลผู้ใช้:
groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
\( \
-user "$user" \( -perm -u=x -o -prune \) -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( ! -perm -u=w -o -print \) -o \
\( -group $groups \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
(นั่นคือกระบวนการทั้งหมด 3 กระบวนการ: id
, sed
และfind
)
สิ่งที่ดีที่สุดที่นี่คือการสืบทอดต้นไม้เป็นรูทและตรวจสอบการอนุญาตในฐานะผู้ใช้สำหรับแต่ละไฟล์
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(นั่นคือหนึ่งfind
กระบวนการบวกหนึ่งsudo
และsh
ประมวลผลทุกๆสองสามพันไฟล์[
และprintf
มักจะสร้างขึ้นในเชลล์)
หรือด้วยperl
:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 กระบวนการทั้งหมด: find
, sudo
และperl
)
หรือด้วยzsh
:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(0 กระบวนการทั้งหมด แต่เก็บรายการไฟล์ทั้งหมดไว้ในหน่วยความจำ)
โซลูชันเหล่านั้นขึ้นอยู่กับการaccess(2)
เรียกใช้ระบบ นั่นคือแทนที่จะทำซ้ำอัลกอริทึมที่ระบบใช้ในการตรวจสอบการอนุญาตการเข้าถึงเราขอให้ระบบทำการตรวจสอบด้วยอัลกอริทึมเดียวกัน (ซึ่งคำนึงถึงการอนุญาตบัญชี ACLs ค่าสถานะที่ไม่เปลี่ยนรูปแบบระบบไฟล์แบบอ่านอย่างเดียว ... ) มันจะใช้หากคุณพยายามเปิดไฟล์เพื่อเขียนดังนั้นสิ่งที่ใกล้เคียงที่สุดที่คุณจะได้รับคือโซลูชันที่น่าเชื่อถือ
ในการทดสอบโซลูชันที่ให้ไว้ที่นี่ด้วยการผสมผสานระหว่างผู้ใช้กลุ่มและการอนุญาตต่าง ๆ คุณสามารถทำได้:
perl -e '
for $u (1,2) {
for $g (1,2,3) {
$d1="u${u}g$g"; mkdir$d1;
for $m (0..511) {
$d2=$d1.sprintf"/%03o",$m; mkdir $d2; chown $u, $g, $d2; chmod $m,$d2;
for $uu (1,2) {
for $gg (1,2,3) {
$d3="$d2/u${uu}g$gg"; mkdir $d3;
for $mm (0..511) {
$f=$d3.sprintf"/%03o",$mm;
open F, ">","$f"; close F;
chown $uu, $gg, $f; chmod $mm, $f
}
}
}
}
}
}'
การเปลี่ยนแปลงผู้ใช้ระหว่าง 1 ถึง 2 และกลุ่ม betweem 1, 2 และ 3 และ จำกัด ตัวเราไว้ที่ระดับต่ำกว่า 9 บิตของการอนุญาตเนื่องจากมันสร้างไฟล์แล้ว 9458694 สำหรับไดเรกทอรีและจากนั้นอีกครั้งสำหรับไฟล์
u<x>g<y>/<mode1>/u<z>g<w>/<mode2>
ที่สร้างผสมเป็นไปได้ทั้งหมด ผู้ใช้ที่มี uid 1 และ gids 1 และ 2 จะมีสิทธิ์เข้าถึงเพื่อเขียนu2g1/010/u2g3/777
แต่ไม่ใช่u1g2/677/u1g1/777
สำหรับอินสแตนซ์
ตอนนี้โซลูชันเหล่านั้นพยายามระบุพา ธ ของไฟล์ที่ผู้ใช้อาจเปิดสำหรับการเขียนซึ่งแตกต่างจากพา ธ ที่ผู้ใช้อาจสามารถแก้ไขเนื้อหาได้ เพื่อตอบคำถามทั่วไปที่มากกว่านั้นมีหลายสิ่งที่ต้องคำนึงถึง:
- ผู้ใช้ $ อาจไม่สามารถเข้าถึงการเขียน
/a/b/file
แต่ถ้าเขาเป็นเจ้าของfile
(และมีการเข้าถึงการค้นหา/a/b
และระบบไฟล์ไม่ได้เป็นแบบอ่านอย่างเดียวและไฟล์ไม่มีการตั้งค่าไม่เปลี่ยนรูปและเขาได้เข้าถึงเชลล์ไปยังระบบ) จากนั้นเขาจะสามารถเปลี่ยนการอนุญาตของfile
และอนุญาตการเข้าถึงของตนเอง
- สิ่งเดียวกันถ้าเขาเป็นเจ้าของ
/a/b
แต่ไม่มีการเข้าถึงการค้นหา
- ผู้ใช้ $ อาจไม่สามารถเข้าถึงได้
/a/b/file
เนื่องจากเขาไม่มีสิทธิ์เข้าถึงการค้นหา/a
หรือ/a/b
แต่ไฟล์นั้นอาจมีฮาร์ดลิงก์/b/c/file
ในกรณีนี้เขาอาจสามารถแก้ไขเนื้อหาของ/a/b/file
โดยเปิดผ่าน/b/c/file
เส้นทาง
- สิ่งเดียวกันกับผูกม้า เขาอาจไม่สามารถเข้าถึงการค้นหา
/a
แต่/a/b
อาจเชื่อมโยงเข้า/c
ด้วยกันเพื่อให้เขาสามารถเปิดfile
เพื่อเขียนผ่าน/c/file
เส้นทางอื่น
- เขาอาจไม่มีสิทธิ์เขียน
/a/b/file
แต่ถ้าเขามีสิทธิ์เข้าถึงเพื่อเขียน/a/b
สามารถลบหรือเปลี่ยนชื่อfile
ในนั้นและแทนที่ด้วยเวอร์ชันของเขาเอง เขาจะเปลี่ยนเนื้อหาของไฟล์/a/b/file
แม้ว่าจะเป็นไฟล์อื่น
- สิ่งเดียวกันถ้าเขามีการเขียนการเข้าถึง
/a
(เขาสามารถเปลี่ยนชื่อ/a/b
ไป/a/c
สร้างใหม่/a/b
ไดเรกทอรีและใหม่file
อยู่ในนั้น
เพื่อค้นหาเส้นทางที่$user
จะสามารถแก้ไขได้ ในการระบุที่ 1 หรือ 2 เราไม่สามารถพึ่งพาการaccess(2)
เรียกของระบบอีกต่อไป เราสามารถปรับfind -perm
วิธีการของเราในการสมมติการเข้าถึงไดเรกทอรีหรือเขียนการเข้าถึงไฟล์ทันทีที่คุณเป็นเจ้าของ:
groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
\( \
-user "$user" -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" -print -o \
\( -group $groups \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
เราสามารถระบุหมายเลข 3 และ 4 โดยการบันทึกอุปกรณ์และหมายเลขไอโหนดหรือไฟล์ทั้งหมดที่ผู้ใช้ $ มีสิทธิ์ในการเขียนและรายงานเส้นทางไฟล์ทั้งหมดที่มีหมายเลข dev + inode เหล่านั้น เวลานี้เราสามารถใช้แนวทางที่เชื่อถือได้มากขึ้น access(2)
:
สิ่งที่ต้องการ:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-w,$_' |
perl -l -0ne '
($w,$p) = /(.)(.*)/;
($dev,$ino) = stat$p or next;
$writable{"$dev,$ino"} = 1 if $w;
push @{$p{"$dev,$ino"}}, $p;
END {
for $i (keys %writable) {
for $p (@{$p{$i}}) {
print $p;
}
}
}'
5 และ 6 มีความซับซ้อนในขั้นแรกโดยt
บิตของสิทธิ์ เมื่อนำไปใช้กับไดเรกทอรีต่างๆนั่นเป็นบิตการลบแบบ จำกัดซึ่งป้องกันผู้ใช้ (นอกเหนือจากเจ้าของไดเรกทอรี) จากการลบหรือเปลี่ยนชื่อไฟล์ที่พวกเขาไม่ได้เป็นเจ้าของ
ตัวอย่างเช่นถ้าเรากลับไปก่อนหน้านี้ตัวอย่างเช่นเราไปถ้าคุณมีสิทธิ์ในการเขียน/a
แล้วคุณควรจะสามารถที่จะเปลี่ยนชื่อ/a/b
ไป/a/c
แล้วสร้าง/a/b
ไดเรกทอรีและใหม่file
ในการมี แต่ถ้าt
บิตตั้งอยู่บน/a
และคุณไม่ได้เป็นเจ้าของแล้วคุณจะสามารถทำมันได้หากคุณเป็นเจ้าของ/a
/a/b
ที่ให้:
- หากคุณเป็นเจ้าของไดเรกทอรีตาม 1 คุณสามารถให้สิทธิ์การเขียนกับตัวเองและ t บิตจะไม่นำไปใช้ (และคุณสามารถลบออกได้) ดังนั้นคุณสามารถลบ / เปลี่ยนชื่อ / สร้างไฟล์หรือ dir ใด ๆ ในนั้นได้ พา ธ ไฟล์ทั้งหมดที่อยู่ข้างใต้นั้นเป็นของคุณที่จะเขียนซ้ำกับเนื้อหาใด ๆ
- หากคุณไม่ได้เป็นเจ้าของ แต่มีสิทธิ์เข้าถึงเพื่อเขียน:
- ทั้ง
t
บิตไม่มีการตั้งค่าและคุณอยู่ในกรณีเดียวกับข้างต้น (เส้นทางแฟ้มทั้งหมดเป็นของคุณ)
- หรือตั้งค่าไว้แล้วคุณจะไม่สามารถแก้ไขไฟล์ที่คุณไม่ได้เป็นเจ้าของหรือไม่มีสิทธิ์เข้าถึงเพื่อเขียนดังนั้นสำหรับจุดประสงค์ของเราในการค้นหาพา ธ ไฟล์ที่คุณสามารถแก้ไขได้นั่นก็เหมือนกับว่าไม่มีสิทธิ์ในการเขียน
ดังนั้นเราจึงสามารถจัดการกับ 1, 2, 5 และ 6 ทั้งหมดด้วย:
find / -type d \
\( \
-user "$user" -prune -exec find {} + -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( -type d -o -print \) -o \
\( -group $groups \) \( ! -perm -g=w -o \
-type d ! -perm -1000 -exec find {} + -o -print \) -o \
! -perm -o=w -o \
-type d ! -perm -1000 -exec find {} + -o \
-print
นั่นและโซลูชันสำหรับ 3 และ 4 นั้นมีความเป็นอิสระคุณสามารถรวมเอาท์พุทของมันเพื่อรับรายการทั้งหมด:
{
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-w,$_' |
perl -0lne '
($w,$p) = /(.)(.*)/;
($dev,$ino) = stat$p or next;
$writable{"$dev,$ino"} = 1 if $w;
push @{$p{"$dev,$ino"}}, $p;
END {
for $i (keys %writable) {
for $p (@{$p{$i}}) {
print $p;
}
}
}'
find / -type d \
\( \
-user "$user" -prune -exec sh -c 'exec find "$@" -print0' sh {} + -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( -type d -o -print0 \) -o \
\( -group $groups \) \( ! -perm -g=w -o \
-type d ! -perm -1000 -exec sh -c 'exec find "$@" -print0' sh {} + -o -print0 \) -o \
! -perm -o=w -o \
-type d ! -perm -1000 -exec sh -c 'exec find "$@" -print0' sh {} + -o \
-print0
} | perl -l -0ne 'print unless $seen{$_}++'
ควรมีความชัดเจนหากคุณอ่านทุกอย่างตอนนี้ส่วนหนึ่งเป็นอย่างน้อยเกี่ยวข้องกับสิทธิ์และความเป็นเจ้าของไม่ใช่คุณสมบัติอื่น ๆ ที่อาจอนุญาตหรือ จำกัด การเข้าถึงการเขียน (FS แบบอ่านอย่างเดียว ACLs ตั้งค่าสถานะไม่เปลี่ยนรูปได้คุณสมบัติด้านความปลอดภัยอื่น ๆ ... ) และในขณะที่เราดำเนินการในหลายขั้นตอนข้อมูลบางอย่างอาจไม่ถูกต้องหากไฟล์ / ไดเรกทอรีกำลังถูกสร้าง / ลบ / เปลี่ยนชื่อหรือแก้ไข / สิทธิ์ / ความเป็นเจ้าของของพวกเขาในขณะที่สคริปต์กำลังทำงานเช่นบนเซิร์ฟเวอร์ไฟล์ที่มีไฟล์หลายล้านไฟล์ .
หมายเหตุการพกพา
รหัสทั้งหมดนั้นเป็นมาตรฐาน (POSIX, Unix สำหรับt
bit) ยกเว้น:
-print0
เป็นส่วนขยายของ GNU ในขณะนี้ยังรองรับโดยการใช้งานอื่น ๆ ไม่กี่ ด้วยfind
การใช้งานที่ขาดการสนับสนุนสำหรับมันคุณสามารถใช้-exec printf '%s\0' {} +
แทนและแทนที่ด้วย-exec sh -c 'exec find "$@" -print0' sh {} +
-exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +
perl
ไม่ใช่คำสั่งที่ระบุ POSIX แต่มีให้ใช้อย่างกว้างขวาง คุณต้องหรือสูงกว่าสำหรับperl-5.6.0
-Mfiletest=access
zsh
ไม่ใช่คำสั่งที่ระบุ POSIX ว่าzsh
รหัสดังกล่าวควรจะทำงานกับ zsh-3 (1995) และสูงกว่า
sudo
ไม่ใช่คำสั่งที่ระบุ POSIX รหัสควรทำงานกับเวอร์ชันใดก็ได้ตราบใดที่การกำหนดค่าระบบอนุญาตให้ทำงานperl
ในฐานะผู้ใช้ที่กำหนด