Author Archive: Peleus Uhley

The Evolution of Exploit Sophistication

When we look at the exploits that Adobe patched from February and March of this year, it is clear that today’s zero-day exploits are increasingly more sophisticated. This increase in sophistication is not limited to the skills needed to find and exploit the vulnerability. The code used to exploit the environment is also more robust in terms of code quality and testing. In short, exploit creation today requires the same level of rigor as professional software engineering projects.

Today’s advanced exploits need to be written to work in any target environment. For instance, February’s Reader 0-day supported 10 different versions of Reader with 2 sub-versions dependent on the end-user’s language. In addition, Flash Player CVE-2013-0634 had shell code for Windows XP, Vista, Windows 7, Server 2003, Server 2003 R2, Server 2008 and Server 2008 R2 as well as supporting six versions of Flash Player. Variants of CVE-2013-0634 also supported Firefox and Safari on Mac OS X. An exploit developer would need a robust testing environment to ensure that the exploit would work in that many different environments for each version of Flash Player. The exploit writers even took into account different CPU architectures by including a signed 32-bit payload and a 64-bit payload. This reflects the fact that these exploits are written with professional code quality and stability requirements for distribution across a dynamic target base.

As vendors are increasing software defenses through techniques such as sandboxing, attackers are now combining multiple vulnerabilities from different vendors to achieve their goals.When I look at the reports from Pwn2Own and some of the recent zero-day reports such as CVE-2013-0643, attacks are moving toward combining vulnerabilities from multiple products, some of which are from different vendors. We are moving away from the model of single vulnerability exploits.

This is all a part of the natural evolution of the threat landscape and the commercialization of exploits. This will require an equal evolution on the part of vendors in their software defences. Karthik Raman and I will be discussing this topic, “Security Response in the Age of Mass Customized Attacks,” in more detail at the upcoming Hack in the Box Conference (HITB) Amsterdam next week. Please stop by our talk if you would like to discuss this further.

Peleus Uhley
Platform Security Strategist

Top 10 Web Hacking Techniques of 2012

I was honored that Jeremiah Grossman asked me to serve again on the panel for the Top 10 Web Hacking Techniques of 2012. During the process, I get to take a look at the year’s most interesting research in greater detail than what is normally allowed in my day-to-day schedule. It makes me question what I thought I knew about existing attack techniques and how this year’s top research reflects on the industry.

Ranking the entries is challenging when you are comparing an SSL cryptography bug vs. the server side request forgery attacks vs. attacks against Chrome add-ons. To do so, requires that you read the full research, review the source code that was written and try to determine what impact the research had on the community. I typically start by considering factors such as:

  • Is the research completely new or is it just an incremental improvement of a known issue?
  • How many end-users are impacted?
  • Is this an individual bug affecting one vendor or an industry-wide issue?
  • What does successful exploitation yield (XSS, file access, OS control, etc.)?
  • Is the exploit practical to conduct?
  • What was the depth of research (a white paper, a tool, etc.)?
  • Extra points for style or creativity?

Reviewing the research to this depth then leads to uncovering smaller bits that are interesting in their own right. For instance, the Blended Threats and JavaScript research leveraged HTML5 to create cross-site file upload capabilities. You will find links to tools that back up the research in entries such as Blended Threats, Attacking OData, Bruteforcing PHPSESSID, and the Chrome add-on research. In some cases, the research reminds you about how many of the old tricks can be re-purposed against new technologies.

As an example, in July, 1998 Microsoft issued security bulletin MS98-004 which allowed, “Unauthorized ODBC Data Access with RDS and IIS”. In 1999, new information was published regarding the bug and Bugtraq ID 529 said exploiting the vulnerability could result in, “obtaining access to non-public servers or effectively masking the source of an attack on another network”. Today, we would classify this type of attack as, “Server Side Request Forgery” (SSRF). Modern SSRF attacks can include both old and new attack techniques. For instance, the ERPScan paper discussed how to use the gopher: protocol in an XML External Entity to attack SAP gateways. Over a decade later, we now have a formal taxonomy for classifying these types of bugs. However, the core issue of effectively sandboxing a web application to a finite set of resources is not much easier than it was 15 years ago. If anything, it has become more difficult as the number of supported communication channels between endpoints has increased.

Finally, you begin to think about why these top 10 bugs are critical to the industry. The panel ranked,”Compression Ratio Info-leak Made Easy” (CRIME) in the top slot due to the overall difficulty of the research, the vendor action required to address it, and the importance of SSL to the security ecosystem. As we move more data to the cloud where we will access it via our mobile or tablet devices, we will increasingly rely on SSL to protect it in transmission. Another interesting trend in this year’s top list was the multiple SSRF entries. As I mentioned, it wasn’t a completely new concept but rather a concept that is seeing a renewed interest as more data moves to the cloud. There were three SSRF entries in the Top 15 and the SSRF entry that was ranked number 2 includes research by two separate groups. As our dependence on cloud storage grows, SSRF will become an increasingly used attack vector for reaching all the critical data that lies behind those front end cloud servers.

As everyone reviews the Top 15, I encourage you to find time to read through each entry. The final list represents great information by talented researchers. Also, I would recommend sharing the list with your engineers. When I shared the Top 15 list with our internal Adobe community a few months ago, I can attest that at least one developer recognized a flaw that existed in his system and corrected the issue. Thanks to Jeremiah Grossman for putting this together each year and allowing me to be a part of it.

Peleus Uhley
Platform Security Strategist

Click-to-Play for Office is Here!

Last week, we introduced a new Flash Player feature that includes a new Microsoft Office click-to-play capability that determines whether Flash Player is being launched within Microsoft Office and automatically checks the version of Office. Launching Flash Player 11.6 from within a version of Office older than Office 2010 will prompt the end-user before executing the Flash content, ensuring potentially malicious content does not immediately execute and impact the end-user. This feature adds another layer of defense against spearphishing attacks by allowing the end-user an opportunity to realize that they have opened a potentially malicious document and close it before the exploit executes.

Click-to-Play for Office should make this attack vector less attractive for attackers. Please update your environments to Flash Player 11.6 as soon as possible.

Peleus Uhley, Platform Security Strategist, ASSET

Raising the Bar for Attackers Targeting Flash Player via Office Files

Adobe has worked hard to make Flash Player more secure. We have worked with our partners Google and Mozilla to sandbox Flash Player in Google Chrome  on Windows, Mac, Linux and Chrome OS, and in MozillaFirefox on Windows. We have also been working closely with Microsoft to deliver Flash Player with Internet Explorer 10 on Windows 8, which means Flash Player runs in Enhanced Protected Mode, further restricting the plugin’s privileges in the browser, and Flash Player updates are delivered through Windows Update. (I’ll be blogging on the protections Enhanced Protected Mode provides for Flash Player users in a separate blog post later this month.) We also welcomed Apple’s initiative to  encourage Mac users to stay up-to-date by disabling older versions of Flash Player and directing users to install the latest, most secure version of Flash Player, and Mozilla’s click-to-play feature in Firefox. And we have worked hard on improving the update mechanism in Flash Player to make it easier for our user s to stay up-to-date. Windows and Macintosh users receive critical security patches through regular update checks by the Flash Player update mechanism. These enhancements help to protect users as they browse the Web.

Over the last year, Adobe has been driving down the number of Flash (SWF)-based zero-days used in the wild. Since the introduction of Adobe Reader X Protected Mode (aka sandboxing) in November 2010, the most common Flash Player zero-day attack vector has been malicious Flash content embedded in Microsoft Office documents and delivered via email. In fact, today’s Flash Player update addresses CVE-2013-0633 and CVE-2013-0634, both of which are being exploited in targeted attacks leveraging this very attack vector. In the next feature release of Flash Player, which is currently in beta, we will be delivering a solution designed to help make this attack vector less attractive.

Microsoft Office 2010 includes a Protected Mode sandbox for limiting the privileges of content within the document. If the document originates from the Internet or Untrusted Zone, the Protected View feature will prevent Flash Player content from executing by default. However, not everyone has the ability to update to Office 2010. In Office 2008 and earlier, Flash Player content will run by default without sandbox protections, making it an attractive attack vector for targeted attacks.

To protect users of Office 2008 and earlier, the upcoming release of Flash Player will determine whether Flash Player is being launched within Microsoft Office and check the version of Office. If Flash Player is launched within a version prior to Office 2010, Flash Player will prompt the end-user before executing the Flash content with the dialogue below:

OfficeClickToPlayFP

Therefore, if an end-user opens a document containing malicious Flash content, the malicious content will not immediately execute and impact the end-user. This extra step requires attackers to integrate a new level of social engineering that was previously not required.

We’ve seen these types of user interface changes lead to shifts in attacker behavior in the past and are hopeful this new capability will be successful in better protecting Flash Player users from attackers leveraging this particular attack vector as well.

We’ll post an update here as soon as this new feature in Flash Player becomes available. Stay tuned!

Peleus Uhley, Platform Security Strategist, ASSET

Firefox Click-to-Play Helps Protect Our Customers

The Adobe team has worked hard to improve patch adoption by delivering background updaters for Flash Player and Adobe Reader. In addition, we have worked with partners, such as Microsoft and Google, to reduce update fatigue by delivering patches through existing update mechanisms. However, one of the hardest challenges in protecting end users is reaching what is sometimes referred to as the “long tail” in an update graph. These are the users who, for various reasons, have not updated their systems in several months or even years. Reaching these last few end users can be difficult if they have disabled their update mechanisms. Unfortunately, they are also the users who are most likely to be successfully attacked.

Yesterday, Mozilla announced an update to the Firefox click-to-play feature that will warn users when they try to play plugin content with an out-of-date browser plugin. Since Mozilla will now be assisting plugin vendors in reminding these users to update, we will hopefully be able to convert more of them to patched versions. At the same time, Mozilla is helping to protect these users from those who would use older vulnerabilities to infect their systems. We support Mozilla in their efforts to protect and inform our mutual customers.

Inside Flash Player Protected Mode for Firefox

Today, we launched Flash Player Protected Mode for Firefox on Windows. Our Protected Mode implementation allows Flash Player to run as a low integrity process with several additional restrictions that prohibit the runtime from accessing sensitive resources. This approach is based on David LeBlanc’s Practical Windows Sandbox design and builds upon what Adobe created for the Adobe Reader X sandbox. By running the Flash Player as a restricted process, Adobe is making it more difficult for an attacker to turn a simple bug into a working exploit. This blog post will provide an overview of the technical implementation of the design.

When you first navigate to a page with Flash (SWF) content, you will notice that there are now three processes for Flash Player. This may seem odd at first, but there is a good reason for this approach. One of the goals for the design was to minimize the number of changes that were necessary in Firefox to support the sandbox. By keeping Flash Player and Firefox loosely coupled, both organizations can make changes to their respective code without the complexity of coordinating releases.

The first process under the Firefox instance is called “plugin-container.exe.” Firefox has run plugins in this separate process for quite some time, and we did not want to re-architect that implementation. With this design, the plugin container itself is only a thin shim that allows us to proxy NPAPI requests to the browser. We also use this process as our launching point for creating the broker process. Forking the broker as a separate process allows us to be independent of the browser and gives us the freedom to restrict the broker process in the future. From the broker process, we will launch the fully sandboxed process. The sandboxed process has significant restrictions applied to it. It is within the sandbox process that the Flash Player engine consumes and renders Web content.

The restrictions we apply to this sandboxed process come from the Windows OS. Windows Vista and Windows 7 provide the tools necessary to properly sandbox a process. For the Adobe Reader and Acrobat sandbox implementation introduced in 2010, Adobe spent significant engineering effort trying to approximate those same controls on Windows XP. Today, with Windows 8 just around the corner and Windows XP usage rapidly decreasing, it did not make sense for the Flash Player team to make that same engineering investment for Windows XP. Therefore, we’ve focused on making Protected Mode for Firefox available on Windows Vista and later.

For those operating systems, we take advantage of three major classes of controls:

The first control is that we run the sandboxed process at low integrity. By default, processes started by the user are executed at medium integrity. Running the process at a low integrity level prevents the process from writing to most areas of the user’s local profile and the registry which require a medium integrity level to access. This also allows us to take advantage of User Interface Privilege Isolation (UIPI) which prevents low integrity processes from sending windows messages to higher integrity processes.

The second class of controls applied to the sandboxed process is to restrict the capabilities of the access token. A process will inherit a list of available Security Identifiers (SIDs) from the user’s security profile. These SIDs represent the different OS groups to which the user belongs. The access token contains this list of SIDs along with a set of controls for those SIDs. The Windows OS will compare the SIDs in the access tokens to the group permissions of the target object (e.g a file) to determine if access is allowed. The Windows OS allows us to define how the process SIDs are used in that comparison.

In general, a sandboxed process will need to be able to access resources directly owned by the user. However, in most cases it is unlikely that the sandbox will need the extended set of resources available to the user via group permissions. As a concrete example, your company may have a contact list on a network share that is available to everyone within your “Employees” group. It isn’t your file but you can access it because you are in the “Employees” group for your company. The Flash Player sandbox process doesn’t need to be able to directly access that file.

We identified that our sandbox process will need to access OS resources using the following SIDs: BUILTIN\\Users, Everyone, the User’s Logon SID, and NTAUTHORITY\\INTERACTIVE. For any other SIDs that are inherited from the user, we set the deny-only attribute to prohibit the process from accessing the resource based solely on that SID. To continue the example of the contact list on the file share, the sandboxed process would not be able to access the contact list because the file is not owned by you and the deny-only attribute on the “Employees” group SID would prevent access using your group permission. Process privileges are also limited to only the SeChangeNotifyPrivilege, which is required for the process to be notified of file system changes and for certain APIs to work correctly. The graphic below shows the permissions applied to the sandboxed process.

The third control applied to the sandboxed process are job restrictions. As one example, we can prevent the sandboxed process from launching other processes by setting Active Processes to 1. We can also limit the sandbox’s ability to communicate with other processes by restricting access to USER Handles and Administrator Access. The USER Handles restriction complements UIPI by preventing the process from accessing user handles created by processes not associated with our job. Finally, we can limit the sandboxes ability to interfere with the OS by limiting access to System Parameters, Display Settings, Exit Windows and Desktop.

More information on job limits, privilege restrictions and UIPI can be found in Part 2 of Inside Adobe Reader Protected Mode.

Once you get past OS-provided controls, the next layer of defense is Flash Player broker controls.

The OS broker process runs at medium integrity and acts as a gatekeeper between the untrusted sandbox process and the operating system. The sandbox process must ask the OS broker process for access to sensitive resources that it may legitimately need. Some examples of resources that are managed by the broker include file system access, camera access, print access and clipboard access. For each resource request, the sandbox contains policies which define what can and cannot be accessed. For instance, the sandbox process can request file system access through the broker. However, the policy within the broker will limit access to the file system so that the sandbox can only write to a predetermined, specific set of file system paths. This prevents the sandbox from writing to arbitrary locations on the file system. As another example, the sandbox cannot launch applications directly. If Flash Player needs to launch the native control panel, the Flash Player engine must forward the request to the broker process. The broker will then handle the details of safely launching the native control panel. Access to other OS resources such as the camera are similarly controlled by the broker. This architecture ensures that the sandboxed process cannot directly access most parts of the operating system without that access first being verified by the broker.

Overall, the Flash Player sandbox process has been a journey of incremental improvements with each step bringing end-users a more secure environment. We started by supporting Protected Mode within Internet Explorer, which enabled Flash Player to run as a low integrity process with limited write capabilities. From there, we worked with Google on building the Chrome sandbox, which converted Flash Player to using a more robust broker implementation. This release of Flash Player Protected Mode for Firefox on Windows takes the Chrome implementation one step further by changing Flash Player to run with job limits on the process. With Flash Player Protected Mode being based on the same technology as Adobe Reader X, we are confident that this implementation will be a significant barrier and help prevent exploits via Flash Player for Firefox users. Going forward, we plan to continue to build on this infrastructure with more sandbox projects, such as our upcoming release of Flash Player for Chrome Pepper. As we combine these efforts with other efforts, such as the background updater, we are making it increasingly more difficult for Flash Player to be targeted for malicious purposes.

Peleus Uhley, Platform Security Strategist, ASSET
Rajesh Gwalani, Security Engineering Manager, Flash Runtime

ColdFusion 10 Provides Powerful New Security Tools

Today marks the release of ColdFusion 10. This release redefines many aspects of the ColdFusion 10 security model and incorporates the principles of the Adobe Secure Product Lifecycle (SPLC). With this release, we’ve worked to improve three major areas: Our goals were to improve patch adoption, improve the default configuration, and to make it easier for developers to create secure ColdFusion applications.

One of the most common reasons for a successful attack against a ColdFusion server is that it doesn’t have the latest security updates. In all fairness, this is not completely the administrator’s fault. Updating a ColdFusion server can be difficult due to the number of manual steps involved. Also, it is easy to miss a security update announcement. With ColdFusion 10, we make both of these steps easier. The ColdFusion 10 administration interface now incorporates a simple “Check For Updates” button. Alternatively, the server can be configured to automatically check for updates and send an email to the administrator once one becomes available.  Finally, the interface allows the developer to apply the patch through a single button click in the administrator interface. These features help make updating the server much more straightforward.

The second major area of improvement focused on making it easier for administrators to securely deploy ColdFusion 10. One of the most attractive characteristics of ColdFusion is that it has always been a simple development environment. Therefore, there were several features that favored making the early phases of development easier by leaving the complicated aspects disabled by default. The cost of this choice was that once developers were ready to deploy to production, they had to review a 35-page lockdown guide to enable and/or configure those more complicated features appropriately. With today’s release, we offer the option of starting the server in a secure-by-default configuration. This greatly simplifies the process of making a server production-ready with a secure configuration.

The last area of improvement focused on providing developers with an increased number of tools for creating secure ColdFusion applications. One example is that we have provided integrated OWASP ESAPI support in the platform. We originally started to include ESAPI in ColdFusion 9 just for our internal needs of addressing cross-site scripting (XSS). Once developers noticed the library in the update, they quickly developed several blogs on how to unofficially start using it in your ColdFusion code. Today’s release formally exposes several aspects of ESAPI through ColdFusion API’s to help developers avoid cross-site scripting vulnerabilities.

We also improved the session management capabilities in ColdFusion–another aspect of making it easier for developers to create ColdFusion applications. We have improved APIs to make it easier to set the HttpOnly and Secure flags on cookies. Session rotation has been improved through new SessionRotate and SessionInvalidate APIs.  To combat cross-site request forgery (CSRF) with active sessions, the ColdFusion team added an API for generating unique tokens for form requests. The team also added support for protecting against clickjacking attacks on active users by adding support for the X-FRAME-OPTIONS header.

ColdFusion 10 is a significant advancement in helping ColdFusion customers improve their secure product lifecycle processes. It is even easier to create secure content, deploy the content on a secure server and manage the server updates once it is deployed. This is only an introduction to the major security enhancements in ColdFusion 10. For more information on all the new security APIs for developers, please see the ColdFusion documentation on Security Enhancements in ColdFusion 10.  ColdFusion administrators should review the Administering Security and the Server Update sections for a complete list of server improvements.

A Basic Distributed Fuzzing Framework for FOE

Last week, CERT released a Python-based file format fuzzer for Windows called Failure Observation Engine (FOE). It is a Windows port of their Linux-based fuzzer, Basic Fuzzing Framework(BFF). CERT provided Adobe with an advanced copy of FOE for internal testing, and we have found it to be very useful. One of the key features of FOE is its simplicity. The configuration file is very straightforward, which makes it easy to introduce to new teams. We have also used the “copy” mode of FOE to help automate triaging large sets of external reports. It is a great tool to have for dumb fuzzing. For this blog, I am going to discuss a simple Python wrapper I created during my initial testing of the tool which helped to coordinate running FOE across multiple machines. This approach allows you to pull seed files from a centralized location. You can also view the status of all of the fuzzing runs and their results from the same location. If you are not interested in writing a distributed fuzzing framework, then you might want to stop reading because the rest of this blog is all about code. :-)

The goal of this distributed fuzzing framework design was to create something simple, lightweight since I was experimenting with a new tool. I set a personal limit of keeping the project to around 1,000 lines of code in order to scope my time investment. That said, I also wanted to build something that I could easily scale later in the event that I liked it enough to invest more time. For the client-side code, I used Python since that was already required for FOE. On the server side, I had a Linux/Apache/MySQL/Perl (LAMP) server. Knowing that everyone has their own preference for server-side authoring, I am only going to describe the server-side architecture rather than providing the Perl source. Nothing in the server-side code is so complicated that a Web developer couldn’t figure out how to do an implementation in the language of their choice from this description. While I designed this for testing the FOE fuzzer, only one file in the entire system is FOE-specific, which makes the infrastructure reusable for other fuzzers. The current name of the main script is “dffiac.py” because I thought of this project as a, “Distributed Fuzzing Framework in a Can”.

For this design, all of the tracking logic is consolidated on the centralized server. The Python script will issue requests for data using simple GETs and POSTs over HTTP. The server will respond to the requests with basic XML. The fuzzing seed files are hosted on the server in a public web server directory from which they can be downloaded. Identified crashes will be uploaded to the server and placed in a public web server directory. Both the client-side and server-side codes are agnostic with regards to the format of the seed files and the targeted application. Therefore, this should be relatively easy to set up in any infrastructure.

 

The database design

In this design, the mySQL server coordinates the runs across all the different machines. You first need a table containing all the files that you want to fuzz. At a bare minimum, it needs a unique primary key (fid), the name of the file and its location on the web server. I currently have a database of more than 60,000 SWF files that are sub-categorized based on type so that I can focus fuzzing to specific types of SWF files. However, name and location will get you started with fuzzing.

 

seed_files

Field Type Description
fid Integer (primary key, autoincrement) The unique File ID for this entry
name VARCHAR The filename
location VARCHAR The relative web directory for the file (e.g. “/fuzzing/files/”)

 

The next thing that you will need is a table to track all of the fuzzing runs. A “run” is defined as one or more servers testing with the same FOE configuration file against a defined set of seed files. There are multiple ways in which you can define the selected seed files for the run. For instance, you may want to use FOE against multiple types of applications. For this scenario, you might have a different seed_files for each file type. To support the need for different seed_files tables, the design of run_records requires that you provide the “table_name” that will be used for this run. Once a seed_files table is selected, it may be necessary to further restrict the run to a subset of files within the seed_files tables. For instance, you may only want to select a subset of files within the given table. Therefore, the design requires that you provide a “type” parameter which denotes the method for selecting files from the seed_files table. The value of type can include values such as “all”, “range” or any other sub-category you want to define. As an example, this particular run may be a “range” type that starts at start_fid and stops at end_fid.

 

run_records

Field Type Description
rid Integer (primary key, autoincrement) The unique ID for this run
name VARCHAR The human readable name for the run
description VARCHAR A description for the run (e.g. config or mutation used, # of iterations, etc.)
type VARCHAR Values can include (all, range, etc)
table_name VARCHAR The name of the seed_files table that will be used for testing
start_fid Integer The first fid from seed_files to be fuzzed in this run
end_fid Integer The last fid from seed_files to be fuzzed in this run
current_fid Integer This tracks the next fid to be tested during the run

 

For every run, you will have multiple servers running FOE. For each server instance, it will be necessary to track the server name, when it started, the current status of the server, and when it last provided an update. The status will include values such as “running” and “complete.”  You can infer whether a machine has died based on whether it has been too long since the timestamp for the last_update field was modified.

 

server_instances

Field Type Description
siid Integer (primary key, autoincrement) The unique server instance ID
server_name VARCHAR The name of the server (e.g hostname + IP address)
status VARCHAR Is it running or has it completed.
start_time timestamp When did this instance start?
last_update timestamp When was the last request from this instance?
rid Integer What run_record is this instance associated with?

 

Lastly, you will need a table to record the results. The script will record the server_instance ID (siid) where the crash was found in case there are issues with reproducing the crash. This will allow a QA to retest on the original machine where the crash occured. It is also necessary to track which run was able to identify the crash. The rid is not recorded because it can already be extrapolated from the siid. According to database normalization rules, redundant information should not be stored in tables. In this design, the script will record a result in fuzz_records regardless of whether a crash was identified.  This allows you to track which files have been tested against which FOE configurations. If a crash is identified, the web server directory where the crash result was stored is also recorded.

 

fuzz_records

Field Type Description
frid Integer (primary key, autoincrement) The unique fuzz record ID
fid Integer The seed_files ID for this entry
siid Integer The server instance ID for this entry
crash Boolean Whether a crash was recorded during this test
location VARCHAR Where the crash result was stored (e.g. /results/run_id/)

 

The config file

You will start the Python script by providing a simple configuration file in the command line: “python dffiac.py dffiac.cfg”. The configuration file is in the same format as the FOE configuration file and contains the following:

 

dffiac.cfg

[foeoptions]
python_location=C:\Python26\python.exe
config_location=C:\FOE\configs\my_foe_config.cfg

 

[runoptions]
run_id=1
web_server=http://my.internal.server.com
upload_cgi=/fuzzers/crash_uploader.cgi
action_cgi=/fuzzers/action_handler.cgi

 

[logoptions]
log_dir=C:\dffiac\logs\

 

The foeoptions section tells the script where to find the Python executable and the location of the FOE config script you will use for this run. The runoptions section provides the run id (rid) the database is using to track this run along with the location of the web server, the path to the action_handler.cgi and the path to the CGI that will handle the file uploads. The logoptions allows you to specify where the script will log local information regarding the run. The logs directory needs to exist prior to starting the script. The config_location and run_id are likely the only two elements that will change from run to run.

 

The transaction flow

For this next section, we will review the transactions between the dffiac.py script and the web server. The web server will read in the GET parameters, execute the relevant SQL query and return the results as XML. All but one request is handled by the action_handler defined in the dffiac.cfg config file. The upload of the crash results is handled by the upload_cgi defined in the dffiac.cfg config file.

Once dffiac.py has started and been initialized by the config file, the script will begin sending requests to the server. An “action” parameter informs the action_handler CGI which query to perform. The server will always respond to the Python script with the relevant information for the request in a simple XML format.

 

The first HTTP request from the Python code will be to gather all the information regarding the run_id provided in the config file:

GET /fuzzers/action_handler.cgi?action=getRunInfo&rid=1

 

The web server will then perform this SQL query with the rid that was provided:

select run_type,start_fid,end_fid from run_records where rid = ?

 

The results from the query will be used to return the following XML (assuming the run is defined as the range of fids from 1-25):

<xml>
  <run_type>range</run_type>
  <start_fid>1</start_fid>
  <end_fid>25</end_fid>
</xml>

 

Now that dffiac.py has the information for the run, it will then inform the web server that the run is starting:

GET /fuzzers/action_handler.cgi?action=recordServerStart&rid=1&serverName=server1

 

This HTTP request will result in the following SQL query:

insert into server_instances (server_name,status,start_time,rid) values (?,'running',NOW(),?)

 

The insert_id from this query (siid) becomes the unique identifier for this instance and is returned for use in later queries:

<xml>
  <siid>1</siid>
</xml>

 

Now that this instance has officially registered to contribute to this run, the Python script will begin requesting individual files to test:

GET /fuzzers/action_handler.cgi?action=getNextFid&rid=1&run_type=range

 

The corresponding SQL query will vary depending on how you have defined your run. For this example, we will assume that this is a basic run that will incrementally walk through the file IDs in the seed_files table. To accomplish this, we create an SQL variable called “value” and assign it the current_fid. By recording the value of the current fid and incrementing the “value” in a single statement, we can avoid a race condition when multiple servers are running.

update run_records set current_fid = current_fid + 1 where rid = ? and @value := current_fid;

 

At this point, “@value” is set to 1 which is the fid the Python script will test and the current_fid in the database table has been incremented to 2. The web server can then fetch ”@value with the following SQL command:

select @value;

 

Since the process of asking for the next fid will automatically increment the value of current_fid, the value of current_fid will eventually exceed the value of the end_fid in the database table. While it may seem weird, it doesn’t hurt the process. This can be allowed to occur or you can add a little more server-side logic to have the server return -1 as the current_fid to stop the run when end_fid is reached.

 

The “select @value” result will be returned to Python script as the current_fid available for testing:

<xml>
  <current_fid>1</current_fid>
</xml>

 

The Python script will then compare the current_fid with the end_fid that it received earlier to determine whether to stop testing.

 

Once we have the fid of the file that we will test, we can then fetch the information for that specific file:

GET /fuzzers/action_handler.cgi?action=getFileInfo&rid=1&fid=1

 

Using the rid, the web server can query the run_records table to find the table_name that contains the seed files.

select table_name from run_records where rid = ?

 

Assuming the result of that query will be saved as the variable, “$table_name”, the web server can construct the query to retrieve the file name and the directory location that corresponds to the file id:

"select name, location from" . $table_name . "where fid = ?"

 

The web server will return the file name and location with the following XML:

<xml>
  <name>seed.txt</name>
  <location>/fuzzers/files/</location>
</xml>

 

Now, that the location of the seed file is known, it can be downloaded by dffiac.py and saved in the FOE seeds directory. The FOE fuzzer is then started, and dffiac.py waits for FOE to finish testing that seed file. Once FOE testing has completed, the result will need to be recorded by sending the fid and a boolean value indicating whether a crash was identified with that test:

GET /fuzzers/action_handler.cgi?action=recordResult&siid=1&fid=1&crash=1

 

This will result in the following query:

insert into fuzz_records (siid,fid,crash) values (?,?,?)

 

The web server will also record that it has received an update from this fuzzing server instance in the server_instances table to let us know that it is still alive and processing:

update server_instances set lastUpdate = NOW() where siid = ?

 

The result is recorded regardless of success or failure so that you can track which files have been successfully tested with which configs. You could infer this from the run_records, but if a machine dies, a file might be skipped. The server-side code will take the insert_id from the fuzz_records statement (frid) and return the following XML:

<xml>
  <frid>1</frid>
</xml>

 

If there was a crash, the Python script will zip up the crash directory, base64 encode the file and POST it to the upload_cgi identified in the dffiac configuration file. The script will leave the zip file on the fuzzing server if an error is detected during the upload. Along with the zip file, it will send the rid and frid. The rid is used to store files in a web server directory unique to that run. The frid is sent so that the action_handler can update the fuzz_records entry with the location of the uploaded crash file (e.g. “/results/1/zip_file_name.zip”) in the following SQL query:

update fuzz_records set location = ? where frid = ?

 

A successful upload will result in the following XML:

<xml>
  <success>1</success>
</xml>

 

A failed upload can return the description of the error to the client with the following XML:

<xml>
  <error>Replace me with the actual error description</error>
</xml>

 

The dffiac.py script will then continue retrieving new files and testing them with FOE until the end_fid is reached. Then the final call to the web server will record that this fuzzing server instance has completed its run and has stopped:

GET /fuzzers/action_handler.cgi?action=recordRunComplete&siid=1

 

The web server will record the completion with the following SQL query:

update server_instances set status='complete', lastUpdate=NOW() where siid = ?

 

The web server will respond to this last request with the following XML:

<xml>
  <success>1</success>
</xml>

 

The last XML response is currently ignored by the Python script but a more robust implementation could double-check for errors.

 

The Python code

The logic for the distributed fuzzing framework is split into one main file (dffiac.py) and three libraries that are contained in a /libs directory. We’ll start with the three libraries in the /libs directory. The code below is the library that contains the utilities for creating the zip file of the crash result.

 

ZipUtil.py (30 lines)

import zipfile
import os

 

class ZipUtil:

 

#Create a zip file and add everything in path_ref
def createZipFile(self, path_ref, filename):
  zip_file = zipfile.ZipFile(filename, 'w')

 

  #Check to see if path_ref is a file or folder
  if os.path.isfile(path_ref):
    zip_file.write(path_ref)
  else:
    self.addFolder(zip_file, path_ref)

 

  zip_file.close()

 

#Recursively add folder contents to the zip file
def addFolder(self, zip_file, folder):
  for file in os.listdir(folder):

 

    #Get path of child element
    child_path = os.path.join(folder, file)

 

    #Check to see if the child is a file or folder
    if os.path.isfile(child_path):
      zip_file.write(child_path)
    elif os.path.isdir(child_path):
      self.addFolder(zip_file, child_path)

 

The second library will base64 encode the zip file prior to uploading it to the web server via a POST method.  On the server side, you will need to base64 decode the file before writing it to disk.

 

PostHandler.py (77 lines)

import mimetools
import mimetypes
import urllib
import urllib2
import base64

 

class PostHandler(object):

 

  def __init__(self,webServer,uploadCGI):
    self.web_server = webServer
    self.upload_cgi = uploadCGI
    self.form_vars = []
    self.file_attachments = []
    self.mime_boundary = mimetools.choose_boundary()
    return

 

  #Add a form field to the request
  def add_form_vars(self, name, value):
    self.form_vars.append((name, value))
    return

 

  #Get the mimetype for the attachment
  def get_mimetype(self,filename):
    mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
    return(mimetype)

  #Add a base64 encoded file attachment
  def append_file(self, var_name, filename, file_ref, mimetype=None):
    raw = file_ref.read()
    body = base64.standard_b64encode(raw)
    if mimetype is None:
      mimetype = self.get_mimetype(filename)
    self.file_attachments.append((var_name, filename, mimetype, body))

  #Get the body of the request as a string
  def get_request_body(self):
    lines = []
    section_boundary = '--' + self.mime_boundary

 

    # Add the form fields
    for (name, value) in self.form_vars:
      lines.append(section_boundary)
      lines.append('Content-Disposition: form-data; name="%s"' % name)
      lines.append('')
      lines.append(value)

 

    # Add the files to upload
    for var_name, filename, content_type, data in self.file_attachments:
      lines.append(section_boundary)
      lines.append('Content-Disposition: file; name="%s"; filename="%s"' % \
        (var_name, filename))
      lines.append('Content-Type: %s' % content_type)
      lines.append('Content-Transfer-Encoding: Base64')
      lines.append('')
      lines.append(data)

 

    #Add the final boundary
    lines.append('--' + self.mime_boundary + '--')
    lines.append('')

 

    #Combine the list into one long string
    CRLF = '\r\n'
    return CRLF.join(lines)
  #Send the final request
  def send_request(self):
    request = urllib2.Request(self.web_server + self.upload_cgi)
    content_type = 'multipart/form-data; boundary=%s' % self.mime_boundary
    request.add_header('Content-type',content_type)

 

    form_data = self.get_request_body()
    request.add_header('Content-length',len(form_data))
    request.add_data(form_data)

 

    result = urllib2.urlopen(request).read()
    return result

 

 

The third library handles the communication between the client and server. It will generate the GET requests and parse the XML responses.

 

actionHandler.py (94 lines)

import urllib
import urllib2
from xml.dom.minidom import parseString

 

class ActionHandler:

 

  #Initialize with the information from the config file
  def __init__(self,options,localLog):
    self.webServer = options['runoptions']['web_server']
    self.uploadCGI = options['runoptions']['upload_cgi']
    self.actionCGI = options['runoptions']['action_cgi']
    localLog.write("Configured web server\n")

 

  #Parse the XML for the requested text value
  def getText(self,nodelist):
    rc = []
    for node in nodelist:
      if node.nodeType == node.TEXT_NODE:
        rc.append(node.data)
    return ''.join(rc)

 

  #Make a web request to the server with the provided GET parameters
  def retrieveInfo(self,values):
    url = self.webServer + self.actionCGI
    data = urllib.urlencode(values)
    response = urllib2.urlopen(url,data)
    xml = response.read()
    response.close()
    return(xml)

 

  #Get the information for the rid provided in the config file
  def getRunInfo(self,rid):
    values = {'action':'getRunInfo',
      'rid': rid}
    xml = self.retrieveInfo(values)
    dom = parseString(xml)
    run_type = self.getText(dom.getElementsByTagName("run_type")[0].childNodes)
    start_fid = self.getText(dom.getElementsByTagName("start_fid")[0].childNodes)
    end_fid = self.getText(dom.getElementsByTagName("end_fid")[0].childNodes)
    return (run_type,start_fid,end_fid)

 

  #Record that this server instance is starting a run
  def recordServerStart(self,rid,serverName):
    values = {'action':'recordServerStart',
      'rid': rid,
      'serverName':serverName}
    xml = self.retrieveInfo(values)
    dom = parseString(xml)
    lastrowid = self.getText(dom.getElementsByTagName("siid")[0].childNodes)
    return (lastrowid)

 

  #Record that the server is now complete with its tests
  def recordRunComplete(self,siid):
    values = {'action':'recordRunComplete',
      'siid': siid}
    xml = self.retrieveInfo(values)

 

  #Get the fid for the next file to be fuzzed
  def getNextFid(self,rid,fid,run_type):
    values = {'action':'getNextFid',
      'fid':fid,
      'run_type':run_type,
      'rid':rid}
    xml = self.retrieveInfo(values)
    dom = parseString(xml)
    current_id = self.getText(dom.getElementsByTagName("current_fid")[0].childNodes)
    return current_id

 

  #Get the file name and location for the selected fid
  def getFileInfo(self,rid,fInfo):
    values = {'action':'getFileInfo',
      'rid':rid,
      'fid':fInfo.fid}
    xml = self.retrieveInfo(values)
    dom = parseString(xml)
    fInfo.name = self.getText(dom.getElementsByTagName("name")[0].childNodes)
    fInfo.location = self.getText(dom.getElementsByTagName("location")[0].childNodes)

 

#Record the result from the fuzzing test
  def recordResult(self,siid,fid,result):
    values = {'action':'recordResult',
      'siid':siid,
      'fid':fid,
      'crash':result}
    xml = self.retrieveInfo(values)
    dom = parseString(xml)
    frid = self.getText(dom.getElementsByTagName("frid")[0].childNodes)
    return frid

 

Finally, we get to the main file which is responsible for reading the config file and driving the fuzzing run. This is the only file that is specific to the FOE fuzzer.

 

dffiac.py (177 lines)

import os
import shutil
import socket
import subprocess
import sys
import urllib2
import ConfigParser
import time

 

sys.path.append("libs")

 

from ZipUtil import ZipUtil
from PostHandler import PostHandler
from ActionHandler import ActionHandler

 

#This will track the fid, and location of the file
class FileInfo:
  pass

 

#Convert the options in the config file to lists
def parse_options(config):
  options = {}
  for section in config.sections():
    options[section] = {}
    for (option, value) in config.items(section):
      options[section][option] = value
  return options

#Create a local text file for logging
def openLog(options):
  localLogDir = options['logoptions']['log_dir']
  runName = options['runoptions']['run_id']
  timestamp = int(time.time())
  localLog = open(localLogDir + runName + '_' + str(timestamp) + '.txt', 'w')
  localLog.write("Starting run: " + runName + " at " + str(timestamp) + "\n")
  return localLog

 

#Close the local text file log
def closeLog(localLog):
  localLog.write("COMPLETE\n")
  localLog.close()

 

#Download the next file to be fuzzed
def getNextFile(fInfo, options, foe_options, localLog):
  u = urllib2.urlopen(options['runoptions']['web_server'] + fInfo.location + fInfo.name)
  localFile = open(foe_options['runoptions']['seedsdir'] + "\\" + fInfo.name, 'wb')
  localFile.write(u.read())
  localFile.close()
  localLog.write ('Created file: ' + foe_options['runoptions']['seedsdir'] + "\\" + fInfo.name + '\n')

 

#Store the results in a zip file
def createZip(outputDir,filename):
  zipTool = ZipUtil()
  zipTool.toZip(outputDir,filename)
  zipFile = open(filename,'rb')
  return zipFile

 

#Post the zip file to the server
def postZip(options,frid,rid,filename,zipFile):
  form = PostHandler(options['runoptions']['web_server'], options['runoptions']['upload_cgi'])
  form.add_form_vars('frid',frid)
  form.add_form_vars('rid',rid)
  form.append_file('fname',filename,zipFile)
  result = form.send_request()
  return result

 

if __name__ == "__main__":
  if (len(sys.argv) < 2):
    print "usage: %s <runconfig.cfg>" % sys.argv[0]
    exit(1)

 

  #Read the dffiac config file
  configFile = sys.argv[1]
  if not os.path.exists(configFile):
    print "config file doesn't exist: %s" % configFile
    exit(1)
  config = ConfigParser.SafeConfigParser()
  config.read(configFile)

 

  #Read the foe config file
  options = parse_options(config)
  config2 = ConfigParser.SafeConfigParser()
  config2.read (options['foeoptions']['config_location'])
  foe_options = parse_options(config2)

 

  #Set up logging
  localLog = openLog(options)

 

  #Configure the web server
  aHandler = ActionHandler(options, localLog)

 

  #Get the information for this run
  rid = options['runoptions']['run_id']
  (run_type,start_fid,end_fid) = aHandler.getRunInfo(rid)

 

  #Record server start
  hostName = socket.gethostname()
  hostIP = socket.gethostbyname(hostName)
  serverName = hostName + "_" + hostIP
  siid = aHandler.recordServerStart(rid, serverName)
  localLog.write("Starting as server instance: " + siid + "\n")

 

  #Get the first file to be processed
  fInfo = FileInfo()
  fInfo.fid = aHandler.getNextFid(rid,start_fid,run_type)
  localLog.flush()

 

  #loop until done
  while (int(fInfo.fid) <= int(end_fid)):
    #Get the location information for the current file
    aHandler.getFileInfo(rid,fInfo)

 

    #Download and store the file
    getNextFile(fInfo,options,foe_options,localLog)

    outputDir = foe_options['runoptions']['outputdir'] + "\\" + foe_options['runoptions']['runid']

 

    #Run fuzzer
    exitCode = subprocess.call(options['foeoptions']['python_location'] + " " + options['foeoptions']['foe_location'] + " " + options['foeoptions']['config_location'], shell=True)

 

    #Check for completion of a succesful run
    if exitCode != 0:
      localLog.write("Error running foe on fid " + fInfo.fid + "\n")
    else:
      dirList = os.listdir(outputDir)

 

      #Detect whether bugs were found
      if len(dirList) > 2:

 

        #Record the result in fuzz_records
        frid = aHandler.recordResult(siid,fInfo.fid,1)
        localLog.write("Recording frid: " + frid + "\n")

 

        #Store the results in a zip file
        filename = frid + "-" + fInfo.name + ".zip"
        file_path = os.getcwd() + filename
        zipFile = createZip(outputDir,file_path)

 

        #Post the zip file back to the server
        result = postZip(options,frid,rid,filename,zipFile)
        zipFile.close()

 

        #Make sure the file got there OK
        if result.find("error") == -1:
          localLog.write("Results successfully uploaded.\n")
          os.remove(file_path)
        else:
          localLog.write("There was an error in the upload: " + result + "\n")

 

        localLog.write("Found bugs with " + fInfo.fid + "\n")
      else:
        #Record no bugs found in the directory
        aHandler.recordResult(siid,fInfo.fid,0)
        localLog.write("No bugs found with " + fInfo.fid + "\n")

 

    #The if len(dirlist) check on the results is complete
    #Erase files so that FOE starts clean on the next run
    os.remove(foe_options['runoptions']['seedsdir'] + "\\" + fInfo.name)
    shutil.rmtree(outputDir)
    localLog.flush()

 

    #Get the next FID
    fInfo.fid = aHandler.getNextFid(rid,fInfo.fid,run_type)

 

  #The while loop is complete
  #Record this run instance as being complete
  aHandler.recordRunComplete(siid)

 

  #Close the local file log
  closeLog(localLog)

 

This blog is only meant to describe how you can stand up a basic distributed fuzzing framework based on FOE fairly quickly in approximately 1,000 lines of code. The client-side code turned out to be 378 lines, my server-side action_handler CGI was 150 lines and the upload CGI was 72 lines of Perl. That is enough to get the script to run based on information from a database. With the remaining 400 lines, I created a CGI to display the status of my runs and a CGI to generate a run. You will also want to write a script to mirror the dffiac.cfg and FOE configuration file across machines. Over time, I expect that you would make this design more robust for your particular infrastructure and needs. You can also expand this infrastructure for your other fuzzers with some modifications to the main file. What I provide here is just enough to help you get started performing distributed fuzzing with a small amount of coding and the FOE fuzzer.

 

Permission for this blog entry is granted as CCplus, http://www.adobe.com/communities/guidelines/ccplus/commercialcode_plus_permission.html

 

An Update for the Flash Player Updater

Peleus here with the second major 2012 security announcement for Flash Player. Today’s release of Flash Player contains a new background updater. This new background updater will allow Windows users to choose an automatic update option for future Flash Player updates.

If you read this September 2011 CSIS report, then you saw that 99.8 percent of malware installs through exploit kits are targeting out-of-date software installations. This point was reiterated recently in volume 11 of the Microsoft Security Intelligent Report. Also, attackers have been taking advantage of users trying to manually search for Flash Player updates by buying ads on search engines pretending to be legitimate Flash Player download sites. Improving the update process is probably the single most important challenge we can tackle for our customers at this time.

Overview of the background updater design

A full technical description of the new background updater design is available on DevNet, but here are the highlights:

After a successful installation of Adobe Flash Player 11.2, users will be presented with a dialog box to choose an update method. The following three update options are available to users:

  • Install updates automatically when available (recommended)
  • Notify me when updates are available
  • Never check for updates (not recommended)

For our initial release, we have set the new background updater to check for updates once an hour until it gets a response from Adobe. If the response says there is no new update, then it will wait 24 hours before checking again. We accomplish this through the Windows Task Scheduler to avoid running a background service on the system. If you are running multiple browsers on your system, the background updater will update every browser. This will solve the problem of end-users having to update Flash Player for Internet Explorer separately from Flash Player for their other open-source browsers. Google Chrome users, who have the integrated Flash Player, will still be updated through the Chrome update system.

Additionally, the user can change their update preferences at any time via the Flash Player Settings Manager, which for Windows users can be accessed via the Control Panel > Flash Player. In the Flash Player Settings Manager, the update preferences can be found and selected in the “Advanced” tab under “Updates.”

Organizations with managed environments do have the capability to disable the background updater feature through the Flash Player mms.cfg file. Also, those users who want to be notified of updates and do not want to be silently updated can continue to use the existing update mechanism. Lastly, the background updater feature is currently Windows-only for Windows XP and newer operating systems. A Mac version is currently under development.

I do want to note that we are not promising that all Flash Player updates going forward will be completely silent. We will be making the decision to silently install on a case-by-case basis. For instance, any update that changes the default settings of Flash Player will require confirmation from end-users even if they have already agreed to allowing background updates. Today’s update is an example of where confirmation would be required since we are changing how updates get applied to the user’s machine. However, we could apply a zero-day patch without requiring end-user confirmation, so long as the user has agreed to receiving background updates. Adobe will also continue to release feature-bearing releases that will trigger an update notification to users that highlight new and exciting features to the Flash Player.

The new background updater will provide a better experience for our customers, and it will allow us to more rapidly respond to zero-day attacks. This model for updating users is similar to the Google Chrome update experience, and Google has had great success with this approach. We are hoping to have similar success.

One last note

Since Flash Player 11 was first released in September 2011, we have continued to maintain Flash Player 10.3 with security updates for users who cannot update to the current version of Flash Player. In support of Microsoft’s initiative to get the world to drop Internet Explorer 6 and upgrade to a newer version of Internet Explorer for a safer browsing experience, Adobe will be dropping support for Internet Explorer 6 starting with today’s release of Flash Player 10.3.

While we will no longer include testing on Internet Explorer 6 in our certification process and strongly encourage users to upgrade to the newest version of Internet Explorer, we will not block the installation of newer versions of Flash Player 10.3 on systems running Internet Explorer 6 and expect functionality on those systems to remain unchanged.

CanSecWest 2012

The team and I are about to head off to CanSecWest. While I have been attending CanSecWest for several years, this year will be a unique experience for me. During my talk, I will demo an open-source tool I just released, called Adobe SWF Investigator. The tool can be useful for developers, quality engineers and security professionals for analyzing SWF applications. It has been a pet project of mine for some time, and I decided to share it with a broader audience.

Within my current role, I have to look at all aspects of SWF applications from cross-site scripting issues to binary analysis. Therefore, the tool includes capabilities to perform everything from testing cross-site scripting to viewing the individual SWF tags within the file format. I am hoping that by releasing the tool as an open-source ActionScript application, it will encourage all ActionScript developers to learn more about security. The tool is designed to be an extensible framework everyone can build upon or modify. More information on the tool can be found in my DevNet article.

In addition to demonstrating the tool, I will also be talking about Advanced Persistent Response. Adobe has been the focus of hackers for some time, and I plan to discuss what we have learned and observed in the process of responding to those threats. My talk will be on Wednesday at 3:30pm, if you are interested. When I am not speaking, you can probably find me and the Adobe team either at the Adobe table or milling around the pwn2own contest for no particular reason. Please feel free to come by and talk with us. See you there!