Being stuck at home for a few days, I decided to distract myself a bit and extend this page with a PoC||GTFO Mirror. For senseless reasons I wanted one of these viewers where you can actually flip pages while reading, just as they use for ads / catalogs from shops. I quickly found a piece of software that would do the job, but had to perform a few changes on the resulting content…

The software I ended up using is called Flip Builder. It loads a PDF (at least if it can) and converts it into a catalog style HTML5 page for reading. Having read quite a few bad comments on their customer support and a few bugs in the software I downloaded the free version and gave it a test run.

Mouse, side

As I went for the free version there was a rather ugly banner mid page. Although I’m happy to keep (major) references to the maker of a software when I’m allowed to use it for free, the add made reading certain pages of the journals rather tough. As such I decided to simply try to shift the ad to the bottom of the page, in between the page numbers, so that easy reading would be possible while still keeping the reference.

Could things be easy?

I had an issue of the journal open in Firefox and simply did a right click on the image in the overlay and selected Inspect Element.

<div style="width: 305px; height: 70px; position: absolute; background-color: rgb(204, 204, 204); opacity: 0.5; z-index: 1000; border-radius: 5px; top: 0px; left: 0px; bottom: 0px; right: 0px; margin: auto;">
  <div style="position: absolute; top: 13px; left: 65px;">
    <span style="font-size: 15px; color: rgb(51, 51, 51);">Flip PDF</span>
    <a href="http://www.flipbuilder.com" target="_blank" style="position: absolute; top: 25px; left: 0px;">
    http://www.flipbuilder.com
    </a>
  </div>
  <img style="position: absolute; top: 10px; left: 5px;" src="mobile/style/icon/demo.png">
</div>

Working on Windows due to the Tool, I opened Notepad++ and simply did a search in the applicable directory for <div style="width: 305px; height: 70px;. Strangely I wasn’t able to find anything so I adjusted my search and simply looked for 305px which luckily resulted in a single entry in a file called main.js. The file then welcomed me with 3261 lines of JS.

I found my 305px in line 579

95,"  demoBar css bookConfig  var left top function      append homePage div style absolute productName parseInt hidden 5px position if body radius is  border 932217 html    trim 6406725519N color BUILD_DATE span opacity href width setDemoPosition bottom right loadImg attr index parseFloat 1000 height getTransform text 500 target _blank 25px 0px size 15px 333333 initDemoBar bdor fn extend webkit img margin  auto background load 10px src uiBaseURL demo png window setInterval display none visibility 70px indexOf parent 13px 65px cccccc moz 305 70 305px font".split(" "),

To verify that I was in the right spot, I changed the 305px to 999px and broke the page. As the line is obviously obfuscated, I guessed it was a simple mechanism to ensure nobody removed the banner. At this point I didn’t spot the second 305 in moz 305 70 305px font which would have saved the day, so I carried on messing around with the code.

Still having the applicable line selected in Firefox’s Inspector, I switched over to the Debbugger and hit the pause button. Firefox was nice enough to directly show me the line I had been looking for

var demoBar;
var initDemoBar=function()
{
  if(BUILD_DATE.indexOf("932217.6406725519N")==-1)
  {
    BUILD_DATE+="932217.6406725519N"
  }
if(!bookConfig.productName||!bookConfig.productName.trim()||!bookConfig.homePage||!bookConfig.homePage.trim())$("body").html("");
var a=bookConfig.homePage;demoBar=$("<div></div>");
demoBar.css({width:'999px',height:'70px',position:'absolute','background-color':'#cccccc','opacity':0.5,'z-index':1000,'-moz-border-radius':'5px','-webkit-border-radius':'5px','border-radius':'5px'});
var b=$("<img />");
b.css({position:'absolute',top:'10px',left:'5px'});
var c=$("<div></div>");
c.css({position:'absolute',top:'13px',left:'65px'});
var d=$("<span>"+bookConfig.productName+"</span>");
var e=$("<a href='"+a+"' target='_blank'>"+a+"</a>");
e.css({position:'absolute',top:'25px',left:'0px'});
d.css({'font-size':'15px',color:'#333333'});
$("body").append(demoBar);
demoBar.append(c);
c.append(d);
bdor[18]="t";
c.append(e);
$.fn.extend(demoBar,{setDemoPosition:function(){demoBar.css({top:0,left:0,bottom:0,right:0,margin:"auto"})},loadImg:function(){b.load(function(){demoBar.append(b)});
b.attr({src:uiBaseURL+'demo.png'})}});
demoBar.loadImg();demoBar.setDemoPosition();
window.setInterval(function(){if(!demoBar||demoBar.css("display")=="none"||demoBar.css("visibility")=="hidden"||demoBar.is(':hidden')||!demoBar.parent()[0]||parseFloat(demoBar.css("opacity"))<=0||parseFloat(demoBar.css("z-index"))<1000||parseInt(demoBar[0].style.left)!=0||parseInt(demoBar[0].style.top)!=0||parseInt(demoBar[0].style.right)!=0||parseInt(demoBar[0].style.bottom)!=0||getTransform(demoBar[0]).x!=0||getTransform(demoBar[0]).y!=0||demoBar.width()!=999||demoBar.height()!=70||d.is(':hidden')||e.is(':hidden')||d.text()!=bookConfig.productName||e.text()!=bookConfig.homePage||e.attr("href")!=bookConfig.homePage)$("body").html("")},500)};

I then removed the raw and obfuscated function

eval(function(b,c,d,f,g,h)
{
  g=function(b){return(b<c?"":g(parseInt(b/c)))+(35<(b%=c)?String.fromCharCode(b+29):b.toString(36))};
  if(!"".replace(/^/,String) )
  {
    for(;d--;)h[g(d)]=f[d]||g(d);
    f=[function(b){return h[b]}];
    g=function(){return"\\w+"};
    d=1
  }
  for(;d--;)f[d]&&(b=b.replace(RegExp("\\b"+g(d)+"\\b","g"),f[d]));
  return b
}
("6 2;6 11=9(){p(D.1n(\"v.B\")==-1){D+=\"v.B\"}p(!4.k||!4.k.A()||!4.g||!4.g.A())$(\"q\").w(\"\");6 a=4.g;2=$(\"<h></h>\");2.3({H:'1v',Q:'1m',o:'j','1a-C':'#1r','F':0.5,'z-N':P,'-1s-u-r':'n','-15-u-r':'n','u-r':'n'});6 b=$(\"<16 />\");b.3({o:'j',8:'1c',7:'n'});6 c=$(\"<h></h>\");c.3({o:'j',8:'1p',7:'1q'});6 d=$(\"<E>\"+4.k+\"</E>\");6 e=$(\"<a G='\"+a+\"' U='V'>\"+a+\"</a>\");e.3({o:'j',8:'W',7:'X'});d.3({'1w-Y':'Z',C:'#10'});$(\"q\").f(2);2.f(c);c.f(d);12[18]=\"t\";c.f(e);$.13.14(2,{I:9(){2.3({8:0,7:0,J:0,K:0,17:\"19\"})},L:9(){b.1b(9(){2.f(b)});b.M({1d:1e+'1f.1g'})}});2.L();2.I();1h.1i(9(){p(!2||2.3(\"1j\")==\"1k\"||2.3(\"1l\")==\"m\"||2.s(':m')||!2.1o()[0]||O(2.3(\"F\"))<=0||O(2.3(\"z-N\"))<P||l(2[0].i.7)!=0||l(2[0].i.8)!=0||l(2[0].i.K)!=0||l(2[0].i.J)!=0||R(2[0]).x!=0||R(2[0]).y!=0||2.H()!=1t||2.Q()!=1u||d.s(':m')||e.s(':m')||d.S()!=4.k||e.S()!=4.g||e.M(\"G\")!=4.g)$(\"q\").w(\"\")},T)};",62,
95,"  demoBar css bookConfig  var left top function      append homePage div style absolute productName parseInt hidden 5px position if body radius is  border 932217 html    trim 6406725519N color BUILD_DATE span opacity href width setDemoPosition bottom right loadImg attr index parseFloat 1000 height getTransform text 500 target _blank 25px 0px size 15px 333333 initDemoBar bdor fn extend webkit img margin  auto background load 10px src uiBaseURL demo png window setInterval display none visibility 70px indexOf parent 13px 65px cccccc moz 999 70 999px font".split(" "),
0,{}));

and replaced it with the decoded function I fetched from Firefox. I now had everything I needed to slightly shift the banner downwards.

Looking at the initDemoBar function I found the line bdor[18]="t";, which made no sense to me (also the name bdor somehow triggered something in me).

And Then Things Escalated

Notepad++’s search function identified 56 occurrences of the string bdor. From there I also found an array called hddr

(this||(0,eval)("(this)")).bdor=[];

bdor[1]="p";

bdor[30]=function(b,c){return bdor[b]-c};

bdor[7]="m";

bdor.i=function(b){return bdor[b]};

bdor.f="f";

this.skin=this.bdor

bdor[8]="o";

bdor[3]="f";

bdor[4]="h";

bdor[10]="l";

bdor[31]=function(b,c){return bdor[b]-bdor[c]};

bdor[6]="l"

var hddr=[];

bdor[5]="i";

bdor[10]="l";

bdor[2]="e"

bdor[32]=function(b,c){return b+c};

bdor[66]="ipht";

bdor[18]="t";

bdor[66]="ipht";

bdor[25]="i";

bdor[27]="f";

bdor[26]="h";

global.hddr.soo4=function(b){return b||".com"};

global.hddr.soq3=function(){return hddr.soo1||"tm"}();

bdor[10]="l";

bdor[29]="t";

bdor[8]="o";

bdor[9]="j";

global.hddr.soo6=function(){return hddr.bbb5};

global.hddr.soo6=function(){return hddr.bbb5};

global.hddr.soa7=bdor[3]+bdor[10];

global.hddr.soo3=bdor[51];

global.hddr.soc8=hddr.sob8+"ph";

bdor[7]="m";

global.hddr.soo1="tm";

global.hddr.soa8=hddr.soa7+"i";

global.hddr.sov9=function(b,c){return b+"ph"+c+"l"};

global.hddr.sob8=hddr.soa8;

global.hddr.soq2=hddr.sov9(hddr.soa8,hddr.soq3)+"5";

if(bookConfig.securityType&&3==parseInt(bookConfig.securityType))return 2},getPassward=function(){return document.getElementById("passward_input")?$("#passward_input").val():global.passward},getUsername=function(){return document.getElementById("username_input")?$("#username_input").val():global.username},a=hddr.soo2,

bdor[33]=function(b,c){return b/bdor[30](c,20)};

bdor[36]=36;

global.bdor[35]=function(){return 3}();

global.bdor[37]=80;

bdor[38]=function(){return bdor[30](37,40)}();

bdor[39]=function(){return 8}();

global.bdor[200]=function(b){return bdor[b]};

bdor[41]=function(){return bdor[32](bdor[30](37,0),bdor[38]/bdor[40])}();

bdor[500]=global.bdor[404];

bdor[13]="p";

bdor[42]=function(){return bdor[33](bdor[32](37,33)+10,36)}();

Here, yet again, Firefox’s Debugger is helpful got get an insight. By searching for bdor and hovering over the variable name, a simple Tooltip appears.

Mouse, side

The line this.skin=this.bdor took me to this codeblock 60>d&&39>f&&9===c&&(b=$("<div>"+skin[12]+"l"+skin[66]+skin.m+skin.i(22)+skin[42]+skin[60]+"</div>"), which is called when flipping a page.

I initially set a breakpoint in the mentioned line to check the values in skin sadly all referenced values were empty.

Something Seems to be Missing

Although the “hidden” functions are mainly there to add an extra <div> element with some content to the page, I carried on looking around the JS. As I had already found one use of the eval() function I decided to check if there where more.

(this||(0,eval)("(this)")).bdor=[];

isPad())&&"slide"==bookConfig.FlipStyle.toLowerCase()?!0:!1},isHigherThanIOS8=function(){return $.system.name==$.system.IOS&&8<=$.system.version},isInTheFrame=function(){return window.top!=window};function isBelowIE9(){return $.browser.msie?9>$.browser.version?!0:!1:!1}function isBelowIE8(){return $.browser.msie?8>$.browser.version?!0:!1:!1}function isBelowIE10(){return $.browser.msie?10>$.browser.version?!0:!1:!1}var global=function(){return this||(0,eval)("(this)")}(),virtual_function=function(){};

return c.join("")}},parse:function(b,c){return b&&"undefined"!=b&&"null"!=b&&""!=b?eval("("+b+")"):c}};

eval("(adsbygoogle = window.adsbygoogle || []).push({});")})}

eval("ga('create', '"+bookConfig.googleAnalyticsID+"', 'auto')");

eval("ga('send', 'pageview')")},3E3)},controlAudioVolume=function(){if(!isNaN(bookConfig.audioVolume)){var b=parseFloat(bookConfig.audioVolume);global.bgSound&&global.bgSound.setVolume(b);flipAudio&&(flipAudio[0].volume=b)}},TurnonAutoFlip=function(){bookConfig.autoFlipOnStart&&window.setTimeout(function(){auto_player&&

function(){window.clearInterval(this.inteval);this.destroy();this.initBookStyle("flip_book")}.bind(this));this.slideModel.bind(_event._end,function(){window.clearInterval(this.inteval);this.destroy();this.initBookStyle("slide_book")}.bind(this));this.initInterval()},initInterval:function(){var b=10,c=getLanguage("lblSelectMode","Select View Mode Please.");this.inteval=window.setInterval(function(){this.description.html(c+"("+b+")");b--;0>=b&&(window.clearInterval(this.inteval),this.destroy(),this.initBookStyle("flip_book"))}.bind(this),

k=eval(h);

this.signatureCookies=d&&"undefined"!=d?eval(d):[]}catch(f){}var g,h;

try{var b=this.getLocalStorage(this.fileName),c=eval(b)}catch(d){}

b.scriptFun&&eval(b.scriptFun);

eval(DeString("f341819f83944fb222ea09f0eaa26b06d7198f1c00b6cd49"));

b.ctrlKey&&b.altKey&&(c==KEY_CODE_HOME||c==KEY_CODE_UP)&&eval(DeString("b29ec71982dc6d9062a0b77ce9ccc59581fd3c4a4fa9bd325e6f5ae7fbf39a0907064bb455b1bade956bc0f3e7c55d348fbaa363c39f7bef7a0648674fd1f688786b79bd6ad03215d634a4736f3f971501f664a09b820932d9bc83a9c2159964e5c571b3ebe4ceb214fd31bb1287fe5449f303a6d5531165e1386ba5aa8d0a3788d4a351d120a43a59c002d761ef"));

Going for the obvious I headed off to take a closer look the DeString function

function DeString(b,c)
{
  if(""==b)return"";
  c&&""!=c||(c="fb5");
  c=escape(c);
  if(null==b||8>b.length)
    alert("A salt value could not be extracted from the encrypted message because it's length is too short. The message cannot be decrypted.");
  else if(null==c||0>=c.length)
    alert("Please enter a password with which to decrypt the message.");
  else
  {
    for(var d="",f=0;f<c.length;f++)
      d+=c.charCodeAt(f).toString();
    var g=Math.floor(d.length/5),g=parseInt(d.charAt(g)+d.charAt(2*g)+d.charAt(3*g)+d.charAt(4*g)+d.charAt(5*g),10),h=Math.round(c.length/2),k=Math.pow(2,31)-1,f=parseInt(b.substring(b.length-8,b.length),16);
    b=b.substring(0,b.length-8);
    for(d+=f;10<d.length;)
      d=(parseInt(d.substring(0,10),10)+parseInt(d.substring(10,d.length),10)).toString();
    for(var d=(g*d+h)%k,l="",m="",f=0;f<b.length;f+=2)
      l=parseInt(parseInt(b.substring(f,f+2),16)^Math.floor(d/k*255),10),m+=String.fromCharCode(l),d=(g*d+h)%k;
    return unescape(m)
  }
}

I then used the Console tab in Firefox and decoded the strings from all calls I was able to find

  1. c3753b3f49449a65f9ad12578d7a4602c8ae34
    • Copyright:
  2. f341819f83944fb222ea09f0eaa26b06d7198f1c00b6cd49
    • initDemoBar()
  3. b29ec71982dc6d9062a0b77ce9ccc59581fd3c4a4fa9bd325e6f5ae7fbf39a0907064bb455b1bade956bc0f3e7c55d348fbaa363c39f7bef7a0648674fd1f688786b79bd6ad03215d634a4736f3f971501f664a09b820932d9bc83a9c2159964e5c571b3ebe4ceb214fd31bb1287fe5449f303a6d5531165e1386ba5aa8d0a3788d4a351d120a43a59c002d761ef
    
    • $(\"<div class='cr'>\" +cr + crBefore + crAfter + \"</div>\").appendTo($(\"body\"));
  4. d2b0aa5705413c96
    • Flip
  5. d35426b1c0d303cfa3012949ee
    • HTML5.com

Here String 4. is the value for crBefore and 5. the value for crAfter as referenced in 3.. 3. actually is the function called in the last eval() call, so it adds an extra footer to the page. Also turns out I just found were initDemoBar() is actually called (and I also know why I wasn’t able to find the call).

And Now?

Well, having had a closer look at main.js I currently cannot rate the overall security situation of the script. A few of the referenced array fields seem to be empty throughout overall execution of the script and I haven’t brought my head round the complete call tree of the functions using bdor and hddr. It might do something to the integrated password protection feature that I’m not using, it might also do something to the Flash feature (I’m only using HTML5, no Flash which would also add an swf of the book). Above that I found a class called PCShoppingCartItem, which is not used at all in my use case, so main.js is possibly reused in other projects of the same maker. I will simply have follow through the complete call tree once I have enough time and energy.

Although this might come as a surprise, I didn’t feel like using the script as is. As I still liked the UI it creates, I (hopefully) simply sanitized it. All references to the bdor, hddr and skin arrays were removed. I also stripped the DeString function and all applicable strings.

Btw, I swapped the used jquery-1.9.1.min.js with a newer jquery-3.3.1.min.js.

Finally

I have a PoC||GTFO Mirror!

Still I’ll be having a further look at main.js and will carry on deleting unused functionality.