Hacker News new | past | comments | ask | show | jobs | submit login
Remote Code Execution Found in CocoaPods (justi.cz)
220 points by st3fan 6 months ago | hide | past | favorite | 72 comments

I found this bug! Here's my blog post about it: https://justi.cz/security/2021/04/20/cocoapods-rce.html

I started looking because I wanted to find bugs in Signal for iOS, which uses CocoaPods: https://github.com/signalapp/Signal-iOS/blob/master/Podfile

> Then think about how much a security audit would cost.

This is why some customers require various security certifications. Too bad the certifications often focus on whether your employees have three groups of characters in their passwords instead of an actual security audit with penetration testing.

My point being, how to make people want a proper audit and how to commuicate you had one. From another point of view, how do you justify the cost without including the risk of being hacked? Because even in this instance, they were (probably) not hacked, and your reward was likely lower than an audit would cost.

You put up a decent sized bug bounty. Whether or not the bounty is claimed, it shows your company either has good security, or is prepared to put a lot of resources into making it good.

This leaves out the possibility that a company puts out a bug bounty without ever paying any reporters.

A company could simply dismiss most reports as duplicates, maybe even with a fake hall of fame, and get away with this for a long time.

Do you know of any company that has done this?

I think it would be fair for the thread to point to your post, so I've swapped your URL in for https://blog.cocoapods.org/CocoaPods-Trunk-RCE/ above.

I like your shameless plug:

> I’m trying to give 10,000 mosquito nets to charity! If you liked this post please consider donating a $2 mosquito net.


That's not a shameless plug, just a plug.

by definition, any plug done without shame is shameless

Didn't Bill Gates purchase a heap of malaria nets only for it to turn that out that people were using them for things like fishing and generally just not using them for their intended purposes?

Not to diminish the finding mind you, just that I was surprised to hear about malaria nets.


So you're saying the world got better because of it? Why is everything some Benthamite calculus?

>So you're saying the world got better because of it?

Unfortunately not - the hole size on the nets is much smaller than a normal fishing net, so it doesn't let juveniles escape, reducing the future fish population.

A lot of the nets are also treated with insecticide, which means you're essentially dumping insecticide into the water, which can be dangerous to humans and toxic to fish.

Yes, it's a shame the foundation spent so much effort on failing to adequately meet a need. One wonders what they did the year after.

> Why is everything some Benthamite calculus

I don't understand what this means

Jeremy Bentham [1] was more or less the founder of utilitarianism [2] as most people understand the notion today.

It's a suggestion that everything is only done on the basis of what will result in the most benefit, ignoring the mortality of how it is achieved.

[1] https://en.wikipedia.org/wiki/Jeremy_Bentham [2] https://en.wikipedia.org/wiki/Jeremy_Bentham

That was really short and sweet. Thanks for the writeup!

I still don't get why the --upload-pack option execute its content... Seems pretty dangerous to me. How did you find that out?

Because it says [--upload-pack=<exec>] in the manual. Whatever that means. The problem here is obviously that git is a user interface and not really meant to be called from code (contrary to what CLI supremacists believe).

You explained what this option is, I was asking why.

We only need CocoaPods, because Apple couldn't build a proper package manager for XCode. And I am quite sure event Apple uses CocoaPods internally.

CocoaPods itself is quite problematic: You need Ruby to run it. Definitions aren't strict enough (you can use too old CocoaPods binary for package that doesn't support it). Pods can cause build conflicts/issues that might only be visible when you run your app.

Swift Package Manager is the official package manager for Xcode. It's been available for a few years now and a lot of third party iOS/macOS projects support it. I haven't had to touch CocoaPods in many years. For the past 5 years I've been using Carthage, and more recently everything is SPM.

Sadly, the existence of Swift Package Manager has pretty much taken all the wind out of the sails of third-party package managers, and it has done so far too early.

Why is it too early? We’ve had great success with SPM even if it’s not at feature parity with some of the 3rd party package managers. Now if only Apple actually accepted contributions on github rather than linking you to the swift jira then we might get more community involvement with SPM.

SPM is missing a lot of things that a package manager typically lets you do–it's very focused on a handful of specific cases and falls down if you try to diverge from that.

Not to mention that things like stats (useful for assessing quality) and search ranking have been broken for years [1].

This isn't meant as a criticism of the CocoaPods team, who seem to be doing the best they can given that they're working on a volunteer basis and even had to pay for infrastructure costs out of pocket. It just amazes me that Apple couldn't donate a little bit to help out such a critical part of their developer ecosystem.

[1] https://github.com/CocoaPods/cocoapods-stats/issues/32

Brew, CocoaPods, who else is carrying Apple's water? It seems to me that Apple never sold a Unix-like dev machine, they provided hardware and a kernel for others to do so for free.

Brew and CocoaPods are quite horrible hacks, but still better than nothing. Apple systems aren't very nice if you have to delve out from the non apple garden.

Pretty sure Apple funded MacPorts

I think the success and rapid adoption of Swift Package Manager as it has become a viable solution speaks to this deep desire in the community to have an Apple provided solution to it.

CocoaPods was a hugely important part of the iOS ecosystem and will be for years to come, but SPM is a great next step.

I think you overestimate how long CocoaPods will be used, all third party libraries I’ve used in the last 6-12 months supports SPM

Our app is in objC and we’re not planning a rewrite anytime soon.

Pseudo-counter point: SPM for firebase iOS SDK is in beta, and was released 2 weeks ago. Before this, you couldn't use SPM. The readme recommended (defaulted) to Cocoapods, with "experimental instructions for carthage". Therefore Cocoapods is likely used by most iOS apps with firebase.

And none of the ones I use support SPM.

So apparently a lot of them do support SPM even when it doesn't say so in the README. Confusing, annoying. Out of a project with 10 dependencies I thought only 4 supported SPM by reading the README but in the end only one (which was my own...) Cocoapad didn't have SPM support.

Can confirm Apple iOS projects use CocoaPods internally, at least the couple I was aware of when I worked there.

They do, although Apple doesn't use third party libraries very often and when they do they tend to vendor them.

> I am quite sure event Apple uses CocoaPods internally.

I’d be very surprised if Apple wasn’t dogfooding their own tool chain

Apple probably uses SPM for new projects. But existing projects are probably not switching to SPM soon.

Kudos to them for a proactive approach.

I'm deprecating my use of Cocoapods (for publishing -I never use them for my own software), in favor of SPM.

That said, it's clearly a labor of love, and filled an important niche for years.

SPM has a lot of issues, though. For example: if you depend on a static library, it will both statically link it _and_ embed the binary; likewise, a static library dependency in a framework will cause build failures ~70% of the time when clean. There's others around resources, etc.

I do not believe SPM is mature enough to be the entire platform, and worst yet if you experience any problems it is entirely impossible to customize the behavior. I think it's going to be another year or 2 before dropping CocoaPods entirely is a fair choice for libraries -- SPM works for the most basic use cases, but not all.

> if you depend on a static library, it will both statically link it _and_ embed the binary

That's fixed in Xcode 12.5 https://developer.apple.com/documentation/xcode-release-note...

Ah. I see why this has not been an issue with me for most (maybe not all) of my projects. Most have only a single-layer dependency.

Looks like this happens for chained dependencies ("a Swift package with binary dependencies", below), in apps with extensions (I have none).

This is from the 12.4 notes[0]:

> If you use a Swift package with binary dependencies in an app with extensions, the build system incorrectly embeds the binary dependencies alongside the extension in the PlugIns directory, causing validation of the archived app to fail. (69834549) (FB8761306) Workaround: Add a scheme post-build action which removes the embedded binaries from the PlugIns directory after the build, e.g. rm -rf "${TARGET_BUILD_DIR}/${TARGET_NAME}.app"/PlugIns/.framework.*

I do not see a note in the 12.5 notes addressing this[1].

[0] https://developer.apple.com/documentation/xcode-release-note...

[1] https://developer.apple.com/documentation/xcode-release-note...

Do you know the difference between dynamic and embedded binaries?

Any particular reason for the insult? I don't remember saying anything bad about you.

Maybe, we could have found more in common than you may think. We both seem to be passionate about the same kind of stuff.

I was under the impression that we should make an effort to be a wee bit more respectful with each other, than we might in venues like Reddit or SO (where they have the "digital sneer" down to a science).

That was the only thing I could find that mentioned anything like it at all.

I checked my builds every which way from Sunday, and I never had a double-embedded static lib. I was wondering if they may have been mixed up.

Otherwise, there's nothing at all about it.

I pretty much always use static libs, but sometimes, I may need a .framework. I've been using SPM for months, and have published a bunch of libs. I do tend to be conservative, and reduce the surface of dependencies wherever possible. I eat my own dog food, and am the only consumer (that I know of) of my packages. My chains tend to be short.

Insult? No no no! I didn't mean to insult you at all, I was trying to find a good common ground to try explain why the Xcode 12.5 release fixes this issue without belabouring points you already know!

A swift package manager product before 12.5 will be embedded inside your target and statically linked.

If you have more than one target using that product, you will end up embedding it more than once, which creates duplicated symbols in your binary, which leads to undefined behaviour when calling that symbol at runtime (which Apple don't allow for apps on the AppStore - ironically Big Sur on M1 Macs has a lot of duplicated libraries for x86 and arm64 which will cause a warning about undefined behaviour when you call them - do as I say, not as I do).

What happens now in 12.5 onwards is those products will be dynamically linked, meaning there will be one copy of it in your binary, that each of your targets can call symbols on, which gets rid of the duplication and the potential for undefined behaviour.

OK. My apologies for being prickly. I guess I spend too much time on SO. I love electronic communications. They give us whole new realms, in which to explore rancor.

I understand. I have not encountered that. I'll look again. In most of my projects, the executables are test harnesses, and not all use SPM. Most of my dependencies are so small, that I might actually be better off directly linking in the source file from inside the package. I do that with the Carthage variants, but the static libs are so small, that I don't sweat it, and it makes life easier, all around.

I know that dylibs are fairly recent additions to SPM. One of the issues that I had with Carthage, was that I was constantly screwing up the signing. Since the libs were so small, I just said "Bugger this for a lark," and directly linked the source files. Since Carthage checks out in the project dir, this was easy. I use a separate derivedData directory, so it's not so straightforward to directly embed SPM source files. I haven't tried the new resources in SPM modules, yet.

Thanks for following up, and again, my apologies for overreacting.

> I think it's going to be another year or 2 before dropping CocoaPods entirely is a fair choice for libraries

For libraries this is probably true, but I would imagine that most apps could start on SPM from today and never need to introduce CocoaPods or Carthage (we did, ~1 year ago). There are certainly limitations, but they are disappearing fairly rapidly, and the advantage of simpler builds, no Ruby setup, simpler Xcode project structures, etc, are worthwhile.

Well, I won't go into the issues that prevent me from using CocoaPods, and why I'm not going to be publishing my libraries on them, from now on, but SPM has worked well for me. I am very careful about what I include in my projects. Most of my dependencies are fairly small ones that I wrote and published. Many are a single source file.

I don't see the libraries being embedded in my project, but maybe I need to use something like iMazing to look at the package.

Carthage has been a happy middle ground for us. It doesn't dictate the structure or configuration of your project.

Only annoyance for me with Carthage is needing to strip architectures out of the built frameworks in order for the app to be validated when uploading to App Store Connect. It's an awesome solution though, and much better than CocoaPods.

Carthage supports XCFrameworks now which means this is no longer required.

Nice! Glad to hear that's finally been fixed

I don't use CocoaPods because a) they are a gaping supply chain vulnerability and b) they lead to bloat as people routinely pull in a whole package for one routine.

I manually import and rewrite snippets of 3rd party code when needed, or I write needed utils myself. I lean on the OS as much as possible, not dodgy and often abandoned 3rd party libraries.

This is an underappreciated approach in modern developer culture.

Looks like they've made an web-app (https://pod-sources.cocoapods.org/) to check the distinct sources of a pod so you can have a fish to see if a source location url changed behind your back.

Would be good to show a list of all repositories where there are more than 1 distinct source as most people who make pods just point to their Github repo release page.

It's very tedious to check the impact of this without that list.

It would be possible to do this by querying the specs repo https://github.com/CocoaPods/cdn.cocoapods.org. This is what the web-app does.

I noticed that quite a few pods have more than 1 distinct source when checking the pods used by projects I have worked on. From what I could see source changes were the result of ownership changes, GitHub account name changes, etc.

So i'm not sure how to distinguish malicious source changes from innocuous ones. Maybe it would be worthwhile to search for source changes that lasted a single release and reverted thereafter.

This seems to be an issue of git. It's very surprising that a git command would invoke its arguments in shell.

The issue is ruby's `system` method which interprets args in the shell.

They should have used `popen` instead.

This is similar to python's subprocess with `shell=True`

Edit: I'm wrong!

> ls-remote has a parameter --upload-pack which can be used to execute a new shell

I don't believe this is the case.

    [1] pry(main)> system "echo", "$(whoami)"
    => true
    [2] pry(main)> system "git", "ls-remote", "--upload-pack=$(whoami)", "HEAD"
    $(whoami) 'HEAD': USERNAME: command not found
    fatal: Could not read from remote repository.

    Please make sure you have the correct access rights
    and the repository exists.
    => false

I had the same reaction as you... it looks like the vulnerability is coming from git, that's a very dangerous behavior.

I think I understand why this is happening. This is not an issue that argument parsing in git goes wrong. But instead, the `--upload-pack=` instructs git to run certain command in the remote server. In my above example (`system "git", "ls-remote", "--upload-pack=$(whoami)", "HEAD"`), HEAD is interpreted as a local file based git server. As such, the command is executed in the same machine.

I don't think so, you just forgot to add the remote server address. If you run `system "git", "ls-remote", " --upload-pack=$(touch hello) git@github.com:torvalds/linux.git", "HEAD"` it will fetch the remote server but still create the file on the local machine.

In your example, `HEAD` is the server address, which is interpreted as a local file. `$(touch hello) git@github.com:torvalds/linux.git` is the command to be run in the remote (locally in this case). It will not connect to the server in GitHub.

P.S. it seems that you have an extra space before `--upload-pack`.

Oh, that's very silly. Don't do that!

I think I understand why this happens. See https://news.ycombinator.com/item?id=26885103

It's an issue with "the UNIX way". I'm not bashing on composability, but the fact that "the UNIX way" is just composability delivered in a bloated, error prone way.

Specifically, the problem here is that git, like almost every other CLI tool, tries to add as many features as possible to make it easy to use from a console. This (coupled with the fact that these things are never documented, as is the case here, unless <exec> is some idiom I'm supposed to be aware of) makes it harder to use in a correct manor (security bugs being a subset of possible problems).

Before passing attacker controlled variables to commands, always end option processing with the -- option!


There's a typo in the title; Should be CocoaPods, not Cococa.

Fixed now. Thanks!

Honestly surprised this wasn't about some internet connected coffee machine!

I expect you could make a hard developer trivia game where people have to guess if a vulnerability was found in an IOT app or a SAAS app based only on the name.

> Honestly surprised this wasn't about some internet connected coffee machine!

Whenever IT folks run out of good names they always turn to coffee.

Like this one? Not IoT or SaaS, but big data. https://pixelastic.github.io/pokemonorbigdata/

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact