פתרון ל-ctf של גוגל js safe 2

By | יוני 29, 2018

במאמר זה נעבור שלב אחרי שלב את האתגר של גוגל העוסק בהינדוס לאחור קוד JS אשר הוכנסו אליו מנגנוני anti debug.

לינק לאתגר

את האתגר וקבצי הפתרונות שלו ניתן למצוא גם בGIT של האתר בלינק הבא: לינק .

לפני שאתם ממשיכים לקרוא, מומלץ מאד לנסות ולפתור את האתגר לבד.

ja_safe_writeup_1

עמוד האתגר

נראה כי האתגר כולו עוסק במנגנונים שאמורים למנוע מאיתנו מלהבין מה הקוד שמריץ הדפדפן.

במאמר נעקוף שלב אחר שלב את המנגנונים האלו עד שנגיע לדגל.

 

שלב ראשון : הפיכת הקוד לקריא

לאחר שהורדנו אתת האתגר ופתחנו את העמוד js_safe_2.html ניתן להתחיל להבין את הלוגיקה שמריץ העמוד.

כשנריץ את הHTML נקבל את העמוד הבא.

עמוד האתגר

עמוד האתגר

כאשר נכנס למצב מפתחים נוכל לראות את הקוד .

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS safe v2.0 - the leading localStorage based safe solution with military grade JS anti-debug technology</title>
<!--
Advertisement:
Looking for a hand-crafted, browser based virtual safe to store your most
interesting secrets? Look no further, you have found it. You can order your own
by sending a mail to [email protected]. When ordering, please specify the
password you'd like to use to open and close the safe. We'll hand craft a
unique safe just for you, that only works with your password of choice.
-->
<style>
body {
  text-align: center;
}
input {
  font-size: 200%;
  margin-top: 5em;
  text-align: center;
  width: 26em;
}
#result {
  margin-top: 8em;
  font-size: 300%;
  font-family: monospace;
  font-weight: bold;
}
body.granted>#result::before {
  content: "Access Granted";
  color: green;
}
body.denied>#result::before {
  content: "Access Denied";
  color: red;
}
#content {
  display: none;
}
body.granted #content {
  display: initial;
}
.wrap {
  display: inline-block;
  margin-top: 50px;
  perspective: 800px;
  perspective-origin: 50% 100px;
}
.cube {
  position: relative;
  width: 200px;
  transform-style: preserve-3d;
}
.back {
  transform: translateZ(-100px) rotateY(180deg);
}
.right {
  transform: rotateY(-270deg) translateX(100px);
  transform-origin: top right;
}
.left {
  transform: rotateY(270deg) translateX(-100px);
  transform-origin: center left;
}
.top {
  transform: rotateX(-90deg) translateY(-100px);
  transform-origin: top center;
}
.bottom {
  transform: rotateX(90deg) translateY(100px);
  transform-origin: bottom center;
}
.front {
  transform: translateZ(100px);
}
@keyframes spin {
  from { transform: rotateY(0); }
  to { transform: rotateY(360deg); }
}
.cube {
  animation: spin 20s infinite linear;
}
.cube div {
  position: absolute;
  width: 200px;
  height: 200px;
  background: rgba(0, 0, 0, 0.51);
  box-shadow: inset 0 0 60px white;
  font-size: 20px;
  text-align: center;
  line-height: 200px;
  color: rgba(0,0,0,0.5);
  font-family: sans-serif;
  text-transform: uppercase;
}
</style>
<script>
function x(х){ord=Function.prototype.call.bind(''.charCodeAt);chr=String.fromCharCode;str=String;function h(s){for(i=0;i!=s.length;i++){a=((typeof a=='undefined'?1:a)+ord(str(s[i])))%65521;b=((typeof b=='undefined'?0:b)+a)%65521}return chr(b>>8)+chr(b&0xFF)+chr(a>>8)+chr(a&0xFF)}function c(a,b,c){for(i=0;i!=a.length;i++)c=(c||'')+chr(ord(str(a[i]))^ord(str(b[i%b.length])));return c}for(a=0;a!=1000;a++)debugger;x=h(str(x));source=/Ӈ#7ùª9¨M¤ŸÀ.áÔ¥6¦¨¹.ÿÓÂ.։£JºÓ¹WþʖmãÖÚG¤…¢dÈ9&òªћ#³­1᧨/;source.toString=function(){return c(source,x)};try{console.log('debug',source);with(source)return eval('eval(c(source,x))')}catch(e){}}
</script>
<script>
function open_safe() {
  keyhole.disabled = true;
  password = /^CTF{([0-9a-zA-Z_@!?-]+)}$/.exec(keyhole.value);
  if (!password || !x(password[1])) return document.body.className = 'denied';
  document.body.className = 'granted';
  password = Array.from(password[1]).map(c => c.charCodeAt());
  encrypted = JSON.parse(localStorage.content || '');
  content.value = encrypted.map((c,i) => c ^ password[i % password.length]).map(String.fromCharCode).join('')
}
function save() {
  plaintext = Array.from(content.value).map(c => c.charCodeAt());
  localStorage.content = JSON.stringify(plaintext.map((c,i) => c ^ password[i % password.length]));
}
</script>
</head>
<body>
<div>
  <input id="keyhole" autofocus onchange="open_safe()" placeholder="<img draggable="false" data-mce-resize="false" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="<img draggable="false" data-mce-resize="false" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="🔑" src="https://s.w.org/images/core/emoji/2.4/svg/1f511.svg">" src="https://s.w.org/images/core/emoji/2.4/svg/1f511.svg">">
</div>
<div class="wrap">
  <div class="cube">
    <div class="front"></div>
    <div class="back"></div>
    <div class="top"></div>
    <div class="bottom"></div>
    <div class="left"></div>
    <div class="right"></div>
  </div>
</div>
<div id="result">
</div>
<div>
  <input id="content" onchange="save()">
</div>
</body>
</html>

 

שורות 6-13: נותנות הסבר נוסף על האתגר.

שורה 119: הינה השורה של הקלט שלנו וממנה הולכים לפונקציה open_safe().

שורה 104: בתוך הפונקציה open_safe() מוודאת את תוכן הקלט ומחייבת אותו להיותר מהתצורה CTF{([0-9a-zA-Z_@!?-]+)}.

שורה 105: קוראת לפנקציה X שבה ככל הנראה מתבצע החישוב שלנו.

שורה 99:  הינה השורה של הפונקציה שכרגע קצת קשה לנו להבין מה היא עושה.

אז נסדר קצת את הקוד…

בדפדפן במצב מפתחים נלחץ על pretty print ונקבל את הקוד מסודר.

נעתיק את הקוד לעמוד חדש ונעבוד איתו.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JS safe v2.0 - the leading localStorage based safe solution with military grade JS anti-debug technology</title>
        <!--
Advertisement:
Looking for a hand-crafted, browser based virtual safe to store your most
interesting secrets? Look no further, you have found it. You can order your own
by sending a mail to [email protected]. When ordering, please specify the
password you'd like to use to open and close the safe. We'll hand craft a
unique safe just for you, that only works with your password of choice.
-->
        <style>
            body {
                text-align: center;
            }

            input {
                font-size: 200%;
                margin-top: 5em;
                text-align: center;
                width: 26em;
            }

            #result {
                margin-top: 8em;
                font-size: 300%;
                font-family: monospace;
                font-weight: bold;
            }

            body.granted>#result::before {
                content: "Access Granted";
                color: green;
            }

            body.denied>#result::before {
                content: "Access Denied";
                color: red;
            }

            #content {
                display: none;
            }

            body.granted #content {
                display: initial;
            }

            .wrap {
                display: inline-block;
                margin-top: 50px;
                perspective: 800px;
                perspective-origin: 50% 100px;
            }

            .cube {
                position: relative;
                width: 200px;
                transform-style: preserve-3d;
            }

            .back {
                transform: translateZ(-100px) rotateY(180deg);
            }

            .right {
                transform: rotateY(-270deg) translateX(100px);
                transform-origin: top right;
            }

            .left {
                transform: rotateY(270deg) translateX(-100px);
                transform-origin: center left;
            }

            .top {
                transform: rotateX(-90deg) translateY(-100px);
                transform-origin: top center;
            }

            .bottom {
                transform: rotateX(90deg) translateY(100px);
                transform-origin: bottom center;
            }

            .front {
                transform: translateZ(100px);
            }

            @keyframes spin {
                from {
                    transform: rotateY(0);
                }

                to {
                    transform: rotateY(360deg);
                }
            }

            .cube {
                animation: spin 20s infinite linear;
            }

            .cube div {
                position: absolute;
                width: 200px;
                height: 200px;
                background: rgba(0, 0, 0, 0.51);
                box-shadow: inset 0 0 60px white;
                font-size: 20px;
                text-align: center;
                line-height: 200px;
                color: rgba(0,0,0,0.5);
                font-family: sans-serif;
                text-transform: uppercase;
            }
        </style>
        <script>
            function x(х) {
                ord = Function.prototype.call.bind(''.charCodeAt);
                chr = String.fromCharCode;
                str = String;
                function h(s) {
                    for (i = 0; i != s.length; i++) {
                        a = ((typeof a == 'undefined' ? 1 : a) + ord(str(s[i]))) % 65521;
                        b = ((typeof b == 'undefined' ? 0 : b) + a) % 65521
                    }
                    return chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF)
                }
                function c(a, b, c) {
                    for (i = 0; i != a.length; i++)
                        c = (c || '') + chr(ord(str(a[i])) ^ ord(str(b[i % b.length])));
                    return c
                }
                for (a = 0; a != 1000; a++)
                    debugger ;
                x = h(str(x));
                source = /Ӈ#7ùª9¨M¤ŸÀ.áÔ¥6¦¨¹.ÿÓÂ.։£JºÓ¹WþʖmãÖÚG¤…¢dÈ9&òªћ#³­1᧨/;
                source.toString = function() {
                    return c(source, x)
                }
                ;
                try {
                    console.log('debug', source);
                    with (source)
                        return eval('eval(c(source,x))')
                } catch (e) {}
            }
        </script>
        <script>
            function open_safe() {
                keyhole.disabled = true;
                password = /^CTF{([0-9a-zA-Z_@!?-]+)}$/.exec(keyhole.value);
                if (!password || !x(password[1]))
                    return document.body.className = 'denied';
                document.body.className = 'granted';
                password = Array.from(password[1]).map(c=>c.charCodeAt());
                encrypted = JSON.parse(localStorage.content || '');
                content.value = encrypted.map((c,i)=>c ^ password[i % password.length]).map(String.fromCharCode).join('')
            }
            function save() {
                plaintext = Array.from(content.value).map(c=>c.charCodeAt());
                localStorage.content = JSON.stringify(plaintext.map((c,i)=>c ^ password[i % password.length]));
            }
        </script>
    </head>
    <body>
        <div>
            <input id="keyhole" autofocus onchange="open_safe()" placeholder="🔑">
        </div>
        <div class="wrap">
            <div class="cube">
                <div class="front"></div>
                <div class="back"></div>
                <div class="top"></div>
                <div class="bottom"></div>
                <div class="left"></div>
                <div class="right"></div>
            </div>
        </div>
        <div id="result"></div>
        <div>
            <input id="content" onchange="save()">
        </div>
    </body>
</html>

שלב שני: ביטול המנגנונים ששמפריעים לנו בתהליך הדיבוג

כאשר ננסה לדבג את הקוד עם קלט כלשהו לדוגמאת {CTF{a נראה שאנחנו כל הזמן נעצרים בשורה 138 ולכן נסיר אותה מהקוד שלנו.

חשוב שלא למחוק את הלולאה בשורה 137 , הלולאה מגדירה משתנה a=1000 שנצטרך שיאותחל להמשך(המשתנה מאותחל בDOM וערכו נלקח בפונקציה h ).

ניתן להסיק כי הפונקציה h שבשורה 125 מקבלת קלט כלשהו ויוצרת ממנו "מפתח" בוגדל 4 בתים.

הפונקציה c  בשורה 132 ככל  הנראה עושה XOR לטקסט a  עם מפתח b.

שורה 140 תכיל את הקוד המוצפן אותו נצטרך לפתוח ובתקווה יכיל את הדגל הדרוש  למעבר שלב.

שורה 141 הינה פונקציה רקורסיבית שבזמן דיבוג תתקע את הדפדפן ולכן נסיר אותה.

שורה 149 תנסה להריץ את התוצאה של ה XOR שלנו ובתקווה תדפיס את המפתח.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JS safe v2.0 - the leading localStorage based safe solution with military grade JS anti-debug technology</title>
        <!--
Advertisement:
Looking for a hand-crafted, browser based virtual safe to store your most
interesting secrets? Look no further, you have found it. You can order your own
by sending a mail to [email protected]. When ordering, please specify the
password you'd like to use to open and close the safe. We'll hand craft a
unique safe just for you, that only works with your password of choice.
-->
        <style>
            body {
                text-align: center;
            }

            input {
                font-size: 200%;
                margin-top: 5em;
                text-align: center;
                width: 26em;
            }

            #result {
                margin-top: 8em;
                font-size: 300%;
                font-family: monospace;
                font-weight: bold;
            }

            body.granted>#result::before {
                content: "Access Granted";
                color: green;
            }

            body.denied>#result::before {
                content: "Access Denied";
                color: red;
            }

            #content {
                display: none;
            }

            body.granted #content {
                display: initial;
            }

            .wrap {
                display: inline-block;
                margin-top: 50px;
                perspective: 800px;
                perspective-origin: 50% 100px;
            }

            .cube {
                position: relative;
                width: 200px;
                transform-style: preserve-3d;
            }

            .back {
                transform: translateZ(-100px) rotateY(180deg);
            }

            .right {
                transform: rotateY(-270deg) translateX(100px);
                transform-origin: top right;
            }

            .left {
                transform: rotateY(270deg) translateX(-100px);
                transform-origin: center left;
            }

            .top {
                transform: rotateX(-90deg) translateY(-100px);
                transform-origin: top center;
            }

            .bottom {
                transform: rotateX(90deg) translateY(100px);
                transform-origin: bottom center;
            }

            .front {
                transform: translateZ(100px);
            }

            @keyframes spin {
                from {
                    transform: rotateY(0);
                }

                to {
                    transform: rotateY(360deg);
                }
            }

            .cube {
                animation: spin 20s infinite linear;
            }

            .cube div {
                position: absolute;
                width: 200px;
                height: 200px;
                background: rgba(0, 0, 0, 0.51);
                box-shadow: inset 0 0 60px white;
                font-size: 20px;
                text-align: center;
                line-height: 200px;
                color: rgba(0,0,0,0.5);
                font-family: sans-serif;
                text-transform: uppercase;
            }
        </style>
        <script>
            function x(х) {
                ord = Function.prototype.call.bind(''.charCodeAt);
                chr = String.fromCharCode;
                str = String;
                function h(s) {
                    for (i = 0; i != s.length; i++) {
                        a = ((typeof a == 'undefined' ? 1 : a) + ord(str(s[i]))) % 65521;
                        b = ((typeof b == 'undefined' ? 0 : b) + a) % 65521
                    }
                    return chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF)
                }
                function c(a, b, c) {
                    for (i = 0; i != a.length; i++)
                        c = (c || '') + chr(ord(str(a[i])) ^ ord(str(b[i % b.length])));
                    return c
                }
                //for (a = 0; a != 1000; a++); or just a=1000;
				a=1000;
    //                debugger ;
                x = h(str(x));
                source = /Ӈ#7ùª9¨M¤ŸÀ.áÔ¥6¦¨¹.ÿÓÂ.։£JºÓ¹WþʖmãÖÚG¤…¢dÈ9&òªћ#³­1᧨/;
                /*source.toString = function() {
                    return c(source, x)
                }
                ;*/
                try {
                    console.log('debug', source);
                    with (source)
                        return eval('eval(c(source,x))')
                } catch (e) {}
            }
        </script>
        <script>
            function open_safe() {
                keyhole.disabled = true;
                password = /^CTF{([0-9a-zA-Z_@!?-]+)}$/.exec(keyhole.value);
                if (!password || !x(password[1]))
                    return document.body.className = 'denied';
                document.body.className = 'granted';
                password = Array.from(password[1]).map(c=>c.charCodeAt());
                encrypted = JSON.parse(localStorage.content || '');
                content.value = encrypted.map((c,i)=>c ^ password[i % password.length]).map(String.fromCharCode).join('')
            }
            function save() {
                plaintext = Array.from(content.value).map(c=>c.charCodeAt());
                localStorage.content = JSON.stringify(plaintext.map((c,i)=>c ^ password[i % password.length]));
            }
        </script>
    </head>
    <body>
        <div>
            <input id="keyhole" autofocus onchange="open_safe()" placeholder="🔑">
        </div>
        <div class="wrap">
            <div class="cube">
                <div class="front"></div>
                <div class="back"></div>
                <div class="top"></div>
                <div class="bottom"></div>
                <div class="left"></div>
                <div class="right"></div>
            </div>
        </div>
        <div id="result"></div>
        <div>
            <input id="content" onchange="save()">
        </div>
    </body>
</html>

 

עכשיו כאשר אנחנו יכולים לדבאג נפזר breakpoints ונריץ

שלב שלישי: הבנת הליך פתיחת התוכן המוצפן

נשים לב למשהו מוזר שקורה בקריאה לפנקציה  h הפונקציה מקבלת כפרמטר את תוכן פונקציה x ולא את המשתנה שלה (הסיסמא שהכנסו).

 

נבחן את ההצהרה של פונקציה X כדי להבין מה קורה ונגלה שהמשנה מקודד בצורה אחרת משם הפונקציה

 

ניתן להסיק מזה כי המפתח אשר נוצר ב h צריך את התוכן של x ללא שינוי ולכן נשנה את הקוד שלנו טיפה,

נעתיק את x המקורי לעמוד שלנו את הפונקציה x שלנו נשנה ונקרא לה בשם אחר לדוגמא x1

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JS safe v2.0 - the leading localStorage based safe solution with military grade JS anti-debug technology</title>
        <!--
Advertisement:
Looking for a hand-crafted, browser based virtual safe to store your most
interesting secrets? Look no further, you have found it. You can order your own
by sending a mail to [email protected]. When ordering, please specify the
password you'd like to use to open and close the safe. We'll hand craft a
unique safe just for you, that only works with your password of choice.
-->
        <style>
            body {
                text-align: center;
            }

            input {
                font-size: 200%;
                margin-top: 5em;
                text-align: center;
                width: 26em;
            }

            #result {
                margin-top: 8em;
                font-size: 300%;
                font-family: monospace;
                font-weight: bold;
            }

            body.granted>#result::before {
                content: "Access Granted";
                color: green;
            }

            body.denied>#result::before {
                content: "Access Denied";
                color: red;
            }

            #content {
                display: none;
            }

            body.granted #content {
                display: initial;
            }

            .wrap {
                display: inline-block;
                margin-top: 50px;
                perspective: 800px;
                perspective-origin: 50% 100px;
            }

            .cube {
                position: relative;
                width: 200px;
                transform-style: preserve-3d;
            }

            .back {
                transform: translateZ(-100px) rotateY(180deg);
            }

            .right {
                transform: rotateY(-270deg) translateX(100px);
                transform-origin: top right;
            }

            .left {
                transform: rotateY(270deg) translateX(-100px);
                transform-origin: center left;
            }

            .top {
                transform: rotateX(-90deg) translateY(-100px);
                transform-origin: top center;
            }

            .bottom {
                transform: rotateX(90deg) translateY(100px);
                transform-origin: bottom center;
            }

            .front {
                transform: translateZ(100px);
            }

            @keyframes spin {
                from {
                    transform: rotateY(0);
                }

                to {
                    transform: rotateY(360deg);
                }
            }

            .cube {
                animation: spin 20s infinite linear;
            }

            .cube div {
                position: absolute;
                width: 200px;
                height: 200px;
                background: rgba(0, 0, 0, 0.51);
                box-shadow: inset 0 0 60px white;
                font-size: 20px;
                text-align: center;
                line-height: 200px;
                color: rgba(0,0,0,0.5);
                font-family: sans-serif;
                text-transform: uppercase;
            }
        </style>
        <script>
		function x(х){ord=Function.prototype.call.bind(''.charCodeAt);chr=String.fromCharCode;str=String;function h(s){for(i=0;i!=s.length;i++){a=((typeof a=='undefined'?1:a)+ord(str(s[i])))%65521;b=((typeof b=='undefined'?0:b)+a)%65521}return chr(b>>8)+chr(b&0xFF)+chr(a>>8)+chr(a&0xFF)}function c(a,b,c){for(i=0;i!=a.length;i++)c=(c||'')+chr(ord(str(a[i]))^ord(str(b[i%b.length])));return c}for(a=0;a!=1000;a++)debugger;x=h(str(x));source=/Ӈ#7ùª9¨M¤ŸÀ.áÔ¥6¦¨¹.ÿÓÂ.։£JºÓ¹WþʖmãÖÚG¤…¢dÈ9&òªћ#³­1᧨/;source.toString=function(){return c(source,x)};try{console.log('debug',source);with(source)return eval('eval(c(source,x))')}catch(e){}}
            function x1(х) {
                ord = Function.prototype.call.bind(''.charCodeAt);
                chr = String.fromCharCode;
                str = String;
                function h(s) {
                    for (i = 0; i != s.length; i++) {
                        a = ((typeof a == 'undefined' ? 1 : a) + ord(str(s[i]))) % 65521;
                        b = ((typeof b == 'undefined' ? 0 : b) + a) % 65521
                    }
                    return chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF)
                }
                function c(a, b, c) {
                    for (i = 0; i != a.length; i++)
                        c = (c || '') + chr(ord(str(a[i])) ^ ord(str(b[i % b.length])));
                    return c
                }
                //for (a = 0; a != 1000; a++); or just a=1000;
				a=1000;
    //                debugger ;
                x = h(str(x));
                source = /Ӈ#7ùª9¨M¤ŸÀ.áÔ¥6¦¨¹.ÿÓÂ.։£JºÓ¹WþʖmãÖÚG¤…¢dÈ9&òªћ#³­1᧨/;
                /*source.toString = function() {
                    return c(source, x)
                }
                ;*/
                try {
                    console.log('debug', source);
                    with (source)
                        return eval('eval(c(source,x))')
                } catch (e) {}
            }
        </script>
        <script>
            function open_safe() {
                keyhole.disabled = true;
                password = /^CTF{([0-9a-zA-Z_@!?-]+)}$/.exec(keyhole.value);
                if (!password || !x1(password[1]))
                    return document.body.className = 'denied';
                document.body.className = 'granted';
                password = Array.from(password[1]).map(c=>c.charCodeAt());
                encrypted = JSON.parse(localStorage.content || '');
                content.value = encrypted.map((c,i)=>c ^ password[i % password.length]).map(String.fromCharCode).join('')
            }
            function save() {
                plaintext = Array.from(content.value).map(c=>c.charCodeAt());
                localStorage.content = JSON.stringify(plaintext.map((c,i)=>c ^ password[i % password.length]));
            }
        </script>
    </head>
    <body>
        <div>
            <input id="keyhole" autofocus onchange="open_safe()" placeholder="🔑">
        </div>
        <div class="wrap">
            <div class="cube">
                <div class="front"></div>
                <div class="back"></div>
                <div class="top"></div>
                <div class="bottom"></div>
                <div class="left"></div>
                <div class="right"></div>
            </div>
        </div>
        <div id="result"></div>
        <div>
            <input id="content" onchange="save()">
        </div>
    </body>
</html>

לאחר הרצה ניתן לראות כי הצלחנו לפתוח את התוכן המוצפן (יאיייייייי)

פתיחת התוכן המוצפן

פתיחת התוכן המוצפן

שלב רביעי: בניית תוכנית המשך

אז הצלחנו לפתוח את החלק המוצפן אבל מה עושים מפה ??

כדי לענות על זה נצטרך להבין את השורות הבאות:

                try {
                    console.log('debug', source);
                    with (source)
                        return eval('eval(c(source,x))')
                } catch (e) {}

ההגדרה with  מוסיפה את האובייקט לסקופ שלנו וחוסכת מאיתנו לכתוב אותו ולכן נוכל לשנות את השורה המחזירה ערך לדבר הבא:

return eval('eval(c(source.source,x))')

הערך שיחוזר מהביטוי source.source הינו הערך הטקסואלי (string) של הביטוי הרגולארי שלנו.

eval  מאפשר לקחת טקסט ולהריץ אותו.

זאת אומרת שלאחר שהסגנו את התוכן המוצפן נוכל להחליף את השורה כולה בשורה הבאה:

return х==c('¢×&Ê´cʯ¬$¶³´}ÍÈ´T—©Ð8ͳÍ|Ԝ÷aÈÐÝ&›¨þJ',h(х))//᧢

הפעם ה X הוא משתנה הפונצקציה ולכן נצטרך למצוא את המפתח הXOR( (פרמטר h(x ) שידע לפתוח את התוכן.

 

שלב רביעי +: הבנת אופן פעולת XOR

כדי שנוכל להמשיך נצטרך להבין כיצד XOR עובד ולנצל את התוכנות שלו..

הנתונים שיש לנו עד עכשיו :

  1. התוכן המוצפן – ¢×&Ê´cʯ¬$¶³´}ÍÈ´T—©Ð8ͳÍ|Ԝ÷aÈÐÝ&›¨þJ
  2. גודל התוכן המוצפן 39
    1. ב- XOR גודל התוכן לא משתנה ולכן גם גודל הטקסט שלנו אמור לצאת 39
  3. גודל המפתח (הרי נוצר על ידי h ) – בתים4

שלב חמישי: bruteforce

כנראה שאין ברירה ונצטרך להריץ ברוטפורס שינסה את כל האפשרויות על 4 תווים ולקוות שהמפתח נמצא בהתחלה.

לשם כך כתבתי את הסקריפט הבא:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JS safe v2.0 - BruteForce</title>

<script>

	ord=Function.prototype.call.bind(''.charCodeAt);
	chr=String.fromCharCode;str=String;

	function c(a,b,c){
		for(i=0;i!=a.length;i++)
			c=(c||'')+chr(ord(str(a[i]))^ord(str(b[i%b.length])));
		return c
	}

	function brute()
	{
	myreg=/^([0-9a-zA-Z_@!?-]+)$/
	for(cha=0;cha<256;cha++){
	console.log("in",cha)
		for(chb=0;chb<256;chb++){
			for(chc=0;chc<256;chc++){
				for(chd=0;chd!=256;chd++){
					mypass=c('¢×&Ê´cʯ¬$¶³´}ÍÈ´T—©Ð8ͳÍ|Ԝ÷aÈÐÝ&›¨þJ',chr(cha)+chr(chb)+chr(chc)+chr(chd))
					  if (myreg.test(mypass)){
					  console.log (cha,chb,chc,chd,mypass);
					  }
				}

			}

		}
	}
}
</script>
    </head>
    <body>
        <div>
		 <button type="button" onclick="brute()">Run!</button>


        </div>
    </body>
</html>

נראה שהסקריפט אכן רץ אך לאחר כמה דקות הבנתי שזאת לא הדרך , הסקריפט לא מצא את הסיסמא ומספר האפשרויות עצום ( 2 בחזקת 32).

bruteforce

bruteforce

חייבת להיות דרך ליעל את הסקריפט.

ואכן דבר חשוב נוסף שצריך לדעת על XOR הוא שבמידה ויש לנו חלק נכון של המפתח נוכל לפתוח חלק מהתוכן המוצפן, לדוגמא אם יש לנו את התו הראשון ממפתח בגודל 4 נוכל לפתוח כל תו רביעי מאינדקס 0 ( התו הראשון , הרביעי וכו').

הוספתי את הבדיקה הזאת לסקריפט:

 

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JS safe v2.0 - bruteforce</title>

<script>

	ord=Function.prototype.call.bind(''.charCodeAt);
	chr=String.fromCharCode;str=String;

	function c(a,b,c){
		for(i=0;i!=a.length;i++)
			c=(c||'')+chr(ord(str(a[i]))^ord(str(b[i%b.length])));
		return c
	}

	function brute()
	{
	myreg=/^([0-9a-zA-Z_@!?-]+)$/
	for(cha=0;cha<256;cha++){
	console.log("in",cha)
		for(chb=0;chb<256;chb++){
			for(chc=0;chc<256;chc++){
				for(chd=0;chd!=256;chd++){
					mypass=c('¢×&Ê´cʯ¬$¶³´}ÍÈ´T—©Ð8ͳÍ|Ԝ÷aÈÐÝ&›¨þJ',chr(cha)+chr(chb)+chr(chc)+chr(chd))
					  if (myreg.test(mypass)){
					  console.log (cha,chb,chc,chd,mypass);
					  document.getElementById('res').innerHTML += '<br>password=' +mypass+'a='+cha+' b='+chb+' c='+chc+' d='+chd;

					  }
					  else{
					  	if (!(myreg.test(mypass.substr(0,1))) || !(myreg.test(mypass.substr(4,1))) || !(myreg.test(mypass.substr(8,1)))|| !(myreg.test(mypass.substr(12,1)))){
						cha++;
						chb=0;
						chc=0;
						chd=-1;

						}
						
						else if (!(myreg.test(mypass.substr(1,1))) || !(myreg.test(mypass.substr(5,1))) || !(myreg.test(mypass.substr(9,1))))
						{
						chb++;
						chc=0;
						chd=-1;
						}
						else if (!(myreg.test(mypass.substr(2,1))) || !(myreg.test(mypass.substr(6,1))) || !(myreg.test(mypass.substr(10,1))))
						{
						chc++;
						chd=-1;
						}
					  }

				}

			}

		}
	}
}
</script>
    </head>
    <body>
        <div>
		 <button type="button" onclick="brute()">Run!</button>

        <div id='res'>
		</div>
        </div>
    </body>
</html>

ואכן לאחר מספר דקות קיבלנו את הדגל =]

הדגל

הדגל

 

קיבלנו את הדגל _N3x7-v3R51ON-h45-AnTI-4NTi-ant1-D3bUg_

 

הדגל

בדיקת הדגל

 

וסיימנו !!!

 

את האתגר פתרתי ביחד עם שלו אלחייני (שהבטיח שאת המאמר הבא הוא רושם, אז יש למה לצפות !!!)

דירוג

Comments

comments

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *