Author:颖奇L’Amore Blog:www.gem-love.com
Beginner’s Capsule▸
solved by [email protected] 题目是TS写的,给了Docker (tar.gz格式)
可以任意执行命令,根据给的这段代码来看我们要读flag,但是flag是个PR,是不能从外界访问的
源码没有什么有用的东西,写的基本都是在容器里执行ts
由于ts是先compile为js再执行的,而js并不支持#
开头私有属性这种语法,那么JS中就肯定有一种东西来支持TS的私有属性 实际上对于TS的私有属性,在编译为JavaScript后使用_classPrivateFieldSet
和_classPrivateFieldGet
函数来赋值,函数中接收的privateMap
参数则是一个WeakMap
的实例,参考这个
当然我们可以通过tsc
命令手工将TypeScript编译为JavaScript,来看一下编译后的结果
基本上和样例一样,主要是_flag
是WeakMap
的一个实例,查询WeakMap文档发现他其实只是一个键值对的集合,可以通过get
方法获取一个键的值,所以答案就呼之欲出了
Capsule▸
代码基本没区别
const fs = require('fs'); |
只是换成了直接执行js
这里有一个issue显示,可以使用inspector模块来获得私有变量,并且这是一个内置模块意味着我们可以在无法npm i
时直接调用
但是直接使用它来读取flag会得到:
private properties undefined
需要先把flag加入global才可以读取
global.flag = flag; |
非预期1▸
其实这个题还蛮简单的,比如直接劫持require
函数。。。。。 思路类似今年DEFCON Final的那个拼图题,利用报错拿到flag
非预期2▸
注意到题目代码的最后一句,它用来禁止从/proc/self/mem
中读取内存
enableSeccompFilter(); |
但是可以利用第三方库读内存,比如v8
模块的getHeapSnapshot()
const v8 = require('v8'); |
Milk▸
表面上是一个XSS,但实际上是个缓存污染攻击 题目有两个域,分别是milk和milk-api,milk域是一个Note App,能够注册、登录、写笔记、提交给管理员访问,这些操作大部分通过api来操作。给了源码 看api的源码,首先可以看到它使用的是token来做认证:
// CSRF Token validation |
管理员访问/flag路由即可得到flag:
router.get('/flag', async (ctx) => { |
所以本题目要做的是获取管理员的token。一开始我以为是xss题目,但是因为有CSP绕不过,csrftoken的jsonp也不是任意可控的
Content-Security-Policy: default-src 'none'; base-uri 'none'; style-src * 'unsafe-inline'; font-src *; connect-src https://milk-api.chal.seccon.jp; script-src 'self' https://milk-api.chal.seccon.jp https://code.jquery.com/jquery-3.5.1.min.js 'sha256-xynbUFfxov/jB5OqYtvdEP/YBByczVOIsuEomUHxc0U='; |
这题的难点在于token一旦被使用就被删除了,所以我们要想办法组织这个token被使用,这样攻击者才有机会去利用这个token伪造管理员。 注意到在note.php中
<script src=https://milk-api.chal.seccon.jp/csrf-token?_='/\d/', '', $_GET['_'])) htmlspecialchars(preg_replace( defer></script> |
api的csrf_token
是一个jsonp
调用csrfTokenCallback()
,一旦调用了这个函数就会AJAX请求api的/notes/get
路由去获取note的内容,一旦去访问notes/get
,就会先经过router.use()
,token就被删除了,所以要在拿到token的同时组织这个回调。
这里有许多种解法,关于非预期解法请直接看出题人写的文档,但基本都是利用缓存污染攻击,只是如何阻止token被删除的方法多种多样。 域名后面加上一个点儿,https://milk.chal.seccon.jp./
,注意这最后加了一个.
但是浏览器访问它依然可以访问,DNS解析正常,NGINX认为它和正常域名的hostname没有区别。但是对于CORS Policy
就不一样了,这两个域名被认为是跨域的,正好CSP的connect-src
只指定了正常域名,故而如果Blocked By CORS那么就可以阻止回调了。 但是利用<script src=>
去Bypass CORS是XSS的一个常用策略,因为script
的引用不遵循CORS,但是现在我们想要他遵循,所以要手工给它一个参数,可以利用 crossorigin="use-credentials"
现在来从头理一下攻击的思路:
- 首先因为nginx服务器缓存了api的所有请求,当我们把一个url提交给管理员访问时,nginx就缓存了管理员的CSRF Token
- 然后因为我们提交的URL是精心构造的,jsonp回调没有执行,就没有访问
/note/get
路由,token就还没有被删除 - 现在我们可以通过
_
参数传入与“note页面的script
标签的src
引用发起一个jsonp
请求到api的csrf_token页面是的_
参数”一样的值给csrf_token来获得管理员的token(因为缓存) - 拿到了管理员token,伪造管理员去拿flag
exp:
const Axios = require('axios'); |
还有个Milk Revenge,但实际上并没有很revenge,很多解法都是通杀这俩题
References
https:[email protected]/ryLh2okDD
https://gist.github.com/po6ix/4af76691ea379957f9e8d68e002ec123
https:[email protected]/S15X3c1wv
https://diary.shift-js.info/seccon-online-pasta/