CGI Upload Script in Perl [duplicate] - perl

This question already has answers here:
Software error while executing CGI script
(2 answers)
Closed 8 years ago.
I have a cgi script for upload which is as follows
#!/usr/bin/perl
use CGI;
use CGI::Carp qw(fatalsToBrowser);
my $cgi = new CGI;
my $file = $cgi->param('file');
$file=~m/^.*(\\|\/)(.*)/; # strip the remote path and keep the filename
my $name = $2;
open(LOCAL, ">/home/Desktop/$name") or die $!;
while(<$file>) {
$data .= $_;
}
print $cgi->header();
print "$file has been successfully uploaded... thank you.\n";
print $data;
The HTML file is as follows
<html>
<head>
<title>Test</title>
</head>
<body>
<form enctype="multipart/form-data" action="upload.cgi" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="30000" />
Send this file: <input name="userfile" type="file" />
<input type="submit" value="Send File" />
</form>
</body>
</html>
I am getting a weird error now..
Software error:
Is a directory at htdocs/upload.cgi line 9.
For help, please send mail to this site's webmaster, giving this error message and the time and date of the error.

It is likely the case that the path specified in the open
open(LOCAL, ">/home/Desktop/$name") or die $!;
is pointing to a directory. This may be because $name is empty (and thus /home/Desktop/ is a directory) or the target name is a directory underneath the Desktop.
I'd have to think about it more, and try to be nefarious, but I'm fairly sure that there are ways of specifying paths outside the path that you are intending allowing someone who is being nefarious to upload files in places you don't expect them to be.

Related

File upload using a Perl CGI script not working

The following code doesn't have any syntax errors, but still doesn't working.Can I use server ip(like 100.100.100.100) for $Domain and what path should be given for $directory(i mean adding the serverip or domain name?Please help
#!/usr/bin/perl
use CGI;
$CGI::POST_MAX= 100 * 1024;
$CGI::DISABLE_UPLOADS=0;
$Referer = $ENV{HTTP_REFERER};
$Domain = "xxx.com";
$cgi = new CGI;
$file=$cgi->upload('text');
print $cgi->header,
$cgi->start_html
(
-title=>'CGI.pm File Upload'
);
print <<EOF;
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="text" size=60><br>
<input type="submit" value="Upload">
</form>
EOF
if($file)
{
if($Referer =~ "$Domain")
{
$directory="var/www/cgi-bin/uploads";
open UPLOAD, ">$directory$file";
binmode UPLOAD;
while(<$file>) {print UPLOAD;}
close UPLOAD;
}
}
$cgi->end_html;
exit;
Looks like you need to read the documentation on file upload basics again. The sample code they have is:
use autodie;
# undef may be returned if it's not a valid file handle
if ( my $io_handle = $q->upload('field_name') ) {
open ( my $out_file,'>>','/usr/local/web/users/feedback' );
while ( my $bytesread = $io_handle->read($buffer,1024) ) {
print $out_file $buffer;
}
}
There are some stylistic differences to your code, but the important thing to note that is that when your code runs this line:
$file=$cgi->upload('text');
Then $file contains an open filehandle. It does not contain the filename. This means that there are at least three errors in these lines of your code:
$directory="var/www/cgi-bin/uploads";
open UPLOAD, ">$directory$file";
The value you store in $directory should almost certainly start with a / (so it's /var/www/cgi-bin/uploads).
You also need another / between $directory and $file (otherwise, it will contain something like /var/www/cgi-bin/uploadsmyfile.dat).
You need to call $cgi->param('text') to get the name of the file that is being uploaded.
This is what is stopping your program from working. The upload section of your code should look like this:
my $filename = $cgi->param('text');
my $fh = $cgi->upload('text');
my $directory = '/var/www/cgi-bin/uploads';
open my $upload_fh, '>', "$directory/$filename"
or die "Can't open '$directory/$filename': $!";
print $upload_fh $_ while <$fh>;
Note that I've made some stylistic improvements here:
Used 3-argument version of open()
Used lexical filehandles
Checked the success of the open() call and killed the program with a useful error message if it fails
All in all, you seem to have learned CGI programming from a resource that is about twenty years out of date. Your code looks like it comes from the 1990s.
A few other tips:
Always use strict and use warnings.
Indirect object notation (new CGI) is potentially very confusing. Use CGI->new instead.
We've known that the HTML-generation functions in CGI.pm are a terrible idea since the end of the last millennium. Please don't use them. Many good templating solutions are available for Perl.
Writing a CGI program in 2017 is a terrible idea. Take a look at CGI::Alternatives for an introduction to Modern Perl Web Development tools.

Can't find string terminator HERE anywhere before EOF [duplicate]

This question already has answers here:
Can't find string terminator "str" anywhere before EOF
(3 answers)
Closed 6 years ago.
I am having trouble getting this script to work, It seems to work fine locally but not on the server for 1 and 1.
The error I am getting is:
Can't find string terminator HERE anywhere before EOF
I also seem to be getting this error:
255 perl returned nonzero status
Status: 500
Content-type: text/html**
I am new to CGI, but I could not find anything online about this particular issue.
Here is the HTML:
<div class="form"><h1>Free Quote</h1>
<form action="/quote.cgi">
First Name: <input type="text" name="fName" placeholder="John"> <br>
Last Name: <input type="text" name="lName" placeholder="Smith"><br>
<br>
Company: <input type="text" name="company" placeholder="Mr Kittles"><br>
Phone Number: <input type="text" name="number" placeholder="xxx-xxx-xxx"><br>
E-Mail: <input type="text" name="email" placeholder="Something#gmail.com"><br><br>
Tell us what you you are looking for:<br><br><textarea name="message" rows="10" cols="35" placeholder="Tell us all about your website. Include type of business, colors, current web address if possible, and anything else you think is relevant for us to do some research before we call you back."></textarea><br><br>
<input type="submit" value="Submit">
</form>
</div>
And here is the CGI
#! /usr/bin/perl
use CGI qw(:standard);
use CGI::Carp qw(fatalsToBrowser);
use strict;
use warnings;
print "Content-type: text/html\n\n";
my $fName = param('fName');
my $lName = param('lName');
my $company = param('company');
my $number = param('number');
my $email = param('email');
print <<HERE;
<HTML>
<BODY>
<H3>Item has been registered and added to inventory.txt</H3>
HERE
open (INVENTORY, ">>tet.txt") or die "File error, $!";
print INVENTORY "$fName|$lName|$company|$number|$email\n";
close INVENTORY;
Your cgi file is not compiling because you have some trailing whitespace after the here-doc closing string HERE.
According to perldoc perlop #Quote-and-Quote-like-Operators in the <<EOF section:
The terminating string must appear by itself (unquoted and with no surrounding whitespace) on the terminating line.
To fix, just remove the extra whitespace characters.

Perl CGI display image to browser from file

Friends,
I have been scouring the web for a solution to displaying images to a web browser with Perl and have found nothing that works for me.
I've tried possible solutions such as:
How To Display an Image with Perl
Outputting Image Data
Return an Image From a Script
and none of it works for what I'm doing. I want to deny client access to the image (Or even simply place the image file out of the www root) and dish it out server-side.
Here is an example of what I'm doing:
In my main perl file:
...
my $query = CGI->new();
sub main {
### This grabs page content from a module, depending on the page name.
### The module returns the HTML.
my $html = get_page('page', 'session');
### Perform any special conditionals here before printing the header...
print $query->header(some cookie/session data here);
print $html
}
In one of the modules:
sub return_page_content {
return <<HTML;
<html>
<body>
<img src="GET IMAGE HERE..." />
</body>
</html>
HTML
}
I've thought about just creating a copy of the image in a temp directory location, but that seems like it would defeat the entire purpose of keeping the image out of client-side access.
The probable solutions do not generate the image. I'm not sure where to go from here, so I am hoping someone here has an idea. Thank you so much!
Please let me know if I need to provide additional information. I feel like this could be beneficial to a lot of people. I hope at least ;-)
For a simple 'send the file to the browser solution', just send the browser the correct headers (to let the browser know what's coming), and then open your image and print the content to STDOUT.
select(STDOUT); $| = 1; #unbuffer STDOUT
print "Content-type: image/png\n\n";
open (IMAGE, '<', '/image_outside_webroot/image.png');
print <IMAGE>;
close IMAGE;
Once that is working, take a look at ImageMagick. There are all kinds of on-the-fly, fun image manipulations you can do (resizing, colorizing, etc.)
Your cgi script would contain code that looks something like this:
select(STDOUT); $| = 1; #unbuffer STDOUT
my $image = Image::Magick->new();
my $x = $image->Read(filename =>"/images_outside_web_root/image1.png");
#some manipulation of the image here
print "Content-type: image/png\n\n";
binmode STDOUT;
$x = $image->Write(.png:-');
You can read more about this on the site linked above.
Hope that helps.
Well, it turns out the reason this wasn't working is because I completely forgot I do not operate with CGI out of the standard cgi_bin directory. Instead, I use an .htaccess file to tell the server how and when to interpret files from the root directory as cgi.
So, this is what I ended up using in my image dishing script:
imagedish.pm
use CGI;
my $cgi = new CGI;
open (IMAGE, 'logo.png');
my $size = -s "logo.png";
my $data;
read IMAGE, $data, $size;
close (IMAGE);
print $cgi->header(-type=>'image/png'), $data;
This did the trick, as it should have been doing from the beginning, but in my .htaccess file, I added:
<files imagedish.pm>
SetHandler cgi-script
</files>
And that did the trick (Well, that, as well as going into Terminal and running chmod +x imagedish.pm to make it executable)! Of course the next steps are additional security measures, but at least it's working now! :-)
The full solution:
mainfile:
!/usr/bin/perl
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use CGI::Session qw/-ip-match/;
use DBI;
use strict;
use warnings;
# Variables
my $query = CGI->new();
my %vars = $query->Vars();
sub main {
my $p1 = $vars{p1};
$p1 = 'Home' if (!$p1);
my $html = get_page();
#I use this method in case we have multiple sessions
#I've omitted how I acquire the session, as this is not part of the solution ;-)
print $query->header(-cookie=>[$query->cookie($vars{p1}=>$session->id)]);
print $html;
}
sub get_page {
return <<HTML;
<!DOCTYPE HTML>
<html>
<head>
<title>Image Disher</title>
<link rel="shortcut icon" href="images/favicon.ico" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container">
<div class="addentry">
<div class="iaddentry">
<form name="client" action="" method="POST">
<div class="form-header" action="">
<center>
<img src="imagedish.pm" width="305" alt=""/><br>
</center>
<br>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
HTML
}
main();
In here, the img tag looks for the source "imagedish.pm", and once it finds it, the .htaccess file tells it to execute as a CGI script. At that point, it dishes out information appropriately, not like before.
Please note, this is not the most-secure way to do it, but it gets me going in the right direction.
The links you found (except the first one) describe how to do it, I think you are just getting confused with the difference between delivering an image and delivering an html file with an img tag on it. Keep in mind that when your browser is parsing an html file and it encounters an img tag, it takes the src url in the tag and makes an additional get request for it.
Try capturing the raw output from a request for an image using curl, wireshark, etc. The result is what you want to try to create. It's just a matter of returning the content type http header, followed by the binary image data.
Have another look at this example, and get rid of the random_file sub, and replace this line:
my $image = random_file( IMAGE_DIRECTORY, '\\.(png|jpg|gif)$' );
with this:
my $image = "path to an image file accessible by www-user";
Hopefully once you that working and understand what it's doing, you'll know what you need to do next.
Yet another way is to embed the image using the <img src="data:image/...,base64,..."> format.
This defeats browser caching and isn't great for very large images. But is useful if its easier to construct the image as part of the initial page load or you don't want the hassle of managing/serving them via a file system.
#!/user/bin/perl
use warnings; use strict;
use MIME::Base64 qw();
sub return_page_content {
my $image_type = shift;
my $image_data = shift;
my $image_base64 = MIME::Base64::encode($image_data);
$image_base64 =~ s{\n}{}g; # lose newlines
return <<HTML;
<html>
<body>
<img src="data:image/${image_type};base64,${image_base64}" />
</body>
</html>
HTML
}
my $image_path = "/tmp/test.jpg";
open(my $fh, '<', $image_path)
or die "unable to open file $image_path: $!";
binmode($fh);
my $image_data = do {local $/; <$fh>};
close($fh);
print return_page_content("jpeg", $image_data);

Perl CGI get multipart/form-data file contents without temp file

In perl is there a lib/package for getting the file upload data directly to a scalar without going through a 'tmp' file. My form is like:
<form method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
</form>
I could not figure out how to get the data using the CGI module without going through a temporary file. I can read the full contents on stdin, but I don't want to have to re-invent the wheel decoding multipart data.
take a look at CGI->upload function
my $fh = $cgi->upload('data');
{
# enable 'slurp' mode
local $/;
$data = <$fh>;
}

How to select parts of a line in Perl?

I have many long files but I am interested just in part of the information of each one. So far I have a code that trims the file and gives me the line that contains the information I need, working one file at the time.
This is the code I am using:
#!/usr/bin/perl
use strict;
use warnings;
my $data;
open FILE, "<$ARGV[0]" or die "cannot open file '$ARGV[0]'!\n\n";
while ($data= <FILE>){
chomp $data;
if( $data=~m/\<input type="hidden" name="description" value="454read"><input type="hidden" name="format" value="fasta"><input type="submit" name="submitbutton" value="FASTA"/)
{
$data=~s/[^ACTGN]//g;
print $data;
}
}
And this is the input I get:
<input type="hidden" name="sequence" value="TTGTTGAGCTCGACGGTCATGACCCAGCTGGAGTCGGCACGGGCACCCGCGCGCTTCTGCCAGACGCCAATGTGGGACTTCTCGGTGTCGAGGC"><input type="hidden" name="name" value="FUY784js_7HL"><input type="hidden" name="description" value="454read"><input type="hidden" name="format" value="fasta"><input type="submit" name="submitbutton" value="FASTA">
From this I only need two parts, the TTGTT....AGGC, this part will always be uppercase A,T,C,G,or N, however the length might differ in each file. I also need to save the name for this that in this case is FUY784js_7HL, this name will change every time.
The ideal output should look like this:
FUY784js_7HL
TTGTTGAGCTCGACGGTCATGACCCAGCTGGAGTCGGCACGGGCACCCGCGCGCTTCTGCCAGACGCCAATGTGGGACTTCTCGGTGTCGAGGC
Do you have any idea of how can I do it? I have many files like this. I will appreciate if any of you can help me to figure out how to get this to work for multiple files.
Thanks!
perl -pe 's/[^ACTGN]//g;'
As a proxy for the bit which appears to be problematic, the above command seems to work, at least with the input line starting with <input and the second output line.
If you don't have any other prints in your real program, I'm not sure how it could produce the line you said it did.
Actually, that was a lie. I got:
TTGTTGAGCTCGACGGTCATGACCCAGCTGGAGTCGGCACGGGCACCCGCGCGCTTCTGCCAGACGCCAATGTGGGACTTCTCGGTGTCGAGGCATA
back because of the FASTA value at the end. If you want to restrict to the main value:
perl -pe 's/.*"([ACTGN]+)".*<input\b[^>]*\bname="name"\s[^>]*\bvalue="([^"]+)".*/$2\n$1/;'
Please note that all of the standard disclaimers about the stupidity and fragility of parsing XML with a regex apply. Specifically, it is perfectly legal to reorder the name and value attributes and this example regex doesn't allow that.
If I understand the problem correctly, it looks like making use of capturing groups addresses your need. Specially since you know the beginning and the end but don't know the middle, something like this should work:
$data =~ /TTGTT(.+)AGGC/;
print $1;
Check out the section on capture groups on perldoc:
http://perldoc.perl.org/perlre.html#Regular-Expressions
From what has been posted, I think this would return the sequence:
$data =~ /name="sequence" value="([AGCT]*).*name="name" value="([^"])"/;
print "$2\n$1";