版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第12章安全編程12.1緩沖區(qū)溢出(BufferOverflow)12.2格式化字符串(FormatString)12.3條件競爭12.4臨時(shí)文件12.5動(dòng)態(tài)內(nèi)存分配和釋放
習(xí)題12.1緩沖區(qū)溢出(BufferOverflow)所謂的緩沖區(qū)溢出,是指一種系統(tǒng)攻擊的手段,它是通過往程序的緩沖區(qū)寫超出其長度的內(nèi)容,造成緩沖區(qū)的溢出,從而破壞程序的堆棧,使程序轉(zhuǎn)而執(zhí)行其它指令,以達(dá)到攻擊的目的的。美國俄勒岡州科學(xué)與技術(shù)研究生院(OGI)最近公布的一篇論文[1]稱:“過去10年中,緩沖區(qū)溢出一直是計(jì)算機(jī)的最大安全隱患。由于這種攻擊使得任何人都能夠完全控制某一臺主機(jī),因此構(gòu)成了對計(jì)算機(jī)安全的最大威脅?!本彌_區(qū)溢出問題大部分是由于在程序中沒有進(jìn)行適當(dāng)?shù)倪吔鐧z查造成的。安全問題分析家認(rèn)為,解決緩沖區(qū)溢出問題的第一步是,人們必須更加小心地進(jìn)行計(jì)算機(jī)的編程。程序員只要增加能夠處理過長字符串的指令,就能夠防止對他們的產(chǎn)品的攻擊。安全分析家AlanPaller說:“造成問題的原因是程序員的粗心大意。你編寫了一個(gè)程序,讓他人輸入信息,為他們提供了一定數(shù)量的字符空間,但是你不檢查程序能不能接受更多的字符。像這樣的程序員是不稱職的,這也是產(chǎn)生問題的根源。他的錯(cuò)誤將要我們花費(fèi)很大的精力來加以解決?!?2.1.1背景知識為了更好的了解緩沖區(qū)溢出的機(jī)理,我們先來介紹一些背景知識。首先看一看執(zhí)行狀態(tài)下的C語言程序結(jié)構(gòu)和處理器處理機(jī)器代碼的情況,如圖12-1所示。圖中:堆棧:保存調(diào)用程序的地址信息、所需要的變量、其它幀指針等;堆:動(dòng)態(tài)內(nèi)存分配區(qū);
BSS:符號塊起始地址,未初始化數(shù)據(jù)段(函數(shù)之外),即“intfoo;floatbaz;”;數(shù)據(jù):初始化數(shù)據(jù)(函數(shù)之外),即“intshit=9,charhead[]=''ugh'';”;
文本:機(jī)器指令=操作碼+操作數(shù)。圖12-1C語言程序結(jié)構(gòu)一個(gè)應(yīng)用程序在運(yùn)行時(shí),它在內(nèi)存中的映像可以分為三個(gè)部分:代碼段,數(shù)據(jù)段和堆棧段。代碼段對應(yīng)于運(yùn)行文件中的TextSection(圖12-1),其中包括運(yùn)行代碼和只讀數(shù)據(jù)。這個(gè)段在內(nèi)存中一般被標(biāo)記為只讀,任何企圖修改這個(gè)段中數(shù)據(jù)的指令將引發(fā)一個(gè)SegmentationViolation錯(cuò)誤。數(shù)據(jù)段對應(yīng)于運(yùn)行文件中的DataSection和BSSSection,其中存放的是各種數(shù)據(jù)(經(jīng)過初始化的和未經(jīng)初始化的)和靜態(tài)變量?,F(xiàn)在讓我們來看一看指令指針(InstructionPointer)(經(jīng)常被稱為程序計(jì)數(shù)器)及其寄存器。
IP寄存器指向下一條被執(zhí)行指令的地址或現(xiàn)在正在被執(zhí)行指令的地址(依賴于設(shè)計(jì)者如何實(shí)現(xiàn))。一般代碼是不能夠直接訪問IP值的。在每條指令被執(zhí)行之后,IP值自動(dòng)增加一個(gè)值以指向下一條要執(zhí)行的指令地址。當(dāng)你要調(diào)用子程序時(shí),系統(tǒng)就需要知道下一條指令的地址以及如何返回原始調(diào)用處。調(diào)用指令通常規(guī)定了要往IP所加的值,并把它壓入堆棧,而調(diào)用函數(shù)中的返回指令會把堆棧值彈出給IP以恢復(fù)調(diào)用后下一條指令的執(zhí)行。圖12-1中的堆棧就是存儲和取出返回地址信息的地方。堆棧的特點(diǎn)是其生長方向與內(nèi)存的生長方向相反,即堆棧的底端是內(nèi)存的高端,而堆棧的高端是內(nèi)存的低端?,F(xiàn)在大部分程序員都用高級語言進(jìn)行模塊化編程。在這些應(yīng)用程序中,不可避免地會出現(xiàn)各種函數(shù)調(diào)用,比如調(diào)用C運(yùn)行庫、Win32API等等。這些調(diào)用大部分都被編譯器編譯為Call語句。當(dāng)CPU在執(zhí)行這條指令時(shí),除了將IP變?yōu)檎{(diào)用函數(shù)的入口點(diǎn)以外,還要將調(diào)用后的返回地址壓入堆棧。這些函數(shù)調(diào)用往往還帶有不同數(shù)量的入口參數(shù)和局部變量,在這種情況下,編譯器往往會生成一些指令將這些數(shù)據(jù)也存入堆棧(有些也可通過寄存器傳遞)。我們稱由于一個(gè)函數(shù)調(diào)用所導(dǎo)致的需要在堆棧中存放的數(shù)據(jù)和返回地址為一個(gè)堆棧幀(StackFrame)。下面我們通過一個(gè)簡單的例子來分析棧幀的結(jié)構(gòu)。voidproc(inti)/*被調(diào)用函數(shù)*/{
intlocal;/*本地變量*/
local=i;}voidmain()/*主函數(shù)*/{
proc(1);/*調(diào)用proc函數(shù),帶一個(gè)參數(shù)*/}這段代碼經(jīng)過編譯器編譯后為(以PC為例):main:PUSH1 /*入口參數(shù)壓棧*/
CALLproc /*主函數(shù)調(diào)用子函數(shù)處*/proc:PUSH EBP/*子函數(shù)入口處,將基址寄存器壓棧*/
MOV EBP,ESPSUB ESP,4/*ESP指針減4,為局部變量分配空間*/
MOV EAX,[EBP+08] /*EBP+08即訪問參數(shù)l*/MOV [EBP-4],EAX /*EBP?4即訪問局部變量local*/ADD ESP,4 /*收回局部變量空間*/
POP EBP /*恢復(fù)EBP值*/
RET 4 /*調(diào)用返回*/圖12-2棧幀結(jié)構(gòu)不難看出,這個(gè)程序在執(zhí)行proc過程時(shí),棧幀的結(jié)構(gòu)如圖12-2所示。由此可以看出,當(dāng)程序中發(fā)生函數(shù)調(diào)用時(shí),計(jì)算機(jī)依次完成如下操作:首先把入口參數(shù)壓入堆棧;然后保存指令寄存器(EIP)中的內(nèi)容作為返回地址(RET);再把基址寄存器(EBP)壓入堆棧;隨后將當(dāng)前的棧指針(ESP)拷貝到EBP做為新的基地址;最后為本地變量留出一定空間,同時(shí)將ESP減去適當(dāng)?shù)臄?shù)值。因此,棧幀的一般結(jié)構(gòu)如圖12-3所示。圖12-3棧幀的一般結(jié)構(gòu)12.1.2緩沖區(qū)溢出基本原理我們先舉一個(gè)例子說明什么是緩沖區(qū)溢出:voidfunction(charstr) /*函數(shù)定義*/{
charbuffer[16];strcpy(buffer,str);}
voidmain() /*主函數(shù)*/{
charlarge_string[256];inti;
for(i=0;i<255;i++)large_string[i]='A';
function(large_string); /*調(diào)用函數(shù)function*/}
首先我們看一下未執(zhí)行strcpy時(shí)(已經(jīng)調(diào)用函數(shù)function)堆棧中的情況(如圖12-4所示)。圖12-4調(diào)用function后的堆棧情況當(dāng)執(zhí)行strcpy時(shí),程序?qū)?56字節(jié)的'A'(0x41)拷入buffer中,然而buffer只能容納16字節(jié)。由于C語言并不進(jìn)行邊界檢查,所以結(jié)果是buffer后面的240字節(jié)的內(nèi)容也被覆蓋掉了,這其中自然也包括EBP、RET地址、large_string地址。因?yàn)榇藭r(shí)RET地址變成了0x41414141h,所以當(dāng)過程結(jié)束返回時(shí),它將返回到0x41414141h地址處繼續(xù)執(zhí)行下一條指令。但由于這個(gè)地址并不在程序?qū)嶋H使用的虛存空間范圍內(nèi),所以系統(tǒng)就報(bào)“SegmentationViolation”錯(cuò)誤。這就是所謂的緩沖區(qū)溢出。12.1.3緩沖區(qū)溢出攻擊方式一般而言,有以下幾種緩沖區(qū)溢出攻擊的方式:(1)如上舉例所示,我們僅僅通過向緩沖區(qū)中寫入任意超長的字符就可以導(dǎo)致程序崩潰。(2)攻擊者可用任意數(shù)據(jù)覆蓋堆棧中變量的內(nèi)容。安全漏洞的一個(gè)經(jīng)典例子是基于口令的認(rèn)證:首先從本地?cái)?shù)據(jù)庫中讀取口令并存儲在本地變量中,然后用戶輸入口令,程序比較這兩個(gè)字符串。關(guān)鍵代碼如下:charorigPassword[12]="Secret\0";charuserPassword[12];
gets(userPassword); /*讀取用戶輸入的口令*/
if(strncmp(origPassword,userPassword,12)!=0){printf("Passworddoesn'tmatch!\n");exit(?1);}
/*口令認(rèn)證通過時(shí)允許用戶訪問*/現(xiàn)在用戶的輸入超過12個(gè)字符(32位對準(zhǔn))就會覆蓋數(shù)組origPassword[]的內(nèi)容。因此如果用戶輸入的是opensesame!!opensesame!!,那么userPassword[]和origPassword[]的內(nèi)容就是同一個(gè)字符串opensesame!!,從而比較結(jié)果為二者相等。(3)覆蓋堆棧中保存的寄存器。通過輸入超長的字符從而覆蓋指令指針I(yè)P,攻擊者可以利用函數(shù)結(jié)尾的RET來執(zhí)行程序中的任意程序代碼。一般而言,不是利用程序本身的代碼,而是植入攻擊者自己的機(jī)器代碼(一般稱之為Shellcode,即外殼代碼)。要做到這一點(diǎn),只需把機(jī)器代碼寫到變量中,然后再拷貝到堆棧中,同時(shí)把保存的IP地址設(shè)成攻擊代碼的開始地址即可。如果變量長度不足以保存機(jī)器代碼,就要把機(jī)器代碼存儲在程序環(huán)境中——堆或任意用戶可訪問的地址空間。當(dāng)函數(shù)執(zhí)行完畢時(shí),RET從堆棧中獲得IP的值(已被攻擊者設(shè)置)并寫入CPU的IP寄存器中,計(jì)算機(jī)就忠實(shí)的執(zhí)行攻擊代碼序列了。(4)覆蓋函數(shù)指針以執(zhí)行第三方代碼。原理是一樣的,攻擊者把他的機(jī)器代碼Shellcode放在一全局或本地變量或編程環(huán)境中,并使函數(shù)指針指向這段程序代碼。當(dāng)用函數(shù)指針調(diào)用函數(shù)時(shí),執(zhí)行的將不是函數(shù)代碼而是攻擊代碼。12.1.4有關(guān)Shellcode
一般而言,攻擊者利用緩沖區(qū)溢出漏洞并不是僅僅想使程序崩潰,而是想通過這種攻擊做更多的事。如通過緩沖區(qū)溢出提升權(quán)限,從而獲得對系統(tǒng)更多的訪問和控制權(quán)。這些目的的實(shí)現(xiàn)就是由所謂的Shellcode來完成的。12.1.5安全建議1.編寫正確的代碼前面提到過,解決緩沖區(qū)溢出問題的第一步是,人們必須更加小心地進(jìn)行計(jì)算機(jī)的編程。程序員只要增加能夠處理過長字符串的指令,就能夠防止對自己產(chǎn)品的攻擊。下面給出了一些容易導(dǎo)致緩沖區(qū)溢出的系統(tǒng)調(diào)用,并給出了正確的、安全的使用方法[2]。1)gets(char*s)
此函數(shù)的功能是從標(biāo)準(zhǔn)輸入讀入數(shù)據(jù)到一靜態(tài)緩沖區(qū)中。在這種類型中,最有名的bug是MorrisInternetWorm在fingerd中開發(fā)的,該漏洞可用來通過網(wǎng)絡(luò)在計(jì)算機(jī)上執(zhí)行命令。錯(cuò)誤的使用方式:charHopeItFits[12];
while(gets(HopeItFits)!=NULL){puts(HopeItFits);memset(HopeItFits,0,sizeof(HopeItFits));}正確的使用方式:使用fgets(char*s,intsize,FILE*stream),通過嚴(yán)格規(guī)定輸入數(shù)據(jù)長度從而安全的讀取數(shù)據(jù)。通過使用sizeof(HopeItFits)等指定數(shù)據(jù)長度,如12字節(jié),fgets()將讀入1~12個(gè)字節(jié)并在最后加一個(gè)NULL字符。charHopeItFits[12];
while(fgets(HopeItFits,sizeof(HopeItFits),stdin)!=NULL){puts(HopeItFits);memset(HopeItFits,0,sizeof(HopeItFits));}2)*sprintf(char*str,constchar*format,...)*sprintf()不作邊界檢查,邊界可由格式標(biāo)簽或由snprintf(char*str,size_tsize,constchar*format,...)、vsnprintf(char*str,size_tsize,constchar*format,va_listap)函數(shù)(二者把目標(biāo)緩沖區(qū)大小作為第二個(gè)參數(shù))定義。錯(cuò)誤的使用方式:
charHopeItFits[12];charBigBadBuffer[120];while(scanf("%.120s",BigBadBuffer)!=NULL){BigBadBuffer[111]='\0';sprintf(HopeItFits,"%s",BigBadBuffer);
memset(HopeItFits,0,sizeof(HopeItFits));memset(BigBadBuffers,0,sizeof(BigBadBuffers));}正確的使用方式:*格式標(biāo)簽
charHopeItFits[12];charBigBadBuffer[120];
while(scanf("%.120s",BigBadBuffer)!=NULL){BigBadBuffer[111]='\0';sprintf(HopeItFits,"%.11s",BigBadBuffer);
memset(HopeItFits,0,sizeof(HopeItFits));memset(BigBadBuffers,0,sizeof(BigBadBuffers));}
*snprintf(char*buf,size_tcount,constchar*format,...)
charHopeItFits[12];charBigBadBuffer[120];
while(scanf("%.120s",BigBadBuffer)!=NULL){
BigBadBuffer[111]='\0';snprintf(HopeItFits,sizeof(HopeItFits),"%s",BigBadBuffer);
memset(HopeItFits,0,sizeof(HopeItFits));memset(BigBadBuffers,0,sizeof(BigBadBuffers));}3)strncpy()/strncat()的使用不當(dāng)許多程序員使用strncat(char*dest,constchar*src,size_tn)或strncpy(char*dest,constchar*src,size_tn)后認(rèn)為就安全了,然而他們總是忘記strncpy(3)的特性(最后都會增加一個(gè)'\0'字符),結(jié)果會導(dǎo)致單字節(jié)緩沖區(qū)溢出,這將導(dǎo)致段錯(cuò)誤。錯(cuò)誤的使用方式:
charHopeItFits[12];charBigBadBuffer[120];while(scanf("%.120s",BigBadBuffer)!=NULL){BigBadBuffer[111]='\0';strncpy(HopeItFits,BigBadBuffer,sizeof(HopeItFits));
memset(HopeItFits,0,sizeof(HopeItFits));memset(BigBadBuffers,0,sizeof(BigBadBuffers));}正確的使用方式:
charHopeItFits[12];charBigBadBuffer[120];
while(scanf("%.120s",BigBadBuffer)!=NULL){
BigBadBuffer[111]='\0';strncpy(HopeItFits,BigBadBuffer,sizeof(HopeItFits)?1);
HopeItFits[sizeof(HopeItFits)?1]='\0';memset(HopeItFits,0,sizeof(HopeItFits));memset(BigBadBuffers,0,sizeof(BigBadBuffers));}4)忽略緩沖區(qū)長度的循環(huán)讀一般情況是循環(huán)讀用戶輸入直到讀入一指定字符(如換行符'\n')。錯(cuò)誤的使用方式:
intByte,i;charHopeItFits[12];i=0;while((Byte=getc(stdin))!='\n'){HopeItFits[i]=Byte;i++;}為了實(shí)現(xiàn)緩沖溢出,攻擊者要提供不包含換行符的超過12個(gè)字節(jié)的字符。正確的使用方式:
intByte,i;charHopeItFits[12];
i=0;while((Byte=getc(stdin))!='\n')
{
HopeItFits[i]=Byte;
if(++i>=sizeof(HopeItFits)){fprintf(stderr,"Toomuchdataread!\n");return(?1);}}
當(dāng)然,這也可以用strncat()來解決。5)其它還有很多函數(shù)不進(jìn)行邊界檢查,包括scanf(3)、strcpy(3)/strcat(3)、getwd(3)等,更多描述請參閱參考資料[2]。2.使用Libsafe
針對這些易受緩沖區(qū)溢出攻擊的Libc函數(shù),ArashBaratloo、TimothyTsai和NavjotSingh(朗訊技術(shù)公司)開發(fā)出了封裝這些庫函數(shù)的Libsafe[4]。Libsafe是一個(gè)簡單的動(dòng)態(tài)載入庫。庫中的函數(shù)實(shí)現(xiàn)了原有功能,但在某種程度上可以確保任一緩沖區(qū)溢出都被控制在現(xiàn)有堆棧幀之內(nèi)。安裝Libsafe后,Libsafe解析那些不安全的Libc庫函數(shù),并用Libsafe中實(shí)現(xiàn)的安全函數(shù)代替,由于Libsafe實(shí)現(xiàn)邊界檢查,從而保證了代碼的安全。3.不可執(zhí)行的緩沖區(qū)技術(shù)根據(jù)上面介紹的緩沖區(qū)溢出原理我們知道,為了利用緩沖區(qū)溢出漏洞以達(dá)到攻擊的目的,往往需要向緩沖區(qū)中寫入可執(zhí)行代碼,因此,防止緩沖區(qū)溢出攻擊的一個(gè)有效方法就是通過使被攻擊程序的數(shù)據(jù)段地址空間不可執(zhí)行,從而使攻擊者不可能執(zhí)行植入到被攻擊程序輸入緩沖區(qū)中的代碼。這就是所謂的不可執(zhí)行的緩沖區(qū)技術(shù)。一些舊版本的操作系統(tǒng)就是這樣實(shí)現(xiàn)的,但是最近的UNIX和MSWindows系統(tǒng)為了實(shí)現(xiàn)好的性能和功能而允許在數(shù)據(jù)段中動(dòng)態(tài)的放入可執(zhí)行代碼。由于絕大部分情況下,合法程序并不需要在堆棧中存放可執(zhí)行代碼,因此完全可以讓操作系統(tǒng)使程序的堆棧段不可執(zhí)行。目前,Linux和Solaris為此發(fā)布了安全補(bǔ)丁。幾乎所有的合法程序都不會在堆棧中存放代碼,因此這種做法幾乎不產(chǎn)生任何兼容性問題,但在Linux中有兩個(gè)特例,其可執(zhí)行的代碼必須被放入堆棧中:(1)信號傳遞:Linux通過把傳遞信號的代碼放入進(jìn)程堆棧,然后引發(fā)中斷,跳轉(zhuǎn)到該代碼處執(zhí)行,從而實(shí)現(xiàn)向進(jìn)程發(fā)送UNIX信號。非執(zhí)行緩沖區(qū)的補(bǔ)丁在發(fā)送信號的時(shí)候是允許緩沖區(qū)可執(zhí)行的。(2)GCC的在線重用:研究發(fā)現(xiàn),GCC在堆棧區(qū)里放置了可執(zhí)行的代碼作為在線重用之用。不可執(zhí)行緩沖區(qū)技術(shù)可以有效地對付把代碼植入自動(dòng)變量的緩沖區(qū)溢出攻擊,而對于其它形式的攻擊則沒有效果。4.?dāng)?shù)組邊界檢查根據(jù)緩沖區(qū)溢出的基本原理可知,要實(shí)現(xiàn)緩沖區(qū)溢出就必須擾亂程序的流程,使程序不按既定的流程運(yùn)行。如果給局部變量分配的內(nèi)存空間沒被溢出,改變程序運(yùn)行狀態(tài)也就無從談起。為此,我們可以利用一些編譯器或工具對程序進(jìn)行數(shù)組邊界檢查,即當(dāng)對數(shù)組進(jìn)行讀寫時(shí)要確保對數(shù)組的操作是在正確的范圍內(nèi)進(jìn)行的。最直接的辦法就是檢查所有的對數(shù)組的操作。目前,CompaqC編譯器、RichardJones和PaulKelly開發(fā)的一個(gè)GCC的補(bǔ)丁以及Purify等能實(shí)現(xiàn)一定的數(shù)組邊界檢查功能。5.程序指針完整性檢查程序指針完整性檢查和邊界檢查有略微的不同。與防止程序指針被改變不同,程序指針完整性檢查在程序指針被引用之前檢測它是否有變。因此,即便一個(gè)攻擊者成功地改變了程序的指針,由于系統(tǒng)事先檢測到了指針的改變,因此這個(gè)指針將不會被使用。目前,程序指針完整性檢查大致有以下三個(gè)研究方向:1)手寫的堆棧監(jiān)測
Snarskii為FreeBSD開發(fā)了一套定制的,能通過監(jiān)測CPU堆棧來確定緩沖區(qū)溢出的libc。這個(gè)應(yīng)用完全用匯編語言手寫而成,而且只保護(hù)libc中的當(dāng)前有效記錄函數(shù)。這個(gè)實(shí)現(xiàn)能很好的防衛(wèi)基于libc庫函數(shù)的攻擊,但是不能防衛(wèi)其它方式的攻擊。2)StackGuard:提供完整性檢測的編譯器技術(shù)[5]
StackGuard通過不允許改動(dòng)活動(dòng)函數(shù)的返回地址RET來防止某些類型的緩沖區(qū)溢出攻擊。它可以通過兩種方式來實(shí)現(xiàn):(1)在函數(shù)返回前檢測返回地址RET的改動(dòng);(2)禁止對返回地址RET寫。函數(shù)返回前檢測RET的改動(dòng):在堆棧中函數(shù)返回地址后面存儲一些附加的字節(jié),當(dāng)函數(shù)返回時(shí),首先檢查這個(gè)附加的字節(jié)是否被改動(dòng)過。如果攻擊者企圖進(jìn)行緩沖區(qū)溢出攻擊,則他要覆蓋緩沖區(qū),修改附加字節(jié),因此會在函數(shù)返回前被檢測到。但是,如果攻擊者預(yù)見到這些附加字節(jié)的存在,并且能在溢出過程中同樣地制造它們,那么他就能成功地躲過StackGuard的檢測。通常,我們有如下的兩種方案對付這種欺騙:●終止符號:附加符號使用C語言中的終止符號,如0(null),CR,LF,?1(EOF)等不能在常用的字符串函數(shù)中使用的符號,因?yàn)檫@些函數(shù)一旦遇到這些終止符號,就結(jié)束函數(shù)過程?!耠S機(jī)符號:附加符號使用在函數(shù)調(diào)用時(shí)產(chǎn)生的一個(gè)32位的隨機(jī)數(shù)來實(shí)現(xiàn)保密,使得攻擊者不可能猜測到附加字節(jié)的內(nèi)容。而且,每次調(diào)用,附加字節(jié)的內(nèi)容都在改變,也無法預(yù)測。禁止對RET寫:Synthetix方法引入了“準(zhǔn)不變量”的概念。所謂的準(zhǔn)不變量,是指某些特定的變量,它們的改變是程序事先能預(yù)知的,而且只能在滿足一定的條件下才可以改變。在函數(shù)的生命期內(nèi),StackGuard把堆棧中的返回地址看成準(zhǔn)不變量,這樣,在函數(shù)運(yùn)行期間,RET是只讀的,從而可以防止覆蓋返回地址。實(shí)驗(yàn)的數(shù)據(jù)表明,StackGuard對于各種系統(tǒng)的緩沖區(qū)溢出攻擊都有很好的保護(hù)作用,并能保持較好的兼容性和系統(tǒng)性能。有分析表明,StackGuard能有效抵御現(xiàn)在的和將來的基于堆棧的攻擊。3)PointGuard:編譯器生成代碼指針完整性檢查
PointGuard是StackGuard的一個(gè)推廣,它通過在所有的代碼指針之后放置附加字節(jié)來檢驗(yàn)指針在被調(diào)用之前的合法性。如果檢驗(yàn)失敗,會發(fā)出報(bào)警信號并退出程序的執(zhí)行,就如同在StackGuard中的一樣。這種方案有兩點(diǎn)需要注意:(1)附加字節(jié)的定位:附加字節(jié)的空間是在被保護(hù)的變量被分配的時(shí)候分配的,同時(shí)在被保護(hù)字節(jié)初始化過程中被初始化。這樣就帶來了問題,為了保持兼容性,我們不想改變被保護(hù)變量的大小,因此我們不能簡單地在變量的結(jié)構(gòu)定義中加入附加字。還有,對各種類型也有不同的附加字節(jié)數(shù)目。(2)檢查附加字節(jié):每當(dāng)被保護(hù)變量從內(nèi)存載入寄存器或讀此變量的時(shí)候,都需要驗(yàn)證附加字節(jié)的完整性。這也存在一個(gè)問題,因?yàn)椤皬膬?nèi)存中讀”的語義在編譯器中沒有很好地定義,編譯器更關(guān)心的是變量什么時(shí)候要用,哪一種優(yōu)化算法能更方便地把變量從內(nèi)存中載入寄存器中。還有,不同類型的變量,讀入的方法也各自不同。目前已有了PointGuard的一個(gè)原型(還是基于GCC),它通過附加字節(jié)來保護(hù)靜態(tài)分配的函數(shù)指針,但不適用于結(jié)構(gòu)和數(shù)組類型。這個(gè)實(shí)現(xiàn)還遠(yuǎn)沒有完成,一旦完成了,那么,使用它和StackGuard構(gòu)建的可執(zhí)行代碼,將不會受到緩沖區(qū)溢出的攻擊。12.2格式化字符串(FormatString)在2000年下半年,一種稱之為“格式化字符串”的漏洞開始威脅系統(tǒng)和網(wǎng)絡(luò)的安全。這種攻擊方式與緩沖區(qū)溢出類似,也是通過覆蓋緩沖區(qū)來達(dá)到攻擊的目的的,只是這種攻擊方式是利用格式化函數(shù),如printf()的格式化字符串%n來覆蓋緩沖區(qū)的。本節(jié)將首先介紹導(dǎo)致格式化字符串漏洞的原因;接著介紹利用格式化字符串漏洞可實(shí)現(xiàn)的攻擊方式;最后針對格式化字符串漏洞提出一些安全建議。12.2.1格式化函數(shù)和格式化字符串格式化函數(shù)是一類ANSIC函數(shù),包括:●fprintf--將格式化的數(shù)據(jù)打印至文件;●printf--將格式化的數(shù)據(jù)打印至標(biāo)準(zhǔn)輸出"stdout";●sprintf--將格式化的數(shù)據(jù)存儲到緩存中;●snprintf--將指定長度的格式化的數(shù)據(jù)存儲到緩存中;●vfprintf--將va_arg結(jié)構(gòu)中的格式化數(shù)據(jù)打印到文件;●vprintf--將va_arg結(jié)構(gòu)中的格式化數(shù)據(jù)打印到標(biāo)準(zhǔn)輸出"stdout";●vsprintf--將va_arg結(jié)構(gòu)中的格式化數(shù)據(jù)存儲到緩存中;●vsnprintf--將va_arg結(jié)構(gòu)中指定長度的格式化數(shù)據(jù)存儲到緩存中。另外,還有與格式化函數(shù)相關(guān)的,如setproctitle、syslog、err、verr、warn、vwarn等函數(shù)。在格式化函數(shù)的參數(shù)中,有一項(xiàng)就是所謂的格式化字符串,它控制格式化函數(shù)所要進(jìn)行的操作,并指定要打印的參數(shù)的數(shù)據(jù)類型和格式。格式化函數(shù)一般的調(diào)用格式為
printf("<格式化字符串>",<參量表>);
其中,格式化字符串包括兩部分內(nèi)容:一部分是正常字符,這些字符將按原樣輸出;另一部分是格式化控制字符,以“%”開始,后跟一個(gè)或幾個(gè)規(guī)定字符,用來控制輸出內(nèi)容格式。參量表是需要輸出的一系列參數(shù),其個(gè)數(shù)必須與格式化字符串所說明的輸出參數(shù)個(gè)數(shù)一致,各參數(shù)之間用“,”分開,且順序一一對應(yīng),否則將會出現(xiàn)意想不到的錯(cuò)誤。表12-1和表12-2分別列出了格式化控制符和一些特殊的格式字符。表12-1格式化控制符符號作用%d十進(jìn)制有符號整數(shù)%u十進(jìn)制無符號整數(shù)%f浮點(diǎn)數(shù)%s字符串%c單個(gè)字符%p指針的值%e,%E指數(shù)形式的浮點(diǎn)數(shù)%x,%X無符號、以十六進(jìn)制表示的整數(shù)%g,%G自動(dòng)選擇合適的表示法%o無符號、以八進(jìn)制表示的整數(shù)%n得到輸出字符的個(gè)數(shù)%m輸出錯(cuò)誤時(shí)相應(yīng)的字符串提示%%輸出正文字符中的?“%”?字符表12-2一些特殊格式字符字符作用\n換行\(zhòng)f清屏并換頁\r回車\tTab符\xhh表示一個(gè)用十六進(jìn)制表示的ASCII碼,其中,hh是1或2個(gè)十六進(jìn)制數(shù)12.2.2格式化字符串漏洞基本原理從基礎(chǔ)知識介紹中我們知道,格式化字符串控制著格式化函數(shù)所要進(jìn)行的操作,并指定要打印的參數(shù)的數(shù)據(jù)類型和格式。格式化字符串是一包含文本及格式化參數(shù)的ASCII串,如:
printf(''Thenumberis:%s\n'',''2001'');這里,要打印的文本是"Thenumberis:",后面跟的是格式化參數(shù)%s,在輸出時(shí)將被后面的參數(shù)(2001)代替。因此,這條語句的執(zhí)行結(jié)果為:Thenumberis:2001。在這個(gè)例子中,格式化字符串指定的參量個(gè)數(shù)和參量表中的參量個(gè)數(shù)均為1,接下來我們看一個(gè)格式化串指定的參量個(gè)數(shù)大于參量表中參量個(gè)數(shù)的例子:/*<-begin->test1.c*/#include<stdio.h>
intmain(void){charstring[]="HelloWorld!";
printf("String:%s,arg2:%#p,arg3:%#p\n",string);return0;}/*<-end->*/這里,我們只提供了一個(gè)數(shù)據(jù)參數(shù)“string”,但在格式串中有三個(gè)打印控制字符。根據(jù)上一節(jié)中所介紹的堆棧知識,我們可以得到程序在調(diào)用printf函數(shù)時(shí)的堆棧情況如圖12-5所示。圖12-5程序堆棧情形因此這個(gè)程序?qū)⒂腥缦逻\(yùn)行結(jié)果:[root@ecmformat]$./test1String:HelloWorld!,arg2:0x6c6c6548,arg3:0x6f57206f
由上面的分析可以看出,arg2、arg3所顯示的是main()中數(shù)組string中的前兩個(gè)字的內(nèi)容。也就是說,printf()只根據(jù)格式控制字符串內(nèi)的控制字符個(gè)數(shù)來依次顯示堆棧中控制字符串參數(shù)后面地址的內(nèi)容,每次根據(jù)“%格式”移動(dòng)相應(yīng)的字節(jié)數(shù)(如%s為4個(gè)字節(jié),%f為8個(gè)字節(jié))。一般來說,格式化字符串由程序員定制,因而不會出現(xiàn)太大的安全問題。但是,如果程序中的格式化字符串由用戶提供,用戶就可以定制格式化字符串實(shí)現(xiàn)格式化字符串攻擊。例如用某個(gè)值覆蓋函數(shù)的返回指針,那么程序返回時(shí)就會跳到指定的位置去運(yùn)行。12.2.3格式化字符串攻擊1.使程序崩潰利用格式化字符串使程序崩潰是比較簡單的攻擊方法。幾乎在所有的UNIX系統(tǒng)中,當(dāng)程序通過一個(gè)指針訪問用戶不可用的緩沖區(qū)時(shí),進(jìn)程將向內(nèi)核發(fā)送SIGSEGV信號,然后程序終止并生成內(nèi)核轉(zhuǎn)存文件core。通過gdb調(diào)試core文件可以獲得一些有用的信息,如程序所用的非法指針。因此最簡單的攻擊方法就是利用類似于下面的語句:
printf(''%s%s%s%s%s%s%s%s%s'');
使程序崩潰。因?yàn)楦袷絽?shù)“%s”顯示由堆棧提供的地址內(nèi)存中的字符串,而堆棧中有許多數(shù)據(jù),因此很可能會去讀取非法地址中的數(shù)據(jù)內(nèi)容,從而導(dǎo)致進(jìn)程崩潰。同樣,我們也可以利用“%n”格式參數(shù)向一個(gè)地址寫數(shù)據(jù)而導(dǎo)致進(jìn)程崩潰。2.查看進(jìn)程內(nèi)存更進(jìn)一步,可以利用格式化字符串來查看堆棧中的內(nèi)容??紤]下面的語句:
printf(''%08x.%08x.%08x.%08x.%08x\n'');
這條語句將以8位十六進(jìn)制數(shù)的形式依次顯示堆棧中的內(nèi)容,其輸出將具有如下形式:40012980.080628c4.bffff7a4.0000005.08059c04
這有助于黑客在開發(fā)攻擊程序時(shí)確定地址偏移量。
同樣,可以利用格式化字符串查看堆棧以外其它內(nèi)存的內(nèi)容。為做到這一點(diǎn),首先要找到一個(gè)格式參數(shù),它能顯示一個(gè)地址中的內(nèi)容,即此格式參數(shù)對應(yīng)的輸出參數(shù)列表中的項(xiàng)為一個(gè)指針,指針指向某個(gè)內(nèi)存區(qū)。很顯然,“%s”能做到這一點(diǎn)。然后必須將所要顯示內(nèi)存的地址存放到堆棧的正確位置。有時(shí),格式化字符串本身會存放在堆棧中,看下面一段代碼:
intsub1(char*fmt,...) { charsubbuf[128]; va_listap; /*pointertolistofarguments*/
va_start(ap,fmt); /*setarg_ptr(ap)tobeginningoflistofoptionalarguments*/ vsnprintf(subbuf,sizeof(buf)?1,fmt,ap); subbuf[127]='\0'; va_end(ap); } intmain(intargc,char**argv) { charbuf[128]; sub1(buf); }在上面的代碼中,buf中存儲的是由命令行參數(shù)argv提供的格式化字符串。假設(shè)提供的格式化字符串為:"peekthememory:abcd%d%d%d%d%s",如果代碼中沒有再定義其它變量,則第一次調(diào)用sub1()時(shí),堆棧中的情況如圖12-6所示。圖12-6堆棧情形執(zhí)行完va_start(ap,fmt)后,變參指針ap指向fmt的下一個(gè)地址,即buf。從地址buf到"abcd"之間共有16個(gè)字符,即16字節(jié),因此我們用4個(gè)%d來對應(yīng)這4個(gè)地址,這樣最后一個(gè)格式參數(shù)“%s”對應(yīng)了地址“abcd”,因此將顯示地址“abcd”中的內(nèi)容。如果地址ap到緩沖區(qū)buf之間還有其它數(shù)據(jù),則可以通過調(diào)節(jié)“%d”的個(gè)數(shù)來使“%s”對應(yīng)“abcd”。如果地址ap到地址“abcd”之間的字節(jié)長度不是4的倍數(shù),則可以在地址"abcd"前添加字節(jié)使其成為4的倍數(shù);當(dāng)然,也可以利用“%c”來使地址步進(jìn),只是此時(shí)步進(jìn)速度太慢而已。3.覆蓋內(nèi)存區(qū)格式化字符串攻擊更主要的是利用格式化字符串來覆蓋內(nèi)存區(qū),一般覆蓋指令指針,改變程序的流程,使調(diào)用結(jié)束時(shí)執(zhí)行攻擊者想要執(zhí)行的代碼。接下來將介紹幾種覆蓋內(nèi)存區(qū)的方法。我們首先介紹類似于傳統(tǒng)的緩沖區(qū)溢出的格式化字符串漏洞。這種漏洞一般是由于格式化函數(shù)對輸出長度不作檢查,而輸出數(shù)據(jù)又是由用戶全部或部分提供而導(dǎo)致的。為了達(dá)到攻擊的目的,黑客一般提供具有以下形式的輸出數(shù)據(jù):''abcdef%.nd<RETs><nops><shellcode>''
其中,“%.nd”中的n是指打印寬度,RET指返回地址,shellcode是用戶提供的可執(zhí)行二進(jìn)制代碼等。這樣,可以用RET覆蓋原來的指令指針,從而使程序返回時(shí)跳轉(zhuǎn)到shellcode執(zhí)行。在QPOP2.53和bftpd軟件中就存在這樣的格式化字符串漏洞。接下來,我們介紹如何直接覆蓋返回地址。首先看一段程序:
/*<-begin->vul.c*//*Asimplevulnerableexampleforformatbug.*/#include<stdarg.h>#include<unistd.h>#include<syslog.h>
#defineBUFSIZE1024
intlog(intlevel,char*fmt,...){charbuf[BUFSIZE];va_listap;
va_start(ap,fmt);vsnprintf(buf,sizeof(buf)?1,fmt,ap);
buf[BUFSIZE?1]='\0';syslog(level,"[hmm]:%s",buf);
va_end(ap);}
intmain(intargc,char**argv){charbuf[BUFSIZE];intnum,i;num=argc;if(argc>1){/*===*/for(i=1;i<num;i++){snprintf(buf,BUFSIZE?1,"argv[%d]=%.200s",i,argv[i]);buf[BUFSIZE?1]='\0';log(LOG_ALERT,buf);//這里有問題printf("argv[%d]=%s\n",i,argv[i]);} }/*===*/}/*<-end->*/這個(gè)程序在調(diào)用子函數(shù)log()的時(shí)候,將buf放到了*fmt所對應(yīng)的位置上,而buf內(nèi)容的一部分是由用戶輸入的,而且沒有做任何檢查,因此,main()函數(shù)會將命令行參數(shù)拷貝到buf中去。前面還加上了“argv[%d]=”字符串,在參數(shù)個(gè)數(shù)小于10的情況下,這個(gè)字符串的長度為10字節(jié)。我們考慮構(gòu)造這樣的字符串作為命令行參數(shù):"align|RET|%d...%d%.SH_RETd|%n";其中,"align"用來調(diào)整buf開頭的數(shù)據(jù)長度為4的整數(shù)倍;"RET"是main()或者log()函數(shù)的返回地址位置,我們會將shellcode的地址放到RET中去;"SH_RET"是我們存放shellcode的地址;"%d...%d"用來使%n所對應(yīng)的地址剛好是儲存RET的地址。在第一次調(diào)用log()時(shí),堆棧中的情況如圖12-7所示。在執(zhí)行完va_start(ap,fmt)后,變參指針ap指向fmt的下一個(gè)地址,也就是main()函數(shù)局部變量i的地址,如果我們提供給argv[1]的是這樣的字符串:"xxabcd%d%d%d%d%d%p"
那么堆棧中的情況如圖12-8所示。圖12-7堆棧情形圖12-8堆棧情形因?yàn)椤癮rgv[1]=”長是10字節(jié),我們用兩個(gè)字節(jié)“xx”來使其變成4的整數(shù)倍:12字節(jié),因此,從變量i的地址到“abcd”之間共有4+4+12=20字節(jié)。20/4=5,因此,我們需要用5個(gè)%d來對應(yīng)這5個(gè)地址,這樣,最后一個(gè)格式化串%p就對應(yīng)了“abcd”的地址,因此打印出來應(yīng)該是:"0x64636261"[root@ecmformat]#./vulxxabcd%d%d%d%d%d%pargv[1]=xxabcd%d%d%d%d%d%p[root@ecmformat]#tail?1/var/log/messagesJul1204:13:08rh62vul:[hmm]:argv[1]=xxabcd21198649097754297839520211384930x64636261注意最后的0x64636261,這說明我們前面的分析是正確的。如果我們將%p換成%n,vsnprintf()就會將打印長度存放到0x64636261中去,這就會導(dǎo)致段錯(cuò)誤。如果我們將命令行參數(shù)中的RET換成保存main函數(shù)返回地址的地址,就會將這個(gè)長度存放到那里去,如果這個(gè)長度的值剛好等于我們存放shellcode的地址,那么當(dāng)main()返回時(shí)就會跳到shellcode去運(yùn)行。由于操作系統(tǒng)實(shí)現(xiàn)的原因,有時(shí)候不能采取上面一次覆蓋返回地址的方法,這時(shí)可能需要多次覆蓋返回地址,從而達(dá)到攻擊的目的。12.2.4安全建議從上面的分析可以知道,格式化字符串漏洞源于函數(shù)接受了不可信源(如鍵盤等)的數(shù)據(jù)并把這些數(shù)據(jù)作為格式化字符串。當(dāng)程序中存在類似
snprintf(buf,sizeof(buf),UntrustedUserDataBuffer);
的語句時(shí),可能導(dǎo)致格式化字符串漏洞。為了避免格式化字符串漏洞,在編程時(shí)應(yīng)采用如下正確的使用方式:
snprintf(buf,sizeof(buf),"%s",UntrustedUserDataBuffer);
更一般來說,當(dāng)采用:
my_sprintf(Buffer,"%s",UntrustedUserDataBuffer);
方式時(shí),就可以使函數(shù)不再解析用戶數(shù)據(jù)中的格式化控制符,從而使程序更安全。12.3條件競爭12.3.1用戶ID
為了更好地理解條件競爭,我們首先介紹UNIX系統(tǒng)中的用戶ID。1.UserID
每個(gè)用戶被賦予一個(gè)惟一的號,簡稱UID(UserID)。UID0屬于根root,任何以UID0工作的用戶將不會有任何的安全限制。普通用戶的UID值一般從100開始,如500。所有小于此值的用戶ID屬于系統(tǒng)進(jìn)程。用戶的UID數(shù)值越小代表其權(quán)限越高。2.GroupIDGroupID即組號,UNIX系統(tǒng)有多個(gè)組,如管理員屬于root組,一般用戶屬于wheel組。用戶一般隸屬于創(chuàng)建此用戶的用戶組或標(biāo)志其行為的組,如fb4、wwwadmin、students、accounting等等。一個(gè)用戶可屬于1~NGROUPS_MAX(32)個(gè)組。3.SetUID和SetGID
通常,有些程序需要更高的權(quán)限以完成某一任務(wù),例如普通用戶在改變自己的口令時(shí),需要往口令文件寫入新的口令值,而通常口令字文件只有超級用戶才有權(quán)限這么做。這時(shí),要么由擁有權(quán)限的用戶(如超級用戶)來執(zhí)行,要么通知系統(tǒng),程序在執(zhí)行時(shí)需要分配一定的權(quán)限。前一種方法非常費(fèi)力并很不安全(每次更改自己的口令都要超級用戶在場),所以要么所有人都知道該口令(即一般用戶都知道超級用戶的口令),要么知道口令的人(超級用戶)不得不為了另外的用戶完成任務(wù)而登錄系統(tǒng)。為了實(shí)現(xiàn)這種目的,UNIX文件系統(tǒng)實(shí)現(xiàn)有兩個(gè)文件隊(duì)列,如果某個(gè)程序帶有SetUID標(biāo)志,則表示調(diào)用該程序的其它程序(如普通權(quán)限用戶)可以暫時(shí)獲得該程序所屬用戶的UID(有效UID),而不是執(zhí)行該程序用戶的UID(真實(shí)UID)。這同樣適用于SetGID標(biāo)志。4.真實(shí)UID/GID和有效UID/GID
真實(shí)UID是用戶的UID。如,用戶Thomas的UID為543,說明Thomas是一般用戶。現(xiàn)在如果他執(zhí)行一個(gè)來自root用戶的SetUID程序,則程序的有效UID為0,真實(shí)UID為543。12.3.2條件競爭當(dāng)特權(quán)應(yīng)用進(jìn)程(aprivilegedapplication)打開文件時(shí),系統(tǒng)需要首先檢查用戶是否有資格打開文件。很多情況下會采用如下錯(cuò)誤的使用方式:
if(access("/home/long/vulfile",W_OK)==0){/*用戶具有寫權(quán)限*/
if((fd=open("/home/long/vulfile",O_WRONLY))<0){fprintf(stderr,"I'mnotallowedtoopenthefile!\n");exit(?1);}}在上面的代碼段中,access()調(diào)用利用的是用戶的真實(shí)UID和真實(shí)GID,而不是SetUID或SetGID程序的有效UID/GID來檢驗(yàn)用戶對目標(biāo)的訪問權(quán)限。但是,open()調(diào)用確是使用用戶的有效UID/GID來檢驗(yàn)用戶的訪問權(quán)限。在access()和open()調(diào)用之間存在一段時(shí)間。這樣,攻擊者就可以在access()調(diào)用后刪除文件/home/long/vulfile,然后讓它與文件/etc/security/shadow(口令字文件)建立鏈接。從而當(dāng)調(diào)用open()時(shí)就會通過鏈接打開文件/etc/security/shadow而不是文件/home/long/vulfile。至此,攻擊者就可以處理它本不能訪問的數(shù)據(jù)了。從上面的分析可以得到整個(gè)攻擊情景:用戶long對文件/home/long/vulfile具有寫權(quán)限;系統(tǒng)通過程序中的語句access("/home/attacker/vulfile",W_OK)檢查了用戶的寫權(quán)限;這時(shí),long刪除了文件/home/long/vulfile,然后與一個(gè)long沒有寫權(quán)限的文件設(shè)置了鏈接。這樣,open()根據(jù)鏈接就打開了被保護(hù)的文件。12.3.3安全建議針對上面的安全隱患,在編程時(shí)可采取以下幾種方法:1.使用faccess()
fd=open("file",O_WRONLY);if(faccess(fd,W_OK)!=0){fprintf(stderr,"Nicetry!\n");exit(?1);}缺點(diǎn):(1)很多操作系統(tǒng)并沒有實(shí)現(xiàn)faccess()。(2)在faccess()前需首先調(diào)用open()。在UNIX系統(tǒng)中,open()可用于打開設(shè)備文件。在打開設(shè)備文件時(shí)可能已經(jīng)對相應(yīng)的設(shè)備執(zhí)行了某種操作。如:當(dāng)打開磁帶設(shè)備文件時(shí),磁帶必須倒帶;在反復(fù)備份的情況下就有可能導(dǎo)致先前備份數(shù)據(jù)的丟失。2.使用標(biāo)志位O_NOFOLLOW
在open()調(diào)用中使用選項(xiàng)O_NOFOLLOW可以禁止根據(jù)符號鏈接來打開文件。缺點(diǎn):攻擊者可以采用硬鏈接來達(dá)到目的。3.使用fork()
惟一可移植的,也是最安全的方法就是使用fork()產(chǎn)生子進(jìn)程。子進(jìn)程永久地放棄特權(quán),然后打開文件,將文件描述符返回給父進(jìn)程后終止子進(jìn)程。pid_tchild_pid;
if((child_pid=fork())<0) /*fork()失敗*/[Exit]elseif(child_pid>0) /*父進(jìn)程部分*/[獲得文件描述符、等待子進(jìn)程]else /*子進(jìn)程部分*/{
inffd;
if(setgid(getgid())<0)[Exit]if(setuid(getuid())<0)[Exit]/*如果進(jìn)程的有效UID為0,則通過setuid()和setgid()會改變所有的用戶ID,否則,只設(shè)置有效用戶ID*/if((fd=open("userfile",O_WRONLY))<0)[Exit]/*現(xiàn)在就可以把文件描述符傳遞給父進(jìn)程了。不過需要注意的是,不同操作系統(tǒng)對此的實(shí)現(xiàn)有所不同*/}12.4臨時(shí)文件在編程過程中,還應(yīng)注意的一個(gè)問題是關(guān)于臨時(shí)文件的,對臨時(shí)文件的錯(cuò)誤處理方式也將導(dǎo)致安全問題。一般來說,臨時(shí)文件是任何人都可寫的目錄(在UNIX下是/tmp、/var/tmp)。UNIX系統(tǒng)可以通過設(shè)置文件屬性(chmodo+t/tmp:表示用戶只能移動(dòng)自己擁有的文件)來防止刪除公共目錄下的文件,但是設(shè)置的文件屬性并不能提供對子目錄的保護(hù)。也就是說,如果任何人都可以對子目錄執(zhí)行寫操作,那么所有人就都可以修改這個(gè)目錄下的所有文件。因此,從安全角度來講,必須對每個(gè)子目錄明確地設(shè)置文件屬性t。
除了對文件的非法刪除外,對于臨時(shí)文件還存在的安全隱患就是上一節(jié)講到的條件競爭,或者說易受到鏈接攻擊。下面是兩種生成臨時(shí)文件方法的代碼段:FILE*tmp_datei;
tmp_datei=tmpfile();
char*tmp_name;inttmpfd;
tmp_name=tmpnam(NULL);
if((tmpfd=open(tmp_name,O_RDWR|O_CREAT))<0)[EXIT]unlink(tmp_name);/*刪除文件,這樣當(dāng)程序執(zhí)行完或關(guān)閉文件描述符后文件已不存在*/由于這兩種方法都是以非原子方式運(yùn)行并遵循符號鏈接,因此,二者都是不安全的。上面代碼中的tmpnam()、tempnam()及mktemp()只保證產(chǎn)生的文件名在調(diào)用時(shí)不存在。但是在生成文件名和open()之間,攻擊者可以為生成的文件名創(chuàng)建鏈接,從而導(dǎo)致條件競爭。為了避免對臨時(shí)文件操作時(shí)的條件競爭,可以采用如下方法:inttmpfd;
if((tmpfd=mkstemp("/tmp/MyTempFile.XXXXXX"))<0)[Exit]
fchmod(tmpfd,0600);
系統(tǒng)調(diào)用mkstemp()可以生成惟一的文件名并以安全的方式打開這個(gè)文件??墒莔kstemp()不是POSIX標(biāo)準(zhǔn)(BSD4.3),而且舊版本的mkstemp會設(shè)置文件的訪問權(quán)限為0666,也就是說,任何人都可以對文件進(jìn)行讀寫操作。為了使代碼更具移植性,可以采用如下方法:inttmp_fd;FILE*tmp_stream;chartmp_name;
if((tmp_name=tm
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026黑龍江齊齊哈爾市富??h房產(chǎn)和物業(yè)服務(wù)中心招聘公益性崗位人員2人備考題庫及完整答案詳解一套
- 2025 小學(xué)一年級道德與法治上冊電影院里不吵鬧課件
- 2026年高鐵安全駕駛規(guī)范操作培訓(xùn)
- 職業(yè)健康檔案電子化操作全流程審計(jì)追蹤體系
- 職業(yè)健康促進(jìn)的企業(yè)協(xié)同策略
- 職業(yè)健康與職業(yè)康復(fù)的政策支持體系構(gòu)建
- 陜西2025年陜西省煙草專賣局(公司)生產(chǎn)操作類崗位應(yīng)屆畢業(yè)生招聘12人筆試歷年參考題庫附帶答案詳解
- 職業(yè)健康與員工職業(yè)發(fā)展關(guān)聯(lián)
- 綿陽四川綿陽三臺縣鄉(xiāng)鎮(zhèn)事業(yè)單位從“大學(xué)生志愿服務(wù)西部”項(xiàng)目人員招聘7人筆試歷年參考題庫附帶答案詳解
- 濱州2025年山東濱州濱城區(qū)招聘中小學(xué)特殊教育學(xué)校教師217人筆試歷年參考題庫附帶答案詳解
- 高校區(qū)域技術(shù)轉(zhuǎn)移轉(zhuǎn)化中心(福建)光電顯示、海洋氫能分中心主任招聘2人備考題庫及答案詳解(考點(diǎn)梳理)
- 航空安保審計(jì)培訓(xùn)課件
- 2026四川成都錦江投資發(fā)展集團(tuán)有限責(zé)任公司招聘18人備考題庫有答案詳解
- 高層建筑滅火器配置專項(xiàng)施工方案
- 2023-2024學(xué)年廣東深圳紅嶺中學(xué)高二(上)學(xué)段一數(shù)學(xué)試題含答案
- 2025年全國職業(yè)院校技能大賽中職組(母嬰照護(hù)賽項(xiàng))考試題庫(含答案)
- 2026江蘇鹽城市阜寧縣科技成果轉(zhuǎn)化服務(wù)中心選調(diào)10人考試參考題庫及答案解析
- 托管機(jī)構(gòu)客戶投訴處理流程規(guī)范
- 2026年及未來5年中國建筑用腳手架行業(yè)發(fā)展?jié)摿Ψ治黾巴顿Y方向研究報(bào)告
- 銀行客戶信息安全課件
- (2025)70周歲以上老年人換長久駕照三力測試題庫(附答案)
評論
0/150
提交評論