Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: split Client into CallBackAction + main()

...

Code Block
public interface CallBack {
  void callMethod();
}
 
class CallBackImpl implements CallBack {
  public void callMethod() {
    System.out.println("CallBack invoked");
  }
}

class CallBackAction {
  private CallBack callback;

  public CallBackAction(CallBack callback) {
    this.callback = callback;
  }
 
  public void perform() {
    callback.callMethod();
  }
}
class Client {
  public static void main(String[] args) {
    CallBackAction action = new CallBackAction(new CallBackImpl());
    // ...
    clientaction.perform(); // Prints "CallBack invoked"
  }
}

...

Code Block
bgColor#ffcccc
langjava
public interface CallBack {
  void callMethod();
}
 
class UserLookupCallBack implements CallBack {
  private int uid;
  private String name;

  public UserLookupCallBack(int uid) {
    this.uid = uid;
  }

  public String getName() {
    return name;
  }

  public void callMethod() {
    try (InputStream fis = new FileInputStream("/etc/passwd")) {
      // Look up uid & assign to name
    } catch (IOException x) {
      name = null;
    }
  }
}

class ClientCallBackAction {
  private CallBack callback;

  public void registerCallBackCallBackAction(CallBack callback) {
    this.callback = callback;
  }
 
  public void doSomethingperform() {
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
          callback.callMethod();
          return null;
        }
      });
  }

  }

This code could be used by a client safely, as in:

Code Block
bgColorlang=java
public static void main(String[] args) {
    int uid = Integer.parseInt(args[0]);

  UserLookupCallBack callBack Client client = new ClientUserLookupCallBack(uid);
  CallBackAction  CallBack callBackaction = new UserLookupCallBackCallBackAction(uidcallBack);

    client.registerCallBack(callBack);
    // ...
    clientaction.doSomethingperform(); // Looks up user name
    System.out.println("User " + uid + " is named " + callBack.getName());
  }
}

Although this code works as expectedHowever, an attacker can use it CallBackAction to execute malicious code with elevated privileges by registering a MaliciousCallBack instance.:

Code Block
class MaliciousCallBack implements CallBack {
  public void callMethod() {
    // Code here gets executed with elevated privileges
  }
}

// ...
Client clientpublic static void main(String[] args) {
  CallBack callBack = new ClientMaliciousCallBack();
client.registerCallBack(  CallBackAction action = new MaliciousCallBackCallBackAction(callBack));
client  action.doSomethingperform(); // Executes malicious code
}

Compliant Solution 

According to Oracle's secure coding guidelines [SCG 2010]:

...

This compliant solution moves the invocation of doPrivileged() out of the Client code CallBackAction code and into the callback itself. This code functions the same as before, but an attacker can no longer run malicious callback code with elevated privileges.

Code Block
bgColor#ccccff
langjava
public interface CallBack {
  void callMethod();
}
  
class UserLookupCallBack implements CallBack {
  private int uid;
  private String name;
 
  public UserLookupCallBack(int uid) {
    this.uid = uid;
  }
 
  public String getName() {
    return name;
  }
 
  public void callMethod() {
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
          try (InputStream fis = new FileInputStream("/etc/passwd")) {
            // Look up userid & assign to UserLookupCallBack.this.name
          } catch (IOException x) {
            UserLookupCallBack.this.name = null;
          }
          return null;
        }
      });
  }
}
 
class ClientCallBackAction {
  private CallBack callback;
 
  public void registerCallBackCallBackAction(CallBack callback) {
    this.callback = callback;
  }
  
  public void doSomethingperform() {
    callback.callMethod();
  }
 
  public static void main(String[] args) {
    int uid = Integer.parseInt(args[0]);
 
    Client client = new Client();
    CallBack callBack = new UserLookupCallBack(uid);
 
    client.registerCallBack(callBack);
    // ...
    client.doSomething(); // Looks up user name
    System.out.println("User " + uid + " is named " + callBack.getName());
  }
}

Applicability

Exposing sensitive methods through callbacks can result in misuse of privileges and arbitrary code execution.

...