Published
Edited
Dec 14, 2021
Insert cell
Insert cell
/*
NOTE: Styles are located in the code block BELOW this one
*/

animation = p5(sketch => {
/*
Text that is presented to the user as they step through the interaction. Each "key" corresponds to a line in the program.
*/
let displayTxt = {
"0": 'Before diving straight into the off-by-one exploit, we need to discover how we can submit an attack string as our input. Once \
we determine our attack string, we can use the off-by-one typo mentioned at the end of the previous section to take over the program.',
"1": 'The most basic form of a takeover attack comes in the form of SHELL code. In exploits, SHELL code is used by an \
attacker to open a command shell within a vulnerable program that will give the attacker access to the machine the \
exploited program is running on. SHELL code attacks are inserted into a "payload" (in this case, a user-input string) \
that is submitted to the vulnerable program. SHELL code is often small and executable, allowing it to be used in many different \
situations, and continues to persist as an exploitable issue to this day.',
"2": 'Visually, running shellcode produces a terminal within a program that has access to the machine where the program resides. \
Here is an example of a terminal:',
"3": 'The terminal is produced by executable code. As such, the attacker can insert this code as an input string to a vulnerable program. \
Considering that SHELL code is very short, an attacker can insert SHELL code in many programs that accept text input. In fact a lot of \
SHELL code executables range from 20 - 23 bytes. The following shell code is less than 40 bytes and is written by Aleph One, \
the author of "Smashing the Stack for Fun and Profit":',
"4": 'Loading the shell code into our program is as simple as inputting the executable string as our input string. \
For the sake of demonstration, we will consider a shorter, 20 byte Linux SHELL code executable. For now, let us \
assume that our for-loop is well formed. We will re-introduce the off-by-one error later...',
"5": 'Once again, we shall visualize our array.',
"6": 'As before, the array is pre-filled with "residual" memory.',
"7": 'The for-loop executes and fills our array with the SHELL code values. Note that, in this case, the values are already interpreted as \
hexadecimal values.',
"8": 'And just like that, we have inserted our attack code into the program! However, we have not coerced the program into executing \
the attack code. As far as the program is concerned, it finished copying in our input to the array, then terminated as it should \
have. How can we trick the program into executing our SHELL code?',
"9": 'As is, we cannot coerce execution of our SHELL code. However, what if we brought back the off-by-one for-loop typo?',
"10": 'In the next section, we will discuss how we can exploit the off-by-one for-loop typo to take over the program\'s instruction pointer, \
then how we can redirect the pointer to our attack code.'
}

let textIn = "\\x31\\xc9\\x6a\\x0b\\x58\\x51\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\xcd\\x80";
let textInArr = ['31','c9','6a','0b','58','51','68','2f','2f','73','68',
'68','2f','62','69','6e','89','e3','cd','80'];
let dispTxt;
let cont; // continue button
let back; // back button
let currScreen = 0;

let descTxtFade = 0;
let opFactor = 0.05;

let terminal;
let shellcode;
let terminalImage;
let shellcodeImage;

let lines = [0, 1, 2, 3, 3, 4, 5, 4, 5, 4, 5]; // correspond to the lines in code user steps through
let currLine = 0; // tracker for the current line of code
let randArrVals = [];
let arrVals = [];
let arrSize = 40;
let rectFade = 255; // faders for transitions
let arrFade = 255;
let shellcodeFade = 0;
let terminalFade = 0;
let fadeFactor = 8; // speed at which fades happen at

let resetWhole; // button to reset the entire component

/*
preload method: preload the images
*/
sketch.preload = function() {
terminal = sketch.loadImage(terminalImg);
shellcode = sketch.loadImage(shellcodeImg);
}

/*
setup method: called once at program initialization
Component creation/initialization stage
*/
sketch.setup = function() {
sketch.createCanvas(width, height).background("#FFFFFF");
sketch.textAlign(sketch.LEFT).textFont('consolas');
sketch.imageMode(sketch.CENTER);

getRandArrVals(20); // Hard-coded 20: corresponds with shell code length

dispTxt = sketch.createDiv(displayTxt[0])
.attribute('class', 'display_text')
.position(0, height/2-10)
.size(width*0.75, height)
.center('horizontal')
.style('opacity', descTxtFade)
.hide();
cont = sketch.createButton("Next")
.attribute('class', 'button')
.style('border', '2px solid #5cb85c')
.size(50, 20)
.position(width/2+27, 260)
.mousePressed(() => {
if (currScreen < 10) {
descTxtFade = 0;
currScreen++;
updateDisplayTxt(dispTxt, currScreen);
if (currScreen === 7) {
updateArrVals(20);
}
}
})
.hide();
back = sketch.createButton("Back")
.attribute('class', 'button')
.style('border', '2px solid #5bc0de')
.size(50, 20)
.position(width/2-27, 260)
.mousePressed(() => {
if (currScreen > 0) {
descTxtFade = 0;
currScreen--;
updateDisplayTxt(dispTxt, currScreen);
if (currScreen === 6) {
arrVals = randArrVals;
}
}
})
.hide();
resetWhole = sketch.createButton('Reset')
.attribute('class', 'button')
.style('border', '2px solid #d9534f')
.size(50, 20)
.position(width/2+75, 120)
.mousePressed(() => {
descTxtFade = 0;
currScreen = 0;
updateDisplayTxt(dispTxt, currScreen);
getRandArrVals(20); // Hard-coded 20: corresponds with shell code length
})
.hide();
};

/*
Draw method: executes around 10-20 times per second; constantly re-called
*/
sketch.draw = function() {
sketch.background("FFFFFF"); // Redraw background to "clear" components
// By default, hide all components
dispTxt.hide();
cont.hide();
back.hide();
resetWhole.hide();

if (descTxtFade < 1) {
descTxtFade += opFactor;
}
else {
descTxtFade = 1;
}
dispTxt.style('opacity', descTxtFade)

// Reveal components based on what "screen" the user is on
if (currScreen === 0) {
dispTxt.show();
cont.show();
back.hide();
}
if (currScreen === 1) {
dispTxt.show();
cont.show();
back.show();
}
if (currScreen === 2) {
dispTxt.show();
terminalFade += fadeFactor;
sketch.tint(255, terminalFade);
terminalImage = sketch.image(terminal, width/2+25, height/2, width*0.87 * 0.65, width*0.87*0.331 * 0.65);
cont.show();
back.show();
}
else {
terminalFade = 0;
sketch.tint(255, terminalFade);
}
if (currScreen === 3) {
dispTxt.show();
shellcodeFade += fadeFactor;
sketch.tint(255, shellcodeFade);
shellcodeImage = sketch.image(shellcode, width/2+25, height/2, width*1 * 0.65, width*1*0.196 * 0.65);
cont.show();
back.show();
}
else {
shellcodeFade = 0;
sketch.tint(255, shellcodeFade);
}

if (currScreen === 4) {
dispTxt.show();
codeSnip(0, 25, textIn, lines[currLine], false);
cont.show();
back.show();
}

if (currScreen >= 5) {
dispTxt.show();
if (currScreen < 9) { codeSnip(0, 25, textIn, lines[currLine], false); }
cont.show();
back.show();
rectFade -= fadeFactor;
drawArr(sketch, width/1.5 - arrSize*((20-1)/2)-180, 190, arrSize, 20); // Hard-coded 20: size of the shell code
}
else {
rectFade = 255;
}

if (currScreen >= 6) {
arrFade -= fadeFactor;
drawArrVals(sketch, width/1.5 - arrSize*((20-1)/2)-180, 190, arrSize, 20); // Hard-coded 20: size of the shell code
}
else {
arrFade = 255;
}

if (currScreen >= 9) {
codeSnip(0, 25, textIn, 6, true); // 6: Outside of the range of the codeSnip reused functio, forces the <= line
}
if (currScreen >= 10) {
resetWhole.show();
}
}

function updateDisplayTxt(dispTxt, currScreen) {
if (currScreen === 0) {
dispTxt.size(width*0.75, height).position(0, height/2-10).center('horizontal').style('text-align', 'center')
cont.position(cont.x, 260);
back.position(back.x, 260);
}
else if (currScreen === 1) {
dispTxt.size(width*0.75, height).position(0, height/2 - 60).center('horizontal').style('text-align', 'left');
}
else if (currScreen === 2) {
dispTxt.size(width*0.75, height).position(0, height/2 - 135).center('horizontal').style('text-align', 'center');
cont.position(cont.x, 290);
back.position(back.x, 290);
}
else if (currScreen === 3) {
dispTxt.size(width*0.75, height).position(0, height/2 - 160).center('horizontal').style('text-align', 'left');
cont.position(cont.x, 260);
back.position(back.x, 260);
}
else if (currScreen === 4) {
dispTxt.size(width*0.50, height).position(380, height/2 - 120).style('text-align', 'left');
}
else if (currScreen === 5) {
dispTxt.size(width*0.50, height).position(380, height/2 - 120).style('text-align', 'center');
}
else if (currScreen === 8) {
dispTxt.size(width*0.50, height).position(380, height/2 - 120).style('text-align', 'left');
}
else if (currScreen === 9) {
dispTxt.size(width*0.50, height).position(380, height/2 - 120).style('text-align', 'center');
}
else if (currScreen === 10) {
dispTxt.size(width*0.50, height).position(380, height/2 - 120).style('text-align', 'left');
}

dispTxt.html(displayTxt[currScreen+""]);
}

/*
Updates the current line of text displayed to the user as they step through the program
*/
function updateCodeTxt(codeText, currLine) {
codeText.html(displayTxt[currLine+2+""]);
}

function getRandArrVals(len) {
randArrVals = [];
arrVals = [];
for (let i = 0; i < len; i++) {
let val = Math.floor(Math.random() * 239+17)
arrVals.push(val);
randArrVals.push(val);
}
}

function updateArrVals(len) {
arrFade = 255;
arrVals = [];
for (let i = 0; i < len; i++) {
arrVals.push(textInArr[i]);
}
}

/*
Draws the boxes for the array as well as the corrsponding low/high address hex values

stroke(rectFade) controls the fade transition for the array squares
-1*rectFade+250 controls the fade transition for the hex text values
*/
function drawArr(sketch, x, y, size, count) {
sketch.fill("#FFFFFF");
sketch.stroke(rectFade);
for (let i = 0; i < count; i++) {
sketch.square(x+size*i, y, size);
}
sketch.noStroke();
let low = 0xffffdab4;
let high = low + count-1;
sketch.textSize(18).fill(0,0,0,-1*rectFade+250).text("0x"+low.toString(16), x-110, y+size-15);
sketch.textSize(18).fill(0,0,0,-1*rectFade+250).text("0x"+high.toString(16), x+size*count+10, y+size-15);
}

/*
Draws the values in the array presented to the viewer.

-1*arrFade+250 controls the fade transition
*/
function drawArrVals(sketch, x, y, size, count) {
for (let i = 0; i < count; i++) {
sketch.textSize(12).fill(0,0,0,-1*arrFade+250).text("0x" + arrVals[i].toString(16), x+size*i+7, y+size-15);
}
}


/*
COPIED AND PASTED FROM Array-Allocation: Can be simplified
*/
function codeSnip(x, y, userIn, currLine, final) {
sketch.textSize(18).textAlign(sketch.LEFT).textFont('consolas');
if (currLine == 1 || currLine == 0) {
sketch.fill(0,77,88,1000).text('char input[] = "' + userIn + '";', x, y);
}
else {
sketch.fill(0,77,88,50).text('char input[] = "' + userIn + '";', x, y);
}
if (currLine == 2 || currLine == 0) {
sketch.fill(0,77,88,1000).text('int SIZE = ' + 20 + ';', x, y+20); // Hard-coded 20: size of the shell code
}
else {
sketch.fill(0,77,88,50).text('int SIZE = ' + 20 + ';', x, y+20); // Hard-coded 20: size of the shell code
}
if (currLine == 3 || currLine == 0) {
sketch.fill(0,77,88,1000).text('char arr[SIZE];', x, y+40);
}
else {
sketch.fill(0,77,88,50).text('char arr[SIZE];', x, y+40);
}
if (currLine == 4 || currLine == 0) {
sketch.fill(0,77,88,1000).text('for (int i = 0; i < SIZE; i++) {', x, y+60);
}
else {
if (final) {
sketch.fill(0,77,88,1000).text('for (int i = 0; i <= SIZE; i++) {', x, y+60);
}
else {
sketch.fill(0,77,88,50).text('for (int i = 0; i < SIZE; i++) {', x, y+60);
}
}
if (currLine == 5 || currLine == 0) {
sketch.fill(0,77,88,1000).text('\tarr[i] = input[i];', x, y+80);
}
else {
sketch.fill(0,77,88,50).text('\tarr[i] = input[i];', x, y+80);
}
if (currLine == 0) {
sketch.fill(0,77,88,1000).text('}', x, y+100);
}
else {
sketch.fill(0,77,88,50).text('}', x, y+100);
}
}
})
Insert cell
styles = html`
<style>

.display_text {
font-size: 14px;
font-family: sans-serif;
font-weight: bold;
text-align: center;
color: #990099;
}

.button {
font-size: 14px;
font-family: sans-serif;
text-align: center;
border-radius: 4px;
background-color: white;
color: black;
border: 2px solid #000000;
}
.button:hover {
box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);
}

</style>`
Insert cell
width = 1100
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more