ArctosDB Oracle SQL Injection & Harvard

Timothy French, Dominik Penner and ItsNux


Discovery:

While searching Google for vulnerabilities in universities to report, I came across a GET parameter in a web-application labeled ArctosDB at Harvard. The parameter was:


mczbase.mcz.harvard.edu/SpecimenResults.cfm?publication_id=1.

Injecting a ' in the parameter resulted in an Oracle SQL error.

From here I attempted to query the database but found myself blacklisted almost instantly, so I referred to my colleagues in hopes we could find a way to bypass this filter in order to execute queries against the database. We were initially unable to figure out an applicable methodology, but ItsNux located a form on another page of the web-application that reacted in the same way.

Exploitation:

Looking at the code, we found that best practices were not being applied in terms of securing against input validation attacks beyond their filter. The vulnerable code is below:


------------------- arctos/SpecimenDetail.cfm -------------------
<cfif isdefined("guid")>
<cfif cgi.script_name contains "/SpecimenDetail.cfm">
<cfheader statuscode="301" statustext="Moved permanently">
<cfheader name="Location" value="/guid/#guid#">
<cfabort>
</cfif>
<cfset checkSql(guid)>
<cfif guid contains ":">
<cfoutput>
         ------------THIS IS WHERE THE VULNERABILITY IS------------
<cfset sql="select #session.flatTableName#.collection_object_id from
                     #session.flatTableName#,cataloged_item
                 WHERE
                     #session.flatTableName#.collection_object_id=cataloged_item.collection_object_id and
                     upper(#session.flatTableName#.guid)='#ucase(guid)#'">
         ----------------------------------------------------------
<cfset checkSql(sql)>
<cfquery name="c" datasource="user_login" username="#session.dbuser#" password="#decrypt(session.epw,session.sessionKey)#">
                 #preservesinglequotes(sql)#
</cfquery>
</cfoutput>
</cfif>
<cfif isdefined("c.collection_object_id") and len(c.collection_object_id) gt 0>
<cfset collection_object_id=c.collection_object_id>
<cfelse>
<cfinclude template="/errors/404.cfm">
<cfabort>
</cfif>
<cfelse>
<cfinclude template="/errors/404.cfm">
<cfabort>
</cfif>
 -------------------------------------------------------------------

At this point, it was plugged into SQLMap & the injection was confirmed, so the payload was taken from the logs & passed to the rest of us. It took time to determine what was going on because when attempting to inject manually, we were still being blacklisted.

After more searching, Dominik located a comment in the web-applications GitHub page that explained the reasoning behind the blacklisting & allowed us to find a solution to the filtering system. The following explanation of the solution by the GitHub account GoogleCodeExporter goes in further detail:


SQL Injection bots like to probe Arctos. I don't like that. Currently, SpecimenSearch filters for 0select characters and displays an error if they are found. This is not a portable solution - implement something reusable and tunable.

There is a list of characters that are filtered for, and if one is found in user supplied input then the IP Address associated with that query is blacklisted. We found the following portion of code on the GitHub page:


<!---- various attempts at SQL injection ---->
<cfif isdefined("request.rdurl") and (
request.rdurl contains "' and 'x'='x" or
request.rdurl contains "%27%20and%20%27x%27%3D%27x" or
request.rdurl contains "%22%20and%20%22x%22%3D%22x"
)>
<cfset bl_reason="URL contains 'x'='x">
<cfinclude template="/errors/autoblacklist.cfm">
<cfabort>
</cfif>

<cfif isdefined("request.rdurl") and request.rdurl contains "'A=0">
<cfset bl_reason="URL contains 'A=0">
<cfinclude template="/errors/autoblacklist.cfm">
<cfabort>
</cfif>
<!--- check these every time, even if there's no error; these things are NEVER allowed in a URL ---->
<cfset x="script,write">
<cfloop list="#lurl#" delimiters="#chr(7)#" index="i">
<cfif listfindnocase(x,i)>
<cfset bl_reason='URL contains #i#'>
<cfinclude template="/errors/autoblacklist.cfm">
<cfabort>
</cfif>
</cfloop>
<!----- END: stuff in this block is always checked; this is called at onRequestStart ------>
<!-----
START: stuff in this block is only checked if there's an error
Performance is unimportant here; this is going to end with an error
------>
<cfif isdefined("inp")>
<cfif len(lurl) gt 0>
<!----
<cfif lurl contains "utl_inaddr" or lurl contains "get_host_address">
<cfset bl_reason='URL contains utl_inaddr or get_host_address'>
<cfinclude template="/errors/autoblacklist.cfm">
<cfabort>
</cfif>
<cfif request.rdurl contains "#chr(96)##chr(195)##chr(136)##chr(197)#">
<cfset bl_reason='URL contains #chr(96)##chr(195)##chr(136)##chr(197)#'>
<cfinclude template="/errors/autoblacklist.cfm">
<cfabort>
</cfif>
----->
<!---- random junk that in combination with an error is always indicitive of bot/spam/probe/etc. traffic---->
<cfset x="">
<cfset x=x & ",@@version,#chr(96)##chr(195)##chr(136)##chr(197)#,'A=0,/)">
<cfset x=x & ",1phpmyadmin,2phpmyadmin,3phpmyadmin,4phpmyadmin">
<cfset x=x & ",account,administrator,admin-console,attr(,asmx,abstractapp,adimages,asp,aspx,awstats,appConf,announce,ads,ackBulletin,aupm">
<cfset x=x & ",ashx,app_debug,assets,auth,App,ASPSamp,AdvWorks,AccountService,Accounts">
<cfset x=x & ",backup,backend,backoffice,blog,board,backup-db,backup-scheduler,batch,bea_wls_deployment_internal">
<cfset x=x & ",career,char,chr,ctxsys,CHANGELOG,content,cms,checkupdate,colorpicker,comment,comments,connectors,cgi,cgi-bin,cgi-sys">
<cfset x=x & ",calendar,config,client,cube,cursor,COLUMN_NAME,CHECKSUM,CHARACTER_MAXIMUM_LENGTH,create,check_proxy,cfide,cfgmaker,cfg">
<cfset x=x & ",catalog,cart,CoordinatorPortType,chat,cpanel,cf_scripts,COMMIT_EDITMSG,console,CHANGELOG,com_sun_web_ui,cfdocs">
<cfset x=x & ",classLoader,cacheObjectMaxSize">
<cfset x=x & ",drithsx,dbg,dbadmin,declare,DB_NAME,databases,displayAbstract,db_backup,do,downloader,DEADBEEF,deployment-config,dbm">
<cfset x=x & ",etc,environ,exe,editor,ehcp,employee,entries,elfinder,erpfilemanager,equipment">
<cfset x=x & ",fulltext,feed,feeds,filemanager,fckeditor,FileZilla,fetch,FETCH_STATUS,ftpconfig,flex2gateway">
<cfset x=x & ",getmappingxpath,get_host_address,git,globalHandler,git,.git">
<cfset x=x & ",html(,HNAP1,htdocs,horde,HovercardLauncher,HelloWorld,has_dbaccess,hana,hooks,heads">
<cfset x=x & ",inurl,invoker,ini,into,INFORMATION_SCHEMA,iefixes,id_rsa,id_dsa">
<cfset x=x & ",jbossws,jbossmq-httpil,jspa,jiraHNAP1,jsp,jmx-console,journals,JBoss,jira,jkstatus,joomla,jsf">
<cfset x=x & ",lib,lightbox,local-bin,LoginForm,localization,logs,logon">
<cfset x=x & ",master,mpx,mysql,mysql2,mydbs,manager,myadmin,muieblackcat,mail,magento_version,manifests,market,mrtg,modules,mychat">
<cfset x=x & ",news,nyet,newdsn">
<cfset x=x & ",ord_dicom,ordsys,owssvr,ol,objects,owa,openshift,onrequestend">
<cfset x=x & ",php,phppath,phpMyAdmin,PHPADMIN,phpldapadmin,phpMyAdminLive,_phpMyAdminLive,printenv,proc,plugins,passwd,pma2,pmc">
<cfset x=x & ",pma4,php5,pre-receive">
<cfset x=x & ",pma,phppgadmin,prescription,phpmychat,pre-push">
<cfset x=x & ",rand,reviews,rutorrent,rss,roundcubemail,roundcube,README,railo-context,railo,Rapid7,register,remote_support,remote_tunnel">
<cfset x=x & ",remote-sync,regex,register,rar,refs,receive,remotes">
<cfset x=x & ",sys,swf,server-status,stories,setup,sign_up,system,signup,scripts,sqladm,soapCaller,simple-backup,sedlex,sysindexes">
<cfset x=x & ",sftp-config,store,shop,server_info">
<cfset x=x & ",sysobjects,svn,sap,ssh,stash">
<cfset x=x & ",servlet,spiffymcgee,server-info,sparql,sysobjects,sample">
<cfset x=x & ",trackback,texteditor,tar">
<cfset x=x & ",utl_inaddr,uploadify,userfiles,updates,update,UserFollowResource">
<cfset x=x & ",verify-tldnotify,version,varien,viagra,vscode,views">
<cfset x=x & ",wiki,wp-admin,wp,webcalendar,webcal,webdav,w00tw00t,webmail,wp-content,wdisp,wooebay,wlwmanifest,webfig,wordpress">
<cfset x=x & ",YandexImages">
<cfset x=x & ",zboard">

Once we saw this, we began picking it apart. The parameter on Harvard was in https://mczbase.mcz.harvard.edu/showLocality.cfm on the Higher Geog form. This was the POST request:


action=srch&higher_geog=test&continent_ocean=&ocean_region=&ocean_subregion
 =&sea=&island=&island_group=&feature=&water_feature=&country=&state_prov=&c
 ounty=&quad=&geog_auth_rec_id=&spec_locality=&collnOper=&collection_id=&Max
 DepthOper=%3D