• 如果您想对本站表示支持,请随手点击一下广告即可~
  • 本站致力于提供原创、优秀的技术文章~
  • 有任何疑问或建议 均可以在站点右侧栏处 通过各种方式联系站长哦~
  • CTF – Prompt(1)解题报告 [Level D – Json Object]

    渗透测试 EXP 184阅读 0评论

    指引

    题目

    解题报告

    前置知识

    相当难的一道综合题型,考察对 Javascript 原理的理解程度,相关知识点如下:


    代码分析

    首先需要清楚代码逻辑,我们逐行分析下。

    这里告知了我们输入的 input 格式只能为 JSON(不过 data 这个变量在本题是毫无用处的):

    然后 input 会与一个固定的 JSON { 'source' : 'http://placehold.it/350x150' } 执行 extend 操作。

    这个 extend 函数看似复杂,但其实做的事情很简单:

    检测 input 的 JSON 顶层是否具有属性 source ,若有则不对 input 做任何修改。否则则在 input 的 JSON 顶层 添加属性 source ,且取默认值为 http://placehold.it/350x150 。实际上这个函数是没什么用的。

    处理后的 input JSON 对象存储到 config 变量中:

    继而利用 test 函数正则校验 config JSON 对象的顶层属性 source 的值,若其值含有 0-9a-zA-Z_:/.、 以外的字符,则删除 source 属性。

    换言之这里是避免我们在顶层属性 source 编写 payload 。

    即使 config JSON 对象的顶层属性 source 得以保留,也会把其中的双引号 " 全部过滤。

    换言之这行代码是避免我们闭合 JSON 属性。

    最后把 source 的值作为 <img> 标签的 src 属性值输出到前端。

    大致的代码逻辑分析完毕,接下来可以开始寻找逻辑缺陷解题。


    replace 正则绕过

    由于过程略复杂,我们不妨从最终期望的目标开始,反向推导 payload 。

    先不管前面代码逻辑如何,最后两行代码是:

    即我们所构造的 source 值,必须不含双引号 ",且能够触发 prompt(1) 事件。

    source 是正常的图片 URL 时,不妨在浏览器控制台调试一下代码,看一下效果:

    正常情况下,如果要在 <img> 标签注入 JS ,一般是可以通过诸如 <img src="0" onerror=prompt(1) > 的方式。

    但因为双引号 " 被过滤了,我们无法通过闭合 src 的双引号再增加 onerror 属性。


    但是根据 JS 的 replace('{{source}}', source) 函数的语法,第二个由我们控制的参数 source 是可以插入特殊变量名以达到某些效果的(详见 这里 ):

    而我们要使用的特殊变量名,就是 $` ,这个变量名的效果是【插入当前匹配的子串左边的内容】。

    就这题而言,因为 <img src="{{source}}">'.replace('{{source}}', source) 第一个参数 {{source}} 匹配了原字符串,而所匹配部分的左边内容是 <img src=",因此若第二个参数 source 含有特殊变量 $` ,就会把 <img src=" 插入到该特殊变量位置。

    注意到所插入的 <img src=" 最右侧刚好有一个双引号,那么我们就可以用来闭合 src 属性的双引号了。

    于是我们可以构造 source 的值为 $` onerror=prompt(1) >

    当特殊变量 $` 被替换后,实际就等价于 <img src=" onerror=prompt(1) > ,再将其通过 replace 替换到原串的 {{source}} ,就可以得到:

    src 属性值等于 "<img src=" ,被成功闭合了,同时因为是一个无效值,会触发到 onerror 的 JS 。


    JSON 欺骗

    那么接下来的问题就是,怎么保留我们所构造的 source 值到最后。

    根据前面的分析知道, source 值应该是 $` onerror=prompt(1) > ,而 source 值本质就是源于我们输入的 json 的 source 属性值。

    但是在此之前有这样的一段 test 代码,当 source 值含有 0-9a-zA-Z_:/.、 以外的字符,则删除 json 的 source 属性:

    很不幸地,我们构造的 source 值是满足删除标准的。

    换言之,若直接 input 的 JSON 为 { 'source' : '$` onerror=prompt(1)' } ,是无法把 source 属性值保留到最后的。


    最直接的想法是,能不能在 JSON 构造两个 source 属性骗过正则校验,使得其中一个没用的 source 被删除,而我们构造的 source 则得以保留。

    不过问题是,JSON 是具备 hash 特性的,若直接在同级构造两个同名属性 source ,后者是会覆盖前者的。

    在浏览器控制台测试了一下,同名属性果然是会覆盖的:

    不过也并非一无所获,从控制台里面注意到,所构造的 JSON 对象具有一个隐藏属性 __proto__


    特意去查了一下这个属性的作用(详见 这里),得知在 JS 代码中,每个 JSON 对象都具有一个隐藏属性 __proto__ ,而这个属性本质上是一个访问器,其作用是当我们需要访问 JSON 对象中的某个属性值时,可以提供类似于 getter / setter 访问方法的语法糖。

    例如若在 JS 代码中定义一个这样的 JSON 变量 var json = {"source": "exp"}

    • 当需要访问 source 的属性值时,如: var src = json.source ,实际上是 __proto__getter 在起作用
    • 当需要修改 source 的属性值时,如: json.source = "EXP" ,实际上是 __proto__setter 在起作用

    虽然 __proto__ 是一个访问器,不过默认情况下,我们是不可以 json.__proto__.source 这样访问属性的。

    但有趣的是,假如在 JSON 中显式设置__proto__ 属性,例如这样:{"__proto__": {"source": "exp"}}

    那么就会给 JS 解析器造成某些“混乱”,使得诸如 json.__proto__.source 的访问属性方式变成可能。

    不但如此,此时 JSON 还同时支持 json.sourcejson.__proto__.source 两种访问属性方式,且他们是等价的:


    利用这个特点,我们就可以在 JSON 的同级构造两个同名属性

    例如在 JS 中定义这样的一个 JSON 变量 var json = {"source": "EXP", "__proto__": {"source": "M02"}}

    "source": "EXP" 属性存在时:

    • json.source 会优先得到 EXP 的值
    • json.__proto__.source 会得到全路径 M02 的值

    "source": "EXP" 属性不存在时:

    • json.source 会通过 __proto__ 访问器得到 M02 的值
    • json.__proto__.source 依旧会得到全路径 M02 的值

    回到这题,我们可以利用这个 JSON 特性进行欺骗,在 input 构造一个类似这样的 JSON :

    input = {"source": "--delete me--", "__proto__": {"source": "payload"}}

    其中第一个 source 只需要满足代码中 test 的正则条件使之被删除即可,这样第二个用于 payload 的 source 则可以保留到最后。


    完成挑战

    结合前面所有分析,最终可以构造 payload 如下,完成挑战:


    转载请注明:EXP 技术分享博客 » CTF – Prompt(1)解题报告 [Level D – Json Object]

    喜欢 (0) 分享 (0)
    发表我的评论
    取消评论

    表情

    Hi,您需要填写昵称和邮箱!

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址