Karabiner-Elements: Big security issue - binary being shipped inside this repo, it is not built from source, aka keylogger

Please help me to confirm is this true.

  1. You are shipping a binary inside this repo that is the kernel extension being installed to intercept the keyboard pressed keys (and this logs all keys somebody is pressing).

https://github.com/tekezo/Karabiner-Elements/blob/master/src/vendor/Karabiner-VirtualHIDDevice/dist/org.pqrs.driver.Karabiner.VirtualHIDDevice.v061000.kext/Contents/MacOS/VirtualHIDDevice

  1. The default installation instructions will make it appear like the user is building the kernel extension when in fact it will fail with signing error and your binary extension will be used as a fallback.
bash ./scripts/codesign.sh dist
code sign org.pqrs.driver.Karabiner.VirtualHIDDevice.v060800.kext
8ECD43BA902B40380BD84C4512385E6C5EB3F160: no identity found
org.pqrs.driver.Karabiner.VirtualHIDDevice.v060800.kext: code object is not signed at all
In architecture: x86_64
bash ./scripts/setpermissions.sh dist
  1. When user is installing the result binary Karabiner-Elements-VERSION.dmg by default your attached binary (not build from the source will be used), because only you have the sigining key!!!

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 8
  • Comments: 24 (11 by maintainers)

Most upvoted comments

Hi @tekezo ,

First of all I would say that I appreciate all of your giant effort to open source this tool. I really loved to use it, and it was almost indispensable to me.

This is also the reason why this is so frustrating. Because of this I can’t use this project anymore. The risk is simply too high.

There are two ways of distributing the code:

  • Download binary: I would never use binary kext downloaded from somewhere that is intended to intercept all of my keystrokes, that’s just insane)
  • Build from source: What you see in the code is what you get.

What went wrong: When a user is trying to build the binary from source files, because they care about security, you are presenting the built from sources progress to the user. Even though the process fails because of signing issues, you ignore the entire build process and just use your home built and signed kext from somewhere and build the result dmg file successfully. This gives the user an impression that everything went smooth, when the expected action actually didn’t happen.

Why is this problematic:

  • Majority of users is probably not aware of this fact. When I’ve shared this finding with some of my coworkers they were shocked because they were also deceived.
  • People are maybe using this in companies that have certain strict policies and you can get those people accidentally in a lot trouble.
  • Because of this I can’t use this product in any kind of commercial environment. Using it in this form would be irresponsible and you are putting a lot of people who really love your product in an awkward position.
  • There are two standard ways to distribute the product: git clone + make or attaching a binary. The normal publishing process on GitHub is to use release attachments for attaching binaries and git for building from source. You are ignoring the usual release process https://github.com/tekezo/Karabiner-Elements/releases. Your releases don’t contain binaries, but they are concealed in the source code itself. Let people decide on their own their personal tradeoffs between security and convenience. Don’t deceive them by mixing the two.
  • There is approximately 0% chance that users can build and sign this on their own. I’ve entered Apple developer program and asked for a signing key to sign my own kext contain in this library. Apple rejected my request although I’ve paid them 100$. There is no indication that they would approve some else developer kext signing certificate for personal use.

However, macOS requires a special signing key for the kernel extensions. Without it, you cannot load the kernel extension with SIP.

I understand why you need to sign the kext. That doesn’t mean that you should attach a signed kext binary and commit it as a part of source code and use it as a fallback.

Thus, normally you have to upgrade the Developer ID Certificate to be able to sign the kext. This is why Karabiner-Elements embed the signed kernel extension binary.

Like I said, I think that you are aware that Apple won’t be giving those kext signing keys to developers for personal use, the risk for Apple would be too high.

Why am I so frustrated with this:

  • I really love your product and I would rather give those 100$ to you than to Apple.
  • You are ignoring a really important issue that is keeping me from using this project anywhere.

How to remedy this so that everyone is happy:

What not to do:

  • Attaching binaries in source code and using them to produce the artifacts rather than source code.
  • Ignore serious issues on GitHub.

Hmm, how do you believe the distributed binary is built from the source code in other projects such as VirtualBox? (If you also doubt other projects, it is consistent…, but if there are some difference, please tell me.)

Well I guess it’s game theory. I think you are already well aware of these reasons.

  • First of all I don’t think that VirtualBox build process has a silent fallback to a pre-built binary in the source code. You can prove me wrong. This is a huge red flag.
  • They are not insisting on shipping a privately built fallback binary in their source code which will be deceitfully distributed to the users wanting to built the product from source.
  • VirtualBox is run by Oracle, which is a 200 billion $ company. If they intentionally inject some malicious code or fail to protect the binary, that market cap is going down drastically. I’m sure they internally care about build reproducibility.
  • There are thousands of companies using their product and probably thousands of security researchers looking for zero day exploits, so I doubt that any fault would be unnoticed for long.
  • Oracle is a company made of thousands of people, if there is any systematic deceit going on there, there is a huge financial incentive to short the company stock with high leverage and leak that malicious information out because that’s criminal behavior.

CGEventPost does not support some keys as far as I investigated.

Why can’t you just fallback to CGEventPost if the kext is not present?

Why do you insist on distributing privately built binary in your source code? This is a huge red flag.

If the user can’t sign the build, it should fail and give the user message he needs to get a signing certificate or use a prebuilt binary from some location.

@tekezo

About proving the distributed binary

Again, you shouldn’t be distributing binary in the source code and fallback on it when the user fails to codesign:

Again, why:

The build process is complete automated and there is not a chance to interfere by malicious human.

I think you are missing the point completely. It doesn’t matter how to binary is generated. I was never saying how to prove your binary is correct, but how to let users build their own binary (reproducible build) for which you can just provide the signature).

As my investigation, codesign modifies the binary and there are no reliable way to strip the sign. (== We cannot get the original unsigned binary from signed binary.)

I have no idea how did you investigate this. The goal isn’t stripping the signature, but replacing the signature with your own for the same binary. Seems there are two parts of mach-o you need to change https://github.com/steakknife/unsign/blob/master/unsign.c

I’m really not convinced at the moment that you actually need a kext to send keystrokes. https://stackoverflow.com/questions/8027146/sending-keystroke-events-to-osx

How is an issue like this closed without being addressed?

I think you can do it by modify key_event_dispatcher.hpp.

Thanks a lot. I’ll try to make it work without the kext.

This way is not strongify security because the distributed binary has not any proof as we discussed in above.

I was surprised because I was not building it from source which exists in the repository, but a different binary was slipped in. If you don’t delete the binary from the repository you are still doing the wrong thing IMHO because users might run make without reading your instructions.

I’m not sure why do you persist on distributing a built binary in your repository. The correct action is to download the binary from some other prebuilt place. In this way there can be no misunderstanding.

You have ~30ish contributors and probably 20-30k users. IMHO it would be more important to inform the users what they are actually doing because that might be not allowed in a lot of companies and they could get in a lot of trouble with the current build process.