一、實驗目的
1. 了解環境變數如何作業,如何對環境變數進行操作
2. 父行程創建子行程時,環境變數如何繼承
3. 環境變數如何影響系統和程式的行為
4. fork、exceve、system三者的功能,并觀察不同現象
5. Set-UID程式的行為
6 環境變數與Set-UID程式中存在的安全問題(如權限泄露)
7. 動態聯結器的保護機制
二、實驗步驟與結果
(一)Task 1:管理環境變數
1. 使用printenv或者env命令輸出環境變數,
seed@VM:~$ env


2. 輸出特定的環境變數(以PWD為例)
(1)printenv PWD方法

(2)env | grep PWD方法

3. 使用unset命令洗掉環境變數(以PWD為例)
當使用unset命令洗掉PWD環境變數后,我們使用printenv方法輸出PWD環境變數,發現為空

4. 使用export命令設定環境變數(以PWD為例)
當使用export命令設定PWD后,我們輸出PWD環境變數

(二)Task 2:將環境變數從父行程傳遞給子行程
1. 實驗目的及原理
目的:研究子行程如何從父行程中獲取其環境變數
原理:Unix中,fork()函式會通過系統呼叫創建一個與父行程幾乎完全相同的子行程,fork()函式會有兩個回傳值,在父行程中回傳子行程的ID;在子行程中回傳0,若出現錯誤則回傳一個負值,
使用man命令查看fork()函式的功能
seed@VM:~$ man fork

2. 示例代碼的解釋
Switch()陳述句中的條件,使用fork()函式創建了一個子行程,但是這里fork()函式會回傳兩個值:0和子行程的ID號,所以在下面通過注釋掉一個printenv(),另一個一定會執行;printenv()函式,用來輸出此時的環境變數,
3. Step 1:編譯并運行示例代碼,輸出子行程的環境變數
(1)使用vim編輯器撰寫程式
seed@VM:~$ vi task2.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
void printenv()
{
int i = 0;
while (environ[i] != NULL)
{
printf("%s\n", environ[i]);
i++;
}
}
void main()
{
pid_t childPid;
switch(childPid = fork())
{
case 0: /* child process */
printenv();
exit(0);
default:
//printenv(); /* parent process */
exit(0);
}
}
(2)編譯運行程式,并將結果保存在child.txt檔案中
編譯C檔案為可執行檔案:
seed@VM:~$ gcc task2.c -o task2
將結果保存為一個檔案
seed@VM:~$ ./task2 > child.txt
輸出結果:

可見已經成功的將子行程的環境變數輸出并保存,
4. Step 2:輸出父行程的環境變數
(1)利用vi命令進行編輯,將子行程的printenv()注釋掉,父行程printenv()取消注釋,
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
void printenv()
{
int i = 0;
while (environ[i] != NULL)
{
printf("%s\n", environ[i]);
i++;
}
}
void main()
{
pid_t childPid;
switch(childPid = fork())
{
case 0: /* child process */
//printenv();
exit(0);
default:
printenv(); /* parent process */
exit(0);
}
}
(2)重新編譯源檔案并運行,將結果保存到parent.txt檔案中
seed@VM:~$ gcc task2.c -o task2
seed@VM:~$ ./task2 > parent.txt

成功地將父行程的環境變數輸出并保存,
5. Step 3:使用diff命令查看父/子行程環境變數的區別
使用diff進行父/子行程環境變數的對比:
seed@VM:~$ diff child.txt parent.txt
運行完后,沒有輸出結果,說明兩次輸出的環境變數完全相同,使用fork()函式創建的子行程的環境變數繼承了父行程全部的環境變數;子行程與父行程共享環境變數,
注: 若有輸出,是因為上面編譯task2.c檔案時,-o 后面的可執行檔案名不同,
(三)Task 3:環境變數與execve()函式
1. 實驗目的及原理
目的:通過execve執行一個新的程式時,環境變數如何變化?
原理:execve()函式通過系統呼叫執行新的程式,但不會創建子行程,原行程的文本、資料、bss以及堆疊被新的行程覆寫,原行程的環境變數會丟失,

示例代碼:
#include<unistd.h>
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
int main()
{
char *argv[2];
argv[0] = "/usr/bin/env";
argv[1] = NULL;
execve("/usr/bin/env", argv,NULL);
return 0 ;
}
2. Step 1:編譯并運行示例代碼
示例程式會執行/usr/bin/enc程式,并列印當前行程的環境變數,

發現編譯程序報錯,原因是缺少execve()函式所在的頭檔案unistd.h
加入頭檔案并重新編譯運行,發現輸出為空:

3. Step 2:修改函式引數,并編譯運行
將示例代碼中的NULL改為environ,編譯運行,此時輸出了當前行程的環境變數:
execve("/usr/bin/env", argv,environ);

4. Step 3:結論
Step2與Step1進行對比,只是將exevce()函式的第三個引數由NULL改為environ,便輸出了當前行程的環境變數,說明原行程將自己的環境變數通過environ變數傳入exceve()函式的第三個引數,也就是第三個引數控制環境變數的傳遞,進而新的行程獲取環境變數,
(四)Task 4:環境變數與system()函式
1. 實驗目的及原理
目的:通過system()執行新程式時,探究環境變數的變化
原理: system()使用fork創建子行程,子行程繼承父行程的環境變數,子行程通過execl()來啟動/bin/sh,并呼叫execve()函式,將環境變數陣列傳遞給新程式;所以呼叫行程的環境變數可以被傳遞給新程式/bin/sh

示例代碼:
#include <stdio.h>
#include <stdlib.h>
int main()
{
system("/usr/bin/env");
return 0 ;
}
2. 編譯運行示例代碼,驗證上述原理

輸出了當前行程的環境變數,整體流程為:system通過fork創建的子行程會繼承父行程的環境變數,然后子行程執行execl將環境變數賦給新的程式,
(五)Task 5:環境變數與Set-UID程式
1. 實驗目的與原理
目的:環境變數是否由Set-UID程式的行程從用戶的行程中繼承
原理:Set-UID程式是指使檔案對任何可以執行此檔案的用戶執行時以檔案所有者的權限執行,Set-UID程式的行為由程式邏輯決定,但用戶可以通過環境變數來影響Set-UID程式的行為,
示例代碼:
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
void main()
{
int i = 0;
while (environ[i] != NULL)
{
printf("%s\n", environ[i]);
i++;
}
}
2. Step 1:編譯運行示例代碼,列印當前行程的所有環境變數


3. Step 2:更改所有權為Root賬戶并使其成為一個Set-UID程式
在這里可執行檔案名為task5:

Chown命令將所有權更改為Root賬戶
Chmod命令使其成為一個Set-UID程式
我們通過task5和task4的屬性對比,說明我們操作成功

4. Step 3:在普通用戶Shell中設定環境變數并運行Set-UID程式
(1)設定PATH

(2)設定LD_LIBRARY_PATH

(3)設定ANY_NAME(以name為例)

(4)運行Set-UID程式,輸出當前行程的環境變數

![]()
Shell會創建一個子行程,子行程執行Set-UID程式,我們發現在shell行程(父行程)中設定的PATH、name環境變數都已經進入了子行程的環境變數,但是我們發現在子行程的環境變數中找不到LD_LIBRARY_PATH環境變數,
解釋:
1. LD_LIBRARY_PATH用于指定查找共享庫(元件)時除了默認路徑以外的其他路徑;LD_LIBRARY_PATH可以被修改,從而加載攻擊者的惡意庫;為了使Set-UID程式更加安全,不受LD_LIBRARY_PATH環境變數的影響,運行時的聯結器或加載器(ld.so)會忽略LD*環境變數;或許可以理解為聯結器所擁有的一個保護機制:當執行程式的行程ID與擁有者的行程ID不一致時,聯結器就會忽略掉LD_LIBRARY_PATH環境變數,所以在子行程的環境變數中找不到此環境變數,
2. LD_PRELOAD和LD_LIBRARY_PATH可以影響程式的運行時的鏈接,它允許你定義在程式運行前優先加載的元件,這個功能主要就是用來有選擇性的載入不同元件中的相同函式,通過這個環境變數,我們可以在主程式和其元件的中間加載別的元件,甚至覆寫正常的函式庫,
3. 一方面,我們可以用此功能來使用自己的或是更好的函式,而另一方面,我們也可以向別人的程式注入程式,從而達到特定的目的,
4. 為了保證Set-UID程式在LD_PRELOAD和LD_LIBRARY_PATH環境的操縱下是安全的,如果程式是一個Set-UID root程式,動態聯結器就會忽略這些環境變數,
參考:
[翻譯] 雪城大學資訊安全講義 三、Set-UID 特權程式-外文翻譯-看雪論壇-安全社區|安全招聘|bbs.pediy.com
(六)Task 6:PATH環境變數與Set-UID程式
1. 實驗目的與原理
目的:在Set-UID行程中呼叫system函式執行ls命令,需要通過修改環境變數執行我們自己設定的程式,
原理:system會呼叫shell,所以在Set-UID中執行system會有一定的安全問題,因為shell的執行可能會受到環境變數的影響,用戶可以修改環境變數,惡意控制Set-UID程式的行為,
示例代碼:
#include<stdlib.h>
int main()
{
system("ls");
return 0;
}
2. 使用命令將/bin/sh鏈接到zsh
seed@VM:~$ sudo rm /bin/sh
seed@VM:~$ sudo ln -s /bin/zsh /bin/sh
Ubuntu16.01上的dash shell有一個保護機制,防止自身在Set-UID行程中執行,如果dash shell檢測到自己在Set-UID行程中執行,會立即將有效的用戶ID更改為行程的實際用戶ID,因為我們要在Set-UID行程中執行system函式會呼叫shell,上述的保護機制會防止我們的攻擊,所以需要更改shell,
3. 修改環境變數PATH,編譯示例代碼

編譯發現warning,添加頭檔案 stdlib.h重新編譯

4. 修改所有者為Root,并將其設定為Set-UID程式

5. 運行上述編譯好的task6
發現執行的是ls命令:

6. 修改環境變數,使得system呼叫的shell執行自己設定的程式
System函式中執行的是ls命令,我們的目的是想要system函式執行的ls命令為我們自己設定的程式,所以我們需要將我們自己設定的程式命名為ls,并且將我們自己設定的程式的路徑放入PATH環境變數的開頭,因為shell程式執行命令時,如果不提供命令的具體路徑,shell程式會按照順序來查找PATH的每一個目錄下是否有同名的可執行檔案,
"""命名為ls"""
#include<stdio.h>
int main()
{
printf("Hello world!");
}
設定的程式是輸出Hello world!,我們將其編譯并命名為ls,使用pwd查看編譯好的ls檔案的路徑,并將其添加到PATH路徑的開頭,然后再執行task6:

然后執行./task6檔案,也就是我們的Set-UID程式,發現成功輸出Hello world!,task6中system執行的是ls命令,但實際上執行了我們自己設定的程式,
7. 運行task6獲得一個具有root權限的shell
將/bin/sh 拷貝為當前目錄下的檔案ls

將當前目錄/home/seed添加到環境變數PATH中

這里的提示只是說日期無法正常顯示,沒有其他影響,
然后執行exec就可以獲得一個具有root權限的shell:

這里我們可以看到命令列已經變成了 ‘#’,也就是root權限的標志,
解釋:因為當前路徑下的 ls 檔案是我們由/bin/sh復制的,所以執行之后會出現shell,而且因為執行Set-UID程式時,會臨時獲得root權限,所以是root權限的shell,
8. 恢復環境變數:采用重啟虛擬機的方法
(七)Task 7:LD_PRELOAD環境變數與Set-UID程式
1. 目的與原理
目的: 設定LD_PRELOAD環境變數,觀察一個程式在不同場景中的不同行為表現
原理:許多UNIX系統允許“預加載”共享庫,可以通過設定LD_PRELOAD環境變數來指定一些庫,這些庫會在其他庫之前加載,
但是動態聯結器會有一些保護機制,子行程繼承環境變數可能會受阻
2. Step 1:構建一個元件
(1)創建mylib.c檔案
#include <stdio.h>
void sleep (int s)
{ /* If this is invoked by a privileged program,
you can do damages here! */
printf("I am not sleeping!\n");
}
(2)編譯上述程式
seed@VM:~$ gcc -fPIC -g -c mylib.c
seed@VM:~$ gcc -shared -o libmylib.so.1.0.1 mylib.o -lc
(3)設定LD_PRELOAD環境變數
seed@VM:~$ export LD_PRELOAD=./libmylib.so.1.0.1
(4)編譯myprog.c檔案,并放在與libmylib.so.1.0.1相同目錄下
/* myprog.c */
int main()
{
sleep(1);
return 0;
}

3. Step 2:在以下四個不同場景中運行myprog可執行檔案
(1)普通用戶下執行myprog
會執行我們設定的庫中的sleep函式,輸出字串,而不會執行原來的sleep函式讓它sleep 1秒,

(2)將myprog設定為Set-UID根程式,在普通用戶下執行
正常執行程式,sleep 1秒,然后退出程式,

(3)將myprog設定為Set-UID根程式,并在root下設定LD_PRELOAD環境變數并運行,
首先我們需要登入root賬戶,并設定環境變數
① 以root用戶運行
會執行我們設定的庫中的sleep函式,而不會執行原來的sleep函式讓它sleep 1秒,

② 以普通用戶運行
退出root賬戶,執行myprog程式,
發現正常執行程式,sleep 1秒,然后退出程式,

(4)將myprog設定為Set-UID user1程式,并在seed用戶下設定環境變數,
①在root用戶下,建立新的用戶user1

②設定myprog和環境變數

可見myprog已經設定為Set-UID user1程式,且在seed用戶下設定了環境變數,
③在seed用戶下運行Set-UID程式
發現正常執行程式,sleep 1秒,然后退出程式,

4. Step 3:設計實驗并解釋上述現象
(1)猜想:myprog行程會從用戶行程中繼承環境變數,但是由于動態聯結器的一些保護機制,不會繼承LD_PRELOAD環境變數,
(2)設計實驗:分別在上述四種場景中,將用戶行程和子行程的環境變數輸出為檔案,并進行對比,找出不同(重點關注LD_PRELOAD),在整個實驗程序中,seed用戶下始終設定LD_PRELOAD環境變數,
(3)實驗代碼:
命名為test.c,編譯為test可執行檔案
seed@VM:~$ vi test.c
seed@VM:~$ gcc test.c -o test
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
void printenv()
{
int i = 0;
while (environ[i] != NULL)
{
printf("%s\n", environ[i]);
i++;
}
}
void main()
{
pid_t childPid;
switch(childPid = fork())
{
case 0: /* child process */
printenv();
exit(0);
default:
//printenv(); /* parent process */
exit(0);
}
}
(4)實驗程序
① 場景一:普通用戶(seed)下執行test程式,seed用戶下修改環境變數

這里可見test程式與用戶行程的環境變數一致,然后分別查看二者的環境變數中是否有LD_PRELOAD變數,
用戶行程的環境變數:
![]()
test中子行程的環境變數:
![]()
結論:場景一下,子行程繼承了用戶行程的LD_PRELOAD環境變數,
② 場景二:test為Set-UID 根程式,在seed用戶下執行

將test設定為Set-UID根程式,并在seed用戶下執行,此時輸出子行程的環境變數;將test設定為普通程式,在seed用戶下執行,此時輸出父行程的環境變數,通過diff命令發現二者在LD*環境變數上有差異,當我們打開父行程的環境變數可以發現LD*環境變數,而在子行程的環境變數中找不到,下圖為父行程中找到的:
![]()
結論:場景二中,子行程并沒有繼承父行程的LD*環境變數,
③ 場景三:test為Set-UID 根程式,并在Root用戶下設定了環境變數,分別在root用戶和seed用戶運行,
Root用戶下運行:

發現此時二者的環境變數一致,分別查看二者的環境變數:
父行程和子行程均為:
![]()
Seed用戶下運行:

此時,二者環境不一致,分別查看二者的環境變數,當我們打開父行程的環境變數可以發現LD*環境變數,而在子行程的環境變數中找不到,下圖為父行程中找到的:
![]()
結論:場景三中,若在root用戶下運行,子行程會繼承父行程的LD_PRELOAD環境變數;在seed用戶下運行,子行程不會繼承父行程的LD_PRELOAD環境變數,
④ 場景四:test為Set-UID user1程式,在seed用戶下執行

此時,二者環境不一致,分別查看二者的環境變數,當我們打開父行程的環境變數可以發現LD*環境變數,而在子行程的環境變數中找不到,下圖為父行程中找到的:
![]()
結論:場景四中子行程并沒有繼承父行程的LD_PRELOAD環境變數
②主要原因:動態聯結器的保護機制,
當運行行程的真實用戶ID與程式的擁有者的用戶ID不一致時,行程會忽略掉父行程的LD_PRELOAD環境變數;若ID一致,則子行程會繼承此時運行行程的真實用戶下的LD_PRELOAD環境變數,并加入共享庫,通俗的來說:只有用戶自己創建的程式自己去運行,才會使用LD_PRELOAD環境變數,多載sleep函式,否則的話會忽略LD_PRELOAD環境變數,不會多載sleep函式,
③解釋現象
場景(1):普通用戶(seed)下執行myprog程式,此時myprog程式的擁有者是seed,而且在seed用戶下設定了環境變數,所以真實用戶ID與擁有者用戶ID一致,子行程會繼承seed用戶下的LD*環境變數,并加入共享庫,執行設定的sleep函式,
場景(2):myprog為Set-UID 根程式,在seed用戶下執行,ID不一致,所以動態聯結器會忽略LD_PRELOAD環境變數,子行程不能繼承seed用戶下的LD*環境變數,所以正常執行sleep函式,
場景(3):myprog為Set-UID 根程式,并在Root用戶下設定了環境變數,所以在root用戶下運行myprog,ID一致,所以子行程會繼承root用戶下的LD_PRELOAD環境變數,并加入共享庫,執行設定的sleep函式;而在seed用戶下運行,ID則會不匹配,環境變數被忽略,從而正常執行sleep函式,
場景(4):myprog為Set-UID user1程式,在seed用戶下執行也會遇到ID不一致,所以忽略環境變數,正常執行sleep函式,
(八)Task 8:使用system與execve呼叫外部程式
1. 實驗目的與原理:
目的:system()與execve()在Set-UID根程式下執行,觀察其對系統檔案的危害行為
原理:system()與execve()在執行時的區別
示例代碼:
#include<unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *v[3];
char *command;
if(argc < 2)
{
printf("Please type a file name.\n");
return 1;
}
v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = NULL;
command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
sprintf(command, "%s %s", v[0], v[1]);
// Use only one of the followings.
system(command);
// execve(v[0], v, NULL);
return 0 ;
}
觀察代碼,當我們在命令列執行此程式時,會輸出command命令,command命令由v[0] + v[1]組成也就是進行了字串的拼接,而v[1]是通過我們在命令列的輸入來決定的,所以我們可以控制我們的輸入來達到對命令的控制,
Argc 統計發送給main函式的引數個數,Argv為字串陣列,V[1]是輸入的第一個字串,
2. 實驗說明
Bob擁有的權限:能夠讀取所有檔案,但是不應該能夠修改任何檔案
system() 實際呼叫/bin/sh,然后在 shell 環境中運行該命令:
seed@VM:~$ sudo rm /bin/sh
seed@VM:~$ sudo ln -s /bin/zsh /bin/sh
3. Step 1:使用system()呼叫外部程式,洗掉根用戶擁有的檔案
(1)編譯并設定為Set-UID根程式

(2)在root用戶下創建一個home1目錄,在該目錄下創建一個名為為hello.c的檔案,并在seed下洗掉
一開始沒有創建home1目錄,直接創建了hello.c檔案:

這里我們發現在seed用戶下竟然可以洗掉寫保護的檔案,經過查資料,發現還需要看上一級目錄的權限,所以先在root下創建一個home1目錄,在此目錄下創建hello.c檔案,然后再seed下洗掉hello.c,此時發現權限不夠:

(3)執行task8程式,洗掉hello.c檔案(不可寫)
根據對代碼的說明,我們在命令列輸入時需要在task8后面加上引數,然后傳遞給argv,此時只需讓argv[1]包含我們想要執行的洗掉命令即可,因為command為v[0]+v[1],也就是/bin/cat v[1],因為argv為字串陣列,所以引數應該輸入字串,想要同時在命令列執行兩個命令,可以用”;”來進行分割,所以我們在seed用戶下執行以下命令:
seed@VM:~$ ./task8 "home1/hello.c;rm home1/hello.c"
那么此時執行的命令為:
/bin/cat home1/hello.c;rm home1/hello.c
第一條命令顯示hello.c檔案內容,第二條命令洗掉hello.c檔案
結果:顯示檔案內容,并且成功洗掉不可寫檔案,
System注入了一條額外的命令(資料與代碼混合)

4. Step 2:使用execve()呼叫外部程式
(1)修改task8.c檔案,重新編譯,設定為Set-UID根程式
將system注釋掉,execve反注釋掉,

編譯發現少了頭檔案unistd.h

加上頭檔案重新編譯并設定為Set-UID根程式

(2)在root用戶下創建一個home1目錄,在該目錄下創建一個名為為hello.c的檔案,并在seed下洗掉,(同Step1中的(2))
(3)執行task8程式,洗掉hello.c檔案(不可寫)
重復上述的輸入,結果如下:

5. 解釋
使用system()可以成功洗掉不可寫檔案,是因為system會創建一個子行程,然后子行程會呼叫一個新的shell程式,而且因為task8是一個Set-UID根程式,所以在執行時會以root權限執行洗掉檔案的命令,可以成功洗掉,
使用execve()不可以成功洗掉不可寫檔案,因為execve會執行一個新程式,而不會呼叫新的shell程式,所以將我們輸入的引數僅僅當成一個字串,不會執行命令,所以不能洗掉不可寫檔案,
(九)Task 9:權限泄露
1.實驗目的與原理
目的:利用權限泄露,對一個沒有寫權限的root檔案寫入字串
原理:Set-UID程式會在不需要root權限時釋放root權限,但是有時候僅僅是將程式的擁有者變為非root用戶,行程仍然擁有root權限下的一些功能,執行一些root權限下才能進行的操作
示例代碼:
#include<sys/types.h>
#include<unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
void main()
{
int fd;
/* Assume that /etc/zzz is an important system file,
* and it is owned by root with permission 0644.
* Before running this program, you should creat
* the file /etc/zzz first. */
fd = open("/home/seed/etc/zzz", O_RDWR | O_APPEND);
if (fd == -1)
{
printf("Cannot open /home/seed/etc/zzz\n");
exit(0);
} /* Simulate the tasks conducted by the program */
sleep(1);
/* After the task, the root privileges are no longer needed,
it’s time to relinquish the root privileges permanently. */
setuid(getuid()); /* getuid() returns the real uid */
if (fork())
{ /* In the parent process */
close (fd);
exit(0);
}
else
{
write (fd, "Malicious Data\n", 15);
close (fd);
}
}
代碼解釋:
根據注釋,我們首先需要創建一個權限為0644且為根用戶所有的檔案,然后模擬使用root權限;任務完成后,不再需要根權限,通過setuid(getuid())放棄根權限,恢復行程的擁有者為真實執行該行程的用戶ID;然后通過fork創建一個子行程,父行程關閉打開的檔案,子行程向創建的新檔案中寫入字串,
2. root下創建一個etc檔案夾,檔案夾內創建zzz檔案,并設定其權限為0644

zzz檔案內容:
![]()
3. 編譯示例代碼,并設定為Set-UID根程式,在seed下運行
修改示例代碼:將檔案設定為我們創建的zzz檔案:
![]()
編譯出現多個warning,添加頭檔案:

然后重新編譯并設定為Set-UID根程式:

在seed用戶下運行:

我們發現字串成功寫入zzz檔案,而zzz檔案是root下的只讀檔案,
![]()
4. 現象解釋
運行Set-UID程式時,行程暫時獲得root權限,打開zzz檔案時,獲得了root權限下的讀寫檔案、向檔案中添加內容的權限,當使用setuid()釋放root權限時,沒有釋放行程已經獲得的特權功能—讀寫檔案、向檔案中添加內容;導致僅僅是將程式的擁有者降為非root用戶,然后行程還擁有root權限下的讀寫檔案、向檔案中添加內容的功能,所以造成了權限泄露的問題,當執行fork創建子行程后在子行程中回傳0,父行程中回傳子行程的PID,父行程打開檔案etc/zzz后,子行程不是直接修改檔案/etc/zzz,而是修改了緩沖區中的文字,即在setuid之前,zzz檔案就已經被打開了,只要將陳述句setuid(getuid())移至呼叫open函式之前,就可以避免,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/310501.html
標籤:其他
