[SUCTF 2019]Pythonginx

@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
//也就是host不能为suctf.cc
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
//也是不能为suctf.cc
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
//但是最后又要是suctf.cc
</code>
<!-- Dont worry about the suctf.cc. Go on! -->
<!-- Do you know the nginx? -->

所以就有一个问题是,为什么前面过滤完之后还可以通过。

所以一定是这一串有问题

newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)

好好好

看到了一个编码h.encode(‘idna’)

查询之后

IDNA

国际化域名(Internationalized Domain Name,IDN)又名特殊字符域名,是指部分或完全使用特殊文字或字母组成的互联网域名,包括中文、发育、阿拉伯语、希伯来语或拉丁字母等非英文字母,这些文字经过多字节万国码编码而成。在域名系统中,国际化域名使用punycode转写并以ASCII字符串存储。

℆这个字符,如果使用python3进行idna编码的话

print('℆'.encode('idna'))

结果

b'c/u'

如果再使用utf-8进行解码的话

print(b'c/u'.decode('utf-8'))

结果

c/u

Nginx重要配置文件

配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
配置文件目录为:/usr/local/nginx/conf/nginx.conf

我们尝试输入suctf.cc

发现直接给我干到最后一个去了

所以,我觉得应该不止传这个上去吧

特别是看到

image-20240520202824145

这应该是要传一个网址上去吧

所以看到了之前的配置文件

有一个大胆的想法

suctf.cc/usr/local/nginx/conf/nginx.conf

这个怎么样?

也不行,试试用伪协议读一读

读文件就用file:了

image-20240520203111941

终于是111了

之前一直是333来着

所以读取方式应该是对的

所以要后面不出现suctf.cc

再结合之前提到的idna

创造payload:

file://suctf.c℆sr/local/nginx/conf/nginx.conf

image-20240520203239417

拿到了flag的文件位置!!!

file://suctf.c℆sr/fffffflag

访问得到flag

嘻嘻

但是说实话看到代码是懵的,不能害怕这么多代码!!!

接下来是对几个函数的理解问题

urlparse与urlsplit

下面是我看的博客里举的例子:

scheme://username:password@hostname:port/path;params?query#fragment

各参数意义如下:

scheme: 协议
username:password:表示用于认证的账号和密码,但是一般不会使用
hostname: 主机(IP/域名)
port: 端口
path: 路径
params: 参数(以;分割)
query: 查询(以&分割)
fragment: 锚点,或者说位置,用于网页定位

然后呢,这两个函数就是把这些一个个都分开来的函数

但是后者不会有params就是了

LIST

list()函数是Python的内置函数。它可以将任何可迭代数据转换为列表类型,并返回转换后的列表。当参数为空时,list函数可以创建一个空列表。

也就是,会把能搞成数组的东西都搞成数组!!!