Converting ASP Classic to PHP5

I posted a while ago that I added a page with ASP Classic to show your current IP. This worked because the site was being hosted in IIS with ASP.NET, ASP Classic, and PHP all enabled but I have now moved to a PHP only host and wanted to quickly get the IP lookup working again (I use it quite often).

I can potter around with PHP and would be able to figure it out but I did a quick search to find a converter and discovered this great online tool which did the work for me!

Here’s the original code.

If Request.QueryString("debug") = "1" Then
  For Each x in Request.ServerVariables
    Response.Write(X & " = " & Request.ServerVariables(X) & vbCrLf)
  If Len(Request.ServerVariables("HTTP_X_FORWARDED_FOR")) = 0 Then
    Response.Write Request.ServerVariables("REMOTE_ADDR")
    Response.Write Request.ServerVariables("HTTP_X_FORWARDED_FOR")
  End If
End If


Here’s the converted result.

if ($_GET["debug"] == "1") {
  echo "<pre>";
  foreach ($_SERVER as $x) {
    echo $X." = ".$_SERVER[$X]."\r\n";
  echo "</pre>";
else {
  if (strlen($_SERVER["HTTP_X_FORWARDED_FOR"]) == 0) {
    echo $_SERVER["REMOTE_ADDR"];
  else {


It works exactly the same as before but this time it’s PHP. You can try it with the same link: What is my IP?

Review of Udemy Online Learning

At the start of 2018 Udemy ran a promotion where courses could be purchased for up to 95% off their original price so, naturally, I had to take advantage of this and picked up an The Complete Android N Developer Course by Rob Percival.

There are hundreds of courses available and I picked this one because I’ve been interested in making mobile applications for a long time but always found the YouTube videos clunky and disjointed to watch. Based on the high quality content provided by Rob in his Android course I picked up a few others on other topics and found that the overall quality of instructors on Udemy appears to be very high. They, or at least the ones I’ve found, take the time to enunciate clearly and speak at an appropriate pace even if English is not their native language which makes their topic much easier to follow and a pleasure to watch.

I will likely make a future post documenting a basic timer app being developed from start to finish or if I feel confident enough maybe even a simple game good enough for the Play store!

TLD & IP Regex

I needed to validate both a domain name and IP addresses in one regular expression (or: regex) and put this together:


It’ll match a single line to validate an IP or domain name, including any of the new TLDs at the time of writing.

What Is My IP?

I needed a quick way to find my IP without using Telnet so I added to my site. Here’s the ASP Classic  code because I was feeling nostalgic.

If Request.QueryString("debug") = "1" Then
  For Each x in Request.ServerVariables
    Response.Write(X & " = " & Request.ServerVariables(X) & vbCrLf)
  If Len(Request.ServerVariables("HTTP_X_FORWARDED_FOR")) = 0 Then
    Response.Write Request.ServerVariables("REMOTE_ADDR")
    Response.Write Request.ServerVariables("HTTP_X_FORWARDED_FOR")
  End If
End If



SQL Injection Example

For years I’ve known that SQL injection is bad and how to protect against it. In fact, I’d go so far as to say that everything except the very first classic ASP pages I wrote were safe against malicious SQL but for some reason I’d never actually tried attacking a vulnerable page to see what was really so bad about it.

We all know the XKCD comic about little Bobby Tables and yes you could simply drop the tables or database very easily on an unsecured page but what if you aren’t simply bent on destruction? As developers we spend our lives building little hand-holds into application to stop people breaking things, and that’s just for those that aren’t trying to do anything bad! What about those that want to slip in, infiltrate, and hold your organisation to ransom?

Here’s a brief example of how easy (scary) it is for one unchecked variable to be used as the gateway to read your entire database.

Some things to note:

  • The pages are classic ASP because .NET does not easily let you be so careless
    • PHP, just like Linux, will let you do what you want even if that is pure self-destruction
  • The database back-end is MSSQL Express but this doesn’t matter too much as the theory is the same for any DB backend

First, I’m greeted by a login prompt:

Simply enter a single apostrophe into either field to see if this page is vulnerable.

Awesome! Not only is the page vulnerable, it’s got error reporting turned on so I can see the exact part of the query I’m interested in! This error is saying there is “unclosed quotation mark” in the query which means the apostrophe we entered has been taken literally by the server and executed as part of the query. Simply because of one extra quotation mark the query fails because MSSQL can’t figure out where each string starts/ends.

How can this be exploited? Well, commonly a low-tech self-made login process would select a record where the username and password both exist. Now we’ve seen part of the SQL statement this website uses we can see if we can “log in” without a password, just for fun…

Jackpot. We’re technically logged in but apparently only as BasicUser which just so happens to be the first user out of the database in the very simplistic query we tried. At this point any developers should be thinking this is pretty bad and that they’d never leave any unchecked user input. Read on…

I’ve logged in but I don’t know anything about this server or its secrets. I’ve decided I want to know:

  • What user tables there are
  • What columns each table has
  • List all usernames and passwords

So, step one, I’d like to know how many tables there are so I enter this into the username part of the login prompt:

' UNION SELECT 1, count(*), '3' FROM sys.objects WHERE type='u';--


There are only two tables in this database so it shouldn’t take long to list them all, except that I can’t simply list all the tables as I’ve only got one output to work with which seems to be trying to welcome the visitor by name. I guess I’ll have to do it one-by-one:

' UNION SELECT 1, name+'+'+convert(varchar,[object_id]), '3' FROM sys.objects WHERE type='u';--


Hey look there’s a messages table, I guess these users can chat to each other. I’ve outputted the object ID because that’s what I’ll use to get the columns later. But first I want to know what the other table is so I’ll exclude Messages and do the query again:

' UNION SELECT 1, name+'+'+convert(varchar,[object_id]), '3' FROM sys.objects WHERE type='u' AND name NOT IN ('Messages');--


Now I can get the field names for each table by doing a similar query but this time looking at sys.columns:

' UNION SELECT 1,name,3 FROM sys.columns WHERE object_id = 277576027;--

Now I’ve got a column I can use “AND name NOT IN(….)” again to get them all and repeat the process for both tables. Here’s the result:

Users (ID: 245575913) Messages (ID: 277576027)
Password Message
Username Recipient


I wonder if logging in as “admin” will work, assuming the account exists. Due to how there’s completely no security on this page I could simply put ‘ OR Username = ‘admin’;– into the username field so if there is an account with that name it’ll log me in without needing the password but that’s easy, what if I want to actually know the password? Very easy, I just do the same sort of query as above:

' UNION SELECT 1,Password,3 FROM Users WHERE Username = 'admin';--


The password is ‘admin’ !! Not only is the page SQL injection vulnerable the username/password policy is ridiculous too and every good developer knows to NEVER have something like this in their code… don’t you 😉

Also, once logged in as admin it seems something else cropped up and it looks like a message from BasicUser. It’s important to note here that the messages sent to the administrator appear because the username and password are both ‘admin’ and would not have shown up otherwise. This is because the example looks for messages sent to a Username so by using the above query to get the password the application would think the password was the username and find no messages logged against it.

Now I know there is somewhere that does an iterative list to output a record set and I know what columns it will use I can get a little more creative and simply list all usernames and passwords from this database:

' UNION SELECT 1,''' UNION SELECT Id,Username,Password,Password FROM Users;--',3;--

In conclusion, I hope this shows you that the little SQL bug you have in a project which crashes whenever anyone called O’Leary turns up is absolutely worth fixing right now.

NOTE: I have omitted some details explaining exactly what each SQL query does as this post is intended to explain to a developer why SQL injection vulnerabilities are a critical security flaw, not just something that results in a few errors every now-and-then.

SpamAssasin Wrapper

UPDATE NOTE: This application was for an older version, do not use it anymore!

I have built a little application to get SpamAssasin working with Mail Enable as the old SAPlugin.exe didn’t seem to work for me. It’s pretty simple and the source code is included so you can modify it however you desire. The features are:

  • Calls SpamAssasin to check emails
  • Can quarantine emails so they don’t go to the user’s inbox
  • Always writes over the email so you can check spam scores

Here’s the config file to show how simple an applciation this is. Please note that SpamAssasin has a problem with long file names and spaces so it seems to be best to use the shortnames for its location.

<?xml version="1.0" encoding="utf-8" ?>
    <add key="SpamAssasinExe" value="C:\PROGRA~2\JAMSOF~1\SPAMAS~1\SPAMAS~1.EXE"/>
    <add key="QuarantineDirectory" value="C:\Program Files (x86)\Mail Enable\Quarantine\Messages" />
    <add key="DebugOutput" value="true"/>

You can download the application & source code from my projects page here.

UPDATE NOTE: This application was for an older version, do not use it anymore!

Parent UUID of the medium does not match UUID of its parent

I recently discovered a problem with Oracle’s VirtualBox that seems to be caused by having snapshots stored in a directory that is not right below the VHD file. I don’t know if this affects other virtual disk formats but VBox seems to assume you have this set up:

My Super VM/
My Super VM/Snapshots/

Instead of this standard installation I had my VHD files on a separate disk (H:/ in my case) but forgot to change the snapshot directory so vbox was saving to C:\users\Luke\VirtualBox VMS\VirtualMachine Name\Snapshots and in doing so seems to forget the Parent UUID of the main VHD and give this error:

After some investigation and learning how the IDs are stored in the VHD files I built a tool called SetVHDParentUUID which you can download here

Quick screenshot…

Virtualization and VHD Files

A while ago I became very interested in virtualization and how Microsoft Virtual PC works. I quickly came up against VHD (Virtual Hard Disk) files and after learning a little about them I realised that you could use them to start learning 32-bit Assembly and build your own bootloader or operating system. All pretty obvious stuff but it all seemed so magical… so I looked for a good interface to let me edit VHD files and couldn’t really find anything that was as visual as I wanted which meant it was time to start coding.

After reading the specification for VHD files (which is here) I learned that there are three types of VHD file which are Fixed, Dynamic and Differencing. The most basic of these is the fixed disk type which is just one file with a block of data representing a hard disk surrounded by a header and a footer. The dynamic disk grows by block sizes as you add data to it but never goes beyond the maximum capacity. This is how you can have a virtualized 60Gb drive taking up only 4Gb – it is because the actual space used is only what is needed rather than the full allocation as is the case with fixed virtual disks. I won’t go into differencing disks in this post because I did nothing with them but they are used to represent the differences between an existing VHD and another state of that VHD. A differencing VHD file relies on having a parent which can be a fixed, dynamic or even another differencing disk.

Footer Format

All VHD disk types share a common footer format which is detailed in the VHD format specification document but here’s the brief version.

Hard Disk Footer Fields Size (bytes)
Cookie 8
Features 4
File Format Version 4
Data Offset 8
Time Stamp 4
Creator Application 4
Creator Version 4
Creator Host OS 4
Original Size 8
Current Size 8
Disk Geometry 4
Disk Type 4
Checksum 4
Unique Id 16
Saved State 1
Reserved 427

Using this table you could build an object to easily read and modify these values from the VHD files footer. Doing this would then allow you to manipulate fixed disks by changing their size (Current Size) or doing other funky things. To save the footer as a fully functional FIXED DISK VHD file you save out the data block as large as needed but in multiples of a ‘block’ size and end with the 512 byte footer as detailed above.

When any changes are made you must re-create the Checksum which can be done like so (this is an example from my code, don’t forget that your variables names are likely to be different).

public void UpdateChecksum()
   uint uChecksum = 0;
   byte[] tmp = new byte[512];
   this.Checksum = 0;
   Array.Copy(_cookie, 0, tmp, 0, _cookie.Length);
   Array.Copy(_features, 0, tmp, 8, _features.Length);
   Array.Copy(_fileFormatVersion, 0, tmp, 12, _fileFormatVersion.Length);
   Array.Copy(_dataOffset, 0, tmp, 16, _dataOffset.Length);
   Array.Copy(_timeStamp, 0, tmp, 24, _timeStamp.Length);
   Array.Copy(_createApp, 0, tmp, 28, _createApp.Length);
   Array.Copy(_creatorVersion, 0, tmp, 32, _creatorVersion.Length);
   Array.Copy(_creatorHostOS, 0, tmp, 36, _creatorHostOS.Length);
   Array.Copy(_originalSize, 0, tmp, 40, _originalSize.Length);
   Array.Copy(_currentSize, 0, tmp, 48, _currentSize.Length);
   Array.Copy(_diskGeometry, 0, tmp, 56, _diskGeometry.Length);
   Array.Copy(_diskType, 0, tmp, 60, _diskType.Length);
   Array.Copy(_checksum, 0, tmp, 64, _checksum.Length);
   Array.Copy(_uniqueID, 0, tmp, 68, _uniqueID.Length);
   tmp[84] = _savedState;
   Array.Copy(_reserved, 0, tmp, 85, _reserved.Length);
   foreach (byte b in tmp)
     uChecksum += b;
   this.Checksum = ~uChecksum;

There are a few things here to take note of. Firstly, the checksum is calculated by simply adding up all the bytes so this is a lot of lines to do really not that much work. The second is the unary bitwise NOT operator ~ (tilde) as this performs a one’s complement NOT operation on the checksum.


If you’re interested in programatically manipulating VHD files this post might help you get started and I would be interested to hear from you if you do something interesting with virtualisation. I may someday finish my VHD project and make a bootloader and if that day comes I’ll post it for download on this site!

Proudly powered by WordPress | Theme: Baskerville 2 by Anders Noren.

Up ↑