Skip to main content

secmem_proc/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![forbid(future_incompatible, unsafe_op_in_unsafe_fn)]
3#![warn(missing_docs)]
4#![warn(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
5#![warn(clippy::must_use_candidate)]
6#![allow(clippy::needless_lifetimes)]
7//! `secmem-proc` is a crate designed to harden a process against
8//! *low-privileged* attackers running on the same system trying to obtain
9//! secret memory contents of the current process. More specifically, the crate
10//! disables core dumps, makes a best effort to disable the ability to trace it,
11//! and makes a minimal effort to detect already attached tracers.
12//!
13//! __Note__: all the crate does is *hardening*, i.e. it tries to make attacks
14//! *harder*. It can by no means promise any security! In particular, when an
15//! attacker ptrace attaches to the process before `harden_process` is
16//! executed, it is game over for the process. This crate is no substitute for
17//! properly hardening your OS (configuration)!
18//!
19//! Note that hardening the process also severely limits the ability to debug
20//! it. Therefore you are advised to only harden release builds, not debug
21//! builds.
22//!
23//! # Windows
24//! On Windows, [`harden_process`] sets a severly restricted DACL for the
25//! process. (More precisely, only the `PROCESS_QUERY_LIMITED_INFORMATION`,
26//! `PROCESS_TERMINATE` and `SYNCHRONIZE` permissions are enabled.) This could
27//! be too restrictive for the application to function correctly. When more
28//! permissions are required, the safe API in the [`win_acl`] module can be used
29//! to create and set a custom DACL instead.
30//!
31//! On windows, this crate depends on `std` via a dependency on the [`windows`
32//! crate].
33//!
34//! # Examples
35//! In the below example the main function of some application calls the main
36//! hardening function provided by this crate: `harden_process`. This will
37//! perform all available hardening steps (except unstable ones) on the target
38//! platform. When one of the hardening steps fails or a debugger is detected,
39//! the function returns an error. It is advised to terminate the application on
40//! any error.
41//!
42//! ```
43//! fn main() {
44//!     // call `secmem_proc::harden_process` before doing anything else, to harden the process
45//!     // against low-privileged attackers trying to obtain secret parts of memory which will
46//!     // be handled by the process
47//!     if let Err(e) = secmem_proc::harden_process() {
48//!         println!("ERROR: could not harden process, exiting");
49//!         println!("ERROR: {}", e);
50//!         return;
51//!     }
52//!     // rest of your program
53//! }
54//! ```
55//!
56//! It is also possible to configure what kind of hardening steps are performed.
57//! For this, the API in [`config`] can be used. An example is shown below:
58//!
59//! ```
60//! fn main() {
61//!     // harden before doing anything else
62//!     let mut config = secmem_proc::Config::DEFAULT;
63//!     config.set_anti_tracing(false);
64//!     config.set_fs(false);
65//!     if let Err(e) = config.harden_process() {
66//!         println!("ERROR: could not harden process, exiting");
67//!         println!("ERROR: {}", e);
68//!         return;
69//!     }
70//!     // rest of your program
71//! }
72//! ```
73//!
74//! In the last example we use the API in [`win_acl`] to set a custom DACL on
75//! Windows. In the example we grant the `PROCESS_CREATE_THREAD` permissions in
76//! addition to the default ones. Note that in this particular use case the same
77//! could have been achieved using [`Config::set_win_dacl_custom_user_perm`],
78//! which is clearly a lot easier. The below approach is, however, a lot more
79//! flexible.
80//!
81//! ```
82//! #[cfg(windows)]
83//! fn set_windows_dacl() -> secmem_proc::Result {
84//!     use windows::Win32::System::Threading::{
85//!         PROCESS_CREATE_THREAD, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE,
86//!         PROCESS_TERMINATE,
87//!     };
88//!     use windows::Win32::Security::WinCreatorOwnerRightsSid;
89//!
90//!     use secmem_proc::win_acl::{AddAllowAceAcl, EmptyAcl, SidRef, WellKnownSidBuffer};
91//!
92//!     // First obtain the well-known owner rights SID
93//!     let mut buf = WellKnownSidBuffer::new();
94//!     let sid = SidRef::well_known(&mut buf, WinCreatorOwnerRightsSid)?;
95//!
96//!     // Now specify the ACL we want to create
97//!     // Only things explicitly allowed with `AddAllowAceAcl` will be allowed; nothing else
98//!     let acl_spec = EmptyAcl;
99//!     let access_mask = PROCESS_QUERY_LIMITED_INFORMATION
100//!         | PROCESS_TERMINATE
101//!         | PROCESS_SYNCHRONIZE
102//!         | PROCESS_CREATE_THREAD;
103//!     let acl_spec = AddAllowAceAcl::new(acl_spec, access_mask, sid);
104//!
105//!     // Create ACL and set as process DACL
106//!     let acl = acl_spec.create()?;
107//!     acl.set_process_dacl_protected()?;
108//!     Ok(())
109//! }
110//!
111//! fn main() {
112//!     // harden before doing anything else
113//!     let mut config = secmem_proc::Config::DEFAULT;
114//!     #[cfg(windows)]
115//!     config.set_win_dacl_custom_fn(set_windows_dacl);
116//!     config.set_fs(false);
117//!     if let Err(e) = config.harden_process() {
118//!         println!("ERROR: could not harden process, exiting");
119//!         println!("ERROR: {}", e);
120//!         return;
121//!     }
122//!     // rest of your program
123//! }
124//! ```
125//!
126//! # Cargo features
127//! - `std` (default): Enable functionality that requires `std`. Currently
128//!   required for anti-tracing on Linux via `/proc/self/status`. This feature
129//!   is enabled by default.
130//! - `unstable`: Enable functionality that depends on undocumented or unstable
131//!   OS/platform details. This feature only enables support for these; to
132//!   actually enable these anti-debugging methods, they have to be specifically
133//!   enabled in the [configuration].
134//! - `dev`: This feature enables all features required to run the test-suite,
135//!   and should only be enabled for that purpose.
136//!
137//! # Implementation
138//! - Disable ptrace and core dumps for the process on linux using prctl
139//! - Disable ptrace and core dumps for the process on freebsd using procctl
140//! - Disable ptrace on macos using ptrace
141//! - Disable core dumps for the process on posix systems using rlimit
142//! - Set restricted DACL for the process on windows
143//! - When the `std` feature is enabled, detect debuggers on linux by reading
144//!   `/proc/self/status` (std, anti-tracing)
145//! - Detect debuggers on windows using `IsDebuggerPresent` and
146//!   `CheckRemoteDebuggerPresent` (anti-tracing)
147//! - With unstable enabled, hide the thread from a debugger on windows
148//!   (unstable, anti-tracing)
149//! - With unstable enabled, detect debuggers on windows by reading from the
150//!   kernel structure `KUSER_SHARED_DATA` (unstable, anti-tracing)
151//!
152//! # Anti-tracing
153//! The hardening methods employed by this crate can be divided into two groups:
154//! * security related process hardening, and
155//! * anti-tracing.
156//!
157//! The difference between the two lies in the thread model. Process hardening
158//! mostly assumes the process is not yet under attack, e.g. it is not yet being
159//! traced. Hardening methods then make changes to the configuration of the
160//! process to limit access other processes have to it, e.g. disable tracing of
161//! the process or disable core dumps. Anti-tracing assumes the process is
162//! already traced/debugged by a malicious process (malware). The goal is then
163//! to detect the tracer/debugger. Anti-tracing methods can always be subverted
164//! by a tracer/debugger, though some are harder to work around than others.
165//! (The `KUSER_SHARED_DATA` unstable anti-tracing method on windows is a
166//! difficult one to work around.) Anti-tracing can be disabled using
167//! [`Config::set_anti_tracing(false)`].
168//!
169//! [`windows` crate]: https://crates.io/crates/windows
170//! [configuration]: config::Config
171//! [`Config::set_anti_tracing(false)`]: Config::set_anti_tracing
172
173#[cfg(windows)]
174extern crate alloc;
175
176mod internals;
177
178pub mod components;
179pub mod config;
180pub mod error;
181pub mod harden;
182pub mod macros;
183
184#[cfg(windows)]
185pub mod win_acl;
186/// This module is only available on windows.
187#[cfg(not(windows))]
188pub mod win_acl {}
189
190pub use config::Config;
191pub use error::Result;
192pub use harden::harden_process;
193
194#[cfg(test)]
195mod tests {
196    /// > Freedom is the freedom to say that two plus two makes four.
197    ///
198    /// Nineteen Eighty-Four, George Orwell
199    #[test]
200    fn freedom() {
201        assert_ne!(2 + 2, 5);
202        assert_eq!(2 + 2, 4);
203    }
204}