看了 Orange大佬的 “Google CTF 2018 Quals Web Challenge - gCalc” 一文。发现了许多有趣的小技巧小知识。这里简单写一写关于这些小知识的理解。
分析正则表达式
原文中提到了一个在线正则表达式平台,使用了下确实好用。
1 | /^(?:[\(\)\*\/\+%\-0-9 ]|\bvars\b|[.]\w+)*$/ |
这个正则表达式主要内容可以分为 三部分,注意各个部分是使用 | 进行分割:
1 | ?:[\(\)\*\/\+%\-0-9 ] |
扩展解释下正则表达式
A|B 为或,匹配捕获 A 或者 B
A(?=B) 匹配时 AB 需要连着。捕获时只返回A
A(?!B) 匹配时 AB 不能连着。捕获时只返回A
A(?:B) 匹配时 AB 需要连着。但是不捕获。仅匹配。一般配合 | 使用。可以理解为 可有可无


这一段正则表达式的意思为,仅匹配 ( ) * / + % - 0-9 空格
这些字符
1 | \bvars\b |
\b 匹配一个单词边界,不过好像不太重要
这一段正则表达式的意思为,待匹配字符串必须存在 vars
这一个字符串
1 | [.]\w+ |
这一段正则表达式的意思为,匹配 .
和各个单词字符
regex101 有 Debugger 模式,可以输入以下字符串进行匹配,更易理解
1 | (vars).toString() |
constructor
Object 都带有一个 constructor,用于返回创建实例对象的构造函数的引用。
不同类型会不一样。即什么类型的 constructor 将会返回什么类型的实例
1 | x1 = 1 |
基本使用看官方文档就好了。
重点说下有意思的地方。
如果调用 一层 constructor。则这个构造函数是 对应类型 的构造函数。只能输入指定类型
1 | x1 = 1 |
但是如果调用 多层 constructor. 则多层构造函数返回一个匿名函数。函数体内容为输入的参数:
1 | x.constructor.constructor("alert(1)") |
要想实现调用,在末尾添加一对 ()
即可。这是 js 调用函数的格式
1 | x.constructor.constructor("alert(1)")() |
在 writeup 中,由于无法传入单双引号。所以第一个 Payload 只能使用 fromCharCode()
的方式获取字符串
Javascript 无引号利用
文章中最终的 payload 为
1 | (1).constructor.constructor(/1/.exec(1).keys(1).constructor.keys(vars).pop()) |
首先需要理解,最终的 payload 是需要获取 vars
的索引值来 alert。
Javascript Object 中有个名为 keys 的方法。官网中有例子。
1 | payload = {"alert(1)":0} |
再结合 Array 的 pop 方法。我们就可以获取索引值了
1 | payload = {"alert(1)":0} |
原 payload 是先使用正则表达式来获取 Object,然后才能获取索引值。我们来分析下
这一段使用正则表达式获取一个 Object
1 | /1/.exec(1) |
通过 Object keys 方法获取到数组迭代器。
1 | /1/.exec(1).keys(1) |
有了数组迭代器,就可以快乐的使用 keys 和 pop 获取索引值了
1 | /1/.exec(1).keys(1).constructor.keys(payload).pop() |