Harden WordPress using database permissions

Here is a small idea that I would like to throw into the world: most web applications use only one database user for most operations (installation, administration, common usage). Couldn’t we harness the database to protect a bit your data?

How to

This is how you could do it:

  • Create one user (called ‘user’) with full privileges on the database
  • Create another user with no privileges (let’s call him ‘read’)
  • Create a copy of wp-config.php that you will name wp-config-admin.php
  • Write the ‘read’ credentials in the wp-config.php and the normal credentials in wp-config-admin.php (don’t forget to use different auth, secure auth, logged in and nonce keys)
  • Create a copy of wp-load.php that you will name wp-load-admin.php
  • Replace in wp-load-admin.php the reference to wp-config.php by wp-config-admin.php
  • Replace in wp-login.php and wp-admin/* the references to wp-load.php by wp-load-admin.php
  • Now, you can use the admin interface, create posts, etc.
  • Grant some permissions to the ‘read’ database user: GRANT SELECT ON `db`.* TO ‘read’; GRANT INSERT, UPDATE ON `db`.`wp_comments` TO ‘read’;

That was a bit of work, but not that hard! So, what did we do here? We created a user for the admin interface with full privileges on the database (create/update posts, change the taxonomy, approve the comments, etc) and another one for the front end interface, with only read privileges on all tables (that bothers me too, but read on).

This means that SQL injections, either in plugins or in WordPress code (out of the admin panel) will be much harder to implement with this setup. Beware of the custom tables for some plugins. Those will require specific permissions. Depending on the plugin, some could be read only for common usage.

Going further

That’s nice, but not enough in my opinion. As I said, the full select permission for the ‘read’ user bothers me. Couldn’t we restrict a bit the permissions on wp_users? Some of the columns are needed, but do we need to access the user_pass column? Also, the “ALL PRIVILEGES” for ‘user’ is a bit too much. Do we really use the “FILE” privilege (out of SQL injections :D )?

Without further ado, here are the SQL commands you should use:

GRANT SELECT, INSERT, UPDATE ON `db`.`wp_comments` TO ‘read’;

GRANT SELECT ON `db`.`wp_commentmeta` TO ‘read’;

GRANT SELECT ON `db`.`wp_links` TO ‘read’;

GRANT SELECT ON `db`.`wp_options` TO ‘read’;

GRANT SELECT ON `db`.`wp_term_taxonomy` TO ‘read’;

GRANT SELECT ON `db`.`wp_usermeta` TO ‘read’;

GRANT SELECT ON `db`.`wp_terms` TO ‘read’;

GRANT SELECT ON `db`.`wp_term_relationships` TO ‘read’;

GRANT SELECT ON `db`.`wp_postmeta` TO ‘read’;

GRANT SELECT ON `db`.`wp_posts` TO ‘read’;

GRANT SELECT (user_activation_key, id, user_login, user_nicename, user_status, user_url, display_name, user_email, user_registered) ON `db`.`wp_users` TO ‘read’;

REVOKE ALL PRIVILEGES ON `db`.* from ‘user’;

GRANT SELECT, INSERT, DELETE, UPDATE, CREATE, DROP, ALTER, INDEX ON `db`.* TO ‘user’;

With these commands, ‘user’ can only manipulate tables. If you’re an evil DBA, you can even revoke the “CREATE, DROP, ALTER” permission after install, and reactivate them only for upgrades or plugin installation. The ‘read’ user has the same permissions as before on wp_comments, has “SELECT” on all tables except the wp_users. For wp_users, we grant “SELECT” on all columns except the user_pass one.

Thanks to this configuration, even a SQL injection in a plugin will not reach the password hashes! We also removed dangerous permissions like “FILE”. I’d like to prevent timing attacks like “SELECT BENCHMARK(5000000,ENCODE(‘MSG’,'by 5 seconds’));” but i did not figure out what is the right syntax for this (I tried variations around: “revoke execute on function benchmark from read”, without result).

Thankfully, WordPress mostly works with this configuration, and I think that a lot of other applications could be protected like this. Imagine: you could grant insert but not select on the credit card table in an e-commerce application, and process transactions with a background task with the right permissions.

Database privileges are indeed a powerful tool to protect your code from SQL injections. They might require some architectural changes, but the profits can be huge for your security.

About these ads

Warning your users about a vulnerability

Somebody just told you about a vulnerability in your code. Moreover, they published a paper about it. Even worse, people have been very vocal about it.
What can you do now? The usual (and natural) reaction is to downplay the vulnerability, and try to keep it confidential. After all, publishing a vuln will make your users unsafe, and it is bad publicity for your project, right?

WRONG!

The best course of action is to communicate a lot. Here’s why:

Communicate with the security researcher

Yes, I know, we security people tend to be harsh, quickly flagging a vulnerable project as “broken”, “dangerous for your customers” or even “EPIC FAIL“. That is, unfortunately, part of the folklore. But behind that, security researchers are often happy to help you fix the problem, so take advantage of that!

If you try to silence the researcher, you will not get any more bug reports, but your software will still be vulnerable. Worse, someone else will undoubtedly find the vulnerability, and may sell it to governments and/or criminals.

Vulnerabilities are inevitable, like bugs. Even if you’re very careful, people will still find ways to break your software. Be humble and accept them, they’re one of the costs of writing software. The only thing that should be on your mind when you receive a security report is protecting your users. The rest is just ego and fear of failure.

So, be open, talk to security researchers, and make it easy for them to contact you (tips: create a page dedicated to security on your website, and provide a specific email for that).

Damage control of a public vulnerability

What do you do when you discover the weakness on the news/twitter/whatever?

Communicate. Now.

Contact the researchers, contact the journalists writing about it, the users whining about the issue, write on your blog, on twitter, on facebook, on IRC, and tell them this: “we know about the issue, we’re looking into it, we’re doing everything we can to fix it, and we’ll update you as soon as it’s fixed“.

This will buy you a few hours or days to fix the issue. You can’t say anything else, because you probably don’t know enough about the problem to formulate the right opinion. What you should not say:

  • “we’ve seen no report of this exploited in the wild”, yet
  • “as far as we know, the bug is not exploitable”, but soon it will be
  • “the issue happens on a test server, not in production” = “we assume that the researcher didn’t also test on prod servers”
  • “no worries, there’s a warning on our website telling that it’s beta software”. Beta means people are using it.

Anything you could say in the few days you got might harm your project. Take it as an opportunity to cool your head, work on the vulnerability, test regressions, find other mitigations, and plan the new release.

Once it is done, publish the security report:

Warning your users

So, now, you fixed the vulnerability. You have to tell your users about it. As time passes, more and more people will learn about the issue, so they might as well get it from you.

You should have a specific webpage for security issues. You may fear that criminals might use it to attack your software or website. Don’t worry, they don’t need it, they have other ways to know about software weaknesses. This webpage is for your users. It has multiple purposes:

  • showing that you handle security issues
  • telling which versions someone should not use
  • explaining how to fix some vulnerabilities if people cannot update

For each vulnerability, here is what you should display:

  • the title (probably chosen by the researcher)
  • the security researcher’s name
  • the CVE identifier
  • the chain of events:
    • day of report
    • dates of back and forth emails with the researcher
    • day of fix
    • day of release (for software)
    • day of deploy (for web apps)
  • the affected versions (for software, not web apps)
  • the affected components (maybe the issue only concerns specific parts of a library)
  • how it can be exploited. You don’t need to get into the details of the exploit.
  • The target of the exploit. Be very exhaustive about the consequences. Does the attacker get access to my contacts list? Can he run code on my computer? etc.
  • The mitigations. Telling to update to the latest version is not enough. Some users will not update right away, because they have their own constraints. Maybe they have a huge user base and can’t update quickly. Maybe they rely on an old feature removed in the latest version. Maybe their users refuse to update because it could introduce regressions. What you should tell:
    • which version is safe (new versions with the fix, but also old versions if the issue was introduced recently)
    • if it’s open source, which commit introduced the fix. That way, people managing their own internal versions of software can fix it quickly
    • how to fix the issue without updating (example for a recent Skype bug). You can provide a quick fix, that will buy time for sysadmins and downstream developers to test, fix and deploy the new version.

Now that you have a good security report, contact again the security researchers, journalists and users, provide them with the report, and emphasize what users risked, and how they can fix it. You can now comment publicly about the issue, make a blog post, send emails to your users to tell them how much you worked to protect them. And don’t forget the consecrated formule: “we take security very seriously and have taken measures to prevent this issue from happening ever again”.

Do you need help fixing your vulnerabilities? Feel free to contact me!

Software security is like medicine

People don’t get software security. Between the legends around hackers, the scaremongering by salesmen and the technical level needed to practice it, you won’t even try to understand. It is hard to get a high level view of how it works, and how you can protect yourself. But there’s a nice little metaphore to understand security: it’s like medicine!

Medicine is about taking care of your body, fighting off illness, healing injuries. The body is a big machine that can fail in a lot of ways, depending on a lot of parameters. It is the same for software security: you want to prevent and repair any disruption that could fail at any time. Security, like medicine, is a very technical domain, and takes a lot of time to learn. Even after learning, you still don’t know everything, and you must keep up to date with recent research.

But basic body maintenance is easy. And protecting your software against basic attacks is easy too. You don’t need to study for 11 years to prevent basic SQL injections. Most of the time, the diseases and bugs that will affect you are common, and you don’t need Dr House to prevent them.

Bodies and software are problematic because they both decay through time, and are very sensitive to their environment. Change the environment, and you could catch something new. The world is full of germs and criminals. Is that a good reason to always stay at home? Obviously not. You must accept that you can get sick at any time. So, take steps to protect yourself. Wash your hands and sanitize your inputs. And beware of snake oil.

Feel free to extend on the metaphore in the comments, and use it to explain software security around you!

By the way, if you need someone to auscultate your applications, you can contact me at geoffroycouprie.com

Fixing the DLL loading vulnerability

Update 24/08/2010: Microsoft published an advisory, there’s an article from the MSRC about the preloading DLL vulnerability, and a tool that fixes the problem. And if you want to know more, there’s an MSDN article.

Update 25/08/2010: if you came here from golem.de, heise.de or h-online.com, good for you! The articles on these websites are blatantly wrong, and their proposed solution doesn’t work . You will find here the real solutions, and links to the relevant blog posts and advisories.

Look! A new shiny vulnerability, affecting a lot of Windows applications! OMG OMG OMG we’re DOOMED!

</crazy mode off>

OK, let’s get serious. This vulnerability is actually a very old one. It is there because the DLL search order includes the current directory (if someone can tell me why, I would be delighted to know that). Here is the search order (source: MSDN):

If SafeDllSearchMode is enabled, the search order is as follows:

  1. The directory from which the application loaded.
  2. The system directory. Use the GetSystemDirectory function to get the path of this directory.
  3. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
  4. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
  5. The current directory.
  6. The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.

If SafeDllSearchMode is disabled, the search order is as follows:

  1. The directory from which the application loaded.
  2. The current directory.
  3. The system directory. Use the GetSystemDirectory function to get the path of this directory.
  4. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
  5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.

For the record: disabling SafeDllSearchMode means that you’re stupid, or that you’re using a Windows before XP SP1 (note that this is not exclusive).

So, let’s assume that your application tries to load a DLL that isn’t present, either in your application directory, or in the system directories. Then, the application will try to load the DLL from the current directory. And that’s it. If there’s a DLL with the same name in that folder, it will be loaded in the application. That’s why you can get owned by opening a file in that same directory: it sets the current directory of the application to the file’s folder.

OMG OMG OMG we’re DOOMED, and it’s incredibly easy to exploit me!

So, now, let’s take a look at the fix. This is an OS flaw. You can’t fix all your applications yourself. H.D. Moore has some sysadmin fixes that you can apply to prevent the exploit from coming on your computer. But your applications will still be exploitable.

If you’re a developer, though, you can fix your application. There’s a function that can remove the current directory from the DLL search path: SetDllDirectory. From MSDN:

After calling SetDllDirectory, the DLL search path is:

  1. The directory from which the application loaded.
  2. The directory specified by the lpPathName parameter.
  3. The system directory. Use the GetSystemDirectory function to get the path of this directory. The name of this directory is System32.
  4. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. The name of this directory is System.
  5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
  6. The directories that are listed in the PATH environment variable.

So, if you pass a safe directory(let’s say, C:\Windows\System32, or your application directory) as argument to SetDllDirectory, you effectively remove the current directory from the search path! It works, I tested it for you :)

But that’s not the end: you have to wipe out the PATH to be safe. From the metasploit blog:

If the application is trying to load a DLL that is normally found within the PATH, but not the Windows system directories, and the PATH contains environment variables that have not been set, then the literal value of the environment variable will be treated as sub-directory of the working directory (the share). For example, if %unknownvariable%\bin is in the system PATH, the share will be searched for a directory called “%unknownvariable%\bin” and the target DLL will be loaded from within this sub-directory.

And if you test  your application with ProcMon, you will surely see that a lot of (potentially unsafe) directories are added to the PATH, and used to look for the DLL. So, remove all the useless directories from the PATH if you can!

(And now, for the disclaimer: this blog post is not endorsed by Microsoft, and if you want to be really safe and know the best solution to employ, wait for Microsoft’s patches and workarounds. But if you trust me enough (you fool… *evil laugh*), you can try that fix on your application, and maybe protect your users. )

Actually, that fix is encouraged by Microsoft, and David Leblanc wrote about it in February 2008.

Web vulnerabilities in an HTML 5 application

For the past few days, I have been messing with some of the features of HTML 5:

  • local storage
  • Offline web applications

These features enable the development of real applications, running in the browser. It has a lot of advantages: easily updating the application, reduce the workload on the server, etc.

But it changes the way you write your code. You have to adapt the usual protection mechanisms to these changes.

Here are some thoughts about the common web application vulnerabilities.

Warning: I consider here a web application with practically no server-side code: everything executes in the browser. And I’ll use the point of view of someone attacking the application running in the browser. And I’ll be optimist enough to trust the browser…

SQL injections

SQL injections in servers let you access the user’s data, and access the server itself (file uploads, starting external programs, etc). With local storage and WebSQL, you won’t be able to access the host, only the data (unless there’s a browser vulnerability about that). And you can use some sort of prepared statement syntax to prevent injection. There may be a risk with key/value stores if you let the user input control the key.

Cross site scripting

This is in my opinion the biggest risk. If all the logic of your application is on the client’s side, unwanted code executing in the browser has access to everything. This one can be mitigated by filtering what will be displayed on your webpage.

Cross site request forgery

This one is not critical, unless you use locally URL parameters (don’t laugh, it has often been done and exploited in Flash applications). Be aware that an attackant could get data in local storage that way.

Persistency

It really worries me that so much data can stay a long time in the user’s browser. With a database hosted on your server, if unwanted data(persistent XSS, malwares…) is stored, you can erase it, patch your website’s code, and your users will be safe.

With HTML 5, you’ll have to clean every user’s data. You can’t be sure that you have  protected all your users (someone could wait 6 months before coming back to your website). And because you can’t be sure, your code has to check for each known bad data. It needs a lot of code, time and tests.

Trust issues

It has been said a lot of times already: don’t trust the data coming from your client. And in our case, don’t trust it, even if it’s data that your website put in local storage. It applies to data that will come back to your server, but also to data that will be displayed with a bit of Javascript/DOM code. Yes, XSS attacks could come from local storage. So, you need to escape everything that wil go into the webpage.

Are we screwed?

These were only quick thoughts about the vulnerabilities you could encounter with client side web applications. It is not really hard to protect the application, but you have to be very careful about what data you will trust. The good thing is, these vulnerabilities are not new: you can see them in lots of Flash applications. So, the mitigation mechanisms are well known, and easy to apply.