From 577474d89ee61aef4a48145cdec82a638d874751 Mon Sep 17 00:00:00 2001 From: Jeff Quast Date: Tue, 31 Mar 2026 07:00:28 -0400 Subject: [PATCH] Prevent "screen scraping", disable DECRQCRA (#7701) * Security fix: prevent screen scraping Problem ------- DECRQCRA was added in 6cbb3ba43, for 9702d1cf5 (esctest integration) Sunday, March 17 2019. Six days later, March 23 2019, although esctest was removed in c93f967bc, DECRQCRA remained enabled by default for all WezTerm releases since. March 2023, @j4james mentioned WezTerm has this option enabled by default https://github.com/microsoft/terminal/issues/14974, "some people consider it a security risk" September 2023, https://dgl.cx/2023/09/ansi-terminal-security#cursor-checksum article writes, "using DECRQCRA [..] potential attack here is reading what is displayed on the terminal before a user SSHes to a remote system." By switching to "alternate screen", it also possible to recover last TUI display, such as contents of the file last opened in an editor. Example CLI script scrapes screens '0' and '1' of WezTerm, https://github.com/jquast/blessed/blob/master/bin/screen-scrape.py Solution -------- Disable https://vt100.net/docs/vt510-rm/DECRQCRA.html by default, may be re-enabled by configuration. --- Refs: https://github.com/wezterm/wezterm/pull/7701 Co-authored-by: Wez Furlong --- config/src/config.rs | 6 ++++++ config/src/terminal.rs | 4 ++++ docs/changelog.md | 3 +++ term/src/config.rs | 4 ++++ term/src/terminalstate/mod.rs | 18 ++++++++++-------- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/config/src/config.rs b/config/src/config.rs index 75afb97a1..0431cdec5 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -260,6 +260,12 @@ pub struct Config { #[dynamic(default)] pub enable_title_reporting: bool, + /// Whether the terminal should respond to DECRQCRA checksum requests. + /// Disabled by default as it allows programs to read screen contents. + /// + #[dynamic(default)] + pub enable_checksum_rectangular_area: bool, + /// Specifies the width of a new window, expressed in character cells #[dynamic(default = "default_initial_cols", validate = "validate_row_or_col")] pub initial_cols: u16, diff --git a/config/src/terminal.rs b/config/src/terminal.rs index 7616abba3..8ff78206e 100644 --- a/config/src/terminal.rs +++ b/config/src/terminal.rs @@ -82,6 +82,10 @@ impl wezterm_term::TerminalConfiguration for TermConfig { self.configuration().enable_title_reporting } + fn enable_checksum_rectangular_area(&self) -> bool { + self.configuration().enable_checksum_rectangular_area + } + fn enable_kitty_keyboard(&self) -> bool { self.configuration().enable_kitty_keyboard } diff --git a/docs/changelog.md b/docs/changelog.md index 62ef35609..0dce04981 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -22,6 +22,9 @@ usually the best available version. As features stabilize some brief notes about them will accumulate here. #### Changed +* DECRQCRA is now disabled by default to prevent silent screen scraping. + Set `enable_checksum_rectangular_area = true` to re-enable it. + Thanks to @jquast! #7701 * Wayland: currently being reimplemented, it maybe more unstable than usual. Please file GH issues for any problems you see. Many thanks to @tzx and @tmccombs! #4777 #5781 diff --git a/term/src/config.rs b/term/src/config.rs index eb53cf2c2..78c68a570 100644 --- a/term/src/config.rs +++ b/term/src/config.rs @@ -220,6 +220,10 @@ pub trait TerminalConfiguration: Downcast + std::fmt::Debug + Send + Sync { false } + fn enable_checksum_rectangular_area(&self) -> bool { + false + } + fn log_unknown_escape_sequences(&self) -> bool { false } diff --git a/term/src/terminalstate/mod.rs b/term/src/terminalstate/mod.rs index 3d812f978..ac2a60220 100644 --- a/term/src/terminalstate/mod.rs +++ b/term/src/terminalstate/mod.rs @@ -2073,14 +2073,16 @@ impl TerminalState { right, .. } => { - let checksum = self.checksum_rectangle( - left.as_zero_based(), - top.as_zero_based(), - right.as_zero_based(), - bottom.as_zero_based(), - ); - write!(self.writer, "\x1bP{}!~{:04x}\x1b\\", request_id, checksum).ok(); - self.writer.flush().ok(); + if self.config.enable_checksum_rectangular_area() { + let checksum = self.checksum_rectangle( + left.as_zero_based(), + top.as_zero_based(), + right.as_zero_based(), + bottom.as_zero_based(), + ); + write!(self.writer, "\x1bP{}!~{:04x}\x1b\\", request_id, checksum).ok(); + self.writer.flush().ok(); + } } Window::ResizeWindowCells { .. } => { // We don't allow the application to change the window size; that's -- 2.51.2