62 reasons to fire your Super Administrator
In our last issue we were discussing about how any Joomla! site belongs to a homogeneous population, why this is bad from a security perspective, and how to avoid that by changing your database table prefix. In this issue, we are going to expand a bit more, by making sure that another set of common characteristics – the Super Administrator user name and ID – are different than those a potential hacker would expect.
Who’s that bloke called “62”, anyway?
As soon as you install Joomla!, a potential hacker already knows too much about your site's set-up. You have a Super Administrator user and his ID is 62. His user-name is admin. The only key information missing for “owning” your site is your password. This can be stolen or changed if the hacker finds a vulnerable extension on your site. You see, known constants are a security nightmare as made clear in the case of the attack against Joomla! 1.5.5, which caused a lot of sites to be compromised as the researcher who found the vulnerability released it to the general public before the Joomla! team had a chance to fix it. Remember, belonging to a homogeneous population can be very bad for your well-being!
One easy workaround is to demote this well-known user account down to the Registered level and block it, hanging potential hackers to dry. However, in order to complete our security modification we do need another Super Administrator. The problem is that if you just create a new user his ID will be 63, which is not secure at all; it's a hacker's next best bet. So, we need a way to create a Super Administrator with a random ID, preferably in the 1-61 range which is otherwise unused in Joomla!. This is what we are going to do, with just three SQL commands.
WARNING: If you follow the advice in this article, you will be modifying your site's database. Even though the following procedure is well-tested, it's best to practice it on a local testing server first. Never, ever and under no circumstances try to apply these on a live site without a valid, tested backup!
Peeking at Joomla!'s user management underbelly
Joomla! 1.5 uses two tightly integrated mechanisms for authenticating users. For starters, we have the data stored in the jos_users table. It's the user-name, password and the group where the user belongs. There are some important pieces of information about this table. To begin with, all user ID's begin counting from 62. User IDs 1-61 are free and can't be assigned using the back-end. Then, the password is stored in the format md5:salt. Salt is a random string. The md5 part is the MD5 hash of the password and the salt. So, for a salt string "abcd" and a password "foobar", we have to calculate the MD5 hash of "foobarabcd". This calculation is easy to perform with an on-line MD5 calculation and results to 9aa618c6255a7259d747113d7e649925. So, the password field would be "9aa618c6255a7259d747113d7e649925:abcd". This means that we can create a user-name/password combination of our liking by assigning it a user ID less than 62.
Of course, this is not enough to log in to Joomla!. The other mechanism it uses is a cut-down version of phpGACL. In short, this mechanism allows the use of Access Control Lists which treats users as "Access Request Objects" (AROs) and what they can do as "Access Control Objects" (ACOs). This might be a bit over your head, but the overall concept is that if the user isn't marked as having access to the group he's supposed to be part of, he won't be able to login to the site. For all you need to know, each user is mapped to a single ARO and each ARO is mapped to one or more groups. If the user group assigned in the jos_users table is not in accordance to the group prescribed in the ARO-group mapping, you can't log in.
You might have guessed it, but this mechanism is not fool proof down to the database level. First off, the table which maps users to AROs (jos_core_acl_aro) has its IDs start from 10. This allows us to insert rows with IDs 1-9 without the fear of accidentally disabling another user's login ability. The other database table involved (jos_core_acl_groups_aro_map), which maps AROs to groups, is a simple glue array of a regular many-to-many relationship - it holds IDs of both ARO and group - is easy to append to.
We're ready for surgery!
Having this insight is good, but putting it to real use is even better. Let's say that we want to create a new Super Administrator user called "hacker", with a password of "hacker", using nothing but database manipulation, i.e. without using the Joomla! back-end interface. If you read the previous three paragraphs you might have figured out how: we just need to run three SQL commands. We'll focus on what these commands are and an easy way to deliver them. I will assume that you are using the default prefix jos_ for the following examples. If you're not – and you shouldn't if you're reading this series of security articles – please substitute jos_ with your site's prefix. You can execute all these commands against your Joomla! site's database using a suitable tool, such as phpMyAdmin.
The first SQL command will create the Joomla! user and looks like this:
REPLACE INTO `jos_users` (`id`, `name`, `username`, `email`, `password`, `usertype`, `block`, `sendEmail`, `gid`, `registerDate`, `lastvisitDate`, `activation`, `params`) VALUES ('60', 'T. Hacker', 'hacker',This email address is being protected from spambots. You need JavaScript enabled to view it. ', '18b42a5df78808abca92bc021a191991:abcdefghijklmnopqrstuvwxyz012345', 'Super Administrator', '0', '0', '25', '2000-01-01 00:00:00', '0000-00-00 00:00:00', '', '');
This creates a user with ID 60 and his name set to "T. Hacker". Do note the password calculation which follows the scheme outlined in the previous section. Now, we'll craft our next SQL command which creates an ARO object out of the Joomla! user:
REPLACE INTO `jos_core_acl_aro` (`id`, `section_value`, `value`, `order_value`, `name`, `hidden`) VALUES ('8', 'users', '60', '0', 'T. Hacker', '0');
This created an ARO object with ID 8. Do note that the 'name' field must match the 'name' field of the jos_users entry! Next up, we'll map this ARO object to the Super Administrator group, which goes by the group ID 25:
REPLACE INTO `jos_core_acl_groups_aro_map` (`group_id`, `section_value`, `aro_id`) VALUES ('25', '', '8');
There you go! The user is active and ready for login. If you're wondering why I use REPLACE instead of INSERT, well, it's simply because I wouldn't like the query to fail if the database record already exists. If your MySQL version doesn't like the REPLACE commands, feel free to change them to INSERTs at any time!
Wrapping up the operation
This is the easiest part of the process. As you are logged in as the new Super Administrator to your own site, you can go to Joomla!'s Users Manager page. First, demote the old Super Administrator down to the Registered level and click on Save. Then, edit again, set "Block" to "Yes" and save again. Your old Super Administrator user has lost his privileges and is now locked so that nobody can use it to log in to your site.
Many people would argue that we would be better off if we simply changed the user's ID instead of creating a brand new account, as demonstrated in this Bodvoc's blog post. This approach works well if you have a brand new site. But if your site already has content, doing so would leave all content items previously owned by user 62 orphans. Fixing all such records in all installed components is a mighty task. Ergo, it's much easier creating a new user with a bespoke user ID and disabling the old user, keeping content association and increasing your site's security in a single go.
What about Joomla! 1.6?
Joomla! 1.6 has some ground-breaking changes in the user management. First off, the default Super Administrator's user ID became... 42. No kidding and I guess there is a pun intended. The single major step, however, is that Joomla! got rid of all the phpGACL legacy it was carrying from the Mambo era and implements an ACL system of its own. Even though it's still a bit rough on the edges, it will be a major step forward once it is polished and ready for mass consumption.
The very positive change with the new ACL system is that user and group relationships are no longer spread across multiple overlapping tables, but neatly confined in the #__user_usergroup_map table, in the way they should have always been. This calls for a change in our SQL commands. First, the command to create the new Super Administrator should use an ID lower than 42 – let's say 40 – and needs to set the usertype field to 'deprecated', or any other value as it's no longer used:
REPLACE INTO `jos_users` (`id`, `name`, `username`, `email`, `password`, `usertype`, `block`, `sendEmail`, `gid`, `registerDate`, `lastvisitDate`, `activation`, `params`) VALUES ('40', 'T. Hacker', 'hacker',This email address is being protected from spambots. You need JavaScript enabled to view it. ', '18b42a5df78808abca92bc021a191991:abcdefghijklmnopqrstuvwxyz012345', 'deprecated', '0', '0', '25', '2000-01-01 00:00:00', '0000-00-00 00:00:00', '', '');
This means that once we have created a user with the first INSERT statement above, all we have to do is assign him to the Super Administrator group:
INSERT INTO `jos_user_usergroup_map` (`user_id`,`group_id`) VALUES('40','8');
As you can see, this greatly reduces the work we have to go through to create a new Super Administrator. Splendid!
Can I have one to go, please?
Oh, I know before you tell me. Fiddling with SQL is seriously not fun and bound to break your site. When the first version of this article was put up on my personal blog, a few people got their SQL commands wrong and inadvertently locked themselves out of their sites. Fear not, my friends! Just like I did with my last article, I've created a smallish PHP script to help you out. Here we go:
<?php
$user_id = ''; // Give a numeric user ID, or leave blank for a random one
require_once 'configuration.php';
$config = new JConfig;
$con = mysql_connect($config->host, $config->user, $config->password);
if(!is_resource($con)) die('Error connecting to db');
$test = mysql_select_db($config->db, $con);
if($test===false) die('Error connecting to db');
$prefix = $config->dbprefix;
$sql = "show tables where 'Tables_in_{$config->db}' like '{$prefix}user_usergroup_map'";
$res = mysql_query($sql);
$joomla16 = mysql_num_rows($res) > 0;
mysql_free_result($res);
if(empty($user_id)) $user_id = rand(1, ($joomla16 ? 41 : 61));
$sql = "select * from {$prefix}users where 'id' = ".($joomla16 ? 42 : 62);
$res = mysql_query($sql); $row = mysql_fetch_assoc($res);
mysql_free_result($res); $row['id'] = $user_id;
$sql = "REPLACE INTO {$prefix}users VALUES(";
$q = array(); foreach($row as $k => $v) { $q[] = "'".mysql_real_escape_string((string)$v, $con)."'"; }
$sql .= implode(',',$q).')';
mysql_query($sql) or die(mysql_error());
if($joomla16){
$sql = "INSERT INTO {$prefix}user_usergroup_map ('user_id','group_id') VALUES ('$user_id','8')";
mysql_query($sql) or die(mysql_error());
} else { $name = mysql_real_escape_string($row['name'], $con);
$sql = "INSERT INTO {$prefix}core_acl_aro ('section_value','value','order_value','name','hidden') VALUES ('users', '$user_id', '0', '$name', '0')";
mysql_query($sql) or die(mysql_error());
$aro_id = mysql_insert_id($con);
$sql = "INSERT INTO {$prefix}core_acl_groups_aro_map ('group_id', 'section_value', 'aro_id') VALUES ('25', '', '$aro_id')";
mysql_query($sql) or die(mysql_error()); }
$uname = uniqid('defsa',true); $salt = md5(microtime(false));
$pword = md5(uniqid('',true).$salt).':'.$salt;
$sql = "UPDATE {$prefix}users SET 'username' = '$uname', 'password' = '$pword', 'email' =This email address is being protected from spambots. You need JavaScript enabled to view it. ', 'block' = 1, 'sendEmail' = 0";
mysql_close($con); echo "OK";
@unlink(__FILE__);
The task at hand is almost trivial. It will take your existing Super Admin user and give it a new ID, while giving the old user a random username and password and disabling it. All you have to do is put this in a file, name it superadmin.php, upload it to your site and call it from your browser, e.g. http://www.yoursite.com/superadmin.php. That's all!
I still have some ideas, but...
...once more, I've run out of space! Anyway, if you've read this series from the very beginning and followed all of my advice, you now have a strong grip on security, and have already taken the basic steps to protect your site. In the next issue we'll continue our discussion with a set of hacking prevention and “cloaking” rules I've been working on and tweaking for the last twelve months.
Until next time, take care and be safe!
Some articles published on the Joomla Community Magazine represent the personal opinion or experience of the Author on the specific topic and might not be aligned to the official position of the Joomla Project
By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/
Comments