[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)