Security

curl 如何保護密碼不出現在 ps 輸出中?

  • August 12, 2017

前段時間我注意到,curl作為命令行參數提供的使用者名和密碼不會出現在ps輸出中(當然它們可能會出現在您的 bash 歷史記錄中)。

它們同樣不出現在/proc/PID/cmdline.

(不過,可以推導出組合使用者名/密碼參數的長度。)

下面的展示:

[root@localhost ~]# nc -l 80 &
[1] 3342
[root@localhost ~]# curl -u iamsam:samiam localhost &
[2] 3343
[root@localhost ~]# GET / HTTP/1.1
Authorization: Basic aWFtc2FtOnNhbWlhbQ==
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.15.3 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Host: localhost
Accept: */*



[1]+  Stopped                 nc -l 80
[root@localhost ~]# jobs
[1]+  Stopped                 nc -l 80
[2]-  Running                 curl -u iamsam:samiam localhost &
[root@localhost ~]# ps -ef | grep curl
root      3343  3258  0 22:37 pts/1    00:00:00 curl -u               localhost
root      3347  3258  0 22:38 pts/1    00:00:00 grep curl
[root@localhost ~]# od -xa /proc/3343/cmdline 
0000000    7563    6c72    2d00    0075    2020    2020    2020    2020
         c   u   r   l nul   -   u nul  sp  sp  sp  sp  sp  sp  sp  sp
0000020    2020    2020    0020    6f6c    6163    686c    736f    0074
        sp  sp  sp  sp  sp nul   l   o   c   a   l   h   o   s   t nul
0000040
[root@localhost ~]# 

這個效果是如何實現的? 它在原始碼的某個地方curl嗎?(我假設它是一個curl特性,而不是一個ps特性?或者它是某種核心特性?)


另外:這可以從二進制執行檔的原始碼之外實現嗎? 例如,通過使用 shell 命令,可能與 root 權限相結合?

換句話說,我能否以某種方式掩蓋我傳遞給某個任意shell 命令的參數出現在輸出中/procps輸出中(我認為是同一件事) ?(我猜這個答案是“不”,但似乎值得包括這個額外的半個問題。)

當核心執行一個程序時,它會將命令行參數複製到屬於該程序的讀寫記憶體(在堆棧上,至少在 Linux 上)。該程序可以像任何其他記憶體一樣寫入該記憶體。當ps顯示參數時,它會讀回儲存在程序記憶體中該特定地址的任何內容。大多數程序保留原始參數,但可以更改它們。POSIX 描述ps的狀態

未指定表示的字元串是參數列表的版本,因為它是在命令啟動時傳遞給命令的,還是參數的版本,因為它們可能已被應用程序修改。應用程序不能依賴於能夠修改其參數列表並將該修改反映在 ps 的輸出中。

提到這一點的原因是大多數 unix 變體確實反映了這種變化,但其他類型的作業系統上的 POSIX 實現可能不會。

此功能的用途有限,因為該程序無法進行任意更改。至少,參數的總長度不能增加,因為程序不能改變ps獲取參數的位置,也不能擴展超出其原始大小的區域。通過在末尾放置空字節可以有效地減少長度,因為參數是 C 風格的以空結尾的字元串(這與末尾有一堆空參數沒有區別)。

如果你真的想深挖,可以看看開源實現的源碼。在 Linux 上, 的來源ps並不有趣,您只會看到它從proc 文件系統中讀取命令行參數,在. 生成此文件內容的程式碼在核心中,. 程序的記憶體部分(用 訪問)從地址到; 這些地址在程序啟動時記錄在核心中,之後無法更改。/proc/*PID*/cmdlineproc_pid_cmdline_read``fs/proc/base.caccess_remote_vm``mm->arg_start``mm->arg_end

一些守護程序使用這種能力來反映它們的狀態,例如,它們將它們更改argv[1]為類似startingor availableor的字元串exiting。許多 unix 變體具有setproctitle執行此操作的功能。一些程序使用此功能來隱藏機密數據。請注意,這是有限的用途,因為命令行參數在程序啟動時是可見的。

大多數高級語言將參數複製到字元串對象,並且不提供修改原始儲存的方法。argv這是一個通過直接更改元素來展示這種能力的 C 程序。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
   int i;
   system("ps -p $PPID -o args=");
   for (i = 0; i < argc; i++)
   {
       memset(argv[i], '0' + (i % 10), strlen(argv[i]));
   }
   system("ps -p $PPID -o args=");
   return 0;
}

樣本輸出:

./a.out hello world
0000000 11111 22222

argv您可以在 curl 原始碼中看到修改。Curl 定義了一個函式cleanargsrc/tool_paramhlp.c用於將參數更改為使用memset. 在src/tool_getparam.c此功能中使用了幾次,例如通過編輯使用者密碼。由於該函式是從參數解析中呼叫的,因此它會在 curl 呼叫的早期發生,但在此之前轉儲命令行仍會顯示任何密碼。

由於參數儲存在程序自己的記憶體中,因此它們不能從外部更改,除非使用調試器。

引用自:https://unix.stackexchange.com/questions/385339