來源:《Computer Security》A Hands-on Approach — Wenliang Du
Chapter02 Attacks Through Environment Variables
書上的內容是很好的。這裏僅僅做個學習筆記。
我把這些整體放在一章。如果對要點進行詳細說明,可能得好幾篇文章才能搞定,比較花時間。而現在網上的文章已經很多,比較舊的知識點應該大體都有不錯的整理。故而,簡略寫之。
當然,還有自身能力的原因。比如動態庫那裏。書上的動態庫那樣寫有它的原因。但我比較喜歡的庫寫法還是 頭文件+編譯參數 來實現。但是我有寫過幾次庫呢?
文章目錄
摘要&&總結
這章是對上一章的補充。
講述環境變量對特權程序的影響。
一般是普通用戶,執行特權程序。而特權程序一般是非特權程序(eg:bash)的子進程。
這導致特權程序的環境變量來自普通用戶。這具有一定的危險性。
從安全的角度,來看本章,並沒有很多知識點。
但是本章提供的背景知識還是不錯的。
要點:
- 環境變量、shell變量、環境變量與shell變量的關係、環境變量在進程中的位置、shell變量對子進程的影響
- 靜態庫、動態庫、增加自定義的庫
getenv()
與secure_getenv()
- Set-UID方法和Service方法
- built-in命令
環境變量對特權程序的影響
在開始本章之前,我們得先熱身下:linux中環境變量的設置。
我隨意從網上搜了兩篇文章。
linux 環境變量設置(臨時 + 永久)
Linux /etc/profile文件詳解
環境變量與shell變量
Linux 環境變量&&進程 ⭐️ (這篇鏈接文章是很好的。)
(記得當年這內容相關的討論課是我講滴。但是當時我肯定是不懂的)
我這裏僅僅做下總結性的描述,不用實驗進行驗證。詳細內容見書上+鏈接文章。
(1) 環境變量是name-value pairs ,存儲在進程的內存中。
(2) 在程序中,存儲位置是**environ
全局變量。可以通過putenv()
、unsetenv()
、setenv()
、 getenv()
進行環境的增刪該查。
//打印所有環境變量
//strings /proc/$$/environ
#include <stdio.h>
#include <unistd.h>
extern char** environ;
void main(int argc,char *agrv[],char *envp[]){
int i=0;
while (environ[i]!=NULL){
printf("%s\n",envp[i]);
i++;
}
}
/*******************************************/
//環境變量&&execve()
/*我們現在要構建一個函數
* ./switch_show_env x
*
* 當參數是1:不傳遞環境變量
* 當參數是2:傳遞自定義的環境變量
* 當參數是3:傳遞所有的環境變量
*
* 之後打印所有變量
* 很漂亮的程序,演示execve的使用,展示了三種不同情況下的環境變量的傳遞
*/
#include <stdio.h>
#include <unistd.h>
extern char **environ;
void main(int argc,char *argv[],char *envp[]){
char *v[2];
char *new_envp[3];
if(argc<2)
return;
v[0] = "/usr/bin/env";
v[1] = NULL;
new_envp[0] = "name=dacao";
new_envp[1] = "Tel=173";
new_envp[2] = NULL;
switch (argv[1][0])
{
case '1':
execve(v[0],v,NULL);
break;
case '2':
execve(v[0],v,new_envp);
break;
case '3':
execve(v[0],v,environ);
default:
printf("enter the correct argv");
break;
}
}
(3)環境變量在內存給中的位置
(4)shell變量是由shell程序來維護。
(5)環境變量的設置,可以見之前的參考文章。
(6)shell程序(eg:bash)會拷貝環境變量到shell變量中。
(7)書上說,shell變量,也能增添到環境變量中。我嘗試了下:export並不能增添進程自身的環境變量,但可以增添環境變量到子進程。
$ export Tong=haha
$ echo Tong
haha
$ strings /proc/$$/environ | grep Tong
#(進程自身)輸出空
$ env | grep Tong
Tong=haha
#env不是內置命令,所以作爲(bash)的子進程。子進程中,有增添的環境變量
(8)子進程的環境變量
(9) 當環境變量和predefined shell 變量 名稱相同時,會發生什麼?
比如說:在~/.bashrc中,定義export BASHPID=1000 ;而bash啓動的時候,會隨機有個pid。
首先我們有觀點:
- 後面的會覆蓋前面的。或者說是後面的起作用。
- BASHPID被修改對程序暫時沒有什麼影響。就像我們修改PWD之後,並不代表我們的工作目錄是指被改動。這些變量記錄“現象”,但本質沒有改動。
好,我們來驗證下。
#打開一個bash
#查看當前的pid
$ echo $$ (echo $BASHPID)
15812
#修改 ~/.bashrc,在末尾增加 export BASHPID=1000
$ vim ~/.bashrc
$ cat ~/.bashrc | grep BASHPID
export BASHPID=1000
$ source ~/.bashrc
$ echo $$ (echo $BASHPID)
15812
#關閉shell重開
echo $$ (echo $BASHPID)
不是1000
#直接修改BASHPID,被自動修正
$ BASHPID=1000
$ echo $BASHPID
20040
實驗至此結束。實驗結果未知。
靜態庫&&動態庫
以前,編譯程序的時候,報錯缺少庫。缺少的庫名+這個庫默認的查找路徑+增添自定義的庫路徑
這一套下來,大體能把問題解決了。參考:
靜態程序鏈接的時候,把庫中對應的函數,拷貝過去。這面臨的兩個問題是:程序加載進入內存,比較費內存;當庫更新的時候,已編譯的程序沒有更新。
動態庫,在鏈接的時候,.interp記錄動態鏈接器(ld)的位置生成可執行文件;可執行文件被加載進內存;在.interp中查找連接器的位置;通過鏈接器找到共享庫函數;進入main函數;
特權程序鏈接的時候,我目前感覺沒有什麼風險。因爲:
在特權程序中,當使用到動態鏈接的時候,與鏈接有關的變量的使用,必須要求用戶ID和有效ID相同。
感覺這挺安全的。
其他
getenv() 和 secure_getenv()
我們在特權程序中,一般不會用getenv(),因爲獲取的環境變量來自普通用戶,這並不安全。
另一個要注意的是,我們可能會使用execve(),來喚醒另一個程序。
這被喚醒的程序,可能是普通程序。這個普通程序中,也不要用getenv()。
**可以用secure_getenv(),代替。**當檢測到有效ID和實際ID不同的時候,其返回NULL。即:
特權程序中,無法正常使用secure_getenv()。
如果要使用secure_getenv(),在程序的開頭加上#define _GNU_SOURCE
(原因見man getenv)
built-in命令
參考:
Shell 的內置(builtin)命令是什麼,常常傻傻分不清
另外一種 Shell 支持的命令即本文要重點總結的 builtin(“內置”) 命令,builtin 命令是 shell (譬如 bash)裏自帶的命令,由於其作爲 shell 程序(進程)的一部分常駐在內存中,所以和一個普通命令相比起來, builtin 命令的執行速度要快得多。具體的原因很簡單,就是因爲在 Shell 中執行普通命令即運行程序,要先
fork()
,然後是exec()
,經歷創建 子 Shell 進程以及從磁盤上調入程序覆蓋原進程的完整過程,而調用一個 builtin 命令本質上只是執行 bash 進程中的一個常駐內存的函數,其速度絕對不可同日而語。
由於各種 Shell 所支持的 builtin 命令各不相同,對於有些常用的命令,爲避免用戶當前所使用的 Shell 不支持,系統會提供同名的程序文件。譬如
echo
,既是 bash 的 builtin 命令也是一個獨立的命令程序。根據 bash 中執行命令的優先級,對於同名的命令,內置命令會優先被執行,所以當我們在 bash 中直接輸入echo
命令時執行的是 bash 的 buildin 命令,如果要執行獨立的命令程序echo
,則需要輸入全路徑/bin/echo
。
Set-UID方法和Service方法
Set-UID方法,沒有Service方法安全。但Set-UID方法,節省內存,不需要常駐的後臺服務。
參考文章
Linux 環境變量&&進程 —> 未讀完