Web Bugs, tracking bugs, pixel tags, web beacons or clear gifs Web Buggery: Analyzing Tracking Images

Web Buggery: Analyzing Tracking Images


Introduction

        Web Bugs are images (Gifs, Jpegs, PNGs, etc.) that companies and organizations put into web pages, e-mails and other HTML supporting documents to track information about the viewer. These images are sometime know by other names such as tracking bugs, pixel tags, web beacons or clear gifs. What ever the name, their function is largely the same.

        Under normal circumstances an organization would just look at their web logs to find the kind of information that a Web Bug might provide them. However, if the content the web bugger wishes to track is not hosted on their site, but instead its hosted from a third party's server, then the web bugger can not obtain this information since they would not have access to the web server logs. By putting an image from one of their servers into an HTML E-mail or a third party's webpage the Web Buggers can find the data they want about the contents viewer. Some of the interesting information items that can be obtained about the viewer are:

        Since the clients IP is logged further information that can be obtained if the web bugger wishes to take further steps. Even going so far as to port map and do an OS detection on the clients IP using Nmap.

Scripts

        In this article I'll give you the source code to two simple web bugs I've written for test purposes, one written in Bash script and the other in PHP. The PHP script is a little more secure to run and a lot more flexible to add functionality to. A more professional web bugger might use a database back end to store information about the viewer's client but for these scripts I'll just use CSV (coma separated values) files that are easy to import into other databases and spread sheets for analyzing. My scripts don't use cookies, which a web bugger might use to find out more about your surfing habits by storing state information. Both scripts should be pretty easy to setup and run on your Linux or BSD box using the Apache web server.

Bash Script:
code:
#!/bin/bash echo "Content-Type: image/gif" echo cat x.gif OUTPUT=" $QUERY_STRING,$REMOTE_ADDR,$HTTP_USER_AGENT,$HTTP_ REFERER,`date`" echo $OUTPUT >> log.txt sleep 2.5

Explanation: The Bash script above is pretty simple. To get this script to run on your server use the following steps:
1. Create a gif image of any size (for my tests I used a 1 by 1 gif) and save it into your cgi-bin.
2. Copy the script text above into a file and call it webbug.cgi.
3. Make the script executable with the following command: chmod +x webbug.cgi
4. Create a blank text file and set permissions with the following three commands:
code:
touch log.txt chown www-data log.txt chmod 600 log.txt


Note: You could set the permissions using the command "chmod 666 log.txt" but this is not a very secure way to do things. It's far better to find out the account Apache is running under (in my case www-data) and assign write permissions to that account only. If you do not know what account Apache is running under try using the "lsof –i" command to figure it out.

        What this Bash script will do is send the proper headers and the gif data to the requesting browser then log the client information to a CSV text file (log.txt in this case). The last line "sleep 2.5" is there because I kept getting error messages like "Premature end of script headers" when I used the script. The sleep time can be lessened if you want to play around with it, or a better programmer than I might have a more elegant solution. A line of output from the log.txt file should look something like the following:

         <QUERY_STRING>,192.168.18.33,Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1),http://irongeek.com/bugtest/index.php,Wed Oct 13 17:15:21 EDT 2004

        Where <QUERY_STRING> is the contents of the server variable QUERY_STRING that was passed to the CGI. The QUERY_STRING variable contains whatever information is inserted past the question mark at the end of the image URL. You will see how we pass the QUERY_STRING variable later in the article.

PHP Script:
PHP:

color="#0000CC"<?php   
header
("Content-type: image/png");
$im = imagecreatefrompng("1by1.PNG");
imagecolortransparent ( $im,imagecolorallocate($im, 255, 255, 255));
imagepng($im);
imagedestroy($im);
$hostname=gethostbyaddr($_SERVER['REMOTE_ADDR']);
$QUERY_STRING = preg_replace("%[^/a-zA-Z0-9@,_]%", '', $_SERVER['QUERY_STRING']);
//Write Log
    
$filename = 'webbug.csv';
    
$fp = fopen($filename, "a");
    
$string ='"'.$QUERY_STRING.'","'
        
.$_SERVER['REMOTE_ADDR'].'","'
        
.$hostname.'","'
        
.$_SERVER['HTTP_USER_AGENT'].'","'
        
.$_SERVER['HTTP_REFERER'].'","'
        
.date("D dS M,Y h:i a").'"'."\n";
    
$write = fputs($fp, $string);
    
fclose($fp);
//end Write Log
?>



Explanation: The PHP script above needs some image manipulation libraries for PHP installed on your server, see http://us3.php.net/manual/en/ref.image.php for more information on the libraries you will need. To get this PHP script to work on your server use the following steps:
1. Create a PNG image of any size (for my tests I used a 1 by 1 PNG) and save it to the same directory where you plan to put the PHP file.
2. Copy the script text above into a file and call it webbug.php.
3. Create a blank CSV file and set permissions with the following three commands:
code:
touch webbug.csv chown www-data webbug.csv chmod 600 webbug.csv

Note: You could set the permissions using the command "chmod 666 webbug.csv" but this is not a very secure way to do things. It's far better to find out the account Apache is running under (in my case www-data) and assign write permissions to that account only. If you do not know what account Apache is running under try using the "lsof –i" command to figure it out.

        What this PHP script will do is send the proper headers and PNG data to the requesting browser then log the client information to a CSV text file (in this case webbug.csv). The PHP script is a little harder to get working than the Bash script, but far more flexible for modification and a bit more secure. A line of output from the webbug.csv file should look something like the following:

                "<QUERY_STRING>,","192.168.26.138","adrian.irongeek.com","Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.3) Gecko/20040910","","Wed 13th Oct,2004 11:04 am"

        Where <QUERY_STRING> is the contents of the server variable QUERY_STRING that was passed to the CGI. The QUERY_STRING variable contains whatever information is inserted past the question mark at the end of the image URL. You will see how we pass this variable in the next section.


Placing Web Bugs into Web Pages

        If you know a little HTML placing a web bug into a page is easy. Just a simple image tag will do it:
code:
<img src="http://tux.irongeek.com/cgi-bin/webbug.cgi?some-extra-stuff">
The string past the question mark (some-extra-stuff) is the data that will be passed into the QUERY_STRING server variable. It could contain and email address, or as the next example shows, information about the viewer's client pulled from java script:

 
code:
<script language="javascript" type="text/javascript"> <!-- document.write("<img width=0 height=0 src=\"http://tux.irongeek.com/webbug.php?adc," +unescape(navigator.appName) +","+screen.width+"x"+screen.height +","+navigator.javaEnabled() +","+screen.pixelDepth +","+screen.colorDepth+"," +escape(document.referrer)+"\">"); // --> </script>


        The HTML and Javascript above would give us something like the following in the web bug log file (assuming Javascript is enabled in the web browser):

                    adc,Netscape,1024x768,true,32,32,,192.168.170.55,Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.5) Gecko/20031007,http://irongeek.com/bugtest/index.php,Wed Oct 13 20:06:09 EDT 2004

        As you can see the above data contains information such as screen resolution, color depth and other data about the browser. Pretty cool huh?

Placing Web Bugs into a Web Forum

        Many forums allow users to place images in their posts and signatures. Look at the forum's documentation to figure out how to insert an image. Please note that most forum systems require that the last part of a URL to an image ends in png, gif or jpeg/jpg. We can still get a web bug script to work by setting up a redirect in Apache. The rediect can be made by adding a line like the following to our httpd.conf file:

 
code:
Redirect /webbug.png /webbug.php


        By doing this we can use the URL http://tux.irongeek.com/webbug.png as the URL to our web bug image. For example, to put a web bug into a post or signature on a phpBB system the bugger could use the following site code:

 
code:
[img]http://tux.irongeek.com/webbug.png[/img]
        This site code works for many popular forum systems, including the one Antionline uses (at least for signatures).

E-mailing a Web Bug

        E-mailing a Web Bug is a little more involved than just placing it on a web page. Newer clients should have options to disallow HTML e-mails or to not download off site images (Gmail does not download off site images by default), but many folks ignore these options or choose to download external images anyway. In this section I'll show how to send and HTML e-mail by telneting to port 25 on the mail server of the target e-mail address' domain. For my demonstration I will be emailing two web bugs to my Gmail account.

        The first thing we need to do is find out the Mail Exchange (MX ) of the domain we want to e-mail. To do this in Windows we can use the following command:
code:
nslookup -querytype=mx gmail.com
        The above command should work in Linux also, but since the nslookup command is depreciated in Linux you may need to use the following dig command instead:
code:
dig gmail.com mx
        By using the nslookup command we get the following information:
code:
C:\>nslookup -querytype=mx gmail.com Server: dns2.irongeek.com Address: 192.168.162.1 Non-authoritative answer: gmail.com MX preference = 10, mail exchanger = gsmtp171.google.com gmail.com MX preference = 20, mail exchanger = gsmtp57.google.com gmail.com nameserver = ns4.google.com gmail.com nameserver = ns1.google.com gmail.com nameserver = ns2.google.com gmail.com nameserver = ns3.google.com gsmtp171.google.com internet address = 64.233.171.27 ns1.google.com internet address = 216.239.32.10 ns2.google.com internet address = 216.239.34.10 ns3.google.com internet address = 216.239.36.10 ns4.google.com internet address = 216.239.38.10 C:\>
        From the above output we see that gsmtp171.google.com is one of the mail servers, so lets telnet to port 25 on gsmtp171.google.com to establish a connection and send som email:
code:
C:\>telnet gsmtp171.google.com 25
        Once the connection is established we use SMTP commands to create an HTML email with image tags pointing to our web bugs. In the following dialog the blue text is what the mail server sends us and the red text is what we send to the mail server:

220 mx.gmail.com ESMTP 70si2094099rnb
helo me.somepalace.com
250 mx.gmail.com at your service
MAIL FROM:<irongeek@iirongeek.com>
250 OK
RCPT TO:<irongeek@ggmail.com>
250 OK
DATA
354 Please start mail input.
To:Irongeek
From:Adrian
Subject: Webbug test
Mime-Version: 1.0;
Content-Type: text/html; charset="ISO-8859-1";
Content-Transfer-Encoding: 7bit;
<html>
<body>
<h2>Web Bug Test</h2>
Here's a Web Bug Test!
<BR>
<img src="http://tux.irongeek.com/webbug.php?irongeek@gmail.com">
<img src="http://tux.irongeek.com/cgi-bin/webbug.cgi?irongeek@gmail.com">
</body>
</html>
.

250 Mail queued for delivery.
quit
221 Closing connection. Good bye.


Connection to host lost.


        In the above example I set the senders name as my account on the Irongeek.com domain, but I could have just as easily faked the information and sent it as any email address I wished (satan@hell.org for example). If the above works, when the target opens the email we should see an entry in our logs like the following:

        "irongeek@ggmail.com","192.168.26.138","adrian.irongeek.com","Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.3) Gecko/20040910","http://gmail.google.com/gmail?view=cv&search=inbox&th=ff8f2f8f4869ea0&zx=a0b38249bec42991407828296","Tue 12th Oct,2004 06:05 pm"

        If a spammer used a web bug and HTML e-mail like this one they would now know that Irongeek@ggmail.com is a valid e-mail address and a little more about the victim's web browser and ISP.

Detection and Prevention

        You can find web bugs by viewing the HTML source code of a web page and looking for strange image tags like the ones in the examples above, or by finding images that don't show on the page because they are one pixel is size and/or transparent. Unfortunately even looking for these signs is no guarantee that you will spot a web bug since it could be any image on the page.

        Preventing web bugs in e-mail is pretty simple, just look in your e-mail readers documentation for how to disable HTML e-mail or block external images. Some web e-mail services like Gmail disable external images by default, which is a good thing. Some newer mail clients, like Outlook 20003, also default to not loading image from outside sites.

        Completely blocking web bugs in regular web pages is close to impossible, barring disabling images altogether. If you're really paranoid you can keep the web bugger from finding your IP address by using a proxy server, but make sure Javascript it turned off in your browser or they may still be able to obtain the information. Tools like Bugnosis ( http://www.bugnosis.org ) for IE and the Ad Block ( http://adblock.mozdev.org/ ) for Mozilla/Firefox can be of some help by choosing to block know web buggers. Another option is to round file know domains that host web bugs by editing you HOSTS file. For those that don't know, the HOSTS file is used by your computer to match a domain name to an IP address before using a DNS server to do the name resolution. See the links section at the bottom of this article for more information on how to edit your HOSTS file in Linux, Windows and Mac OS to help block web bugs and ads. Anti-spyware tools like Ad Aware and Spybot Search and Destroy can help find and destroy the cookies left by some web bugs.

        For the most part I feel that web bugs outside of e-mail are only a problem for the most paranoid among us. I use web bugs regularly myself to track pages I host on web servers I can't get the logs for and web bugs have some great beneficial uses for a web master trying his best to tailor his site to the reader's web browser by finding out the most common browser types and screen resolutions. I do however recommend turning off images in HTML email since they can be used by spammers to confirm e-mail addresses.

I think that's about all I have to say on the subject of web bugs, so I guess I bugger off now.

WebBug Source Code

        If you would like a more complicated example of a web bug, please take a look at this source code:

                Irongeek's Webbug PHP & MySQL Souce Code

       It's more or less the same code I use for my sites logo script. It logs to a MySQL server so it's easy to query results using something like PHPMyAdmin, you just have to make some minor changes to config.php.

        Enjoy.


Further Reading:
The Electronic Frontier Foundation had a great FAQ on Web Bugs:
http://www.eff.org/Privacy/Marketing/web_bug.html

Wikipedia enty on Web Bugs.
http://en.wikipedia.org/wiki/Web_bug

7 reason why HTML e-mail is EVIL:
http://www.georgedillon.com/web/html_email_is_evil.shtml

Using the HOSTS file in Linux, Windows and Mac OS to block web bugs and ads:
http://www.ecst.csuchico.edu/~atman/spam/adblock.shtml
http://www.mvps.org/winhelp2002/hosts.htm

Special thanks to Jake Howlett who wrote the following article that help me figure out how to send HTML in e-mail:
http://www.codestore.net/store.nsf/unid/EPSD-587VVX?OpenDocument

Change Log:
07/04/2008: Added source code for my PHP & MySQL web bug. I also cleaned up the page's layout and changed email addresses to make them non-valid.
10/15/2004: Article first posted.



If you would like to republish one of the articles from this site on your webpage or print journal please contact IronGeek.

Copyright 2020, IronGeek