.bash_profile に LD_PRELOAD を設定すると su を実行時に「ld.so.1: su: warning: .... : open failed: illegal insecure pathname」エラーが発生する

現象

-bash-3.00$ id 
uid=501(oracle) gid=501(oinstall)
-bash-3.00$ cp -pi /usr/lib/32/libc.so /home/oracle/
-bash-3.00$ vi /home/oracle/.bash_profile
export LD_PRELOAD_32=/home/oracle/libc.so
-bash-3.00$ source .bash_profile
-bash-3.00$ su - oracle
ld.so.1: su: warning: /home/oracle/libc.so: open failed: illegal insecure pathname
Password: 

原因

  • Trusted Directories に設定されていないディレクトリから共有ライブラリをロードしようとしているため。

対処

  • Trusted Directories に任意のパスを追加する。
  -bash-3.00# crle -u -s /home/oracle
  -bash-3.00# crle
  Configuration file [version 4]: /var/ld/ld.config  
    Default Library Path (ELF):   /lib:/usr/lib  (system default)
    Trusted Directories (ELF):    /lib/secure:/usr/lib/secure:/home/oracle
  
  Command line:
    crle -c /var/ld/ld.config -s /lib/secure:/usr/lib/secure:/home/oracle
  • 共有ライブラリをプリロードしてもエラーが発生しないことを確認する。
-bash-3.00# su - oracle
-bash-3.00$ su - oracle
Password: 

エラーが発生しない。

参考


追記(2011/02/12):

crle(1)


名前
crle – configure runtime linking environment

...

-s dir
Specify a new trusted directory dir for secure ELF or AOUT objects. See SECURITY in ld.so.1(1) for a definition of secure objects.

Multiple occurrences of this option are permitted. The type of object that is applicable to the search is specified by the preceding -t option, or defaults to ELF.

The default trusted directories for secure 32-bit ELF objects are /lib/secure followed by /usr/lib/secure. For 64-bit secure ELF objects, the default trusted directories are /lib/secure/64 followed by /usr/lib/secure/64.
The default trusted directories for secure AOUT objects are /usr/4lib, followed by /usr/lib, followed by /usr/ucblib, and finally /usr/local/lib.

Use of this option replaces the default trusted directories. Therefore, a -s option is normally required to specify the original system default in relation to any new directories that are being applied. However, if the -u option is in effect, and a configuration file does not exist, the system defaults are added to the new configuration file. These defaults are added before the new directories specified with the -l option.

http://download.oracle.com/docs/cd/E19253-01/819-1210/crle-1/index.html

ld.so.1(1)


Name
ld.so.1– runtime linker for dynamic objects

...

Security
Secure processes have some restrictions applied to the evaluation of their dependencies and runpaths to prevent malicious dependency substitution or symbol interposition.

The runtime linker categorizes a process as secure if the issetugid(2) system call returns true for the process.

For 32-bit objects, the default trusted directories that are known to the runtime linker are /lib/secure and /usr/lib/secure. For 64-bit objects, the default trusted directories are /lib/secure/64 and /usr/lib/secure/64. The utility crle(1) can be used to specify additional trusted directories that are applicable for secure applications. Administrators who use this technique should ensure that the target directories are suitably protected from malicious intrusion.

If an LD_LIBRARY_PATH family environment variable is in effect for a secure process, only the trusted directories specified by this variable are used to augment the runtime linker's search rules.

In a secure process, runpath components that are provided by the application or any of its dependencies are used, provided the component is a full path name, that is, the path name starts with a '/'.

In a secure process, the expansion of the $ORIGIN string is allowed only if the string expands to a trusted directory. However, should a $ORIGIN expansion match a directory that has already provided dependencies, then the directory is implicitly secure. This directory can be used to provide additional dependencies.

In a secure process, LD_CONFIG is ignored. A secure process uses the default configuration file, if a configuration file exists. See crle(1).

In a secure process, LD_SIGNAL is ignored.

Additional objects can be loaded with a secure process using the LD_PRELOAD, or LD_AUDIT environment variables. These objects must be specified as full path names or simple file names. Full path names are restricted to known trusted directories. Simple file names, in which no '/' appears in the name, are located subject to the search path restrictions previously described. Simple file names resolve only to known trusted directories.

In a secure process, any dependencies that consist of simple filenames are processed using the path name restrictions previously described. Dependencies expressed as full path names or relative path names are used as is. Therefore, the developer of a secure process should ensure that the target directory referenced as a full path name or relative path name dependency is suitably protected from malicious intrusion.

When creating a secure process, relative path names should not be used to express dependencies, or to construct dlopen(3C) path names. This restriction should be applied to the application and to all dependencies.

http://download.oracle.com/docs/cd/E19253-01/816-5165/6mbb0m9jg/index.html


追記(2011/02/17):
仮説的なまとめ。

仕様
  • LD_PRELOAD 環境変数を使って Trusted Directories に設定されていないディレクトリから共有ライブラリをロードしようとすると、「ld.so.1: su: warning: ... : open failed: illegal insecure pathname」という警告が出る。
目的
  • root以外のユーザがroot権限で任意のコードを実行できないようにするための機能ではないかと思う。
  • setuid属性、setgid属性が付与されているバイナリの実行可能ファイルを実行する際、実行時リンカ(ld.so.1)は Trusted Directories (デフォルトは /lib/secure, /usr/lib/secureなど)以外にあるコンパイル時にリンクされていない共有ライブラリをロードしようとすると、「illegal insecure pathname」という警告を出す。
  • これは、悪意を持ったユーザーによってシンボルがインターポジショニングされるのを防ぐため。
検証

ちょっと検証してみた。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

void setlocale(void) {
  pid_t pid;
  int status;
  
  pid = fork();

  if ( pid < 0 )
  {
    fprintf (stderr, "fork error\n");
    exit(1);
  }

  if (pid == 0) 
  {
    execl("/usr/bin/sleep", "sleep_forked_by_hoge.so", "60", NULL);
    perror("/bin/cat");
    exit(-1);
  }
  else
  {
    waitpid(pid, &status, 0);
    printf ("child(PID=%d) finished!\n", pid);

    
    if (WIFEXITED(status)){
      printf("exit, status=%d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)){
      printf("signal, sig=%d\n", WTERMSIG(status));
    } else {
      printf("abnormal exit\n");
    }
    exit(0);
  }
}

hoge.c をコンパイルして共有ライブラリを作成する。

-bash-3.00$ cc -o hoge.so -G -K pic hoge.c 
  • LD_PRELOAD_32 に指定して共有ライブラリをプリロードする。
-bash-3.00$ export LD_PRELOAD_32=/home/oracle/lib/hoge.so 
  • su コマンドを実行する。
-bash-3.00$ su - 
  • 別ターミナルで ps コマンドで確認すると su からプロセスが fork されていて、実効ユーザが root になっている。
-bash-3.00$ ps -ef|grep [2]2824
    root 22824 22783   0 22:23:08 pts/3       0:00 su -
    root 22825 22824   0 22:23:08 pts/3       0:00 sleep_forked_by_hoge.so 60