2013-08-05 26 views
5

Trong môi trường tùy chỉnh của tôi, thư viện trình chặn được tải sẵn để chạy một triển khai đặc biệt gồm các cuộc gọi bind(), connect(), v.v.Việc sử dụng khả năng của Linux có vô hiệu hóa LD_PRELOAD

Vấn đề tôi thấy là bất cứ khi nào ứng dụng được kích hoạt một cách rõ ràng khả năng bằng lệnh setcap, thực thi ứng dụng không tải trước thư viện chặn và gọi mặc định là libc connect().

Đây có phải là hành vi mong đợi không? Nếu có, điều gì có thể là lý do để vô hiệu hóa LD_PRELOAD?

Có bất kỳ tinh chỉnh hoặc phương pháp nào có thể sử dụng để tải trước thư viện thành công với các khả năng được bật hay không.

+2

Xem [câu hỏi này] (http://stackoverflow.com/questions/9843178/linux-capabilities-setcap-seems-to-disable-ld-library-path) để có câu trả lời. – scai

+1

Bạn có thể viết chương trình bao bọc cho nhị phân đích. Nó sẽ nhiều hơn hoặc ít hơn phải được setuid gốc. Nó sẽ nĩa một tiến trình con, sau đó thực hiện nhị phân đích (với 'LD_PRELOAD'); nhị phân đích không có bất kỳ khả năng tệp nào được đặt. Thư viện tải trước của bạn sau đó giao tiếp với tiến trình con (thông qua ví dụ: một cặp socket nói fd 3), với quá trình con cấp các khả năng cần thiết cho quá trình đích, sau đó thoát (và thư viện tải trước gặt con). Hãy cho tôi biết nếu bạn muốn có một ví dụ. –

+0

@NominalAnimal Rất vui nếu bạn có thể chỉ cho tôi một ví dụ. –

Trả lời

4

Giống như Oliver Matthews đã trả lời, LD_PRELOAD bị tắt đối với cả hai tệp nhị phân setuid và đối với các tệp nhị phân có khả năng tệp, vì lý do bảo mật.

Để tải trước một thư viện trong khi vẫn cho phép khả năng tập tin, bạn có hai lựa chọn:

  1. Đặt thư viện tải trước setuid gốc

    (The Linux mối liên kết động ld.so không preload thư viện ngay cả đối với setuid/file các tập tin nhị phân có khả năng bật, nếu các thư viện thuộc sở hữu của thư mục gốc và được đánh dấu là uid.)

  2. Sử dụng trình bao bọc gốc setuid

    Trình bao bọc có được các đặc quyền gốc đầy đủ (cả ID người dùng thực và hiệu quả và ID nhóm không) và lưu trữ ID người dùng thực và ID nhóm ban đầu, ví dụ: biến môi trường (s).

    Thư viện được tải sẵn có hàm tạo, ví dụ:

    static void my_library_init(void) __attribute__((constructor)); 
    static void my_library_init(void) 
    { 
        /* ... */ 
    } 
    

    được tự động chạy trước main() (nhưng có thể sau khi nhà xây dựng khác trong các thư viện tải trước kia, hoặc trong thư viện mà các thư viện tải trước phụ thuộc vào).

    Nhà xây dựng này có được các khả năng mong muốn, được chỉ định qua biến môi trường (getenv(), cap_from_text()) hoặc tệp thực thi nhị phân (cap_from_file("/proc/self/exe")).

    Các nhà xây dựng phải tạm thời sử dụng prctl(PR_SET_KEEPCAPS, 1) để giữ khả năng qua một sự thay đổi danh tính, và duy trì CAP_SETUIDCAP_SETGID khả năng để có thể thay đổi danh tính từ gốc đến người dùng và nhóm quy định tại các biến môi trường, trước khi giới hạn bản thân với khả năng chính thức bộ.

Cả hai tùy chọn đều có những cân nhắc bảo mật rõ ràng. Tôi khuyên bạn nên kiểm tra sanity (và xóa LD_PRELOAD) trong hàm tạo thư viện được tải sẵn. Nếu có bất kỳ điều gì đáng ngờ, hãy sử dụng _exit() để hủy quá trình ngay lập tức.Nói chung, tôi khuyên bạn nên chọn tùy chọn đầu tiên để đơn giản (cả vấn đề triển khai và bảo mật), nhưng nếu có lý do nào đó không thể sử dụng, tôi cũng có thể cung cấp bằng chứng về mã khái niệm cho trường hợp thứ hai. (Tôi đã xác minh cả hai tùy chọn hoạt động trên Ubuntu 12.04.2 LTS chạy một hạt nhân x86-64 3.8.0-27 chung, sử dụng hệ thống tệp ext4.)

Hy vọng điều này sẽ hữu ích.

+0

Tùy chọn 1, thiết lập root setuid thành thư viện tải trước đã giúp tôi tải trước. Tuy nhiên khi tôi thiết lập khả năng lập trình có vài thay đổi biên sau khi 'exec',' prctl (PR_SET_KEEPCAPS, 1L) 'không giữ lại các khả năng. Thực thi chương trình thử nghiệm của tôi chạy với 'strace/gdb./Executable' không thể duy trì khả năng và đã thành công khi nó chạy như'./Executable'. Điều này là do nó có sự thay đổi ranh giới từ shell thành 'strace/gdb' theo sau là'./Executable'. Muốn biết những gì đang gây ra khả năng không giữ lại thay đổi ranh giới. –

+0

@SunEric: 'prctl (PR_SET_KEEPCAPS, 1L)' chỉ có hiệu lực cho 'exec *()' tiếp theo, vì 'PR_SET_KEEPCAPS' luôn được đặt lại về 0 sau lệnh gọi' exec *() '. Điều này được đề cập trong man page 'man 2 prctl'. –

4

Có, vì lý do bảo mật (xem man sudo).

Bạn sẽ phải làm việc xung quanh nó bằng cách mở thư viện một cách rõ ràng từ bên trong mã của bạn khi bắt đầu main() sử dụng dlopen (hoặc bằng cách gói chính hoặc tương tự).

+0

Sửa lỗi nếu hiểu sai về 'ld_preload': thư viện đối tượng dùng chung được tải trước rất trước bất kỳ hàm thư viện chuẩn nào. Nếu điều này đúng, thư viện bắt buộc được tải trước, sau đó là 'libc.so'; Khi bạn đề nghị mở một thư viện bằng cách sử dụng 'dlopen' trong ứng dụng' main() ', không phải là ký hiệu đã được giải quyết bởi thư viện được tải sẵn hoặc libc vào lúc' dlopen' được mở ra chưa? –

Các vấn đề liên quan