0x00前言
目前Web安全经过日新月异的发展,许多新兴框架都替换掉了原来老旧的技术。而在CTF上,NodeJS安全的题目也出的越来越频繁,常见的一般为NodeJS原型链污染。预计未来NodeJS也会像JAVA,PHP等后端语言一样同样在安全问题上受到更多的重视。
简介
Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型, 让JavaScript 运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。
0x01启动服务
1 | var http = require('http'); |
0x02大小写转换攻击
toUpperCase()————小写转大写
对于toUpperCase(): 字符"ı"
、"ſ"
经过toUpperCase处理后结果为 "I"
、"S"
toLowerCase()————大写转小写
对于toLowerCase(): 字符"K"
经过toLowerCase处理后结果为"k"
注:(这个K不是大写字母K)
0x03弱类型比较漏洞
数字与字符比较
数字与字符比较时,会将纯数字字符串强制转换为数字类型再比较。
字符串与字符串比较时,会将字符串的首位字符转换为ASCII码进行比较
示例
1 | console.log(1=='1'); //true |
数组间比较
空数组间比较永远返回false
,数组间只比较各个数组的第一个元素值
数组与非数值型字符串比较,永远小于非数值型字符串
数组与数值型字符串比较,是取数组的第一位元素与数值型字符串进行比较
示例
1 | console.log([]==[]); //false |
特殊类型
null是等于undefined的,但是不是全等于undefined
NaN不等于/全等于自身
示例
1 | console.log(null == undefined) //true |
0x04MD5绕过
MD5是根据原文加密的一串序列,NodeJS中当两者都为对象时,MD5的值也会相等
1 | a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag) |
代码解释:定义一个a和b对象,其中a和b为真且a的长度与b的长度相同,并且a不等于b,则它们md5的值都相等
1 | a={'x':'1'} |
0x05编码绕过
与一般的语言相同,NodeJS也可以编码绕过
常见编码
base64编码
1 | eval(Buffer.from('Y29uc29sZS5sb2coImhhaGFoYWhhIik7','base64').toString()) |
16进制编码
1 | console.log("a"==="\x61"); // true |
unicode编码
1 | console.log("\u0061"==="a"); // true |
0x06常见危险函数
命令执行
exec()
该命令需要调用child_process模块
child_process是node的一个模块,负责子进程调用
创建子进程的方式:
1 | 异步方式:spawn、exec、execFile、fork |
1 | require('child_process').exec('open /System/Applications/Calculator.app'); //打开计算器 |
eval()
1 | console.log(eval("document.cookie")); //执行document.cookie |
文件读写
读
readFile()————异步文件读
readFileSync()————同步文件读
1 | require('fs').readFile('文件路径','utf-8',(err,data)=>{ |
1 | require('fs').readFileSync('文件路径','utf-8'); |
写
writeFile()————异步文件写
1 | require('fs').writeFile('文件路径','文件内容',(err)=>{}); |
writeFileSync()————同步文件写
1 | require('fs').wirteFileSync('文件路径','文件内容'); |
0x07NodeJS拆分攻击
属于HTTP走私攻击,因其主要原理利用了HTTP的回车换行注入(CRLF)
CRLF
回车换行注入\r\n(%0d%0a)
引用一篇简书文章
正常数据包
1 | HTTP/1.1 200 OK |
是一种
NodeJS中,默认的编码是latin1编码,该编码是单字节编码,不能解析高位编码unicode
当请求路径有多字节编码的unicode字符,会被截断取最低字节,比如 \u0130 就会被截断为 \u30
1 | > v = "/caf\u{E9}\u{01F436}" |
该漏洞需要在Node环境小于8时产生,在Node的10版本里修复,当检测到非法unicode编码会报错
配合CRLF
1 | require('http').get('http://example.com/\r\n/test')._header |
其数据包变为
1 | GET //test |
已经通过回车换行覆盖原数据包
我在做CTF题的时候碰到一题是利用拆分攻击+SSRF再配合pug模板注入实现rce,题目来自GYCTF2020,感兴趣可以去看看
还有一个为NodeJS原型链污染内容比较多,需要先理解原型链,下一章再分析这个漏洞