execve()
Mặc dù system() và execve() đều có thể được sử dụng để chạy các chương trình mới, system() khá nguy hiểm nếu được sử dụng trong một chương trình đặc quyền, chẳng hạn như các chương trình Set-UID. Chúng ta đã thấy biến môi trường PATH ảnh hưởng như thế nào đến hành vi của system (), vì biến này ảnh hưởng đến cách hoạt động của shell. Nhưng đối với exevec() không có vấn đề, bởi vì hàm không gọi shell. Gọi shell có một hậu quả nguy hiểm khác, và lần này, nó không liên quan gì đến các biến môi trường.
Tình huống task: Bob làm việc cho một cơ quan kiểm toán và anh ta cần điều tra một công ty về hành vi gian lận đáng ngờ. Đối với mục đích điều tra, Bob cần có khả năng đọc tất cả các tệp trong hệ thống Unix của công ty; mặt khác, để bảo vệ tính toàn vẹn của hệ thống, Bob không thể sửa đổi bất kỳ tệp nào. Để đạt được mục tiêu này, Vince, superuser của hệ thống, đã viết một chương trình set-root-uid đặc biệt (xem bên dưới), sau đó cấp quyền thực thi cho Bob. Chương trình này yêu cầu Bob nhập tên tệp tại command lines, sau đó nó sẽ chạy /bin/cat để hiển thị tệp được chỉ định. Vì chương trình đang chạy dưới dạng root nên nó có thể hiển thị bất kỳ tệp nào Bob chỉ định. Tuy nhiên, vì chương trình không có hoạt động ghi, Vince rất chắc chắn rằng Bob không thể sử dụng chương trình đặc biệt này để sửa đổi bất kỳ tệp nào.
Trước tiên, ta tiến hành biên dịch và thực thi chương trình của Vince mà task cung cấp thành một file có tên là task8compiled. Ta tiếp tục chuyển chương trình thành root và thành chương trình Set-UID với quyền thực thị của user khác.
Ta tạo ra một file text giả task8Test.txt.
Trong quá trình chạy chương trình task8compiled, hàm chức năng cơ bản của nó sẽ cho ra output nội dung tệp được chỉ định, ở đây ta chỉ định file dummy text task8Test.txt ta vừa tạo ở trên.
Tiếp theo, hãy xem xét rằng Bob đang sử dụng tài khoản người dùng quochoang (Đối xử với Bob như những người khác (người dùng bình thường)). Ở đây, như chúng ta có thể thấy chương trình chạy bình thường khi chúng ta chỉ cung cấp tệp để đọc. Tuy nhiên, nếu chúng tôi cung cấp một đầu vào độc hại chẳng hạn như “document; /bin/sh”, ở đây chương trình sẽ đọc nội dung của tài liệu trước tiên và sau đó chạy /bin/sh dưới dạng một lệnh (theo chương trình.) /sh cho phép Bob chạy chương trình shell có đặc quyền root và bob sau đó chạy lệnh rm để xóa tệp mà nó không có quyền ghi. Thiết bị đầu cuối gốc được biểu thị bằng dấu #. Điều này cho thấy rằng mặc dù Bob không có bất kỳ quyền nào để viết, nó có thể xóa một tệp dễ dàng bằng cách giả định các đặc quyền của người dùng root.
Vấn đề ở đây là lệnh gọi hệ thống bên trong chương trình không tách biệt câu lệnh và input của người dùng. Input của người dùng viết ở cuối cùng được coi là lệnh thay vì tên dữ liệu / tài liệu.
Điều này có thể tránh được bằng cách tách biệt đầu vào của người dùng và lệnh trong chương trình. Vì lệnh gọi hệ thống yêu cầu xây dựng lệnh bằng cách sử dụng đầu vào, chúng ta nên tránh sử dụng hàm system() trong chương trình và thay vào đó sử dụng hàm execve() để xử lý bất kỳ thứ gì được nhập từ người dùng dưới dạng chuỗi đầu vào và không cho phép nó chạy dưới dạng câu lệnh. Đối với điều này, ta chỉnh sửa chương trình của mình và biên dịch lại, và cũng biến nó thành chương trình SET-UID thuộc sở hữu của root.
Ta lại thử thực hiện cùng một cuộc tấn công và thấy rằng nó không thành công vì toàn bộ chuỗi người dùng nhập vào được coi là tên tệp chứ không phải tách chuỗi trên ‘;’ làm tên tài liệu và lệnh như trước. Ngoài ra, nếu người dùng quên dấu ngoặc kép và chỉ nhập chuỗi, câu lệnh của cùng một người dùng sẽ được mở chứ không phải cần thiết của người dùng root, do đó Bob sẽ không có quyền viết, sửa hay xóa.
Điều này xảy ra bởi vì, như đã thấy trong chương trình, lệnh trong hệ thống được xây dựng bằng cách sử dụng các chuỗi được nhập vào trong khi thực thi. Trong terminal, chúng ta có thể nhập nhiều lệnh bằng cách sử dụng ‘;’ và do đó phần thứ hai sau dấu ‘;’ trong đầu vào được coi là lệnh trực tiếp chứ không phải là một phần của tên tệp. Không có xác thực đầu vào khi sử dụng system (), nhưng có một số khi chúng ta sử dụng execve(). Khi chúng ta sử dụng execve(), input được nhập trực tiếp dưới dạng tham số thứ hai cho hàm mà trên thực tế, nó được coi là toàn bộ tên tệp và không được nối vào một chuỗi để xây dựng lệnh, như trước đây. Điều này tránh được kiểu tấn công này.