如何使腳本的set-user-id位起作用

 一:前記

以下討論圍繞的一個問題:爲什麼C程序可以通過set-user-id位提權,但shell腳本不可以。

 

文章會比較羅嗦,其實只要瞭解unix程序的fork/exec並結合shell的執行過程,即可以明白。結論其實很簡單就在最後,二句話。不耐煩的可以直接拖到最後邊看呵。而且估計答案是很讓人失望的。

 

寫本文的目的主要將我對這個問題的認識的過程記錄了下來,包括長期以來的一個誤解。爲解決這個問題,重翻了apue4.4/8.11/8.12/8.13等章節,並同時閱讀了bash/set mannual手冊頁,終於對這個問題的原因和解決方法有所瞭解,餘留的問題涉及到內核exec的執行了,有空再繼續深入

 

執行環境:

系統:Red Hat Enterprise Linux Server release 5.1 (Tikanga)

內核:2.6.18-53.el5xen

bash版本:GNU bash, version 3.1.17(1)-release (i686-redhat-linux-gnu)

 

 

二:問題顯現

 

考慮以下這個場景

有一個文件名:

-rwx------ 1 root root   11 05-04 18:40 test.txt

文件所有者爲root用戶,文件對其他用戶不可讀寫,我們希望通過某種型式(接口),比如提供某個程序,

通過該程序進行文件讀寫(這裏先考慮讀),而不是直接將文件讀寫權限附給其他用戶

 

比較典型的,像/etc/passwd文件那樣,我們希望能通過設置passwd程序的set-id-root方式,使其他用戶在運行該程序裏,可以臨時獲得root的權限。

 

當然所有例子中的 set-id-user裏的user並不侷限於root,出於安全考慮,一般的服務程序也不直接使用root進行權限限制,而是另行創建一個用戶,畢竟root用戶太過行特殊,並且在進行setuid(id)系統調好用過程中,如果當前euidroot,而目標idroot的話,root用戶將會把uid,euid,suid一同降權爲該id,除非再執行一個set-id-root程序,否則進程無法再獲得root權限,這個變化在當前進程中是不可恢復的。具體的例子可以參考Unix Incompatibility Notes: UID Setting Functions http://unixpapa.com/incnote/setuid.html)而實際上unix相關係統爲考慮到形形色色的需求,相關的系統調用包括了setuid,seteuid,setreuid,setresuid等等。具體的可以參考相關的manual。下邊僅以seteuid進行討論

 

於是乎開始寫程序,首先考慮用shell程序

於是我們寫下下邊這個腳本

#/bin/bash

cat  test.txt

腳本名:test.sh,所有者root,

-rwsr-xr-x 1 root root   26 05-06 02:45 test.sh

 

然後我們通過其他用戶調用這個腳本:

$ sh test.sh

cat: test.txt: Permission denied

發現了問題:儘管附給了程序(腳本)set-id-root位了,但仍然沒有讀取test.txt文件的權限

 

 

在腳本不行的情況下,我試着用c

 

  

 

很簡單的一段代碼,用root用戶編譯後沒設置set-uid-root之前與之後的執行情況

[bobo@localhost c]$ l a.out

-rwx--x--x 1 root root 5000 05-08 11:00 a.out

[bobo@localhost c]$ ./a.out

fopen faild

[bobo@localhost c]$ l a.out

-rws--x--x 1 root root 5000 05-08 11:00 a.out

[bobo@localhost c]$ ./a.out

12345678

 

 

 

 

三:setuid()與一個誤解的澄清

在發現上邊的情況之後,加上以前的一個誤解,讓我在解決問題的過程中多走了個彎路。

 

這個誤解是這樣的:以前在某個網址(Unix/Linux OS: real and effective user id, binary image, controlled access http://en.allexperts.com/q/Unix-Linux-OS-1064/real-effective-user-id.htm

上看到passwd的執行過程中uideuid的轉變,最後有這麼一段話:Note one other thing, setting a Set-UID on a executable file is not enough to make it run as privileged process. The program itself must make a system call.意思基本上是說對於程序設置的set-uid並不能使進程擁有相應權限,還要進行一次系統調用。

 

不管這句話的系統調用是指exec還是setuid,因爲exec是無論如何都會調用的,其後好長一段時間裏我一直存在的誤解,即程序裏沒需要進行系統調用,通過系統調用更改了euid後纔能有相關文件的操作權限。這個理解後來證明是錯的,或說是不完整的。但至少一定程序上解釋了爲什麼shell設置set-id位無效了:c程序裏的設置是在當前進程裏的,而shell並沒有提供如setuid類似的命令可以調用用來更改進程id,更何況即使用類似的命令,一旦shell調用起來,也是在創建的子進程裏有效,不會變更到父進程。

 

 

這個誤解,直到前不久,羣裏再討論set-id問題的時候,有人提到c程序只需要設置set-id就可以起做用時,我爭論說:還需要內部進行一次setuid系統調用!,事後我爲了驗證,將上邊的程序修改了下

  

去掉了seteuid一句,同樣編譯連接,設置同樣的權限與set-id-root,運行,發現仍然是可以生效的。

這就推翻了上邊那段話,說明了,c程序的set-id位生效,並不需要額外的系統調用

 

三:撥開雲霧

一切的迷團都是表象,要看清表象就需要了解事物的本質。

目前的問題在於

1:用戶在執行set-id-root時,在什麼情況下進行的權限變更的

2shell程序爲什麼不能產生c程序這樣的效果

 

這個時候對內核進程處理並不熟悉的我,只能求助於apue了。

 

4.4節關於文件的set-id位介紹如下

"when this file is executed, set the effective user ID of the process to be the owner of the file (st_uid)."

 即在文件執行時設置的權限

 

8.3上提到fork子進程繼續了父進程的環境

there are numerous other properties of the parent that are inherited by the child

其中包括

Real user ID, real group ID, effective user ID, effective group ID The set-user-ID and set-group-ID flags

 

8.10提到程序fork子進程後執行新程序需要調用exec,繼續當膽子函數的執行和環境變量,但在euid上可能根據文件進行了更改

 

When a process wants to execute a different program. This is common for shells. In this case, the child does an exec (which we describe in Section 8.10) right after it returns from the fork.

 

 

Note that the real user ID and the real group ID remain the same across theex ec, but the effective IDs can change, depending on the status of the set-user-ID and the set- group-ID bits for the program file that is executed. If the set-user-ID bit is set for the new program, the effective user ID becomes the owner ID of the program file. Otherwise, the effective user ID is not changed (it's not set to the real user ID). The group ID is handled in the same way.

 

 

並在8.11 介紹了setuid,seteuid等函數的調用。

 

8.12上介紹了腳本語言的執行情況

The recognition

of these files is done within the kernel as part of processing the exec system call. The actual file that gets executed by the kernel is not the interpreter file, but the file specified by the pathname on the first line of the interpreter file.

 

 

到此,二個問題都解答出來了

1:用戶在執行set-id-root時,在什麼情況下進行的權限變更的

答:執行程序過程中,創建進程爲fork, 實際執行程序由exec來執行,

fork繼續承了父進程的環境

實際上程序的set-id位是在exec時進行判斷並做相應euid更改的

 

2shell程序爲什麼不能產生c程序這樣的效果

 

答:實際上內核進行exec的並不是這個腳本程序,而是實際上的內部程序,而這程序一般由第一行指定(默認一般爲/bin/sh)

比如,我們寫個awk腳本,只需要二行

[root@localhost c]# cat cat.awk

#!/bin/awk -f

1;

這個腳本實現實際上就是找印文件

腳本命名爲:cat.awk

 

我們用root用戶創建,並設置set-id權限

-rwsr-xr-x 1 root root   18 May  8 12:42 cat.awk

執行的結果仍然是權限不足

[bobo@localhost c]$ ./cat.awk test.txt

awk: ./cat.awk:4: fatal: cannot open file `test.txt' for reading (Permission denied)

 

因爲實際上exec的讀文件程序並不是cat.awk腳本,而是awk程序(當然,awk也佔有一個進程,這個後邊會說到)

我們修改awk的程序權限

-rwsr-xr-x 1 root root 320416 Jan 15  2007 /bin/gawk

執行程序

[bobo@localhost c]$ ./cat.awk test.txt

1234567890

 

即可實現。(/bin/awk在環境下爲/bin/gawk的軟鏈接)

 

 

 

 

四:未完成的事

 

這邊還是有個問題,爲什麼設置腳本就不起作用呢,用戶不管是在控制檯還是在終端,敲入命令,對於c程序和腳本表面上都是一樣的。處理上,也是從當前進程上fork一個子進程去執行。

腳本的執行不是也是一個進程嗎?

 

確實,兩者都是同樣執行一個新進程,但c程序執行程序是該程序本身,而腳本的執行程序由第一行決定,或者默認爲shell(bash),

 

那麼實質性的問題就在於bash shell的處理了。不管文本是否有set-id,創建的進程uid/euid實際上繼承於父進程並且受shell程序影響,以bash爲例,程序所在爲:/bin/bash

 

設置腳本的set-id位實際上只是設置了一個輸入文件,是沒有意義的,需要使shell可以產生效果,那就去設置/bin/bashset-id

 

Chmod +s /bin/bash

 

並且要使用-p參數

Set -p

 

因爲當shell設置了set-id位時,若不打開-p開關,shell(bash v2版本以上)在執行exec時,會自動將euid更改爲實際用戶id,使作用關閉,我們看下bash裏的函數調用即可明白

[bobo@localhost c]$ strings /bin/bash |grep set|grep id

setuid

setpgid

setgid

set_ppid

 

另外:在bash manualIVOCATION一節最後一段如此描述:

       If  the  shell  is started with the effective user (group) id not equal to the real user (group) id, and the -p  option is not supplied, no startup files are read, shell functions are not inherited from the environment,  the SHELLOPTS  variable, if it appears in the environment, is ignored, and the effective user id is set to the real  user id.  If the -p option is supplied at invocation, the startup behavior is the same, but the effective  user        id is not reset.

 

set mannual中關於-p選項

              -p      Turn  on  privileged  mode.  In this mode, the $ENV and $BASH_ENV files are not processed, shell  functions are not inherited from the environment, and the SHELLOPTS variable, if it  appears  in    the  environment,  is  ignored.   If the shell is started with the effective user (group) id not   equal to the real user (group) id, and the -p option is not supplied, these  actions  are  taken  and  the effective user id is set to the real user id.  If the -p option is supplied at startup, the effective user id is not reset.  Turning this option off causes the effective user and group   ids to be set to the real user and group ids.

 

 

 

綜上所述,如何讓腳本的set-id位起作用,這個問題其實在明白上邊討論的內容後答案已經明瞭了,腳本的set-id位跟普通文件的set-id位一樣,是無意義的。只有實際執行程序纔有作用。如bash,awk,perl

 

如前言所說,若想要進一步瞭解進程相關的問題,那就要深入內核去分析exec程序了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章