Trustwave and Cybereason Merge to Form Global MDR Powerhouse for Unparalleled Cybersecurity Value. Learn More

Trustwave and Cybereason Merge to Form Global MDR Powerhouse for Unparalleled Cybersecurity Value. Learn More

Services
Managed Detection & Response

Eliminate active threats with 24/7 threat detection, investigation, and response.

Co-Managed SOC (SIEM)

Maximize your SIEM investment, stop alert fatigue, and enhance your team with hybrid security operations support.

Advisory & Diagnostics

Advance your cybersecurity program and get expert guidance where you need it most.

Penetration Testing

Test your physical locations and IT infrastructure to shore up weaknesses before exploitation.

Database Security

Prevent unauthorized access and exceed compliance requirements.

Email Security

Stop email threats others miss and secure your organization against the #1 ransomware attack vector.

Digital Forensics & Incident Response

Prepare for the inevitable with 24/7 global breach response in-region and available on-site.

Firewall & Technology Management

Mitigate risk of a cyberattack with 24/7 incident and health monitoring and the latest threat intelligence.

Solutions
BY TOPIC
Microsoft Security
Unlock the full power of Microsoft Security
Offensive Security
Solutions to maximize your security ROI
Rapidly Secure New Environments
Security for rapid response situations
Securing the Cloud
Safely navigate and stay protected
Securing the IoT Landscape
Test, monitor and secure network objects
Why Trustwave
About Us
Awards and Accolades
Trustwave SpiderLabs Team
Trustwave Fusion Security Operations Platform
Trustwave Security Colony
Partners
Technology Alliance Partners
Key alliances who align and support our ecosystem of security offerings
Trustwave PartnerOne Program
Join forces with Trustwave to protect against the most advance cybersecurity threats

Accidental Stored XSS Flaw in Zemanta 'Related Posts' Plugin for TypePad

Note that the vulnerability described here was fixed by Zemanta.

It all started innocently enough

I was here in the TypePad editor working on a new web honeypot blog post about some XSS attacks we were seeing when, BOOM, my browser was suddently redirected to the hxxp://www.txt2pic.com website. What the heck just happened?!? I clicked back in my browser to take me back to the TypePad editor session. Then, a few moments later... I was redirected yet again. Something was seriously wrong here. I sat there for a moment with a blank stare on my face as my mind quickly ran through different scenarios of what could be happening when it suddenly hit me. Wait a minute, that URL (hxxp://www.txt2pic.com) was familiar.

A few days earlier...

It was only a few days eariler that I had posted another blog post on some XSS attacks that were identified in the User-Agent string of requests. Here are the example entries:

8066_16179eac-9871-4f10-9283-c48b504cca73
You will notice that this is actually a graphics image. In the original blog post, however, I had actually pasted in these entries directly in as text. If you clicked on the HTML editor link, however, you would see that the TypePad editor properly escaped these HTML characters using HTML Entity Encoding:

9255_506036fe-2e96-4f9a-ba53-5309adade85e

This means that the Javascript data would be rendered as text when it reaches the blog reader's web browser rather than it being mistakenly executed as code. I quickly went to this blog post and click on view-source to verify this fact. It was indeed HTML Entity Encoded. So what was happening to me? How was this same exact JS code popping me when I was in my TypePad blog editor?

BurpProxy to the rescue

I quickly fired up BurpProxy and reconfigured my browser to use it. I set the interception proxy to not not stop traffic and under Options I enabled "Intercept Server Responses". My intent was to simply have a complete HTTP audit log of traffic I could analyze if/when this happened again. After this was setup, I then went back into my TypePad Editor session to work on my blog again and sure enough, off my browser went to that txt2pic.com site. I then quickly switched over to my Burp Proxy History tab and entered in the txt2pic.com text to search the saved data for that string:

11943_d14e3856-ca3f-4a6d-810b-6b1cdda28dbc

The results screen quickly identified the following single transaction:

10310_82d28484-ca95-487b-afdc-043c94008db7

But where is the JS script tag data? I then clicked on the Response tab and found it in the JSON response body:

10657_9352e5b8-881c-432c-a35a-8e1980f369f3
There is my web honeypot XSS log data... and it has been HTML Entity Decoded which turns it back into executable code!

11002_a3bfc9f3-ca6e-4ad9-bba0-91af75679181"ZEMANTA!"

Zemanta Related Posts Plugin for TypePad

The Zemanta Related Posts plugin for TypePad appears at the bottom of the TypePad Editor and looks like this:

11183_ac9187d7-6bf1-4265-b7fb-a4d2fa2161e6
Here is how the Related Posts plugin is described:

The benefits of using Related Posts

You can add Related Posts to the bottom of your text with a click of a button to give your post more depth and provide clues to readers on where to get further opinions and information on the subject.

Other benefits of using related posts are again numerous. First and foremost your post gains on credibility but there is another hidden value: bloggers that you link out to will notice you. You will get attention from writers who are interested in the same topics as you. This often leads to more comments, reactions and backlinks but most of all to new important acquaintances.

Vulnerability Data Flow

"Published Post" Beacon Call to API

When you publish a blog post, it sends a beacon request from the Zemanta plugin in your browser session to the Zemanta API with URL information. Here is the RESTful API GET request notifying Zemanta of my new blog post URL -

9889_70b9738f-6aa7-4932-9915-c1fad2be4037
Here is the corresponding Response confirming the new URL to be indexed:

10559_8f925d13-75ec-4a24-862e-acb5606e5dba
Zemanta API Web Client Index

After receiving the Beason API call, Zemanta fires off their blog post scraper web client to gather the text from your blog post. This is the critical step in this XSS issue as the Zemanta API web client captured therendered text of the XSS payload from my Apache logs. At this point, the active JS data is sitting in the Zemanta API index waiting to be called up by the "Related Posts" plugin for TypePad posts.

Related Posts Plugin API Call

Then when other TypePad users are editing new blog posts, the Related Posts plugin will send a request to the Zemanta API (as shown in Burp screen shots above) with the text from the current draft blog.

API JSON Response

The Zemanta API will then return JSON data with title and blog post snippets of data that matched the text in the current draft blog post. It is in this response that the JS code from my previous blog post reflected back into my own browser session and executed. Here is the actual JS file with the vulnerability -

11316_b2f5d58b-69bc-440d-b72c-b95b3e924bc1
Here is the exact section of code with the problem -

                       var excerpt = $('<span />')                               .html(obj.text_preview || obj.text_highlight || obj.title_highlight || 'Excerpt is not available at this time.')                               .text()                               .replace(/\n/g, '<br />'),                               title = obj.title.replace(" and related posts", ""),

Notice the bolded section of code as it is treating the JSON response data elements as html.

Zemanta Fixes the Related Posts TypePad Plugin

I notified the Zemanta team of the stored XSS issue I had found. I want to thank both Andraz Tori and Dušan Omerčevič for working with me to understand the issues and for quickly updating the Related Posts plugin for TypePad users. Here is the updated section of JS code from above:

                      var excerpt = $('<span>' +                                       (obj.text_preview || obj.text_highlight || obj.title_highlight || 'Excerpt is not available at this time.') +                                       '</span>')                                      .text()                                      .replace(/\n/g, '<br />'),                              title = $('<span>' + obj.title.replace(" and related posts", "") + '</span>').text(),

As you can see from the highlighted section of code, the JSON data returned by Related Posts API call is now treated as text rather than html. In addition, the Zemanta team also updated their "Zemanta Suggests" API documentation warning other consumers of the data to treat this data with care:

10716_9610a261-037e-404d-9922-48fcaa3c086f

Conclusions

The adventure has taught me a few key things:

  1. Use graphics for any executable code. Even though I took care to encode the data, I didn't have any control over other systems or clients that might process that data.
  2. Beware of APIs and Plugins - these may add extra value to your application and users however they may also introduce unexpected vulnerabilities.
  3. Burp Proxy is invaluable for HTTP forensics - without Burp Proxy it would have much more difficult to track this down.

ABOUT TRUSTWAVE

Trustwave is a globally recognized cybersecurity leader that reduces cyber risk and fortifies organizations against disruptive and damaging cyber threats. Our comprehensive offensive and defensive cybersecurity portfolio detects what others cannot, responds with greater speed and effectiveness, optimizes client investment, and improves security resilience. Learn more about us.

Latest Intelligence

Discover how our specialists can tailor a security program to fit the needs of
your organization.

Request a Demo