|
---[ Phrack Magazine Volume 8, Issue 54 Dec 25th, 1998, article 08 of 12 -------------------------[ NT Web Technology Vulnerabilities --------[ rain.forest.puppy / [WT] <rfpuppy@iname.com> *Note: most of the vulnerabilities in this document have NOT been made public; they were discovered by rain.forest.puppy, or other members of WT. Lots of new toys out there on the Internet lately. Seems like the web is the way to go, and every software spigot is demanding they be 'web-enabled'. A lot are reinventing the wheel, bundling sub-standard web servers to serve up their HTML and Java interface. But this article isn't about them. There's too many, and they're to easy to use as vulnerable targets. It's much more fun to find the needle in the haystack, so I'm going to focus on some more common setups. On to the show. ----[ IIS 4.0 IIS is not too bad as a web server. It still doesn't compare to Apache, but it has flexible scripting and server-side abilities. But, of course, everything has its price... One interesting problem (and probably the only one that may be previously published at the time of this writing) is that appending an ".idc" extension to the end of a URL will cause IIS installations to try to run the so-called .IDC through the database connector .DLL. If the .IDC doesn't exist, than it returns a rather informative page stating that it can't open %documentroot%\<bogus name>.idc. For example: "Cannot open c:\inetpub\wwwroot\index.html.idc" Wow, absolute paths on the server. Very interesting. What good does this do? Well, it gives you some insight and hints. If you're trying to exploit CGI or other server-based programs, knowing what drive you're on when trying to access outside documents blindly helps a lot. For example, if the IDC query came back: f:\webs\1\index.html.idc then you know you'll probably have to specify 'c:\' to get to any Windows NT system files; you can't do silly stuff like: ../../../../winnt/system/repair/sam._ since you're doing relative addressing, and staying on drive F. Another common return is something like" "Cannot open d:\20x.140.3x.25\index.html.idc" Where the IP address is the full IP address of the webserver. This usually indicates that the site is on a system that's probably hosting multiple websites. Also, usually the site that's based in \inetpub\wwwroot is the 'default' site, and may have other things associated with it (like sample files, etc... We'll get to these later). This is important to remember. ----[ FrontPage Webbots A really quick recap on how webbots work: Frontpage inserts some HTML comments that specify the parameters of the webbot. Then, the form is submitted to /_vti_bin/shtml.dll, and the URL of the page is given. shtml.dll reads through the given page, and interprets the webbot/HTML comment code. So, all the parameters that are involved in (most) webbots are embedded in the HTML page themselves. Let's take an example from a corporate site that makes a very popular FTP suite (this is HTML code): <!--webbot BOT="GeneratedScript" endspan --> <form method="POST" action="../_vti_bin/shtml.dll/downloads/ftp.html" name="FrontPage_Form1" webbot-action="--WEBBOT-SELF--"> <!--webbot bot="SaveResults" u-file="d:\us\product_downloads\download_log.csv" s-format="TEXT/CSV" s-label-fields="FALSE" s-builtin-fields="Date Time" s-form-fields u-confirmation-url="../_confirmations/ftp.html" startspan --> Notice that this site is saving the results to a file (and the fact that it has "d:\.." says that it is a Windows-based server). But the more important part to notice is the 'u-confirmation-url' field. This page has a large form for you to fill in. When you submit it, what you entered is saved in the 'u-file', and then you're redirected to 'u-confirmation-url'. Don't want to give all your personal information to them? Well, just go to 'u-confirmation-url'. In this case, this was a registration page for download of the eval. Since I got tired of filling out my information all the time, I now just go to the confirmation URL and download away, bypassing the form. On a related note, if bot="SaveResults", and u-file is in the web structure (which it happens to be a lot on virtually hosted accounts), you're able to view the contents of the file. For instance, <!--webbot bot="SaveResults" u-file="/_private/download.log" s-format="TEXT/TEXT" s-form-fields startspan --> means you can go to htp://site/_private/download.log and view all the info everyone else entered. ----[ IIS 3.0 to IIS 4.0 There are several changes between IIS 3.0 and IIS 4.0. Sure, MMC is important and all, but there's something else even better: there are default associations made between certain file extensions and .DLLs. Let's look at a particular example... In IIS 3.0, you'd administer the website by going to http://site/iisadmin/, which would pop over to using /scripts/iisadmin/ism.dll, and routing the various .HTR files in that directory through itself. The .HTR files are relatively useless without ism.dll to process them, and ism.dll has hard-coded authentication built into it. Now, upgrade from IIS 3.0 to 4.0. You now administer your site through http://localhost:5416/. What about all those .HTRs in /scripts/iisadmin? They're still there, unless you actually deleted them. And the problem? IIS 4.0 associates all .HTRs with a new and improved ism.dll, which contains no hard-coded authentication. So now, whenever you request a .HTR file, IIS will happily process it for you, not caring about authentication. You can now use the .HTR files in /scripts/iisadmin to your liking. Kinda. None of them work, due to so many changes. EXCEPT FOR ONE: bdir.htr. bdir.htr seems to still be happy, and gladly shows you all the directories on any drive. You can navigate all the server's drives (and network mappings), but all you get to see is directories (no files). In case you're wondering, you can tell bdir.htr where to look by doing /scripts/iisadmin/bdir.htr??<path> ie: /scripts/iisadmin/bdir.htr??d:\webs\ I haven't played with the other file extensions, but there's a half-dozen or so that IIS will now happily process (the normal ones like .ASP, .IDC, .HTR, and other unfamiliar ones like .HTW, .IDQ, .IDA, .CER, etc). ----[ Sample pages While it's not a good idea to put included sample pages and applications on a public server, still many places do. IIS 4.0 includes a rather large and comprehensive demo site called 'Exploration Air', which employs many IIS 4.0 web technologies. An interesting feature is the 'How It Works' button on the bottom of every page, which takes you to a script that parses the pages code into colorful tags. This is a problem. It uses the Scripting.FileSystemObject to request the page. Luckily, it will only let you use virtual paths; unfortunately, it allows the use of /../ to escape to higher directories, including up into the root directory. This allows it to open any file on the same drive. Using the .IDC bug above to determine where the file rests, you can determine if you can get to WinNT system files. You can also view the code of any page application (.ASP, .CFM, .IDC, etc). For example: http://site/iissamples/exair/howitworks/codebrws.asp?source=/../../boot.ini could show the Windows NT boot.ini file. It's used in the ExAir sample site, as shown above, and also the SDK, if installed, at http://site/iissamples/sdk/asp/docs/codebrws.asp ----[ Cold Fusion app.server 3.1 Cold Fusion is a rather creative scripting language; it's a nice front end to ODBC database connections. But I wouldn't be mentioning it here if it didn't have any problems. Like IIS 4.0, there's a few alarming things with the sample pages included with CF. One is the Expression Evaluator at: http://site/cfdocs/expeval/eval.cfm They have a security check. It calls check_ip.cfm, which allows access only from 127.0.0.1 (localhost). Bummer, we can't run raw code on the server. But, let's check out: http://site/cfdocs/expeval/exprcalc.cfm It still doesn't do us any good, because it still uses eval.cfm to process the expression(s) we enter. But, there's something more interesting: the expression calculator lets us save and load files of expressions to evaluate. And it just so happens that exprcalc.cfm is the form used to LOAD files. And it let's us load any file we want. For instance: http://site/cfdocs/expeval/exprcalc.cfm?OpenFilePath=c:\boot.ini will display the contents of boot.ini in the window. Just like the IIS codebrws.asp program, we can use it to look at any file we want. However, exprcalc.cfm lets us specify other drive letters, while codebrws.asp is limited to only the current drive. ----[ Anonymous Mail Very simply and quickly, /cfdocs/expeval/sendmail.cfm?MailFrom=&MailTo=&Subject=&Message= lets you send email. Not exactly a security breach, but not pleasant either. You must fill in the variable values. ----[ Proxy Problems This is an interesting problem brought about not only by CF, but possibly proxy software in general. CF includes an 'http client' application in /cfdocs/examples/httpclient/mainframeset.cfm which lets you type in an URL, and it will show you the HTML code in the bottom window. Now, let's say, remotely I try to administer the IIS 4.0 server that CF is running on by going to http://site:5416/. I get an error stating I have to be local (127.0.0.1). Now, I go to the http-client CF application on that same server. For the URL, I type "http://localhost:5416". I get the correct page as the result. I have effectively bypassed the security check. Using GET commands in the CF http-client application, I can administrate the server. What's really interesting in theory is that applications like this, and proxys in general, can be used to abuse trust relationships and 'localhost only' security. It'd be interesting in hearing what other people find along this line. One example: I surf to a company's firewall/web proxy from the 'outside'. I get an error stating 'Denied/Unauthorized Access'. I then request from their proxy 'GET http://localhost/'; and now I get the 'inside' web page with instructions on how to use the proxy correctly to get out. Yes, there's obvious setup problems (allowing outside requests), but that's not the point... ----[ ODBC and MS SQL server 6.5 Ok, topic change again. Since we've hit on web service and database stuff, let's roll with it. Onto ODBC and MS SQL server 6.5. I worked with a fellow WT'er on this problem. He did the good thing and told Microsoft, and their answer was, well, hilarious. According to them, what you're about to read is not a problem, so don't worry about doing anything to stop it. - WHAT'S THE PROBLEM? MS SQL server allows batch commands. - WHAT'S THAT MEAN? I can do something like: SELECT * FROM table WHERE x=1 SELECT * FROM table WHERE y=5 Exactly like that, and it'll work. It will return two record sets, with each set containing the results of the individual SELECT. - WHAT'S THAT REALLY MEAN? People can possibly piggyback SQL commands into your statements. Let's say you have: SELECT * FROM table WHERE x=%%criteria from webpage user%% Now, what if %%criteria from webpage user%% was equal to: SELECT * FROM sysobjects It would translate to: SELECT * FROM table WHERE x=1 SELECT * FROM sysobjects which would be valid SQL and execute (both commands). But wait, there's more. Say you had: SELECT * FROM table WHERE x=%%criteria%% AND y=5 If we used our above example, we'd get: SELECT * FROM table WHERE x=1 SELECT * FROM sysobjects AND y=5 which isn't valid SQL, and won't work. Well, there's a comment indicator, which tells MS SQL server to just ignore the rest of the line. If criteria is "1 SELECT * FROM sysobjects --", then the '--' causes the rest of the statement ("AND y=5") to be ignored. - WHAT FILES OF MINE ARE AFFECTED? Well, ASP and IDC files are problematic. At least you can fix ASP files, but you're kinda stuck when it comes to IDCs. - EXACTLY HOW ARE IDCs AFFECTED? Say we wanted to query a database of names=phone #s, where the user gives us a name, and we supply all the matching phone numbers. A Sql call like SELECT * FROM phonetable WHERE NAME='namewewant' would work. However, we need to dynamically specify "namewewant" to be the name the user does want. So, if we write the Sql statement: SELECT * FROM phonetable WHERE NAME='%name%' And in our HTML form, we have an input box called 'name'. If this .idc was called 'phone.idc', we'd call it: http://site/phone.idc?name=rfp The server would place "rfp" in place of %name%, and query the SQL server to select * where name='rfp'. Now, stick more commands on the line. Executing our phone.idc from above like so: phone.idc?name=rfp select * from table2 would lead to an expanded Sql query in the .idc to SELECT * FROM phonetable WHERE name='rfp select * from table2' Semi-close, but the single quotes cause all of the stuff to be the selection criteria. What if we introduced OUR OWN single quote? phone.idc?name=rfp' select * from table2 -- would be SELECT * FROM phonetable WHERE name='rfp' select * from table2 --' We need to add the comment to get rid of the trailing single quote. BUT... .idc's are smart...they will escape a single quote into two single quotes, which indicate a data single quote. I.e. phone.idc?name=rfp' command will become SELECT * FROM phonetable WHERE name='rfp'' command' And since two '' make one data ', the table will be queried for a column that matches: "rfp' command" Now wait, if .idc's protect against this, then why the hell am I wasting my breath? You see, they're still vulnerable. They suck when they secretly put an extra single quote into the SQL string. But....when you query numeric values, you don't use single quotes; single quotes are only for strings. So, lets's say we want to use our phone number database, but give a phone number, and look up the associated name. We'll also say that phone numbers are stored as long ints (numeric values), rather than strings, since we need a numeric entry for this example. So, I want to know who has the phone number 5551212. A hardcoded SQL call would be SELECT * FROM phonetable WHERE phone=5551212 And the variable version (in an .idc): SELECT * FROM phonetable WHERE phone=%phonenum% Whoa! No single quotes to worry about. Now we just do a simple: phone.idc?phonenum=5551212 select * from table1 And that expands to SELECT * FROM phonetable WHERE phone=5551212 select * from table1 - ARE THERE ANY .IDCs SOMEONE COULD USE AGAINST ME? Glad you asked. There's a file included with IIS 3.0 in the /scripts/tools directory, called ctss.idc, which has a SQL statement like: CREATE TABLE %table% (...table defs...) This is simple to exploit. Since you stuck with the inital 'CREATE TABLE', you must finish that to be a valid command. Giving a table name and a simple column definition will be sufficient. And then we tack on our command, and then a '--' to ignore the rest of the table defs. So, ctss.idc?table=craptable (f int) select * from table1 -- Would give us CREATE TABLE craptable (f int) select * from table1 -- \ (...table defs...) (However, with ctss.idc, you need to know the DSN, UID, and PWD beforehand... so you're somewhat safe) - EXACTLY HOW ARE ASPs AFFECTED? Typical ADODB code looks something like: <% SQLquery="SELECT * FROM phonetable" Set Conn = Server.CreateObject("ADODB.Connection") Conn.Open "DSN=websql;UID=sa;PWD=pwd;DATABASE=master" Set rec = Server.CreateObject("ADODB.RecordSet") rec.ActiveConnection=Conn rec.Open SQLquery %> Which essentially performs a SELECT * FROM phonetable on the websql DSN, using user=sa, pwd=pwd, on database=master. Then you use fancy formating of 'rec' to display the output in ASP. Well, let's take into account user supplied variables now. <% SQLquery="SELECT * FROM phonetable WHERE name='" & _ request.querystring("name") & "'" Set Conn = Server.CreateObject("ADODB.Connection") Conn.Open "DSN=websql;UID=sa;PWD=pwd;DATABASE=master" Set rec = Server.CreateObject("ADODB.RecordSet") rec.ActiveConnection=Conn rec.Open SQLquery %> So, now our variable "name" is stuck into the SQLquery string, between the two ' '. Guess what?! ASP doesn't care about single quotes. It won't be smart like an .IDC and put in the extra ' to make the command ' into a data '. So, what does the SQLquery string look like when we call it like phone.idc? Let's say the above is phone.asp: phone.asp?name=rfp' select * from table1 -- Gives us SQLquery that is: SELECT * FROM phonetable WHERE name='rfp' select * from table1 --' Which works. No sweat. I'm sure some interesting questions come to mind: - BUT I DON'T KNOW THE DSN NAME, LOGIN NAME, OR PASSWORD! You don't need them. The developer of the page that contains the SQL will already take care of that. We're piggy-backing SQL commands onto a command that will work (otherwise, the page/application wouldn't work normally anyway!). If the normal page can get to the SQL server through a firewall, VPN, etc, then so can this command. It can, and will, go wherever the normal pages/SQL can go. - BUT I CAN'T VIEW THE SECOND RETURNED RECORDSET! Yes, this is a problem most of the time. Not too many applications are built assuming multiple recordset returns, so usually don't cooperate. But, let me just say there's a stored procedure in SQL that lets you email results of a command to anywhere....you don't need to see the results in your web browser. - BUT WHAT GOOD IS RUNNING MORE SQL COMMANDS? My friend, my friend. Think bigger. Think better. Think stored procedures. I'm not going to include exploit examples, because that's not what this is about. This is simply to show that the problem exists. - BUT WHAT IF THEY HAVE COMPLEX SQL COMMANDS? Yes, this can be tricky, but it's still possible. Think of it like writing a buffer overflow. ;-) If we have: SELECT * FROM table WHERE ((x=%%criteria) AND (y=5)) then we have parentheses to deal with. But still doable. The goal is to close out any open parentheses opened before the piggybacked SQL statement, and use -- (comment) to ignore anything after. - HOW CAN I PROTECT MYSELF? Put quotes around every string taken from the web user that's used in your SQL statement, and also change any single quotes (') into double single quotes ('')--this protects everything. In case of numeric criteria, check to see that the numeric string given back is, in fact, all numbers. And since you can't do any of the above in IDCs, switch to ASP. Don't allow access to any of the SQL servers extended procedures. Best of all, don't use raw SQL in your web applications; called custom stored procedures on the SQL server, and pass the web user's dynamic criteria as parameters. Note: we've only had the time (and resources) to conduct batch SQL vulnerabilities against MS SQL server 6.5. We'd be interested in hearing from other people if other DB platforms (Oracle, Informix, etc) are also vulnerable. ----[ Conclusion Well, that about wraps it up for now. What are the morals to the above stories? - Don't use sample files/applications on public/production servers. - Don't use 'local-host only' security, especially on proxys. - Watch what exactly is changed when you upgrade. - Don't assume user's input is ok for SQL queries. In short, use your brain. Till next time, have fun. rain.forest.puppy / [WT] rfpuppy@iname.com ----[ EOF