天天看點

雪城大學資訊安全講義 3.2 Set-UID 程式的漏洞

2 Set-UID 程式的漏洞

2.1 隐藏的輸入:環境變量

特權程式必須對所有輸入進行安全檢查。輸入檢查實際上是通路控制的一部分,特權程式必須這麼做,來確定程式的安全。很多安全問題都是輸入檢查的錯誤造成的。

如果輸入在程式中顯式存在,程式員可能記得執行輸入檢查;如果輸入隐式存在,輸入檢查可能會忘記,因為程式員可能不知道這個輸入的存在。環境變量就是這類輸入。

每個 Unix 程序都在特定環境下運作。環境由環境變量表組成,每個變量都有指派。一些程式内部使用這些環境變量,Shell 程式就是這些程式的例子。換句話說,一些環境變量的值可以 Shell 程式的行為。

由于環境變量由使用者控制,如果程式依賴這些變量,使用者可以間接影響這類程式的行為,通過修改一些環境變量的值。是以,了解特權程式是否依賴任何環境變量的值就十分重要。一種程式可能被環境變量影響的方式,就是在程式中顯式使用環境變量的值。在 C 語言中,程式可以使用

getenv

來通路環境變量的值。但是,也有許多例子,程式隐式使用環境變量。這就是我們在許多 Set-UID 程式中看到的漏洞。我們會在這一節中展示幾個例子。

  • PATH

    環境變量
    • 在 Shell 中執行指令式,Shell 會使用

      PATH

      環境變量搜尋所有指令,它包含一個目錄清單。Shell 程式通過目錄清單(和他們在

      PATH

      環境變量的相同順序)來搜尋。第一個比對指令名稱的程式會被執行。
    • 下面會發生什麼?要注意

      system (const char *cmd)

      庫函數首先調用

      /bin/sh

      程式,之後讓 SHell 程式執行

      cmd

      system ("mail");           
  • 攻擊者可以将

    PATH

    修改成這個,并使目前目錄下的

    mail

    被執行。
    PATH=".:$PATH"; export PATH           
    拿超人來比喻的話,如果超人的指令是“左轉”(壞人在左邊而好人在右邊,你可以假設他要攻擊壞人)。如果攻擊者準确知道左轉指令什麼時候以及在哪裡執行,他就可以做出與上面類似的攻擊。因為“左邊”是個相對的方向,并不是絕對方向。如果攻擊者事先在你想要左轉的地方,放置一個旋轉裝置,并将你旋轉 180 度,隻要你踏上了它,“左轉”就變成了轉到好人那裡。如果你遵循了指令,你最後就會攻擊好人。
  • IFS

    • IFS

      變量決定了哪些字元被解釋為空白字元。它代表了内部字段分隔符。假設我們設定它來包含斜杠字元:
      IFS="/ \t\n"; export IFS 
      PATH=".:$PATH"; export PATH           
    • 現在從 Bourne shell 中調用任何使用絕對

      PATH

      的程式(李儒

      system

      )。它現在解釋為下面的東西,它會在目前使用者目錄下,嘗試執行指令行調用

      bin

      system("/bin/mail root"); ---> system(" bin mail root");           
    • IFS 的 bug 現在已經在 SHell 中禁止了;所引用的新的 Shell 程序不會繼承 IFS 變量。
    • 假設在超人的故事中。超人知道使用“左轉”指令的風險,是以它将其改為“轉到北邊”,它現在是個絕對方向。這仍然存在漏洞,因為“北”由磁場決定,不幸的是,磁場可以通過攻擊者放置的磁鐵來影響。
  • LD_LIBRARY_PATH

    • Linux 中,除非編譯時期通過

      -static

      顯式指定,所有 Linux 程式需要在運作時期連結到動态連結庫。動态連結器或加載器

      ld.so/ld-linux.so

      加載程式所需的共享庫,準備要運作的程式,之後運作它。你可以使用下面的指令來觀察程式需要什麼共享庫。
      % ldd /bin/ls           
    • LD_LIBRARY_PATH

      是一個環境變量,被動态連結器或加載器(

      ld.so/ld-linux.so

      )使用。它含有一個目錄清單,讓連結器或者加載器在搜尋共享庫時尋找。可以列出多個目錄,以冒号(

      :

      )分隔。對于任何可執行檔案,這個清單放在現存的編譯器加載路徑,以及任何系統預設加載路徑的前面。
    • 基本上每個 Unix 程式都依賴于

      libc.so

      ,并且每個 Windows 程式都一拉李雨 DLL。如果這些苦可以替換為惡意的副本,惡意代碼就可以在共享庫函數被調用時執行。
    • 由于

      LD_LIBRARY_PATH

      可以由使用者充值,攻擊者可以修改這個變量,并強制庫加載器在攻擊者的目錄中搜尋庫,進而加載攻擊者的惡意庫。
      % setenv LD_LIBRARY_PATH .:$LD_LIBRARY_PATH           
    • 為了使 Set-UID 程式更加安全,不受

      LD_LIBRARY_PATH

      環境變量的影響,運作時的連結器或加載器(

      ld.so

      )會忽略環境變量,如果程式是個 Set-UID 程式。
    • 防護應用也可以靜态連結到可信庫來避免這個問題。
    • 在 Windows 主機上,通常在加載 DLL 的時候,在搜尋系統目錄之前,會搜尋目前目錄中的 DLL。如果你點選 Word 文檔來啟動 Office,會在包含該文檔的目錄下搜尋 DLL。
  • LD PRELOAD

    • 許多 Unix 系統允許你“預加載”共享庫,通過設定環境變量

      LD PRELOAD

      。這些使用者指定的庫會在所有其它庫之前加載。這可以用于選擇性重載其他庫中的函數。例如,如果你已經建構了一個庫,你可以使用下列指令預加載它:
      % export LD_PRELOAD=./libmylib.so.1.0.1           
      如果

      libmylib.so.1.0.1

      包含函數

      sleep

      ,它是個标準的

      libc

      函數,當程式執行并調用

      sleep

      時,

      libmylib.so.1.0.1

      中的函數會被調用。
  • 這裡是一個程式,重載了

    libc

    中的

    sleep

    #include <stdio.h> 
    
    void sleep (int s) { 
        printf("I am not sleeping!\n"); 
    }           
    我們可以使用下列指令編譯程式(假設上面的程式名為

    name.c

    ):
    % gcc -fPIC -g -c a.c 
    % gcc -shared -o libmylib.so.1.0.1 a.o -lc           
    現在,我們運作下列程式:
    int main() { 
        sleep(1); 
        return 0; 
    }           
    如果環境變量

    LD PRELOAD

    設為

    libmylib.so.1.0.1

    ,标準

    libc

    sleep

    沒有使用,反之我們共享庫中的

    sleep

    函數會調用,并且列印

    "I am not sleeping!"

  • 為了確定 Set-UID 程式安全,不受

    LD PRELOAD

    環境變量的控制,運作時連結器或加載器(

    ld.so

    )會忽略這個環境變量,如果程式是 Set-Root-UID 程式,除非真實 UID 也為 0。

2.2 調用其它程式

當特權程式調用其它程式時,必須注意是否調用了非預期的程式。我們知道,環境變量是個我們需要注意的地方,也有一些我們需要注意的其它地方。

  • 如果 Set-UID 程式執行下面的事情,會發生什麼?
    // The contents of User_Input are provided by users. 
    sprintf(command, "/bin/mail %s", User_Input); 
    system(command);           
  • User_Input

    可能包含 Shell 的特殊字元(例如

    | & < >

    )。要記住,

    system

    調用實際上先調用 Shell,之後讓 Shell 程式執行

    /bin/mail

    。如果我們不注意,攻擊者就可以執行其它程式,通過讓

    User_Input

    是下面的字元串:
    [email protected] ; rm -f /* ; /bin/sh           

2.3 其它知名的漏洞模式

除了上面的輸入校驗漏洞,也有一些其他的知名漏洞模式。我們會在單獨的章節中讨論它們。這裡是這些模式的總結:

  • 緩沖區溢出
  • 競态條件
  • 格式化字元串

2.4 雜項漏洞

有許多其他漏洞,并不易于歸納進上面讨論的任何分類。一些可能被歸納為更廣泛的“呼入椒鹽漏洞”,但是由于他們的獨特特性,我們在這裡單獨讨論它們。我們不能枚舉所有漏洞。我們隻能給出一些示例,來展示程式員在程式邏輯中的不同錯誤,并且展示這些錯誤如何變為漏洞。

  • lpr

    漏洞:它在

    /tmp

    目錄下生成臨時檔案。檔案名稱應該是随機的,但是,由于僞随機數生成的錯誤,檔案名稱每一千次就會發生重複。這個程式是 Set-UID 程式。将可預測的檔案名稱連結到

    /etc/passord

    會導緻

    lpr

    覆寫

    /etc/passord

  • chsh

    漏洞:

    chsh

    讓使用者輸入 Shell 程式的名稱s,并在

    /etc/passwd

    中儲存輸入。

    chsh

    并不會做清晰的檢查。程式假設使用者的輸入隻有一行,不幸的是,這個假設可以為假:使用者可以鍵入聯行輸入,其中第二行是類似

    xyz::0:0::

    的東西美麗如,使用者可以插入一個新的超級使用者賬戶(UID:0),不帶密碼。
  • sendmail

    漏洞
    • sendmail

      :(1)入境的郵件會添加在

      /var/mail/wedu

      。(2)如果

      /var/mail/wedu

      的所有者不是 Wedu,

      sendmail

      會使用

      chown

      将所有者修改為 Wedu。
    • 你能利用它來讀取 Wedu 的郵件嗎?
    • 你能利用它來給 Wedu 造成更大的損失嗎?