Just Another Security/Programming Blog

Saturday, 23 April 2016

Monitoring Network I/O w/ Download & Upload Speeds on FreeBSD, in C

While creating my custom i3bar-equivelant program, I decided one of the features I wanted was the ability to display my network's I/O so I can watch it whenever I want.

Ends up, in FreeBSD, this isn't exactly clear as to how it's done(and a bug may exist?)
After a little while I was able to find the correct manpage to read the documentation -- which I then just later read the header file since it was much clearer than the manpage(you must read the manpage for `if_data(9)' too)

Ends up, we have to use `ifmib(4)' with sysctl and if_data, to grab our network data.


From ifmib(4):

           The generic interface information, common to all interfaces, can be
     accessed via the following procedure:

           int
           get_ifmib_general(int row, struct ifmibdata *ifmd)
           {
                   int name[6];
                   size_t len;

                   name[0] = CTL_NET;
                   name[1] = PF_LINK;
                   name[2] = NETLINK_GENERIC;
                   name[3] = IFMIB_IFDATA;
                   name[4] = row;
                   name[5] = IFDATA_GENERAL;

                   len = sizeof(*ifmd);

                   return sysctl(name, 6, ifmd, &len, (void *)0, 0);
           }

     The fields in struct ifmibdata are as follows:

     ifmd_name       (char []) the name of the interface, including the unit
                     number

     ifmd_pcount     (int) the number of promiscuous listeners

     ifmd_flags      (int) the interface's flags (defined in <net/if.h>)

     ifmd_snd_len    (int) the current instantaneous length of the send queue

     ifmd_snd_drops  (int) the number of packets dropped at this interface
                     because the send queue was full

     ifmd_data       (struct if_data) more information from a structure
                     defined in <net/if.h> (see if_data(9))


And from if_data(9):
           ifi_ibytes          Total traffic received, in bytes.

           ifi_obytes          Total traffic transmitted, in bytes.



So, here is a quick version of printing the uploaded/downloaded bytes of an interface since it's been up.

#include <unistd.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_mib.h>
#include <stdio.h>
#include <string.h>
/*
 Grabs the total amount of bytes that we have sent("upload") and received("download") and prints it.
 Returns 1 on error.
*/
int main(void) {
 struct ifmibdata ifmd;
 size_t a_size = sizeof ifmd;
 int n_ntwk;
 size_t int_size = sizeof n_ntwk;
 int name[6];
 const char* ifname = "wlan0";


 /* Prepare for sysctl to grab IFMIB_IFDATA */
 name[0] = CTL_NET;
 name[1] = PF_LINK;
 name[2] = NETLINK_GENERIC;
 name[3] = IFMIB_IFDATA;
 name[5] = IFDATA_GENERAL;

 /* Check how many network interfaces are up */
 if(sysctlbyname("net.link.generic.system.ifcount", &n_ntwk, &int_size, (void*)0, 0) == -1) {
  fprintf(stderr, "Error when getting number of networks\n");
  /* sysctlbyname failed. Something is extremely wrong. */
  return 1;
 }


 /*
 From the manpage:
  The index of the last row in the
  table is given by ``net.link.generic.system.ifcount''

  ....


   A management application searching for a particular
  interface should start with row 1 and continue through the table row-by-
  row until the desired interface is found.

 Thus, start at i=1, and continued until <= n_ntwk
 */
 /* Loop through all the interfaces, grabbing data, and looking for the one named 'wlan0' */
 for(int i=1; i <= n_ntwk; i++) {
  name[4] = i;
  /* Grab the actual data. */
  if(sysctl(name, 6, &ifmd, &a_size, (void*)0, 0) == -1) {
   fprintf(stderr, "Error with sysctl\n");
   /* Sysctl failed.. Not good. */
   continue;
  }

  /* Is this interface named 'wlan0'? */
  if(strcmp(ifmd.ifmd_name, ifname) == 0) {
   /* This is the interface we're looking for. */
   printf("Downloaded bytes: %lu\n", ifmd.ifmd_data.ifi_ibytes);
   printf("Uploaded bytes: %lu\n", ifmd.ifmd_data.ifi_obytes);
   return 0;
  }
 }

 return 1;

}


In my case, 'wlan0' is the interface I want to monitor. So for you, replace it with what you need.


However, since we want the average upload/download in kb/s, we have to edit the code a bit.
In this code, we look at the inbytes, outbytes, and time, to work out an average kb/s. We save those, and then calculate the change from now to when it was saved(calculated by the displacement in time).
 
This is done fairly simply by doing (now_outbytes - before_outbytes) / (now_time - before_time) where the 4 variables are intuitively named. now_time-before should be 2, since we're sleeping for 2 seconds. now_outbytes-before_outbytes is the amount of bytes that we have uploaded in the past 2 seconds(or, in the past now_time - before_time seconds.)


Here is the code:


#include <unistd.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_mib.h>
#include <stdio.h>
#include <string.h>

/*
 ifdata[0] holds the old upload byte count.
 ifdata[1] holds the old download byte count.
 ifdata[2] holds the old time when the info was retreived.
*/
unsigned long ifdata[3];

/*
 Grabs the total amount of bytes that we have sent("upload") and received("download") and prints it.
 Returns 1 on error.
*/
int printit(void) {
 struct ifmibdata ifmd;
 size_t a_size = sizeof ifmd;
 int n_ntwk;
 size_t int_size = sizeof n_ntwk;
 int name[6];
 const char* ifname = "wlan0";
 float avg[2]; /* [0] contains upload, [1] contains download */


 /* Prepare for sysctl to grab IFMIB_IFDATA */
 name[0] = CTL_NET;
 name[1] = PF_LINK;
 name[2] = NETLINK_GENERIC;
 name[3] = IFMIB_IFDATA;
 name[5] = IFDATA_GENERAL;

 /* Check how many network interfaces are up */
 if(sysctlbyname("net.link.generic.system.ifcount", &n_ntwk, &int_size, (void*)0, 0) == -1) {
  fprintf(stderr, "Error when getting number of networks\n");
  /* sysctlbyname failed. Something is extremely wrong. */
  return 1;
 }


 /*
 From the manpage:
  The index of the last row in the
  table is given by ``net.link.generic.system.ifcount''

  ....


   A management application searching for a particular
  interface should start with row 1 and continue through the table row-by-
  row until the desired interface is found.

 Thus, start at i=1, and continued until <= n_ntwk
 */
 /* Loop through all the interfaces, grabbing data, and looking for the one named 'wlan0' */
 for(int i=1; i <= n_ntwk; i++) {
  name[4] = i;
  /* Grab the actual data. */
  if(sysctl(name, 6, &ifmd, &a_size, (void*)0, 0) == -1) {
   fprintf(stderr, "Error with sysctl\n");
   /* Sysctl failed.. Not good. */
   continue;
  }

   /* Is this interface named 'wlan0'? */
  if(strcmp(ifmd.ifmd_name, ifname) == 0) {
   /* This is the interface we're looking for. */

   if(ifdata[0] > 0 && ifdata[1] > 0 && ifdata[2] > 0 && ifmd.ifmd_data.ifi_obytes >= ifdata[0] && ifmd.ifmd_data.ifi_ibytes >= ifdata[1] && time(NULL) > ifdata[2]) {
    avg[0] = (ifmd.ifmd_data.ifi_obytes - ifdata[0]) / (time(NULL) - ifdata[2]);
    avg[1] = (ifmd.ifmd_data.ifi_ibytes - ifdata[1]) / (time(NULL) - ifdata[2]);
    printf("Average upload speed over %d seconds: %fkb/s\n", time(NULL) - ifdata[2], avg[0]/1000);
    printf("Average download speed over %d seconds: %fkb/s\n", time(NULL) - ifdata[2], avg[1]/1000);
    printf("\n\n");
   }

   ifdata[0] = ifmd.ifmd_data.ifi_obytes;
   ifdata[1] = ifmd.ifmd_data.ifi_ibytes;
   ifdata[2] = (unsigned long)time(NULL);
   return 0;
  }
 }

 return 1;

}
int main(void) {
 ifdata[0] = ifdata[1] = ifdata[2] = 0;

 for(;;) {
  printit(); /* Don't really care about the return */
  fflush(stdout);
  sleep(2);
 }
}








There are also some caveats when it comes to this code, which are all noted in the manpage. I've noted them in the code too.

Also, before I 'upgraded' to 11-CURRENT, I was using 10-RELEASE, which when running this program, seemed to always have the upload speed at 0, and the download speed was a combination of the upload and download speed.


There's not much more to say. I just wanted to litter Google with this blog post in case somebody else was going through the trouble I was.

Friday, 13 March 2015

Specifying an DNS/NS server for address resolution / getaddrinfo in C

I was recently trying to get my C program to use a custom NS server when resolving an address, within the program. It ended up being a challenge to not have to use to create my own function that would have to deal with the connections directly(connect(), etc.)
The purpose of this was to resolve opendns's server to find out the runner's IP address.


I found that 'libresolv' could be used to overwrite a private/protected struct, "_res".
We must include resolv.h to do this.


Here is an example code as to how to do this:

                inet_pton(AF_INET, "208.67.220.220", &server); 

                serverSock.sin_family = AF_INET;
                serverSock.sin_port = htons(53);
                serverSock.sin_addr = server;

                _res.nscount = 1;
                _res.nsaddr_list[0] = serverSock;


Then, using the code to get resolve something:


        if((getaddrinfo("myip.opendns.com", NULL, &hint, (struct addrinfo**)&result)) == 0) {
                p = result;
                h = (struct sockaddr_in *)p->ai_addr;

                printf("myip.opendns.com resolves, using 208.67.220.220, to: %s\n", inet_ntoa(h->sin_addr));
                freeaddrinfo((struct addrinfo *)result);

                exit(0);
        }



Strangely enough, On the first run of getaddrinfo(), resets _res. So, we have to either do a fake getaddrinfo call, or a loop. [It took me 3 hours to work this out :(] -- This is a bug! bug report

Since a loop could cause problems, we're just going to do a fake getaddrinfo call, which is simple enough:

        struct addrinfo hints, *servinfo;



        memset(&hints, 0, sizeof hints);
        getaddrinfo("google.com", NULL, &hints, &servinfo);
        freeaddrinfo((struct addrinfo*)servinfo);


This must be placed before the overwrite of _res, and will use our regular DNS server.

NOTE: _all_ subsequent DNS resolutions within the program will use whatever we packed _res with.
If you want to reset _res afterwards,we would use res_init(); again.


If we did want to use a loop, it would look something like this:


for(int i=0; i<2; i++) {

        if((getaddrinfo("myip.opendns.com", NULL, &hint, (struct addrinfo**)&result)) == 0) {
                p = result;
                h = (struct sockaddr_in *)p->ai_addr;

                printf("myip.opendns.com resolves, using 208.67.220.220, to: %s\n", inet_ntoa(h->sin_addr));
                freeaddrinfo((struct addrinfo *)result);

                exit(0);
        }

}





So, combining all of this, we end up with this code:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>


int main(void) {

        struct in_addr server;
        struct addrinfo hint, *result=0, *p=0;
        struct sockaddr_in serverSock, *h=0;

        //For our 'fake' resolution
        struct addrinfo hints, *servinfo;


        if(inet_pton(AF_INET, "208.67.220.220", &server) == 0) {
                exit(1);
        }
        //Have to run getaddrinfo before we do this, atleast once, for some reason.
        memset(&hints, 0, sizeof hints);
        getaddrinfo("google.com", NULL, &hints, &servinfo);
        freeaddrinfo((struct addrinfo*)servinfo);

        serverSock.sin_family = AF_INET;
        serverSock.sin_port = htons(53);
        serverSock.sin_addr = server;

        _res.nscount = 1;
        _res.nsaddr_list[0] = serverSock;

        memset(&hint, 0, sizeof hint);

        int error;
        if((error = getaddrinfo("myip.opendns.com", NULL, &hint, (struct addrinfo**)&result)) == 0) {
                p = result;
                h = (struct sockaddr_in *)p->ai_addr;
                char *heh = inet_ntoa(h->sin_addr);
                printf("Our IP is: %s\n", heh);
                freeaddrinfo((struct addrinfo *)result);
                res_init();
                exit(0);
        } else {
                printf("%s\n", gai_strerror(error));
                exit(1);

        }

}

Sunday, 11 January 2015

Incorrect volume in PulseAudio indicator, with fix.

I was encountering a strange bug with PulseAudio, and the indicator/applet that is used to change the volume on my laptop.

I'm using external, Bose Companion 5 speakers, in Ubuntu, with PulseAudio through ALSA.

When I used the speakers, the audio would only come out of them if the pulseaudio("Sound Settings") indicator/applet was set to >90%. That is when audio began to work.

Strangely, it wasn't that it wasn't loud enough, it was because it just wouldn't actually start increasing the real volume.
91% volume in PulseAudio was 11%, 92% was 22%, etc. etc.




This was very irritating because I couldn't, through PulseAudio, choose to have the speakers at the volume, say, 7%.

`alsamixer' correctly set the speaker level, which is how I worked out that PuleAudio would be at around 91% for 10%, etc.

According to the helpful Raymond at bugs.freedesktop.org, "the USB audio only supports 6 channels, and has PCM playback volume control with a very small dB range, from -3.12dB to 0dB."

His recommendation was to add  " ignore_dB=1" to Pulse.

The way I did this, was add to it ~/.pulse/default.pa, since I had already set that file up when I set up my 'secure Ubuntu.'
If I hadn't set it up in ~/.pulse/default.pa, I would have to edit /etc/pulse/default.pa.

I found the line:
"load-module module-udev-detect" in default.pa and replaced it with
"load-module module-udev-detect ignore_dB=1"

And then restarted pulseaudio(`pulseaudio -k')

Likely, there is a way to set this "ignore_dB" option only for the external speakers, compared to the whole of PulseAudio. But the option doesn't seem the affect my internal speakers' usage.

Monday, 10 November 2014

`dpkg' format string vulnerability. CVE-2014-8625

A few days ago, I found a strange/stupid vulnerability in dpkg.

Dpkg is the package manager for Debian-based operation systems, such as Ubuntu.
It handles .deb files.

When creating a dpkg package / .deb a file, a 'control file' must be made. This includes information about the package, such as the package name, description of package, maintainer(s), and version of the package.

That control file is used to display the information before the package is installed.

I found that in the "Architecture" part of the control file, you could put formatting strings, such as %s, %d, etc., and it would output the stack pointer.

For my control file, I have this:

Package: backup
Architecture: %08x.%08x.%08x.%08x.%08x\n
Description: Stuff
maintainer: Joshua Rogers
Version: 1


When building the package, I receive this warning:

dpkg-deb: warning: parsing file 'folder//DEBIAN/control' near line 2 package 'backup:01485120.00415cf8.00000001.00000001.0000001c\n':
 '%08x.%08x.%08x.%08x.%08x\n
Description: Stuff

[....]
01485120.00415cf8 is the stack pointers


Saturday, 27 September 2014

Having fun with passwords in Ubuntu RE: intruders/police/etc.

In movies, TV shows, and comics, the idea of a "trap passcode", "secondary passwords", "fake passwords", "kill-switch password", etc., is used every now and then, where if a bad-guy(or a good-guy) is provided with a passcode to something, only to find out that that passcode is set up purely for intruders.
Whether it be wiping the whole system, or a strange gas coming out of the console to concuss the person typing it, the idea is always the same.

In the real world, it's hard to do this sort of thing without it being detected. One method is using incorrect passwords through PAM. This is what I'll be detailing through this blog.


To set this up correctly, it needs to work in a way that after the script has run, it removes itself, and any traces of its actions.
If, say, you're in a corrupt/fascist country such as Australia, the police can apply for a 3LA order, which requires you to hand over your computer passwords, encryption keys, and anything they feel that would help them with their investigation, or you can be charged with a crime, which the maximum prison term is 2 years. -- This includes SSH keys to foreign servers.


If you're a political activist, the access of your sensitive data is 100% unacceptable.
Arguably, forcing somebody to hand over encryption keys is undemocratic, and is comparable to the acts of the Stasi.

Tuesday, 5 August 2014

Paypal Complete 2-Factor Authentication(2FA) Bypass Exploit.



Update: It has been patched on the 12th of August. "Surprise?"




[Revision; 6th of August, 2014]
To make it clear: The Paypal account you were 'hacking' did NOT have to be affiliated with the eBay account you were using. In my original tests, I had made a new eBay account using a temporary email, and had gotten into my Paypal through the same method.

It works even without an eBay account, actually.

https://www.paypal.com/cgi-bin/webscr?cmd=_integrated-registration&key=0&stamp=1364194631&data=JGHnP2g2ybqbgKfR7%2B1loOlg24LvI/VppQIqFE8DyTO9hqc1x1pQw42CCLy3EdEogm85LYOTKtU2wYNfjFZvuHSx4PjAHLVtlv6sYdPl2FIBLN7BNr3l%2BPe0WPeDhopUWqhw0PYE9EAyZPkgIZWJgWKGGGNPqdQRjlbNGoCCIox7RLfKmtEDeH8KXEOzZDSmvETO%2B7fkoy06CLe9CkJhE0V8Mh9QN/wNYIF6WMFgHsze7RAS8Qe3j/U9I9zYXDPcfB2L5AVCYI53jcWUOxeKXSlcoV0eIcxkLOkLfmSqnaY9vywEQEhEU2PYoKSqefaZBPFh6Y7kWXVD/7id8PvkrJzKaCUq0nhBRfFGtf1kYrK0ZgX%2Byws4HmiTn4GEL/gaUPtpWviP4BCJmeGOhzQEhbFNYwzuzmOWAaqYfsa62DsAcq3LUy1DyAmBfsLhwzRyzZhKlg1NRz5MxTsuBqlh72W6ytc1gEMwh%2BJtBxZTf7EggIaTRLdpjXMlZmwRjkMH2BjX8P4968XicykzmLhTpqpg507flV%2Belq3QNBd9cAliSskS3n/%2Bd1os7FQBnogr4tZ7srcTkoPM5nezXqz3caE/loqoJnkWvlRYfNJpSSysjQ%2BThTgiwNtk4eh8X2r3LhepLD27KdM7I299%2BnWVF9veVjw625ZT%2B3MyQMiO7FbMJdng5baW%2BZIRFIear2GlEJVXMlftP3ibMJAmzGrnKqB0sPwY3augnaBNnz4u32QAaxg8zhvz5FEaELdpFxJ4ptLdRc2MFUBFkUDm%2B5tlpuNl9JzgKTDQnXzYxX/2KYAznivHTlsCcwH68kL6EqoiGGTsFoLzp8TqnLvizULu6tdfnTAhhxV6kCeRRoyN/a62wahvxDibJgTnTjp4d3/xm4nhkQhQ5/xUgtAN9T1aa7n5PinOWS84AOFR0TB3KpwHsQkoQCGXvzdYZh4wD8ECQzYS9lbpaCLm13GqPGK4xC6K2vat8/gt9uoiJbiy77SK2PcMhcRS3KbK9Z0HtDCl&ev=1.0&locale=en_US


--




This blog is an excerpt from my blog entry, "Paypal's 2-FActor Authentication(2FA): The Good, The Bad, And The Ugly", in which I detail the use[fulness] of Paypal's 2Factor system.

On the 5th of June, 2014, I found a complete bypass for Paypal's 2FA service, in which anybody would be able to access a Paypal account that has 2FA setup, by only logging in through a "special" Paypal page.




Saturday, 5 July 2014

PTV; The police, and the aftermath.

For backstory regarding the PTV story, the following articles should be read:

(Sydney Morning Herald) Schoolboy hacks Public Transport Victoria website - PTV #1
(Wired) Teen Reported to Police After Finding Security Hole in Website - PTV #2
(ABC) Melbourne schoolboy exposes security flaw in Public Transport Victoria's website - PTV #3


This blog entry is more a "diary" of what happened after the story broke.

Originally, I found the bug on the 26th of December, 2013, and around 2AM.
I reported the bug at around 3AM to around 30 company emails.

On the 6th of January, 2014, the original reporter(Adam Carey) was contacted by PTV, and was told that PTV had contacted the police(this was assumed that, if he didn't publish the story, they wouldn't contact the police; but this is unknown)

On the 7th of January, 2014, the story was run in The Age(Fairfax) newspaper.




On Thursday, the 8th of May, 2014, at 8:15AM, 6-8 fully armed police officers showed up at my place of residence(my house). Three of them were e-crime. 

A warrant was served to me, and two e-crime officers went into my room and started to catalogue my electronic belongings, and then seal them for evidence.

Two of the other(non e-crime) officers sat me down, and started asking me general questions, such as how I was, etc. I commented to them how I had been warned around a week earlier that a search warrant may have been approved by the court, and would subsequently executed(I have a contact). They were definitely stunned, but we didn't speak of it other than that.


Friday, 27 June 2014

Paypal's 2-Factor-Authentication(2FA): The Good, The Bad, And The Ugly. (Incl. full 2FA bypass without security questions)

Introduction

Paypal, like many other services, offer 2-Factor-Authentication in an attempt to strengthen the security of users' accounts. As noted on Paypal's website, "The security key gives you an extra layer of security when you log in to your PayPal account. It creates random security codes to use along with your regular username and password."

Paypal provides two ways of using this service; through a one-time code sent as an SMS to your mobile phone, or through a physical, creditcard sized code generator.(Or optionally, a VeriSign ID Protection key, which you can set-up on your phone for free here.)

An example of Paypal's security-card

Paypal's implementation of 2FA has been heavily scrutinized[1] again[2] and again[3] due to the lack of apparent security surrounding it. They allow security questions to be used to bypass the blockade of not having access to your 2FA device, and sometimes even when you do have access to your device, the code just doesn't work.


In this article, I'll be detailing "The good, the bad, and the ugly" of Paypal's 2FA programme. This includes what works, how it works, how it doesn't work, and security implications(full disclosure: there is/was a complete bypass for the 2FA without security questions.)


Personally, I use the SMS version of Paypal's 2FA, thus I can only directly comment on that. Nonetheless, I'll reference a few articles in regard to their creditcard sized number generator, and the VeriSign key generator.



Tuesday, 10 June 2014

Securing Ubuntu-Desktop From the Bad-Guys, and the Good-Guys.

Securing your Ubuntu Desktop OS from intruders

Recently I have become interested in securing my laptop from predators such as hackers, thieves, and law enforcement.
To do this, I've explored various programs to run; and how to run them, without interrupting usability by the average user.

In this blog we'll be running through vectors of attacks that one could use to gain access to your unencrypted data.


Before starting, the following must be known:

1. The author of this article is currently running Ubuntu 14.04 LTS(Trusty), and all commands and patches work on it for the author. The author accepts no liability when it comes to these commands/patches being run by other users; this is purely informational.
2. It is assumed Full-Disk-Encryption is being used.
3. It is assumed your $HOME directory is encrypted using ecryptfs, with filenames encrypted. This can be checked using the command `ecryptfs-verify -h -e'
4. It is assumed you do not have the evil program called Java, or any of its counterparts like IcedTea, etc. installed.


When you're told to run the program 'Nano', you can use vim,vi,emacs, etc. Nano is purely the text editor that I use. To exit out of Nano, you press control-x.



Monday, 26 May 2014

Facebook "Skype-to-Email" leak [$3,000 Bounty]

Facebook Bug Bounty

 

 

 In the middle of January of 2014, I submitted a bug to Facebook through its bug bounty program.



The bug was effectively a Skype account email disclosure. You would find somebodies Skype name, add them on Skype(they didn't have to accept you), and then login to Facebook with your Skype.



Here's a look at how it worked exactly:



In Facebook's "Find Friends" feature, you can login to your Yahoo,Outlook,Skype, and other accounts to add people into your contacts list on facebook(and then in turn add them, I guess).

The feature in question

By logging into your skype account on the feature, and pressing "Find Friends", you were submitted to the next page.

Sunday, 25 May 2014

SQL Injection on eBay.com.au subdomain / eBay.de, eBay.fr subdomains

eBay



Whilst looking for some bugs in ebay.com and ebay.com.au, I came across the domain http://3.ebay.com.au/. It appears to be a domain for phone users on the old "Three" phone carrier/network, but I'm unsure. Three was bought out by Vodafone awhile ago.

The website is the exact same as http://imode.ebay.de/, http://imode.ebay.fr/, etc.
The database itself was most likely part of http:// ebay.com/, too.


On the third tab of the page, there's a link to the 'Categories' section. -- If anybody has ever used eBay before, they would understand what this is; a list of categories as to where you can view items to buy.(Or in this case, go into a sub-category.)

Instinctively, I saw that there were a few $_GET parameters being used, so I just put a simple apostrophe into the end of the first parameter, "emv_CatParent".
To my amazement, it came back with a half-completed page. -- Pretty much the poster-child of a blind SQL Injection.