Executing code written in any language -- dynamic, static, compiled, interpreted -- would be problematic here.
> That service loads the low level antivirus engine, and analyzes untrusted data received from sources like the filesystem minifilter or intercepted network traffic.
Forget JS. Do not load or execute code from untrusted sources in an unsandboxed environment with system permissions. This is about capabilities, not syntax. If your main takeaway is, "they should have used a C interpreter instead", then you have entirely missed the point.
But how many C/C++ engineers would think to design a system that runs a min interpreted code, vs JS ones? The take isn’t as bad as you think.
Multiple people, in this very thread, including you[0]. And apparently at least one Avast engineer and their upper management.
I'll requote/paraphrase another commenter[1] down-thread: it wasn't JS devs who wrote a custom interpreter inside a privileged C/C++ program. It was a C/C++ developer who thought, "I can handle this."
It's very important when calling out security failings to point out the real failing. If people are reading this and trying to take away security advice, I don't want their takeaway to be, "so my custom LUA interpreter is fine."
Avast is (was) running untrusted code in their system, by design. If your intention is to say that they shouldn't run untrusted code, then just say that. Don't waste time talking about whether memory safety matters for untrusted code -- just avoid untrusted code.
A lean C++ solution to the design problem of "how do we run untrusted code" is no better than an interpreted solution. You're focusing on the implementation, not the core design, and it is the core design that's flawed.
The only result of bringing JS up in a conversation like this is going to be to make people doing equally unsafe things in/with other languages feel better about themselves.
No interpreter code should be running there, C, C++ or JS. What I am saying is that this design is made by or for people who are seeking to run JS as part of the business logic, which is ridiculous. I haven't brought up "memory safety" at all. Perhaps read the thread before commenting?
You can, of course, safely use JS for business logic without executing code from unsafe sources, in the same way that you can safely use C#, LUA, Lisp, or any other language. But that aside, your criticism is completely irrelevant to the actual security flaw.
I don't see any indication Avast was trying to use JS for business logic in the first place. I'm sure they over-embed crap for other parts of their interface, but that's not what's happening here. Avast was not loading a custom interpreter to execute their own business logic, they were loading a custom interpreter to analyze user files as part of their virus scan.
> That service loads the low level antivirus engine, and analyzes untrusted data received from sources like the filesystem minifilter or intercepted network traffic.
> Despite being highly privileged and processing untrusted input by design, it is unsandboxed and has poor mitigation coverage.
It wasn't Avast's reliance on JS as a development tool that caused them to say, "maybe we should parse and run arbitrary files on the filesystem in a process with elevated permissions."
It's their business logic that's the problem. Avast's business logic is, "we want to execute untrusted source code to see if it contains viruses." It wasn't a JS engineer saying, "I can't do my job in C++". It was a C/C++ engineer saying, "I know how I can tell if this file is dangerous -- I'll run it in the main process to see what it does."
If you can tell me a safe way to accomplish that business logic in C/C++ or in any other language without process isolation or sandboxing, then I'll concede the point. But I'm pretty sure you can't.
As an aside, an interesting followup to this disclosure would be if someone tested whether or not Avast is also interpreting other languages like LUA that they regard as potentially dangerous. I wouldn't necessarily take it as a given that JS was the only language they were doing this with.
Nope, you've fundamentally misunderstood the issue at hand. The JS is not Avast's business logic. Rather, the program is attempting to analyze and detect malware in JS that the user encounters, similar to how an AV engine might inspect Microsoft Office files for malware. "Low-effort JS developers" had nothing to do with this. "C/C++ engineers" (and/or those above them) made this decision.
Or maybe stop trying to "gotcha" other commenters by making assumptions about what they were trying to say.
> but the scale of a JS interpreter + the fact that it's meant for executing arbitrary code
"the fact that it's meant for executing arbitrary code" is the only part of that statement you need. Avoid executing arbitrary code in unsandboxed/unisolated environments, even in a normal C++ app, even if the code is compiled. The scale doesn't matter.
If you're following best practices and isolating the process, then the number of lines of code shouldn't matter for the actual security. You should assume that a custom-built interpreter designed to run malicious code always has bugs -- whether it's running JS, LUA, whatever -- so you should run that code in a separate, sandboxed process that doesn't have system access.
It's not that the scale doesn't make a difference in complexity, it's that (for the most part) if you find yourself at the point where you're asking questions about the scale, you have already seriously messed up, and you need to go back and rethink your design.
----
The business problem Avast was trying to solve was, "how do we tell whether or not a random Javascript file contains malware?" The answer they came up with was, "we'll run the file in a process with system-access and see what happens."
I'll ask the same question I asked the original commentor: what is a safe way to solve that business problem without process isolation? And if you are correctly isolating the untrusted code, then why does the complexity of the JS interpreter matter?
The JS interpreter isn't there to lay out an interface, it's there to help them understand untrusted code that they find on the filesystem.
To be honest though, in the modern world, picking a stable compiler like GCC is a good enough choice for life - this isn't the 90s where you might have to dumpster dive to find copies of that specific borland compiler your company decided to tailor their code to.
(edit: All the above holds until you start making assumptions about uninitialized memory, at that point you're really in trouble and, honestly, C++ really should be better about preventing you from using dirty memory)
void doDangerousStuffIfAuthorized(AUTHORIZATION* authorizationPtr){
AUTHORIZATION authorization = *authorizationPtr
if(authorizationPtr == null || !isValid(authorization)) return;
doDangerousStuff();
}
into something that executes doDangerousStuff() when passed null. When users complain about such things, the answer is that the code was causing undefined behaviour and so what GCC is doing is correct according to the standard.This is essentially how antivirus software works. Every one of them packages an emulator to execute malicious binaries.
I'd say the number one thing stopping C++ devs from running eval'd C++ code is the lack of a std eval, and that's probably it.
Frankly, I am far less concerned with the js interpreter than I am the rest of the codebase.
http://computervirus.uw.hu/ch11lev1sec4.html
https://www.blackhat.com/presentations/bh-europe-08/Feng-Xue...
http://joxeankoret.com/download/breaking_av_software_44con.p...
So, I did what any sane devops engineer would do; I throttled the CPU use limit for its cgroup in the systemd service file. Now no more scans.
Except now the UI wouldn't load. Couldn't figure out why. Just an empty white window. Turns out, it's running a node.js server and the whole UI is rendered in HTML/CSS/JS, but because of that it was so non-performant that the UI would effectively not render at all if it couldn't slam your CPU.
I can't think of any native-code, native-widget, control-panel-type UI that would completely fail to render the entire window at all if limited to 10% CPU time, but hey, here we are.
> Turns out, it's running a node.js server and the whole UI is rendered in HTML/CSS/JS, but because of that it was so non-performant that the UI would effectively not render at all if it couldn't slam your CPU.
So was it the naive approach to system scanning, or the web-based UI that was the problem? Because there are some performant desktop applications using web rendering, like VS Code. Though I'm not sure how VSCode would behave if limited to 10% of CPU, because that's kind of a weird scenario.
I think this makes it worse.
What reason is there to even have such an interpreter in a highly privileged process?
FFS, even display drivers don't run with full system privileges anymore.
Benchmarks. It's faster if you don't push all the scanned data through a process boundary.
And BTW, this is why WebAssembly runtime on the server is a big deal. Being able to painlessly run any untrusted code from "nonsafe language" in a sandboxed environment.
Also, if you read it correctly, Avast is running "wild" Javascript in a custom privileged VM (potentially written in C++)
Contrast this with tailor-made, slim and well tested C++ code. And yes, I do expect security companies to have well-written and well-tested code.
Your expectation makes no sense, given the vulnerabilities we've seen in AV software in the past decade.
If they insist that executing suspect JS is a good idea, they a) probably should use an established interpreter unless there's good reasons not to and b) not run it privileged.
EDIT: Avast appears to have deactivated this now: https://twitter.com/avast_antivirus/status/12376853435807539...
Therefore when you see an exposed unsandboxed VM, you instantly know it's critical issue.
No, not really? Depending on the browser they have generally have a small-to-medium attack surface. Yes, they can JIT, but often they can't do much else.
> and are prime candidates for gray and black market vulnerability hunts
Because they are remotely exploitable, nothing more.
> They are often not maintained
The world's deepest pockets and countless hours from the world's smartest minds go into maintaining them…
> once a vulnerability is discovered, the entire app is compromised
Not in modern browsers.
> In the case of a highly-privileged process
Oh good, so not the JavaScript process, right?
I meant maintained by app developers who include the runtimes, not the runtimes themselves.
Significantly fewer than you'd find in a comparable C++ application, probably, and with much less effort put into securing things like "if I index into this array am I allowing for an arbitrary write primitive" and "can I safety use this object without giving an attacker code execution". Electron bugs tend to be of the sort like "oops, we can load a file from the filesystem because we forgot a string check", and C++ bugs are "that, but with the other things I just mentioned".
Based on what? On C++ you have complex systems with difficult code to get correctly. With Electron, you have terrible chat apps that take 1GB of memory to display a few chat bubbles that allow remote execution into machines running them.
The data to compare the two is just not there to assume anything like you just did. Meanwhile, electron apps have proven quite insecure, despite not being able to allow arbitrary write primitive by indexing into an array.
The V8 engine, in 2020, is one of the most actively-maintained software projects of any kind. And (for better or worse) nearly everyone who needs a JS engine uses that one. This includes - among others - Chrome, NodeJS (which means Electron too), and now Edge. The only major outliers I can think of are JavaScriptCore (iOS/Safari) and SpiderMonkey (Firefox).
The sin committed by Avast was rolling their own version of something, as a less-than-massive-company, when the state-of-the-art implementation is OSS. That has nothing to do with JavaScript the language. You're commenting on things you clearly know nothing about.
Serious question, what are reasons to use JS in non-web contexts, apart from developer familiarity?
More than that, at least last time I checked, V8 is really fast. It is many times faster than the usable Python implementations, or practically any other memory-managed runtime. Only luajit seemed to sit in the same ballpark when I pulled up the shoot-out a couple years back.
I personally hate all of these facts, but sometimes, they really do mean that prioritizing JavaScript, or at least something that compiles down to JavaScript, is the best choice.
Wait, hold on: you can usually run C on most devices.
With JS, you have to worry way less about hardware-specific builds, platform-specific linking implications, differing system behavior and intrinsics, or any of the other substantial hangups that become relevant when you need to distribute a native application across a wide range of devices.
We don't need to repeat the rest of the thread where everyone hops in and says "tut tut, hypothetically, it would be possible for it to not be that way". We're talking about the way things actually are. In an ideal world, JS would've been out of the picture about 3 years after it was born. :)
I never said that.
Some even find it fun to bend something that isn't meant to be bent.
V8 on the server has a very nice eventloop that's very easy to leverage for high performance while avoiding horrifying overflow issues and fits well for a large majority of web request/response patterns while still offering significant developer speed.
I take it you're not a big fan of JS? That's a lot like saying your not a fan of hammers. Maybe you aren't good at using them, maybe the noise scares you; maybe you think hammer wielders are all idiots and the only smart people are shovelers. It's a tool, it works better in some situation, worse in other.
Low effort <insert language here> developers are everywhere. Lol. Please just stop. Any language is a bad language if used poorly. Literally, JS is just as bad as C++ in the hands of the incompetent.
AvastSvc.exe is not the place where you need programming 'at scale'.
Said who?
> You can't have an entire industry push this terrible ecosystem, then expect security companies to miss out on the fun.
I would expect most security experts to push you to use JavaScript instead of C++, since the former will protect you from a number of rather common security issues in the latter…
> Locating and hiring C++ engineers at a scale is something that has become very, very difficult.
Is it really that hard? Here, I can help: I know C++, and I'll be graduating soon. Hire me ;)
Even if you did all that, odds are that you still don't know C++.
Yeah this is part of the reason why I wont even try to learn the language
You can very well write something without really knowing it. In fact it's more than obvious that the majority of code written these days falls under that category.
Programming is hard and it takes years if not decades to "know" how to do it to an extent that's not harmful. This also applies to learning the tools. Some tools are easier to learn than others. C++ is notoriously difficult to learn.
JavaScript isn't free of this at all, it's also a very complex syntax, due to many years of lumping more and more features without any coherent design. The tooling around JavaScript is notoriously bad and broken. Having to rely on package for basic stdlib functionality, having to understand how nested dependencies can and will collide, etc.—all of that creates much higher cognitive load than having to use C Lion or Visual Studio (not Code) for C++ development.