Sparkle is an open source update framework that is used within thousands of Mac apps, including my own AutoCasperNBI & AutoImagrNBI.
A vulnerability within which was recently disclosed, with an update to Sparkle issued soon after.
However, the update may take some time to reach all the apps that are on the Macs that we admin. So the below is some more detail, methods of mitigation & detection.
Contents [hide]
Vulnerability
For details on the vulnerability please read the original post disclosing it here & another example here.
As mentioned, there is an update to Sparkle which will close this vulnerability. However, not all app developers may be quick to the mark to resolve by either:
- Updating the apps Sparkle framework to 1.13.1.
- Move their apps Software Update Feed URL (SUFeedURL) from http:// to https://.
The latter is what the developers should have done.
Heck this little blog is https because of Sparkle & it’s update framework being used in AutoCasperNBI & AutoImagrNBI, with their respective SUFeedURL pointing here.
Mitigating The Risk
As admins there are a few things we can to to mitigate the risk of a MITM attack from one of these apps affecting Macs that we manage.
Auto Updates
Many apps have auto update features. Often these can be turned off & I’d advise they are for apps used across your Macs. At least until the apps have been updated.
Greg Neagle has a number of examples here which might help for starters.
UPDATE: Allister Banks has written a script to create configuration profiles like those linked above for applications that include the Sparkle framework.
AutoPKG
With updates disabled client side, we can pull the updates via AutoPKG to deploy to our Macs.
Even if not currently managing updating applications via AutoPKG, i’d advise you add the recipes for those applications found in the script at then end of this post, so you can push out the patched versions of the applications once released.
Admin vs Non-Admin
Sparkle can update apps that live in any location on your Macs.
If your users are not admins then they risk is reduced, but they may still be caught by this vulnerability with anything malicious being limited to their account unless the exploit then manages somehow to escalate privileges further.
Detection
There are a few methods to detect apps using Sparkle that are vulnerable to MITM.
SUFeedURL
A few folks in the #autopkg channel on macadmins.org Slack have been scraping the SUFeedURL’s from within AutoPKG recipes & other sources & then reaching out to the developers.
However, the developer would need to push out an update with the https SUFeedURL in the applications info.plist for the application to see the change as well as https whatever site they host the update XML on. So this will need one more update for them to make the change.
Also, if you maintain any AutoPKG recipes, you may need to change the URLs to https once the developer has made the change themselves.
The other option is that the developers update their applications with the newer Sparkle update framework that has this vulnerability patched, this would not reflect in the applications SUFeedURL so the above could lead to false positives, & again requires the application to be updated.
sparkle:releaseNotesLink
(Thanks to Allister for pointing this out).
The SUFeedURL points an applications Sparkle framework to an XML file (referred to as the AppCast.xml), within which a few details can be found.
Below is a cut down example of AutoCasperNBI’s AppCast.xml:
<?xml version="1.0" encoding="utf-8"?> | |
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/"> | |
<channel> | |
<title>AutoCasperNBI's Changelog</title> | |
<link>https://macmule.com/AutoCasperNBI-AppCast.xml</link> | |
<description>Most recent changes with links to updates.</description> | |
<language>en</language> | |
<item> | |
<title>Version 1.3.3</title> | |
<description><![CDATA[ | |
<h2>1.3.3</h2> | |
Resolves an issue where ARD &/or VNC cannot be enabled on a 10.11.2 NBI with the "Reduce Image Size" option selected for NBI creation. <a href="https://github.com/macmule/AutoCasperNBI/issues/75" class="issue-link" title="10.11.2 ARD/VNC Unable to Authenticate">#75</a> | |
<br> | |
For more information, see <a href="https://macmule.com/2015/12/18/10-11-2-cannot-enable-ard-or-vnc-if-libraryapplication-supportappleremote-desktop-is-missing/">this post.</a> | |
]]> | |
</description> | |
<pubDate>Fri, 18 December 2015 11:00 +0000</pubDate> | |
<enclosure url="https://github.com/macmule/AutoCasperNBI/releases/download/1.3.3/AutoCasperNBI.zip" | |
sparkle:version="101" | |
length="1623481" | |
type="application/octet-stream" | |
sparkle:dsaSignature="MCwCFHoNKXujxHEsccUTiKd8S0Yn+ECmAhQTD6psO0Z2x3hnzm++gg6l3OheXA==" /> | |
<sparkle:minimumSystemVersion>10.9</sparkle:minimumSystemVersion> | |
</item> | |
</channel> | |
</rss> |
Within the second set <description> tags are the release notes. This is an example of safe release notes as they are within the AppCast.xml & are only available over https.
The below is taken from another application. Within the <sparkle:releaseNotesLink> tags there is a http link.
<?xml version="1.0" encoding="UTF-8"?> | |
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> | |
<channel> | |
<title>ExampleApp ChangeLog</title> | |
<link>http://www.ExampleApp.com/</link> | |
<description>Most recent changes with links to updates.</description> | |
<language>en</language> | |
<item> | |
<title>Version 0.4.3</title> | |
<sparkle:releaseNotesLink>http://dl.dropbox.com/u/1140644/ExampleApp/release_note.html</sparkle:releaseNotesLink> | |
<pubDate>Mon, 09 Nov 2009 01:30:08 GMT</pubDate> | |
<enclosure url="http://dl.dropbox.com/u/1140644/ExampleApp/ExampleApp_0.4.3.dmg" | |
length="1131824" | |
sparkle:version="0.4.3" | |
type="application/octet-stream" | |
sparkle:dsaSignature="MC1CFHMWcZvgIvJa/uAg1o4EesJXqeSgAhUAyEcyE3SaVb1az4jYWlvG58Erz4UU=
" /> | |
</item> | |
</channel> | |
</rss> |
The above should be easily remedied as the link can be changed to https within the AppCast.xml without updating the application.
Sadly, it’s not that easy to detect without downloading an applications AppCast.xml & then verifying that the release notes are not within the AppCast.xml & are instead linked to a http source.
If you find a link like the above, please contact the developer.
Command Line
There have been a few command line methods to detect is an application is potentially vulnerable, such as:
https://twitter.com/jzdziarski/status/693258522063192064
However, these can be be quite wasteful (as pointed out by @Allister on macadmins.org Slack) & whilst they would get any applications with an SUFeedURL that is http://, I didn’t see many command line methods that would also check the version of the Sparkle framework included within the application to check to see if that had been updated & therefore patched the vulnerability.
Extension Attribute
With the above issue of checking both the applications info.plist for the SUFeedURL & the applications bundled Sparkle frameworks version, I came up with the below script.
This is is intended to be used as an Extension Attribute, but could be used in other ways, & will return a list of all vulnerable apps on the Macs it’s run on.
If running via as an Extension attribute, you should get a list of applications with their paths that are vulnerable, as well as their bundle ID’s for generating profiles to block their updates via Extinguish.
[github file = “/macmule/JSS-Extension-Attributes/blob/master/Sparkle-MITM-Vuln-Apps.py”]
#!/usr/bin/python | |
""" | |
EA to get a list of all apps that are vulnerable to Sparkle MITM attacks | |
License: https://macmule.com/license/ | |
""" | |
# Imports | |
import os.path | |
import subprocess | |
from pkg_resources import parse_version | |
def main(): | |
""" | |
Get a list of apps with Sparkle frameworks | |
""" | |
# Variables | |
vulnerable_apps = '' | |
sparkle_info = '/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist' | |
# Use mdfind to get all apps | |
get_apps = subprocess.Popen(['/usr/bin/mdfind', 'kind:app'], stdout=subprocess.PIPE,) | |
# Make into a list | |
app_list = get_apps.communicate()[0].splitlines() | |
# For each app returned | |
for app in app_list: | |
# If the app has a sparkle framework | |
if os.path.exists(app + sparkle_info): | |
# Try to get CFBundleShortVersionString | |
try: | |
get_version = subprocess.Popen(['/usr/bin/defaults', 'read', app + \ | |
'/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist', \ | |
'CFBundleShortVersionString',], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
sparkle_version = get_version.communicate()[0] | |
except: | |
pass | |
# If the above fails, try to get the CFBundleVersion | |
if not sparkle_version: | |
get_version = subprocess.Popen(['/usr/bin/defaults', 'read', app + \ | |
'/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist', \ | |
'CFBundleVersion',], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
sparkle_version = get_version.communicate()[0] | |
# Try & get the SUFeedURL | |
try: | |
get_feed_url = subprocess.Popen(['/usr/bin/defaults', 'read', \ | |
app + '/Contents/Info.plist', 'SUFeedURL',], \ | |
stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
sparkle_feed_url = str(get_feed_url.communicate()[0]) | |
except: | |
pass | |
# If we have a SUFeedURL | |
if sparkle_feed_url: | |
# If Sparkle version is less than 1.13.1 & SUFeedURL is http | |
if parse_version(sparkle_version) < parse_version('1.13.1') \ | |
and not sparkle_feed_url.startswith('https://'): | |
# Append to string | |
vulnerable_apps = vulnerable_apps + app + '\n' | |
# Run function | |
vuln_apps(vulnerable_apps) | |
def vuln_apps(vulnerable_apps): | |
""" | |
For EA, print list of all vulnerable apps including file paths | |
""" | |
# If we've found any vulnerable apps | |
if len(vulnerable_apps) > 0: | |
# List apps in EA | |
print '<result>%s</result>' % vulnerable_apps | |
else: | |
# If no vulnerable apps found | |
print '<result>None found</result>' | |
if __name__ == '__main__': | |
main() |
Credit
Thanks to the number of Mac Admins whom tweeted or posted about this on the macadmins.org Slack.
As well as @Bruienne for the Python pointers & @Allister for looking further into the issue, rewriting my EA & writing Extinguish to help create profiles to block application updates.
One thought on “Sparkle Updater Framework HTTP man-in-the-middle vulnerability”