Андрей Смирнов
Время чтения: ~15 мин.
Просмотров: 23

Binary Patching: Attempting to Restore NPAPI Support to Firefox 60 ESR

1*OpDhnRD2XAEXzI4WfXMaIg.jpegJacob ThompsonFollow</span>Oct 12, 2018 · 13 min read</span></span>

Abstract. Did you know it is possible to change the behavior of a compiled program by editing the machine code directly rather than recompiling it from source? Though well-known by hackers, firmware engineers, and other low-level types, this can be intriguing to those whose main focus is in other areas of computer science. By focusing on removing a restriction added to Mozilla Firefox between version 52 ESR and 60 ESR, I show from the ground up how to edit a binary to bypass a plugin check, with mixed results. I use only basic GNU binutils tools to focus on the “bits and bytes” and not on how to use a particular tool. If you are interested in brushing up on some x86–64 assembly, basic reverse engineering and hex editing, take a look at this blog.

Introduction. The Netscape Plugin Application Programming Interface (NPAPI) is a relic of a simpler time on the Internet where programmers focused on just making it work — now — and not on security concerns. Indeed, such basic modern browser features as reading PDF files through the browser and playing sound and video replace old implementations provided using plugins.

1*oBquWh55AX8-PiS3ri9oUg.jpeg?q=20

The good side of the NPAPI is that it allows third parties to extend web browsers in almost-arbitrary ways. There is no need to get anyone’s permission and no central authority for distributing the plugin. There is no opportunity for web browser vendors to “monetize” the plugin or restrict distribution to an app store in any way, nor any opportunity for the web community to create a controversy over what does or does not belong in web technology; only the end user has any say in the matter. NPAPI plugins were great for niche market products with, perhaps, a few thousand users who needed to quickly develop browser-neutral software to break out of what is available in HTML and JavaScript. A cynical view could include these reasons, in addition to security, for deprecating NPAPI.

The bad side, of course, is security. The design of NPAPI, in which arbitrary, unsandboxed, unsigned code is loaded directly into the browser’s address space and exposed directly to untrusted inputs from all Internet sites, would never be repeated in today’s threat environment. NPAPI plugins such as Adobe Flash and the Oracle Java applet plugin have well-deserved reputations for endless security advisories. Microsoft’s similar ActiveX technology has an even worse reputation, partly earned due to that technology’s aggressive installation prompts and poor user interface for implementing that feature in early versions of Internet Explorer.

For this reason, browser vendors are determined to deprecate and remove NPAPI, both to reduce the attack surface it introduces and simplify browser design by removing all the code needed to support plugins. Google set the standard of click-to-run and disabled-by-default features for plugins, followed by removing NPAPI support entirely. Other browser vendors have followed suit. Mozilla Firefox 52 ESR was the last mainstream browser with full support for NPAPI plugins. Version 60 ESR disallows loading all NPAPI plugins except for Adobe Flash, because it is too popular to disable yet. Ignoring the fact that this exemption reeks of “too big to fail,” an interesting question to consider is whether it is possible to take a copy of Firefox 60 ESR with Flash-only NPAPI support and remove the Flash-only restriction, without recompiling it from source code. The practical application of this, if it works, is a temporary workaround to continue to use niche-market NPAPI plugins that still have not migrated to other technologies.

The remainder of this post focuses on (1) reviewing the Firefox 60 ESR source code to locate the sections of code that implement the restriction of plugins to Flash only, (2) determining where those functions exist in the compiled Firefox binary and libraries, (3) reviewing the assembly code and determining how to modify the instructions to bypass the check, and (4) testing the resulting copy of Firefox to see if NPAPI is working.

Disclaimer: This is not an endorsement of continuing to use NPAPI, or an argument for restoring NPAPI support to browsers. The binary produced is for research purposes only and should be used in production.

Locating the Plugin Check Source. Firefox 60 ESR source code is available. I downloaded the source archive and searched for references to Adobe Flash’s MIME type, which is application/x-shockwave-flash. This revealed that the relevant code is in dom/plugins/base/nsPluginHost.cpp:

Looking closer at that file, I found a few places that enforce the restriction to Flash only and that will need to be changed.

ScanPluginsDirectory, WritePluginInfo, and ReadPluginInfo read a plugin.load_flash_only preference (which does not exist as a valid preference in Firefox 60 ESR) and set a boolean flashOnly variable based on its value. I will need to locate each of these functions in the binary and change this boolean to false:

ShouldAddPlugin only returns true for four plugin MIME types on a whitelist. I will need to locate this function and change it so that the final return false is return true, instead, for plugins that do not match the whitelist:

CanUsePluginForMIMEType implements another whitelist, so I will also need to locate this function and change it so that it returns true:

After determining how Firefox changed to block non-Flash plugins, the next step is to find the instructions inside the binaries that correspond to that source code in order to change it later.

Locating the Plugin Check Machine Code. Next, I needed to locate the machine code implementing the plugin restrictions I found in the source. As each restriction was implemented in the same source file, they should also be implemented in the same binary. Indeed, I found it best to look for the plugin.load_flash_only preference string to find the relevant machine code from the 64-bit Linux version of Firefox 60.2.1esr. I found the checks to be in libxul.so:

Now, I need to find the three places in the code that check the load_flash_only preference, and then find ShouldAddPlugin and CanUsePluginForMIMEType and change their return values. Because I am choosing not to use tools such as IDA Pro for the purpose of focusing on the fundamentals, I must use more primitive means:

(1) locate the file offsets of the strings plugin.load_flash_only, application/x-Second-Test, and application/x-third-test in libxul.so.(2) refer to the program headers of libxul.so to determine where the relevant section of the libxul.so binary will be loaded in virtual memory space at runtime, and use that information to convert the string offsets to memory addresses.(3) disassemble libxul.so, and look for instructions that read from the addresses determined in (2), which will pinpoint the machine code corresponding to the source code containing those strings.

Determining the string file offsets is straightforward:

Using readelf, I can determine the conversion factor from disk offsets into libxul.so to virtual memory addresses:

To convert, subtract 0xeaa68 from the file offset and add 0x8b9a68:

Now, I need to disassemble libxul.so and search for references to those three addresses. In position independent, x86–64 code, these addresses will not be encoded in the instruction — instead, they are referenced as offsets from the instruction pointer. Luckily, GNU objdump does the conversion for me. First, look for the three references to plugin.load_flash_only’s memory location:

Then, locate ShouldAddPlugin by reference to the string application/x-Second-Test’s memory location:

$ grep '# 46ab661' libxul.txt 193ab35:48 8d 35 25 0b d7 02 lea    0x2d70b25(%rip),%rsi        # 46ab661 

Then, locate CanUsePluginForMIMEType by reference to the string application/x-third-test’s memory location:

Thus, I have located three references to the preference plugin.load_flash_only that all need to be changed to obtain false, plus the two functions that need to be changed to return true.

Determining How to Patch the Code. Patching the code that reads the plugin.load_flash_only preference is straightforward. First, take a look at the three places in the code that read that preference:

I know that 0x46ab5ee is the address of plugin.load_flash_only, and can infer that 0x2844430 must be the address of the function Preferences::GetBool. Ordinarily, I would expect that function to return its true or false result in %rax, but (although I have not shown the code subsequent to that call) I discovered the call-and-return sequence is not working that way here, possibly due to optimization. Instead, I took a brute force approach: notice that at 0x193984f, 0x193bc92, and 0x31ac348, the code saves a true value into a register. At 0x1939857, 0x193bc94, and 0x31ac350, the code stores a true value on the stack. My approach was to simply overwrite all of these instructions so that they store false (or zero), instead. For example, to change mov $0x1,%edx to mov $0x0,%edx, all I need to do is change the binary encoding ba 01 00 00 00 to ba 00 00 00 00. The process is analogous for all the other instructions.

Patching the other two functions to accept non-Flash plugins was not as straightforward. Both functions contain a few conditional statements, that bail out and return false if some string comparisons all fail. In theory, I should be able to find the ret instruction in both functions and work backward to find a convenient place to overwrite %rax with 1. However, it appears these functions were inlined by the compiler so that they just branch to different locations to depending on whether they would have returned true or false were they not inlined.

First, taking a look at the portion of ShouldAddPlugin that checks to see if the plugin’s MIME type is application/x-Second-Test, I see that if strcmp returns zero (the strings are equal), the code jumps to 0x193ab4e, so jumping to 0x193ab4e must be equivalent to returning true:

To get a better idea of what happens if ShouldAddPlugin returns false, I decided to look up the address of the string Shockwave Flash in libxul.so, which I found to be 0x46ab620. Looking around the code that calls strcmp against that string, I can infer that if either the string Shockwave Flash matches or flashOnly is false, the code jumps to 0x193aad1 which must mean branching to the first set of MIME type checks; otherwise the code falls through to 0x193a962. Looking above at 0x193a936, I infer that if the info.fName check fails, the code branches to that same location, confirming my suspicion:

Based on that knowledge, I know that to “emulate” having ShouldAddPlugin always return true, I must replace the code at offset 0x193a962 with a relative jump to 0x193ab4e. How would such an instruction be encoded? Being restricted to binutils, the quickest way is to write, assemble and disassemble a short executable, determining the encoding as e9 e7 01 00 00:

Next, taking a look at CanUsePluginForMIMEType, since I know that at 0x193d735 begins the check for the application/x-third-test string, I can look backward and infer that at 0x193d71d must begin the check for the application/x-second-test string. Observe that at 0x193d733, the function jumps to 0x193d6f6 if LowerCaseEqualsLiteral returned true, but falls through if it returns false:

All I need to do here is change the conditional jump jne at 0x193d733 to an unconditional one jmp, which I can accomplish by changing the 75 opcode to eb.

Patching the Binary. Now that I know the code addresses and the changes I need to make, I can use reverse the conversion I initially used to convert these addresses to file offsets. That is, add 0xeaa68 and subtract 0x8b9a68:

For demonstration purposes, I will show how to make the first change using xxd:

I made the remainder of the changes using vi. Then:

Testing and Conclusion. Upon launching Firefox with the modified libxul.so, I was successfully able to launch and connect to a VPN that makes use of a Linux-based NPAPI plugin. I suspect the reason this plugin worked is that it does not have its own graphical user interface. Other plugins, unfortunately, did not work correctly. I was able to load mozplugger with a configuration file set only to support TIFF files, as shown in the about:plugins page:

1*YXZWZ5Cw_Y_A9tMiqazLtQ.png?q=20

After loading mozplugger, however, upon writing a test HTML page and TIFF file, I could not get the TIFF to display in the web page. Instead, upon visiting the page, the browser wrote the error Connection reset by peer: file /builds/worker/workspace/build/src/ipc/chromium/src/chrome/common/ipc_channel_posix.cc, line 353 to the console.

In the end, this is an interesting exercise (and works for my purposes for the VPN plugin), but clearly is not practical for production use. Every time Firefox updates itself, the libxul.so file will also be updated. In addition, patching this file invalidated any code signatures it may have had. Even if I proceeded further and fixed the mozplugger issues, the full removal of NPAPI from Firefox is inevitable. The bug to disable non-Flash NPAPI plugins was blocking resolution of 23 other bugs. Mozilla has or soon will remove “windowed mode” plugin support, GTK3 plugins, element support, and special-case code for Windows Media Player, Acrobat Reader, QuickTime, RealPlayer, Google Earth, Silverlight, Unity, and Shockwave plugins. Just as Firefox 52 ESR is now unsupported, Firefox 60 ESR soon will be as well, and the versions that succeed it will have no NPAPI support at all.

Jacob Thompson, Principal Security Analyst at Independent Security Evaluators

Twitter: @ISEsecurity

Sign up to get our latest blogs.

Firefox 60 vyšel minulý týden vlastně ve dvou verzích – pro běžné uživatele a s rozšířenou podporou pro firmy. Tato druhá verze se nazývá ESR a od té běžné se drobně liší.

Hlavním rozdílem jsou aktualizace, tedy ona rozšířená podpora. Verze ESR obecně vychází cca jednou za rok a do vydání další verze dostávají jenom bezpečnostní opravy. Všechny ostatní změny a funkce, které Mozilla provede, se do ESR dostanou najednou až za rok s dalším vydáním.

Oproti běžné verzi má ESR ve výchozím stavu vypnuté Service Workers (lze zapnout předvolboudom.serviceWorkers.enabled), push notifikace (dom.push.enabled), a lze vypnout kontrolu podpisu doplňků (xpinstall.signatures.required).

Nakonec bych rád zmínil, že vydání Firefoxu 60 ESR znamená také blízký konec 52 ESR letos v září. Tím definitivně skončí podpora pro NPAPI moduly Java, Silverlight a další (kromě Flashe). Také už Firefox nebude podporovat Windows XP a Visty. Pokud tyto staré verze Windows používáte, doporučujeme brzy přejít na nějaký podporovaný systém, ať už Windows 7/10 nebo nějakou Linuxovou distribuci.

NPAPI (Netscape Plugin Application Programming Interface) представляет собой интерфейс программирования приложений Нетскейп Плагин в качестве универсального программного обеспечения, способного обеспечить работу на разных аппаратных платформах или операционных системах для разработки и внедрения дополнительных утилит во многие браузеры, в том числе и для Mozilla Firefox. Но ту сразу нужно пояснить, что данный программный интерфейс поддерживается браузером Фаерфокс только до пятьдесят третьей версии.

Включаем NPAPI

В выпущенной 52-ой версии Мазилы весной 2017 года прекращается поддержка всех плагинов с NPAPI. Исключением стал только Adobe Flash. Все эти новаторские идеи и решения в пользу реализации многообещающего проекта Web API. Благодаря этим новым аналогам старых версий плагинов пользователи глобальной сетью интернет станут реже сталкиваться с нарушениями безопасности, а так же увеличится стабильность работы и быстродействие браузера.

kak-vkl-npapi-v-firefox-6-1024x768.jpg

Отталкиваясь от этих разумных доводов, пользователю стоит призадуматься, решать проблему «что делать с npapi Firefox как включить» или может не отставать от прогресса и скачать себе актуальную версию популярной поисковой системы? Выбор стоит исключительно за пользователями.

Скачать актуальную и проверенную версию можно тут или на официальном сайте разработчиков.

Однако у пользователей пятьдесят второй версии ещё есть возможность включить Firefox NPAPI, проведя нехитрые манипуляции в командной строке интернет обозревателя. Для этого в новой странице в поисковой строке необходимо набрать команду «adout:config» и нажать кнопку «Enter» на клавиатуре своего персонального компьютера.

kak-vkl-npapi-v-firefox-2.jpg

Появившееся предупреждение о рисках, в принципе формальность, но изменения в настройках действительно могут испортить стабильную работу браузера. Если решение внести изменения непоколебимы, нажимаем кнопку принятие риска на себя.

Создаём новую логическую опцию. Правой кнопкой по полю «Имя настройки», выбираем свойство «Создать» => «Логическое».

kak-vkl-npapi-v-firefox-3.jpg

В название нового логического значения набираем следующее имя «plugin.load_flash_only».

kak-vkl-npapi-v-firefox-4.jpg

Само значение выбираем «false».

kak-vkl-npapi-v-firefox-5.jpg

После этого необходимо закрыть инженерную вкладку и перезапустить браузер Mozilla Firefox. Если всё прошло успешно, пользователь вновь сможет наслаждаться видео контентом, музыкой и играми.

Используемые источники:

  • https://blog.securityevaluators.com/binary-patching-attempting-to-restore-npapi-support-to-firefox-60-esr-f1b10c804fe7
  • https://www.mozilla.cz/zpravicky/jak-se-lisi-firefox-60-esr/
  • https://firefox-downloads.ru/kak-vklyuchit-npapi-v-firefox.html

Рейтинг автора
5
Подборку подготовил
Максим Уваров
Наш эксперт
Написано статей
171
Ссылка на основную публикацию
Похожие публикации