Trillex - programming in perl with cgi and dbi

From Teknologisk videncenter
Jump to: navigation, search

Programming: Perl with Webinterface and Database Injections

Introduction

As a final project in Perl, I decided to make a script that could be useful as a linux adminstrator. The script is very situational, however, since it makes use of a specific setup.

The idea of the script can be changed and used for many situation, especially when you just need to inject simple things into a table. It certainly beats having to do it manually through that cursed client or through other means like phpmyadmin etc.

What the script does, is add a new user to the database "pureftpd" in the table "ftpd". This is in a MySQL database. The only situation you'd need to do this, is when you have set your FTP daemon up for virtual users, i.e. the user accessing the FTP does not have access to the server itself through SSH, like it would normally if we create another user on most UNIX systems.

The Script

#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use DBI;
use CGI::Carp qw(fatalsToBrowser);

print CGI::header();

my $username = fixtext(CGI::param('user'));
my $status = fixtext(CGI::param('status'));
my $password = fixtext(CGI::param('password'));
my $uid = fixtext(CGI::param('uid'));
my $gid = fixtext(CGI::param('gid'));
my $dir = fixtext(CGI::param('dir'));
my $upload = fixtext(CGI::param('upload'));
my $download = fixtext(CGI::param('download'));
my $comment = fixtext(CGI::param('comment'));
my $ipaccess = fixtext(CGI::param('ipaccess'));
my $quotasize = fixtext(CGI::param('quotasize'));
my $quotafiles = fixtext(CGI::param('quotafiles'));

unless($username) {
print <<PAGE;  
<h1>Add a FTP account</h1>  
<form action=testadddatabase.pl method=post>  
Username: <input type=text name=user><br>  
Status: <input type=text name=status><br>  
Password: <input type=text name=password><br>  
User ID: <input type=text name=uid><br>  
Group ID: <input type=text name=gid><br>  
Home Dir: <input type=text name=dir><br>  
Upload Limit: <input type=text name=upload><br>  
Download Limit: <input type=text name=download><br>  
Comment: <input type=text name=comment><br>  
IP Access: <input type=text name=ipaccess><br>  
Quota Size: <input type=text name=quotasize><br>  
Quota Files: <input type=text name=quotafiles><br>  
<input type=submit value="Add an FTP account">  
</form>  
PAGE

exit;
}

my $dbh = DBI->connect("dbi:mysql:pureftpd:localhost", "user", "password");

my $sth = $dbh->prepare("insert into ftpd(User, status, Password, Uid, Gid, Dir, ULBandwidth, DLBandwidth, comment, ipaccess, QuotaSize, Quotafiles) values('$username', '$status', '$password', '$uid', '$gid', '$dir', '$upload', '$download', '$comment', '$ipaccess', '$quotasize', '$quotafiles')");

$sth->execute();

print <<PAGE;
<h1>Account added</h1>
The account $username was just added. want to <a href=testadddatabase.pl>add another</a>? 
PAGE

sub fixtext {
        my ($str) = @_;

$str =~ s/”/\\”/g;
$str =~ s/\\/\\\\/g;
$str =~ s/’/\\’/g;

 return $str;
}

Breaking it Down

I won't use comments (#) since I will not be able to explain enough in it. I will, hopefully, explain everything here so a newbie can look at it and understand it.

#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use DBI;
use CGI::Carp qw(fatalsToBrowser);

Essentially this is the start of the script. It tells it what kind of "modules" to make use of. These modules can contain certain functions or be able to give you more information on errors etc.

#!/usr/bin/perl

What this does is explain that this is now a perl script and it needs to be compiled at /usr/bin/perl. This is, obviously, different on Windows machines and can differ from linux distro.

use strict;

Strict makes it a bit more "strict" and pretty much slaps you, if you are about to do something nasty in your script that could create a mess. An example is that without strict, you don't have to define variables first but can just write it out. This can cause issues with typos and such, which could lead to strange bugs that will be impossible, or very hard, to find. It's good for beginners.

use warnings;

Does pretty much what it says. It gives you detailed information if it sees an error in your script when you run it, and outputs it to you. In some situations, it will even tell you exactly what is wrong. Always a good idea to use this, at least for debugging purposes.

use CGI;

This loads a module for use in CGI, Common Gateway Interface. This is usually used when you want to make web development with perl. It's what making this script work

use DBI;

Stands for DataBase Interface. Essentially it's the same thing as the CGI, except that it is for database use instead. This can connect, change, view, create databases of various kinds, like SQL, Oracle and more. A very powerful tool.

use CGI::Carp qw(fatalsToBrowser);

You might have noticed that we have two calls for CGI. This one, however, only calls the functions for Carp with fatalsToBrowser. So to make sure that there is absolutely no chance of messing up, I included a call for normal CGI and then just this one.

print CGI::header();

What this does, is to define the Content-type in the html code it is going to create through CGI. It essentially makes the script a webpage by defining this.

my $username = fixtext(CGI::param('user'));
my $status = fixtext(CGI::param('status'));
my $password = fixtext(CGI::param('password'));
my $uid = fixtext(CGI::param('uid'));
my $gid = fixtext(CGI::param('gid'));
my $dir = fixtext(CGI::param('dir'));
my $upload = fixtext(CGI::param('upload'));
my $download = fixtext(CGI::param('download'));
my $comment = fixtext(CGI::param('comment'));
my $ipaccess = fixtext(CGI::param('ipaccess'));
my $quotasize = fixtext(CGI::param('quotasize'));
my $quotafiles = fixtext(CGI::param('quotafiles'));

What this does is define our variables. 'my' "creates" them and the dollar sign ($) defines that it's a variable that will only hold one value. In perl, it is called a scalar. The equal shows it what kind of variable it should be defined with. fixtext is a subroutine, which is explained later. CGI::param'value' will get filled out later on with a value of some kind, through the script. Notice that the "params", the parameters, all have different names. This is what is used in the HTML page. Make sure these add up.

unless($username) {
print <<PAGE;  
<h1>Add a FTP account</h1>  
<form action=testadddatabase.pl method=post>  
Username: <input type=text name=user><br>  
Status: <input type=text name=status><br>  
Password: <input type=text name=password><br>  
User ID: <input type=text name=uid><br>  
Group ID: <input type=text name=gid><br>  
Home Dir: <input type=text name=dir><br>  
Upload Limit: <input type=text name=upload><br>  
Download Limit: <input type=text name=download><br>  
Comment: <input type=text name=comment><br>  
IP Access: <input type=text name=ipaccess><br>  
Quota Size: <input type=text name=quotasize><br>  
Quota Files: <input type=text name=quotafiles><br>  
<input type=submit value="Add an FTP account">  
</form>  
PAGE

exit;
}

This creates a website inside the script you are running. This makes it so you don't have to have a HTML page that links to this form. It essentially makes a frontend to the script.

unless($username) {

This checks what "value" is being passed about. If it already had some information (i.e. the form has already been used), it won't call forth the form. If there is nothing, it will. The "{" starts the "form script" itself.

print <<PAGE;  
<h1>Add a FTP account</h1>  
<form action=testadddatabase.pl method=post>  
Username: <input type=text name=user><br>  
Status: <input type=text name=status><br>  
Password: <input type=text name=password><br>  
User ID: <input type=text name=uid><br>  
Group ID: <input type=text name=gid><br>  
Home Dir: <input type=text name=dir><br>  
Upload Limit: <input type=text name=upload><br>  
Download Limit: <input type=text name=download><br>  
Comment: <input type=text name=comment><br>  
IP Access: <input type=text name=ipaccess><br>  
Quota Size: <input type=text name=quotasize><br>  
Quota Files: <input type=text name=quotafiles><br>  
<input type=submit value="Add an FTP account">  
</form>  
PAGE

exit;
}

This creates a webpage inside the script. As you can see, it is just basic HTML code, which points to the script itself so it can run what input the form gives us. Notice that the "name=" refers to something I showed earlier. Exit stops it.

my $dbh = DBI->connect("dbi:mysql:pureftpd:localhost", "user", "password");

This defines the variable $dbh. It calls the connect function from DBI module. Then makes use of the input in the parenthesis. It goes like this: "DBI:what kind of database:the database's name:host where the database is". User and password is self explanatory.

my $sth = $dbh->prepare("insert into ftpd(User, status, Password, Uid, Gid, Dir, ULBandwidth, DLBandwidth, comment, ipaccess, QuotaSize, Quotafiles) values('$username', '$status', '$password', '$uid', '$gid', '$dir', '$upload', '$download', '$comment', '$ipaccess', '$quotasize', '$quotafiles')");

$sth->execute();

Defines the variable $sth and then calls $dbh with the added function prepare. This inserts the values from the various variables defined earlier in the script. It inserts it into the table "ftpd".

It then executes all of it. Doing it this way, gives it a better, more readable output to put into the database.

print <<PAGE;
<h1>Account added</h1>
The account $username was just added. want to <a href=testadddatabase.pl>add another</a>? 
PAGE

When the previous have been executed, this creates another website that links to the original script if you want to create another user account.

sub fixtext {
        my ($str) = @_;

$str =~ s/”/\\”/g;
$str =~ s/\\/\\\\/g;
$str =~ s/’/\\’/g;

 return $str;
}

This creates a subroutine to a function encountered earlier. What it does, is make sure that there won't be any syntax errors through a simple method of regular expressions (regex). If this wasn't added and someone put in an odd symbol, like " \ / or ', it could wreck the script and add odd things to the database. With this, it adds another \, indicating that this is still a text and not an essential command.

Conclusion

This is a basic script but is very powerful, because it simplifies an action that could have taken several minutes. Now it just takes seconds. Imagine what you could do with more advanced scripts.