今日练习


明晚数学考试,还是要复习一下。还差一个物理期中了,但是基本没有问题

[NISACTF 2022]babyupload

1分
文件上传PythonFlask
文件上传的题目,看标签也知道不是常规解法
查看网页源代码,

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="file">
<input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>

访问目录/source,下载了一个压缩包
有一个app.py的文件,我们可以用vscode打开
这里需要知道

综上,后端代码的逻辑如下:上传的文件不能有后缀名,上传后生成一个uuid,并将uuid和文件名存入数据库中,并返回文件的uuid。再通过/file/uuid访问文件,通过查询数据库得到对应文件名,在文件名前拼接uploads/后读取该路径下上传的文件。

但肯定要想如何读取 flag 文件,在文件名前被uploads/拼接意味着只能读取上传后的文件,而且上传的文件没有后缀名,不能直接利用,但os.path.join()函数存在绝对路径拼接漏洞

1
2
3
4
5
绝对路径拼接漏洞

os.path.join(path,*paths)函数用于将多个文件路径连接成一个组合的路径。第一个函数通常包含了基础路径,而之后的每个参数被当作组件拼接到基础路径之后。

然而,这个函数有一个少有人知的特性,如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将视为绝对路径

我们这里利用bp抓包,修改文件名为 /flag 后发包,利用/file/uuid即可读取 flag
我上传图片,就改一下文件名,其他不需要改,/file/da60638345b84ce9b212fa625de5758d访问拿到flag

[HNCTF 2022 Week1]What is Web

1分
源码泄漏信息收集其他
题目描述

想学web吗,不妨从这篇文章开始吧!!
burp链接:https://pan.baidu.com/s/1-H9vXWl-eBuhNjfk0RpGzg
提取码:gz4l
–来自百度网盘超级会员V5的分享
签到级别的题目,
注释里直接有了
base64解码直接拿到flag

[HCTF 2018]Warmup

1分
文件包含代码审计PHP
题目描述

该题目复现环境尚未取得主办方及出题人相关授权,如果侵权,请联系管理员删除
查看网页源代码,进入source.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
 <?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

首先是定义了一个类,在该类中有一个静态方法checkFile用于检查要包含的文件是否在白名单中,白名单是一个关联数组$whitelist,其中包含了允许包含的文件的键值对。在代码中,允许包含的文件有”source”=>”source.php”和”hint”=>”hint.php”
第一个检查:checkFile方法首先检查传入的$page参数是否为字符串类型,如果不是或者未设置,将输出”you can’t see it”并返回false
第二个:接下来,它检查传入的$page是否直接在白名单中存在,如果存在,返回true

1
2
3
4
5
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);

上面为第三个部分:

1
2
3
4
5
6
strpos(string,find,start)函数:

返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。注意: 字符串位置是从 0 开始,不是从 1 开始
string:必需。规定被搜索的字符串
find:必需。规定要查找的字符
start:可选。规定开始搜索的位置

给一个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
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
这个表达式:

mb_strpos($page . '?', '?')

我们来拆开解释:
📌 mb_strpos() 是什么?

这是 PHP 多字节字符串函数之一,它的作用和 strpos() 一样:

查找一个子字符串(needle)在另一个字符串(haystack)中第一次出现的位置。

不同的是,mb_strpos() 支持 UTF-8、中文等多字节字符,不容易出错。
📌 表达式含义详解

mb_strpos($page . '?', '?')

我们假设:

$page = "index.php"

那么表达式就变成了:

mb_strpos("index.php?", "?")

🔍 结果分析

"index.php?" 里面,字符 ? 是在第 9 位(下标 9):

i n d e x . p h p ?
0 1 2 3 4 5 6 7 8 9

所以这个表达式返回的就是:

9

❓那为什么要写 $page . '?' 呢?

这通常是为了确保字符串中一定含有一个问号 '?',以便后续逻辑能正确处理。

例如,有的代码可能后面会处理 URL 参数:

$url = $page;
if (mb_strpos($url, '?') === false) {
$url .= '?';
} else {
$url .= '&';
}

而你这段直接 $page . '?',相当于确保一定有问号再去找它的位置,就不会返回 false。
✅ 总结

mb_strpos($page . '?', '?')

含义是:找出字符串中第一个 ? 的位置,确保即使 $page 没有问号也能返回一个合法下标。

如果你在看 CTF 题里遇到这个,多半是在防某些路径拼接或者做参数判断,感兴趣我可以帮你一起分析上下文 🌟

关于另外一个函数的gpt解释,mb_substr

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
mb_substr() 是多字节版本的 substr(),用于在字符串中截取子串,而且能正确处理中文、日文等多字节字符,避免乱码。
📚 基本语法

mb_substr(string $string, int $start, int $length = null, string $encoding = null): string

$string:要截取的字符串。

$start:开始位置(从 0 开始)。

$length(可选):截取长度,不写就是从 $start 到结尾。

$encoding(可选):字符编码,常用 "UTF-8"。

📌 举个例子

$str = "你好,世界!";
echo mb_substr($str, 0, 2, 'UTF-8'); // 输出:你好

如果用普通的 substr(),中文字符会乱码或截不准,因为一个汉字在 UTF-8 中占三个字节。
🔧 常见用途

截取中文字符不会乱码

用于防止 XSS 时只保留前面一部分输入内容

截取 URL、参数、代码等特定字段

配合 mb_strpos() 做字符串提取

✅ 总结
函数 用途 是否支持多字节
substr() 截字符串 ❌ 可能乱码
mb_substr() 截多字节字符串 ✅ 安全处理中文

如果你手头有某段题目的具体用法,或者结合 mb_strpos() 一起用的场景,我可以继续帮你分析完整逻辑 👀

我们始终是在source.php页面下进行的传参操作,目的是利用include函数将flag文件包含出来,不要被这里的白名单搞混了。

让我们来分析一下传入这个东西后,php代码是如何进行判断的:

传入file=hint.php,首先检查’hint.php’是否是一个字符串,它是字符串,条件通过;

检查’hint.php’是否在白名单中(白名单包括hint.php和source.php),在,继续执行后面的代码;

对’hint.php’执行mb_substr函数,但是函数内一个参数是来自另一个函数mb_strpos的返回值,因此我们先看mb_strpos函数,使用.进行字符连接,即连接了一个问号字符 ‘?’,得到hint.php?

然后查找’?’在字符串’hint.php?’中第一次出现的位置,从0开始算,返回8,即length=8

接下来我们执行mb_substr函数,即 mb_substr(‘hint.php’,0,8)

从字符串中的第一个字符处开始,返回8个字符,其实还是返回的hint.php;

然后对返回的内容进行url解码,重复执行上面的检查和截取操作。

我们只需要传入一个在白名单内的文件名(source.php或者hint.php),并添加上问号,这样可以保证每次找去用于检查的内容都在白名单,返回true。
因为我们当前的source.php一般是在html目录下,往上是www,var,然后到根目录,flag一般就放在根目录下面,这里还有一个hint.php?/或者source.php?/,因此需要返回四层才能到根目录
payload

1
source.php?file=source.php?/../../../../ffffllllaaaagggg

还有一些我不是很懂的地方问了下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
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
mb_strpos()怎么读?file=source.php?/../../../../ffffllllaaaagggg
ChatGPT 说:

我们来逐步解析这句代码中的重点:
🧠 你问的是:

mb_strpos($page . '?', '?')

如果你传入的参数是:

?page=source.php?/../../../../ffffllllaaaagggg

🔍 首先理解 mb_strpos($page . '?', '?') 是啥意思

这是在找 字符串中某个子串的位置。
mb_strpos() 的作用和 strpos() 类似,是“从字符串中查找某个子串的位置”。
✅ 举例说明:

$page = "source.php?/../../../../ffffllllaaaagggg";
$pos = mb_strpos($page . '?', '?');

此时:

$page . '?' = "source.php?/../../../../ffffllllaaaagggg?"

所以:

mb_strpos("source.php?/../../../../ffffllllaaaagggg?", '?')

结果是:
返回第一个 ? 出现的位置,也就是 10(从 0 开始数,s 是第 0 个)。
❓那这有什么用?

这个通常是配合一些 WAF 规则或者路径处理逻辑,来判断是否 URL 中有 ?,从而决定是否截取、跳转、解析参数等。
🔐 小技巧

因为你用了这种结构:

file=source.php?/../../../../ffffllllaaaagggg

这是利用 Apache 的解析特性,叫做 路径参数伪协议绕过(又叫“问号截断技巧”):
🔥 用法原理:

include($_GET['file']);

如果你传:

file=source.php?/../../../../flag

PHP 会把整个字符串当路径,但 Apache 会认为 source.php 是要执行的文件,后面的路径是其 PATH_INFO,并不会传入 PHP,所以你就能绕过只允许包含 source.php 的限制,去包含 ../../../../flag。
✅ 总结

mb_strpos($page . '?', '?') 是找 ? 的位置。

在你这个例子里,会返回 10。

如果搭配 mb_substr(),可以用来截取路径前面或后面的内容。

你构造的 payload 利用了 source.php?/路径 的 PATH_INFO 伪协议绕过技巧,绕过 include("source.php") 这类限制。

如果你能贴出整段源码或上下文,我能帮你更具体分析路径利用方式
怎么绕过的我没有懂,可以再举列子讲的再清楚一些吗
ChatGPT 说:

当然可以,来,我给你完整讲一遍这个经典的 ? 截断绕过包含限制的利用方式,尽量用最直白的语言和例子,讲清楚原理和实战场景。
🧩 背景假设

先假设有一个 PHP 代码如下:

<?php
if (isset($_GET['file'])) {
include($_GET['file']);
}

看似很危险对吧?直接 include($_GET['file']),我们可以传任意路径。

于是我们测试:

?file=php://filter/read=convert.base64-encode/resource=flag.php

✅ 成功读取源码!

但很多时候,题目作者怕你太快乐,加了个“白名单”限制。比如这样:

<?php
if (isset($_GET['file'])) {
if (strpos($_GET['file'], 'source.php') === 0) {
include($_GET['file']);
}
}

意思是:

只有当 file=source.php... 开头的路径才允许 include()。

🧨 绕过的关键来了

我们想让 include() 实际上包含别的文件,比如 ../../flag,但又必须传 file=source.php... 开头的内容。

于是你用:

?file=source.php?/../../flag

⚠️ 为什么这样写就绕过了?
✅ 答案是:Apache 和 PHP 的解析方式不一致

Apache:解析 source.php?/../../flag 会认为你访问的是 source.php,其后面 /../../flag 是路径信息(PATH_INFO),不参与实际的物理路径寻找。

PHP:接收到的 $_GET['file'] 是 source.php?/../../flag,它会直接原样传给 include(),于是:

include('source.php?/../../flag');

PHP 会尝试加载这个文件。虽然这个路径表面上是以 source.php 开头,但实际文件系统上找的却是 ../../flag。

这是利用了 Apache 仅执行问号前的 PHP 文件 的行为,? 后面的内容并不参与脚本解释,但 PHP include() 会把整个字符串当作路径来解析!
🎯 总结为一句话:

让 Apache 执行的是 source.php,让 PHP include() 的却是其他文件。

🧪 举个完整例子

假设你有如下路径结构:

/var/www/html/
├── source.php
├── index.php
└── flag.txt

source.php 内容不重要,只要是个存在的文件。

index.php 内容如下:

<?php
if (isset($_GET['file'])) {
if (strpos($_GET['file'], 'source.php') === 0) {
include($_GET['file']);
}
}

你访问:

http://target/index.php?file=source.php?/../flag.txt

strpos() 判断你是以 source.php 开头 ✅

PHP include('source.php?/../flag.txt') 实际就包含了 ../flag.txt ❗

于是你就读取到了 flag 内容 🎉

✅ 判断能不能用这个技巧的方式

你可以传:

source.php?/test

然后看返回的错误提示中,是不是会说:

failed to open stream: No such file or directory in /var/www/html/test

如果是,那说明你已经绕过了!

如果你给我你要打的代码结构或目标路径验证方式,我还能更精准地帮你分析用哪种变体更好😎

继续问!

[GXYCTF 2019]BabyUpload

1分
.htaccess文件上传MIME绕过
上传我们的.htaccess文件,内容如下

1
AddType application/x-httpd-php .jpg .txt 

上传类型也太露骨了吧!这里我们就要直接上传还是被过滤了。那我们把MIME类型修改为image/jpeg试一下
这里我们也可以试着上传一下我们的其他类型的文件发现可以上传.jpg格式的,故想到要改content-type
这里.htaccess文件上传成功
这里还是不好上传,要用到两个知识点,之前都遇到过的

1
2
3
4
5
6
7
8
9
10
# [SWPUCTF 2023 秋季新生赛]ez_talk

1分
文件头绕过文件上传PHP
题目描述



```我先传一个图片
改了type我就不知道你这个不是图片了是吧

又是一个新的没有见过的文件上传的题目
MIME绕过+文件头GIF89a绕过,木马最上面加一行GIF89a
然后再改content-Type,上传到了php.amum/sdaolpu/.
然后链接蚁剑

1
http://node4.anna.nssctf.cn:28766/uplaods/muma.php

发现php.amum/sdaolpu/.
就是地址反了过来
最后拿到flag

1
GIF89a原理

GIF89a 在文件上传绕过中的原理,属于经典的 文件头魔改 技巧,下面我给你讲清楚:
🎯 核心问题:文件上传限制了类型(如只允许图片)

很多题目会在上传文件时检查:

文件后缀是不是 .jpg / .png / .gif

文件头是不是图片格式(magic bytes,文件签名)

但我们其实想上传 恶意脚本,比如 .php,来达到执行代码的目的。

所以我们就动歪脑筋了——能不能把 PHP 代码伪装成图片?
🧪 GIF89a 是什么?

是 GIF 图片格式的文件头标志,固定开头是:

GIF89a

也有老版本是 GIF87a

这是判断是否为 GIF 图片的“魔数”。
🧨 绕过原理:伪装图片头 + PHP 代码

我们可以构造一个文件,比如:

GIF89a

保存为 shell.php,这样做有什么好处?

  1. 绕过文件头检查

很多后端只会读前几个字节判断是否是图片,比如:

$f = fopen($_FILES[‘file’][‘tmp_name’], ‘rb’);
$head = fread($f, 6);
if ($head === ‘GIF89a’) {
// 认为是图片
}

你文件开头真的是 GIF89a,直接通过 ✅
2. PHP 是容忍前面有“非 PHP”内容的

PHP 执行时会忽略非 <?php 开头前的内容。

例如:

123456

一样可以正常执行,前面的 123456 会被当作垃圾数据忽略掉。

所以 GIF89a 写在前面无碍。
✅ 实战用法:上传 + 解析
上传阶段:

我们上传的是一个伪装成 GIF 的 PHP 文件,比如:

GIF89a

保存为 .php 或伪装成 .php.gif,看题目限制而定。
访问阶段:

如果上传后文件存放路径是可访问的,并且服务器按扩展名或实际 MIME 类型解析为 PHP 脚本,那么访问:

http://target/uploads/shell.php?cmd=ls

服务器就会执行你写在文件中的 PHP 代码。
👮‍♂️ 补充:如何避免被防护

一些题目会做如下防护:

MIME 检查:通过 file 或 mime_content_type() 检查类型

图像函数检测:如用 getimagesize() 判断是不是图片(它会尝试读取图片完整信息)

禁止 .php 后缀上传,或上传路径不解析 PHP

🧪 常见绕过组合
绕过方式 示例
伪装头 + PHP GIF89a
双扩展 shell.php.jpg,绕过只检查最后扩展名
null 字节截断 shell.php%00.jpg,PHP <=5.3 可利用
.htaccess 配置改变 上传 .htaccess 把 .jpg 当作 .php 解析
文件名绕过路由 shell.jpg/.php 利用 Nginx 解析路径执行
✅ 总结一句话:

GIF89a 是伪装文件为图片的“魔数”,用于骗过上传时的图片头检查,而不影响 PHP 正常执行,配合路径可控和解析漏洞就能 RCE。

如果你有题目源码或限制方式,可以贴给我,我能帮你判断能不能用 GIF89a 成功绕过 👀

1
这里想到了env,在蚁剑里,打校赛的时候忘记可以用这个了。还看到一个文件包含的

[SWPUCTF 2022 新生赛]Ez_upload

1分
.htaccess文件上传MIME绕过
题目描述

简单的文件上传
不算最常规的文件上传的题目。当然,在看wp前我可以尝试着试试先搜索,边学边做
mime类型就是改content-type
然后就是发现还是不可以
发现过滤了上传文件后缀 MIME类型 以及 文件内容中的<? 和 <?php 组合进行了限制

script版本的一句话木马

对应的文件
<FilesMatch “muma.jpg”> //上传的文件名
SetHandler application/x-httpd-php

也可以
post请求访问phpinfo()页面 post传参 shell=phpinfo();
要打开图片,然后post传参。有可能拿到flag
难点:我卡了很久,发现一开始我传入.htccess后缀的也不行,必须要绕过才可以。抓包改content-type为image/jpeg
然后再上传,后在蚁剑右键打开虚拟终端,输入env拿到flag

这里我们直接找到flag就可以了
# [FSCTF 2023]源码!启动!
1分
信息收集源码泄漏其他
题目描述

原…!诶不对,源啥呀这是
最后找个简单的题目做下
这个要我们查看网页源代码来拿到flag
我们可以
1.ctrl+u
2.f12
3.先打开百度,打开f12,再打开我们要进入的地址
4.鼠标右键

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