Nov 10, 2015

"Notice: Undefined index: und in eval() (line . . . . . /modules/php/php.module(80) : eval()'d code)"

On my local instance of a Drupal 7 site, I'd get warnings like the following on almost every page for a custom content type called Poem.

Notice: Undefined index: und in eval() (line 12 of /var/www/html/power-poetry/modules/php/php.module(80) : eval()'d code).

Although most probably innocuous, these warnings were also being logged numerous times in the system log, cluttering it up and making it harder to spot other, significant messages. It was enough of an annoyance that I decided to fix it.

I was able  to track this to a snippet of PHP code that is used in a Drupal block of ours. The code is part of the block's Visibility Settings. It determines if the current Poem being rendered satisfies certain criteria or not. If so, the block is made visible.

The offending line turned out to be

  $slam_id = $current_slam['und'][0]['target_id'];

The variable $current_slam was often an empty array. So by replacing the line with an additional check, the PHP warnings disappeared.

  if (!empty($current_slam)) {
    $slam_id = $current_slam['und'][0]['target_id'];
  }

The key to finding this quickly was remembering that we have this user-supplied snippet of code that needs to be evaluated at runtime

  - - - - -

While debugging, I wanted to see the type and the value of the variable $current_slam. The following worked to output it to the system log.

  watchdog('debug', print_r($current_slam, TRUE), array(),
           WATCHDOG_NOTICE);

The second parameter to print_r() determines what kind of return value the function passes back. By default, print_r() returns its status. But by setting the second param to TRUE, the return value is the output string instead.

  - - - - -

The site is hosted on Pantheon, which provides this status message:

  • PHP Filter: PHP Filter is enabled! Executable code should never be stored in the database, and support for this feature was removed in Drupal 8 - https://drupal.org/node/1203886
    Remove all executable code from your content and move it to your codebase.

Source:

Remove the PHP module from Drupal core
https://drupal.org/node/1203886

Nov 3, 2015

Malicious page redirects and Drupal's Filtered-HTML text format

One of the Drupal 7 sites I maintain is www.powerpoetry.org which is a platform for publishing poems and comments about them.

Recently, we heard from an irate poet. When browsing to one of her poems, the page would partially load, then automatically redirect to a movie streaming site that had nothing to do with her poem nor with the site in general. Her poem page somehow got hijacked, and readers could never read her work.

It turned out that all site users, even Anonymous ones, had permissions for the Full HTML Text Format. This allowed comments to be submitted that contained malicious code to redirect the page.

So far, I have found two different techniques that were used to implement redirection.

(1)  Use of the <meta> tag with an attribute of http-equiv="refresh". For example,

  <meta http-equiv="refresh" 
        content="2;url=http://scumbags.com/">

where the value of 2 means a delay of two seconds before the refresh.

(2)  Use of the onmouseover attribute to execute JavaScript. For example,

<a href="//tinyurl.com/nm8ugh
   onmouseover="document.location='//tinyurl.com/nm8ugh';">

As documented, this attribute and closely related ones are valid for almost all HTML tags, which would make it potentially a big problem where markup is allowed.

If you have good knowledge of HTML, these techniques may be obvious, but for me as a back-end developer they were new.

Because I have direct access to the Drupal database, I was able to use a SQL query such as the following to find malicious comments.

  select entity_id, comment_body_value
    from field_data_comment_body
    where comment_body_value like "%onmouseover%";

 - - - - -

To help plug these security holes, we decided to allow only Administrators to have access to all Text Formats. Depending on the needs of your site, you might want to disable the Full HTML format entirely.

For all other users, the Filtered HTML Text Format was permitted, configured using Limit Allowed HTML Tags so that only the following tags are processed.

  <p> <br> <em> <strong> <cite> <blockquote> <ul> <ol> <li>

Although not part of this security problem, we purposely disallowed <a> tags, deciding that they are not essential for comments on our site and almost always only appear in spam comments. As part of eliminating links, we also turned off Convert URLs Into Links.

Note that the user can still type in whatever they want, including any kind of HTML. The original text is stored unchanged as the content of the comment.

However, what the Filtered HTML format does is effectively strip out disallowed tags as part of the rendering process.

 - - - - -

Even with Filtered HTML, I was concerned that the onmouseover attribute could still be used inside one of the tags that is allowed. What about something like

  <p onmouseover="....">

But some testing and some stepping through core code revealed that onmouseover as well as other risky attributes are stripped out of those tags for output even though the tags themselves are processed.

The holes were plugged for these two exploits.

 - - - - -

The final thing left to do was to go back and check again the malicious comments, which I had left in the database while carrying out the above steps. I thought that applying Filtered HTML would neutralize them. I was wrong.

In the database table field_data_comment_body there is a column comment_body_format that stores the format in effect when the comment was created. The format apparently is used to render the comment even if it is no longer allowed.

As a result, those comments were still redirecting!

So I used SQL queries to find their entity ids, then deleted them by browsing to, for example, the following path where 30259 is the entity id of a comment.

  /comment/30259/edit

or

  /comment/30259/delete

The latter path goes directly to the delete confirmation dialog. In some cases I had to use it because loading the edit form for the comment triggered the redirection, so I couldn't even edit it.

 - - - - -

Michael Richardson at Pantheon support did an awesome job of doing the initial troubleshooting when I was at a loss as to where to start. It was he who uncovered the use of onmouseover on our site. Thanks, Michael!


Sources:

What is the Meta Refresh Tag?
http://webdesign.about.com/od/metataglibraries/a/aa080300a.htm

HTML onmouseover Event Attribute
http://www.w3schools.com/tags/ev_onmouseover.asp

Drupal Text Formats and Filters Tutorial
https://www.hostknox.com/tutorials/drupal/formats-and-filters

Text Filters and Input Formats
https://www.drupal.org/node/213156