[HN Gopher] Using Rust Macros to exfiltrate secrets
       ___________________________________________________________________
        
       Using Rust Macros to exfiltrate secrets
        
       Author : superjared
       Score  : 108 points
       Date   : 2021-05-14 14:12 UTC (8 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | yannoninator wrote:
       | Blocks of text again... TL;DR?
        
         | superjared wrote:
         | There's a TL;DR at the top
        
         | dnautics wrote:
         | as linked in the readme:
         | 
         | https://www.youtube.com/watch?v=RRLw3OBJ0fM
        
       | vlovich123 wrote:
       | Is there a reason that access to the filesystem isn't sandboxed
       | aggressively by the compiler? Even having build macros that can
       | access arbitrary parts of the filesystem (vs a dedicated scratch
       | directory) seems like a bad idea. Is there any legitimate use-
       | case here?
        
       | rhooke wrote:
       | A lot of the work I do recently has been using devcontainers in
       | VSCode [1]. They even have a Rust sample one. I feel like this
       | would provide at least a little bit of protection against this
       | kind of attack if you do not mount any imporant stuff into the
       | container.
       | 
       | I can't see a robust solution to this, though.
       | 
       | [1] https://code.visualstudio.com/docs/remote/containers
        
       | kam wrote:
       | This is why VSCode is adding Workspace Trust to prevent
       | extensions from running untrusted code by merely opening a
       | directory.
       | https://github.com/microsoft/vscode/issues/120251#issuecomme...
        
       | terseus wrote:
       | I can't believe that people is comparing opening a project in a
       | code editor with running a build script.
       | 
       | The PoC doesn't even open a file, it just opens the directory.
       | It's a pretty big difference, when you execute a build script you
       | _expect_ to run code, when you open a directory in your editor
       | you don't expect any side effect _at all_.
       | 
       | My guess is that since the proc_macros returns a TokenStream,
       | rust-analyzer have no way to know what it provides except running
       | it.
       | 
       | I'm not sure there's a solution for this that doesn't cripple
       | macros in Rust, apart from being able to configure rust-analyzer
       | to ignore the macros, which clearly limit its usefulness.
        
         | greenshackle2 wrote:
         | By default rust-analyzer _also_ executes Rust build scripts
         | (build.rs) just by opening the project in an IDE, so as far as
         | Rust goes the comparison is apt.                   rust-
         | analyzer.cargo.runBuildScripts (default: true)              Run
         | build scripts (build.rs) for more precise code analysis.
         | 
         | https://rust-analyzer.github.io/manual.html
        
         | Jakobeha wrote:
         | One potential solution:
         | 
         | - During a session, the first time rust-toolchain encounters a
         | proc macro it must run to analyze, it will first prompt the
         | user and warn them.
         | 
         | - If the user accepts the prompt, rust-toolchain will freely
         | run any proc macros until the next session.
         | 
         | - If the user rejects the prompt, that analysis will be
         | disabled until the next session.
         | 
         | Similar to how VSCode and other apps handle opening links.
        
         | xfer wrote:
         | Open it in notepad? You don't install software that
         | automatically build your project and complain that it is doing
         | that.
        
         | parhamn wrote:
         | Agreed. The top comments on this thread are wrong,
         | overconfident and silly. Read the article people.
        
         | cogman10 wrote:
         | You'd have to sandbox the analyzer. Let it run arbitrary code
         | but don't let it do IO. That can be pretty tricky to do for a
         | language not designed to be sandboxed.
         | 
         | Safest way would probably be something hilarious like having
         | the analyzer compiled to WASM and ran in node.js.
        
       | ngalstyan4 wrote:
       | The more general problem with trusting software supply chains is
       | well described in Ken Thompson's Turing award lecture on
       | "Trusting Trust"[1]
       | 
       | [1]:
       | http://users.ece.cmu.edu/~ganger/712.fall02/papers/p761-thom...
        
       | the_duke wrote:
       | Proc macros can run arbitrary code, so this POC is not that
       | interesting - apart from raising awareness for the problem.
       | 
       | This can be done even easier without users having to use a macro:
       | with `build.rs` build scripts, which are run by default. So all
       | you'd need is to compromise some popular dependency with a custom
       | build.rs
       | 
       | Many other languages have the same (or at least similar) problem
       | (Makefiles, npm hooks, ...)
       | 
       | There is an interesting proposal and prototype for compiling proc
       | macros to WASM so they can be run in a sandbox:
       | https://github.com/dtolnay/watt
       | 
       | But in the end it doesn't make that much difference: nothing
       | prevents a random library from just reading your secrets and
       | calling curl to send it to a server at runtime.
       | 
       | Build time execution is definitely an additional attack vector.
       | 
       | But if you use a third party dependency, you have to trust it or
       | review all it's code for every version. There is no way around
       | this, and it's true for any language.
        
         | rabidferret wrote:
         | I would just like to tack on that malicious code is against the
         | crates.io terms of service, and something like exfiltrating
         | secrets in a build script is something that very clearly
         | qualifies as malicious. If you ever encounter this in the wild,
         | please make sure you report it to the crates.io team, so it can
         | be removed.
        
           | bluejekyll wrote:
           | I think it would be better to report here,
           | https://rustsec.org/, and folks running cargo audit would be
           | aware of the issue even if they've already downloaded the
           | dependency.
        
         | judofyr wrote:
         | > But in the end it doesn't make that much difference: nothing
         | prevents a random library from just reading your secrets and
         | calling curl to send it to a server at runtime.
         | 
         | The difference here is that it happens when you _open the
         | project in the editor_. If I 'm suspicious of some code my
         | first reaction would be to open it my editor and inspect it.
         | 
         | The ESLint extension always asks whether you trust the `eslint`
         | executable before it's enabled. It's still quite easy to click
         | "allow" without thinking about it, but at least you'll have a
         | choice to not execute potentially random code.
        
           | IshKebab wrote:
           | That's a really recent feature and I'm sure Rust-analyzer
           | will support it soon.
           | 
           | I suspect the same problem exists in many other languages.
           | How can you open a CMake project without executing it?
        
         | db48x wrote:
         | Also, it's not a new problem; a Makefile or configure script
         | can run arbitrary code as well.
        
           | parhamn wrote:
           | Citation needed. Show me a Makefile + IDE combination that
           | executed code by simply opening a file. I think you're
           | missing the language server part of this.
        
             | remram wrote:
             | bash autocompletion will run arbitrary code from a
             | Makefile. I wouldn't be surprised if many editors do too.
        
               | mjw1007 wrote:
               | I think programmers' editors in general have treated
               | "automatically run arbitrary code supplied by files
               | you're editing" seriously as a security vulnerability
               | since sometime around 2000.
               | 
               | (For example, Emacs realised that 'local eval' wasn't a
               | good thing to have enabled globally in Emacs 19, in 1994,
               | and spent the next decade or more closing many other
               | loopholes involving local variables specified directly in
               | files.)
               | 
               | If modern editors and IDEs are no longer thinking that
               | way, I think that's a mistake.
        
             | efaref wrote:
             | IntelliJ with "Build in background" enabled?
             | 
             | https://www.jetbrains.com/help/idea/executing-build-file-
             | in-...
        
             | VWWHFSfQ wrote:
             | Does opening an android project in android studio
             | implicitly run any of the code in the project? I'm guessing
             | it does, because the ide seems to be very busy all the
             | time, even when idle
        
               | Nullabillity wrote:
               | build.gradle can contain arbitrary Groovy code with full
               | system access, and needs to be executed to figure out the
               | project structure.
        
           | j4yav wrote:
           | Apart from running make which of course runs the makefile,
           | under what scenario does viewing a makefile run it?
        
             | efaref wrote:
             | You're not just viewing it. You're opening it in an IDE
             | which compiles it behind the scenes for you.
             | 
             | Many IDEs also do this for other languages (e.g. by running
             | make), and the same problem applies.
        
             | remram wrote:
             | Makefiles can actually be quite dynamic, so a program
             | merely trying to figure out the list of target has to
             | execute code. For example put this in a Makefile and do
             | `make <TAB>`, the file will be created (no need to press
             | enter):                   VALUE := $(shell touch
             | /tmp/something)
        
           | mprovost wrote:
           | Yes this has always made me wonder about the pushback you see
           | with the recent move towards curl | sh installers. In the
           | past you'd download a random tarball and then run ./configure
           | which could do anything.
        
             | db48x wrote:
             | There are three main problems with curl | sh: the file one
             | the web server could be replaced without modifying the
             | source in version control (and unlike a git checkout, the
             | hash of the file is not verified), you can't read the code
             | before it runs, and curl could fail to download the whole
             | file.
             | 
             | Of course, I bet a lot of people don't bother to read any
             | of the source code of a program that they've downloaded
             | anyway.
        
               | slimsag wrote:
               | Downloading a tarball and running ./configure from it
               | (pretty dang common) also does not have the changes
               | checked into version control, nor the hash verified.
               | 
               | Same is true of `npm install`, deb/rpm/etc packages, etc:
               | you don't have proof what was distributed to you matches
               | up with what was in VCS.
               | 
               | You can read the code before it runs and solve the "curl
               | could fail" theoretical arguments by just.. removing `|
               | sh` and examining + running yourself.
        
               | db48x wrote:
               | I agree; distribution via git is better in many ways than
               | distribution via tarball. I believe that npm and similar
               | package managers mostly pull code from git repositories.
               | Of course, even then you might want to double check that
               | the package name hasn't been hijacked or sold off.
               | 
               | Of course you can break the curl|sh into separate steps
               | and check that the script isn't malicious before you run
               | it, but the fact that you have to do that makes it a bad
               | idea to distribute software this way. If you were told to
               | download an installation script, inspect it, and only
               | then to run it then there would be less of a problem.
               | curl|sh is yet another sign that we so often prefer
               | convenience over reliability and safety.
        
               | fiddlerwoaroof wrote:
               | You can solve the third problem by declaring a shell
               | function `install` that is run at the send of the script.
               | The first problem is a problem but, as far as I know,
               | most language package managers don't verify provenance
               | anyway: yarn install foo can perform arbitrary side-
               | effects either directly or through its transitive
               | dependencies.
        
               | mprovost wrote:
               | You can break the pipe and curl the file first, read it,
               | and then run it. But I doubt that anyone ever reads
               | through the thousands of lines of m4 that come with a
               | typical program that uses autoconf either.
        
               | db48x wrote:
               | If the project uses Autoconf/Automake, then you can just
               | read the .in files instead. If they include anything
               | unexpected, then it will be pretty obvious (since
               | anything unexpected will be a lot more complicated-
               | looking than anything normal). But if they do include a
               | bunch of custom m4 files, then you're going to be
               | spending more time on it than you would want.
        
         | parhamn wrote:
         | > Many other languages have the same problem (Makefiles, npm
         | hooks, ...)
         | 
         | This simply isn't true. All of these require an action by a
         | user to execute the command (e.g npm install, make build). What
         | the author is claiming is that a typical rust LSP setup will
         | execute the arbitrary macro code simply by viewing the file in
         | certain IDEs.
         | 
         | Feel free to show me an example of this in makefiles or npm and
         | I'm happy to retract.
        
           | cogman10 wrote:
           | There aren't a bunch of languages with proc-macros and IDEs.
           | That'd be where you'll see a major intersection. (Maybe C++
           | has this problem with some ides?)
           | 
           | Languages with similar risks are ones where a Repl is is the
           | key form of development. In those scenarios you are also one
           | bad dependency from stolen info.
        
             | trulyme wrote:
             | That's not really relevant though. Anything that runs code
             | on my computer without my awareness of it should be
             | considered a security bug.
        
               | tialaramex wrote:
               | Alas, the nature of computation makes this only ever a
               | matter of squinting hard enough at the problem.
               | 
               | Just as it turns out that matter and energy are almost
               | the same thing seen from a different point of view, it's
               | the same with code and data. Running code and processing
               | data are no different to a computer.
               | 
               | You think a picture of a dog and a Windows program are
               | plainly different kinds of things, the computer does not
               | agree.
               | 
               | Something like Wuffs + aims to at least control the blast
               | radius. If (in some alternate or far future world) you
               | were only ever looking at pictures of a dog via Wuffs,
               | you could at least feel confident that doing so did not
               | have some entirely unforeseen consequences, like
               | exfiltrating your SSH private keys. Today you certainly
               | can't be sure of that, none of the tools you use have
               | such a cautious approach.
               | 
               | + https://github.com/google/wuffs
        
           | foepys wrote:
           | Visual Studio has the same problem with C#. By default VS
           | will load all defined analyzers into Roslyn which can execute
           | arbitrary code.
           | 
           | VS warns you with a confirmation dialog that shouldn't just
           | be ignored because "I just want to look and not compile". So,
           | don't open any random .csproj or .sln and assume you are
           | safe.
        
       | cryptonector wrote:
       | Meh. You could do this in C also. Nothing new here.
        
       | Jaygles wrote:
       | This is a huge deal right? VSCode has to be one of the most
       | popular editors and the standard way of setting up the Rust
       | toolchain on a machine would get you in a state that makes you
       | vulnerable to this.
        
         | iudqnolq wrote:
         | It requires never actually running the program to be worse than
         | the status quo in any language. If you run code it's trivially
         | a code execution.
        
         | kzrdude wrote:
         | The only component is an editor that runs some code
         | automatically.
         | 
         | A python plugin for an editor would have the same problem - if
         | it imports a python module for any reason, like code
         | completion. Same problem of arbitrary code execution.
         | 
         | I think we should work on solutions. Sandboxing both for editor
         | plugins and for regular rust builds, should become the norm.
        
           | db48x wrote:
           | Sandboxing isn't necessary. Proper use of SELinux, which the
           | Linux kernel in your computer already supports even if your
           | distro doesn't enable it, would already prevent any process
           | other than ssh from reading your private key. The build
           | system could run ssh and ssh would be allowed to read the
           | key, but the key is still safe as long as ssh cannot be
           | tricked into revealing it. Since that's generally believed to
           | be the case, no sandboxing is necessary.
           | 
           | If your distro doesn't enable SELinux, or your distro's
           | SELinux policy doesn't protect your ssh keys, then you need
           | to upgrade. If you don't use Linux, then you need to upgrade
           | to Linux.
        
             | tux3 wrote:
             | Calling it a need to upgrade if I don't have SELinux is a
             | little combative --- I'm perfectly happy with my AppArmor
             | thankyouverymuch :)
        
               | db48x wrote:
               | I've not used AppArmor much, but I guess it's at least
               | better than nothing :)
        
             | mkesper wrote:
             | We use SELinux at work. It's really a lot of work to check
             | all requests whether they're legitimate or not.
        
               | db48x wrote:
               | No argument there. The one downside of any fine-grained
               | security system is the work it takes to build and
               | maintain the security policy.
        
             | aseipp wrote:
             | SELinux is nothing but a maintenance burden for developers
             | and users. There's a reason no major distro beyond the
             | Fedora line that enables it by default, including the one I
             | work one; it almost invariably frustrates users, has
             | incomplete support for the workflows they use, and so they
             | turn it off anyway. I literally turned off SELinux on my
             | new ARM64 Fedora machine last week, because it prevented me
             | from installing a third party binary (which I am a
             | developer of.) That binary in turn needed its own ability
             | to use namespacing support to sandbox applications (in a
             | manner that works and offers enhanced security on any Linux
             | distro, not just SELinux ones), etc. It's a non-starter.
             | 
             | > Since that's generally believed to be the case, no
             | sandboxing is necessary.
             | 
             | That's where you're wrong. It's necessary even if you
             | believe it's not. It's been proven time and time again that
             | this is the case and that the "belief" no flaws exist is
             | wrong.
             | 
             | Sandboxing approaches that use techniques like namespaces,
             | and capability security have become vastly, vastly more
             | popular over the years on Linux, and they're going to keep
             | getting more popular, precisely because they work where
             | SELinux fails (that is, 98% of the running Linux systems
             | and distros that actually exist). Browsers, WebAssembly,
             | systems like Flatpak with "Portals" -- all of them have
             | moved into capability-inspired and "component" sandboxing
             | approaches, to achieve this level of security independent
             | of the host operating system. If Chrome had decided to use
             | SELinux instead of its own sandboxing approach, it's
             | security model would be completely inferior to what it is
             | today.
        
               | db48x wrote:
               | Don't confuse the sandboxing a browser does with other
               | types. I would never argue that a browser shouldn't
               | sandbox javascript. The kernel should also prevent the
               | browser from reading your SSH keys as well, just in case
               | :)
        
           | peq wrote:
           | If I rememver correctly, the python plugin for vscode asks me
           | whether I trust the project before running anything. At least
           | that was the case when I last opened a Jupyter notebook in
           | vscode.
        
         | duped wrote:
         | This is as huge a deal as "using ./configure && make install to
         | exfiltrate secrets."
         | 
         | It's a class of supply chain attack focusing on build time code
         | evaluation. Almost every programming language has some kind of
         | support for arbitrary code execution at build time, and any
         | project of scale is going to require it.
         | 
         | RCE isn't an interesting exploit when the system is literally
         | designed to run code from somewhere else.
        
           | not2b wrote:
           | No, it's worse. People will have very different expectations.
           | Running 'make install' especially as root implies a high
           | level of trust, so users will be appropriately cautious.
           | Users won't expect that simply opening code in an editor will
           | be similarly risky (though it's similar to malicious Word and
           | Excel macros, Office now disables those by default for
           | documents coming from an untrusted source, like via email).
        
           | j4yav wrote:
           | This isn't build time though really, which I agree is a
           | moment you would expect to run arbitrary code. This is "edit
           | time."
        
             | duped wrote:
             | It is build time. Whether rust-analyzer should run build-
             | time code at initialization is a different discussion.
        
       | not2b wrote:
       | This issue is very similar to the problem of malicious macros in
       | Microsoft Office documents, and I think it needs to be addressed
       | somehow (by figuring out a proper security model and asking for
       | user confirmation for actions outside this model).
        
       | mike-cardwell wrote:
       | This doesn't affect me because I
       | https://www.grepular.com/Sandbox_Rust_Development_with_Rust_...
        
       | greenshackle2 wrote:
       | You can also just put arbitrary code in build.rs, it will be run
       | by cargo check, rust-analyzer, etc. Though I admit macro
       | expansion hacks are more fun and easier to hide.
        
       | juancampa wrote:
       | Are there any working groups or teams in the rust foundation[0]
       | looking into stuff like this? I know every package manager has
       | these issues but there's no technical reason preventing us from
       | building sandboxes (i.e. WASM, deno, ...) for this and making it
       | a first class citizen of cargo/rustup/etc.
       | 
       | Just installing a relatively popular crate (say Hyper) makes you
       | realize that all of your secret could have been stolen by any of
       | the myriad of dependencies.
       | 
       | [0] https://www.rust-lang.org/governance
        
       | duped wrote:
       | For what it's worth, any VSCode extension that integrates with
       | language tooling could be used to implement this.
        
         | estebank wrote:
         | This is an inherent problem of languages where execution is
         | needed to understand its semantics. Most interpreted languages
         | have this issue, and Rust has this issue due to proc-macros
         | being Rust code that needs to be compiled and executed to
         | process other Rust code.
        
       ___________________________________________________________________
       (page generated 2021-05-14 23:01 UTC)