home *** CD-ROM | disk | FTP | other *** search
/ Collection of Hack-Phreak Scene Programs / cleanhpvac.zip / cleanhpvac / SATAN11.ZIP / PERL / HTML.PL < prev    next >
Perl Script  |  1995-04-11  |  11KB  |  473 lines

  1. #!/usr/local/bin/perl5
  2. #
  3. # version 1, Thu Mar 23 21:53:31 1995, last mod by wietse
  4. #
  5.  
  6. #
  7. # Run an off-the-shelf HTML client against a dedicated HTML server.  The
  8. # server executes PERL files that are specified in HTML requests.
  9. #
  10. # Authentication is magic-cookie style via the file system.  This should
  11. # be good enough: the client-server conversation never goes over the
  12. # network so the magic cookie cannot be stolen by a network sniffer.
  13. # Values in POST attribute-value lists are assigned to the corresponding
  14. # global PERL variables.  See &process_html_request() for details.
  15. #
  16.  
  17. sub html {
  18.     local($helper, $wd, $host);
  19.  
  20.     #
  21.     # Start the HTML server and generate the initial cookie for
  22.     # client-server authentication.
  23.     #
  24.     $running_from_html = 1;
  25.     chmod 0700, <~/.mosaic*>;    # Yuck!
  26.     chmod 0700, <~/.netsca*>;    # Yuck!
  27.     chmod 0700, <~/.MCOM*>;        # Yuck!
  28.     &start_html_server();
  29.     &make_password_seed();
  30.  
  31.     #
  32.     # These strings are used in, among others, PERL-to-HTML scripts.
  33.     #
  34.     $wd = `pwd`;
  35.     chop $wd;
  36.     $html_root = "$wd/html";
  37.     $start_page = "satan.html";
  38.     $THIS_HOST = &getfqdn(&hostname());
  39.     die "Can't find my own hostname: set \$dont_use_nslookup in $SATAN_CF\n"
  40.         unless $THIS_HOST;
  41.     $HTML_ROOT = "file://localhost$html_root";
  42.     $HTML_SERVER = "http://$THIS_HOST:$html_port/$html_password/$html_root";
  43.     $HTML_STARTPAGE = "$HTML_ROOT/$start_page";
  44.  
  45.     #
  46.     # Some obscurity. The real security comes from magic cookies.
  47.     #
  48.     $html_client_addresses = find_all_addresses($THIS_HOST) ||
  49.         die "Unable to find all my network addresses\n";
  50.  
  51.     for (<$html_root/*.pl>) {
  52.         s/\.pl$//;
  53.         unlink "$_.html";
  54.         open(HTML, ">$_.html")
  55.             || die "cannot write $_.html: $!\n";
  56.         select HTML;
  57.         do "$_.pl";
  58.         close HTML;
  59.         select STDOUT;
  60.         die $@ if $@;
  61.     }
  62.  
  63.     #
  64.     # Fork off the HTML client, and fork off a server process that
  65.     # handles requests from that client. The parent process waits
  66.     # until the client exits and terminates the server.
  67.     #
  68.     print "Starting $MOSAIC...\n" if $debug;
  69.  
  70.     if (($client = fork()) == 0) {
  71.         foreach (keys %ENV) {
  72.             delete $ENV{$_} if (/proxy/i && !/no_proxy/i);
  73.         }
  74.         exec($MOSAIC, "$HTML_STARTPAGE") 
  75.             || die "cannot exec $MOSAIC: $!";
  76.     } 
  77.     if (($server = fork()) == 0) {
  78.         if (($helper = fork()) == 0) {
  79.             alarm 3600;
  80.             &patience();
  81.         }
  82.         &init_satan_data();
  83.         &read_satan_data() unless defined($opt_i);
  84.         kill 'TERM',$helper;
  85.         $SIG{'PIPE'} = 'IGNORE';
  86.         for (;;) {
  87.             accept(CLIENT, SOCK) || die "accept: $!";
  88.             select((select(CLIENT), $| = 1)[0]);
  89.             &process_html_request();
  90.             close(CLIENT);
  91.         }
  92.     }
  93.  
  94.     #
  95.     # Wait until the client terminates, then terminate the server.
  96.     #
  97.     close(SOCK);
  98.     waitpid($client, 0);
  99.     kill('TERM', $server);
  100.     exit;
  101. }
  102.  
  103. #
  104. # Compute a hard to predict number for client-server authentication. Exploit
  105. # UNIX parallelism to improve unpredictability. We use MD5 only to compress
  106. # the result.
  107. #
  108. sub make_password_seed {
  109.     local($command);
  110.  
  111.     die "Cannot find $MD5. Did you run a \"reconfig\" and \"make\"?\n"
  112.         unless -x "$MD5";
  113.     $command = "ps axl&ps -el&netstat -na&netstat -s&ls -lLRt /dev*&w";
  114.     open(SEED, "($command) 2>/dev/null | $MD5 |")
  115.         || die "cannot run password command: $!";
  116.     ($html_password = <SEED>) || die "password computation failed: $!";
  117.     close(SEED);
  118.     chop($html_password);
  119. }
  120.  
  121. #
  122. # Set up a listener on an arbitrary port. There is no good reason to
  123. # listen on a well-known port number.
  124. #
  125. sub start_html_server {
  126.     local($sockaddr, $proto, $junk);
  127.  
  128.     $sockaddr = 'S n a4 x8';
  129.     ($junk, $junk, $proto) = getprotobyname('tcp');
  130.     socket(SOCK, &AF_INET, &SOCK_STREAM, $proto) || die "socket: $!";
  131.     listen(SOCK, 1) || die "listen: $!";
  132.     ($junk, $html_port) = unpack($sockaddr, getsockname(SOCK));
  133. }
  134.  
  135. #
  136. # Process one client request.  We expect the client to send stuff that
  137. # begins with:
  138. #
  139. #    command /password/perl_script junk
  140. #
  141. # Where perl_script is the name of a perl file that is executed via
  142. # do "perl_script";
  143. #
  144. # In case of a POST command the values in the client's attribute-value
  145. # list are assigned to the corresponding global PERL variables.
  146. #
  147. sub process_html_request {
  148.     local($request, $command, $script, $magic, $url, $peer);
  149.     local(%args);
  150.  
  151.     #
  152.     # Parse the command and URL. Update the default file prefix.
  153.     #
  154.     $request = <CLIENT>;
  155.     print $request if $debug;
  156.     ($command, $url) = split(/\s+/, $request);
  157.     if ($command eq "" || $command eq "QUIT") {
  158.         return;
  159.     }
  160.  
  161.     ($junk, $magic, $script) = split(/\//, $url, 3);
  162.     ($script, $html_script_args) = split(',', $script, 2);
  163.     ($HTML_CWD = "file:$script") =~ s/\/[^\/]*$//;
  164.  
  165.     #
  166.     # Make sure they gave us the right magic number.
  167.     #
  168.     if ($magic ne $html_password) {
  169.         &bad_html_magic($request);
  170.         return;
  171.     }
  172.  
  173.     #
  174.     # Assume the password has leaked out when the following happens.
  175.     #
  176.     $peer = &get_peer_addr(CLIENT);
  177.     die "SATAN password from unauthorized client: $peer\n"
  178.         unless is_member_of($peer, $html_client_addresses);
  179.     die "Illegal URL: $url received from: $peer\n" 
  180.         if index($script, "..") >= $[
  181.         || index($script, "$html_root/") != $[
  182.         || $script !~ /\.pl$/;
  183.  
  184.     #
  185.     # Warn them when the browser leaks parent URLs to web servers.
  186.     #
  187.     while (<CLIENT>) {
  188.         if (!$cookie_leak_warning && /$html_password/) {
  189.             &cookie_leak_warning();
  190.             return;
  191.         }
  192.         last if (/^\s+$/);
  193.     }
  194.  
  195.     if ($command eq "GET") {
  196.         perl_html_script($script);
  197.     } elsif ($command eq "POST") {
  198.  
  199.         #
  200.         # Process the attribute-value list.
  201.         #
  202.         if ($_ = <CLIENT>) {
  203.             s/\s+$//;
  204.             s/^/\n/;
  205.             s/&/\n/g;
  206.             $html_post_attributes = '';
  207.             $* = 1;
  208.             for (split(/(%[0-9][0-9A-Z])/, $_)) {
  209.                 $html_post_attributes .= (/%([0-9][0-9A-Z])/) ? 
  210.                     pack('c',hex($1)) : $_;
  211.             }
  212.             %args = ('_junk_', split(/\n([^=]+)=/, $html_post_attributes));
  213.             delete $args{'_junk_'};
  214.             for (keys %args) {
  215.                 print "\$$_ = $args{$_}\n" if $debug;
  216.                 ${$_} = $args{$_};
  217.             }
  218.             perl_html_script($script);
  219.         } else {
  220.             &bad_html_form($script);
  221.         }
  222.     } else {
  223.         &bad_html_command($request);
  224.     }
  225. }
  226.  
  227.  
  228. #
  229. # Map IP to string.
  230. #
  231. sub inet_ntoa {
  232.     local($ip) = @_;
  233.     local($a, $b, $c, $d);
  234.  
  235.     ($a, $b, $c, $d) = unpack('C4', $ip);
  236.     return "$a.$b.$c.$d";
  237. }
  238.  
  239. #
  240. # Look up peer address and translate to string form.
  241. #
  242. sub get_peer_addr {
  243.     local($peer) = @_;
  244.     local($junk, $inet);
  245.  
  246.     ($junk, $junk, $inet) = unpack('S n a4', getpeername($peer));
  247.     return &inet_ntoa($inet);
  248. }
  249.  
  250. #
  251. # Wrong magic number.
  252. #
  253. sub bad_html_magic {
  254.     local($request) = @_;
  255.     local($peer);
  256.  
  257.     $peer = &get_peer_addr(CLIENT);
  258.     print STDERR "bad request from $peer: $request\n";
  259.  
  260.         print CLIENT <<EOF
  261. <HTML>
  262. <HEAD>
  263. <TITLE>Bad client authentication code</TITLE>
  264. <LINK REV="made" HREF="mailto:satan\@fish.com">
  265. </HEAD>
  266. <BODY>
  267. <H1>Bad client authentication code</H1>
  268. The command: <TT>$request</TT> was not properly authenticated.
  269. </BODY>
  270. </HTML>
  271. EOF
  272. }
  273.  
  274. #
  275. # Unexpected HTML command.
  276. #
  277. sub bad_html_command {
  278.     local($request) = @_;
  279.  
  280.     print CLIENT <<EOF
  281. <HTML>
  282. <HEAD>
  283. <TITLE>Unknown command</TITLE>
  284. <LINK REV="made" HREF="mailto:satan\@fish.com">
  285. </HEAD>
  286. <BODY>
  287. <H1>Unknown command</H1>
  288. The command <TT>$request<TT> was not recognized.
  289. </BODY>
  290. </HTML>
  291. EOF
  292. }
  293.  
  294. #
  295. # Execute PERL script with extreme prejudice.
  296. #
  297. sub perl_html_script {
  298.     local($script) = @_;
  299.  
  300.     if (! -e $script) {
  301.         print CLIENT <<EOF
  302. <HTML>
  303. <HEAD>
  304. <TITLE>File not found</TITLE>
  305. <LINK REV="made" HREF="mailto:satan\@fish.com">
  306. </HEAD>
  307. <BODY>
  308. <H1>File not found</H1>
  309. The file <TT>$script</TT> does not exist or is not accessible.
  310. </BODY>
  311. </HTML>
  312. EOF
  313. ;        return;
  314.     }
  315.     do $script;
  316.     if ($@ && ($@ ne "\n")) {
  317.         print CLIENT <<EOF
  318. <HTML>
  319. <HEAD>
  320. <TITLE>Command failed</TITLE>
  321. <LINK REV="made" HREF="mailto:satan\@fish.com">
  322. </HEAD>
  323. <BODY>
  324. <H1>Command failed</H1>
  325. $@
  326. </BODY>
  327. </HTML>
  328. EOF
  329.     }
  330. }
  331.  
  332. #
  333. # Missing attribute list
  334. #
  335. sub bad_html_form {
  336.     local($script) = @_;
  337.  
  338.     print CLIENT <<EOF
  339. <HTML>
  340. <HEAD>
  341. <TITLE>No attribute list</TITLE>
  342. <LINK REV="made" HREF="mailto:satan\@fish.com">
  343. </HEAD>
  344. <BODY>
  345. <H1>No attribute list</H1>
  346.  
  347. No attribute list was found.
  348. </BODY>
  349. </HTML>
  350. EOF
  351. }
  352.  
  353. #
  354. # Scaffolding for stand-alone testing.
  355. #
  356. if ($running_under_satan == 1) {
  357.  
  358.     require 'perl/socket.pl';
  359.     require 'config/paths.pl';
  360.     require 'perl/hostname.pl';
  361.     require 'perl/getfqdn.pl';
  362.     require 'config/satan.cf';
  363.  
  364. } else {
  365.     $running_under_satan = 1;
  366.  
  367.     require 'perl/socket.pl';
  368.     require 'config/paths.pl';
  369.     require 'perl/hostname.pl';
  370.     require 'perl/getfqdn.pl';
  371.     require 'config/satan.cf';
  372.  
  373.     &html();
  374. }
  375.  
  376. #
  377. # Give them something to read while the server is initializing.
  378. #
  379. sub patience {
  380.     for (;;) {
  381.         accept(CLIENT, SOCK) || die "accept: $!";
  382.         <CLIENT>;
  383.         print CLIENT <<EOF
  384. <HTML>
  385. <HEAD>
  386. <TITLE>Initialization in progress</TITLE>
  387. <LINK REV="made" HREF="mailto:satan\@fish.com">
  388. </HEAD>
  389. <BODY>
  390. <H1>Initialization in progress</H1>
  391. SATAN is initializing, please try again later.
  392. </BODY>
  393. </HTML>
  394. EOF
  395. ;
  396.         close(CLIENT);
  397.     }
  398. }
  399.  
  400. # Look up all IP addresses listed for this host name, so that we can
  401. # filter out requests from non-local clients. Doing so offers no real
  402. # security, because network address information can be subverted.
  403. # All client-server communication security comes from the magic cookies
  404. # that are generated at program startup time. Client address filtering
  405. # adds an additional barrier in case the cookie somehow leaks out.
  406.  
  407. sub find_all_addresses {
  408.     local($host) = @_;
  409.     local($junk, $result);
  410.  
  411.     ($junk, $junk, $junk, $junk, @all_addresses) = gethostbyname($host);
  412.     for (@all_addresses) { $result .= &inet_ntoa($_) . " "; }
  413.     return $result;
  414. }
  415.  
  416. sub is_member_of {
  417.     local($elem, $list) = @_;
  418.  
  419.     for (split(/\s+/, $list)) { return 1 if ($elem eq $_); }
  420.     return 0;
  421. }
  422.  
  423. sub cookie_leak_warning {
  424.     print CLIENT <<EOF;
  425. <HTML>
  426. <HEAD>
  427. <TITLE>Warning - SATAN Password Disclosure</TITLE>
  428. <LINK REV="made" HREF="mailto:satan\@fish.com">
  429. </HEAD>
  430. <BODY>
  431. <H1><IMG SRC="$HTML_ROOT/images/satan.gif" ALT="[SATAN Image]">
  432. Warning - SATAN Password Disclosure</H1>
  433.  
  434. <HR>
  435.  
  436. <H3> 
  437.  
  438. Your Hypertext viewer may reveal confidential information when you
  439. contact remote WWW servers from within SATAN.
  440.  
  441. <p>
  442.  
  443. For this reason, SATAN advises you to not contact other WWW servers
  444. from within SATAN.
  445.  
  446. <p>
  447.  
  448. For more information, see <a
  449. href="$HTML_ROOT/tutorials/vulnerability/SATAN_password_disclosure.html">the
  450. SATAN vulnerability tutorial</a>.
  451.  
  452. <p>
  453.  
  454. This message will appear only once per SATAN session. 
  455.  
  456. <p>
  457.  
  458. In order to proceed, send a <i>reload</i> command (Ctrl-R with Lynx),
  459. or go back to the previous screen and select the same link or button
  460. again.
  461.  
  462. </H3>
  463.  
  464. </BODY>
  465. </HTML>
  466. EOF
  467.     $cookie_leak_warning = 1;
  468. }
  469.  
  470. 1;
  471.