0x00 前言
老师让搞的一次寒假赛,一共7天… 没打,抽空上去做了做题目
主要看了密码和misc,跟着炜哥学了一点智能合约,还是挺有收获的
0x01 PWN
1.pwn1
简单ROP,,,貌似程序提供的栈地址没什么用,,,可以直接泄露got地址
但本着充分利用题目所给条件的原则,可以选择泄露栈地址
1 | from pwn import * |
2.easystack
还记得你当年名字叫start,怎么就改名了呢/滑稽
1 | from pwn import * |
3.pwn3
最基础的栈迁移,没canary并且程序告诉了栈地址,迁移到栈上即可
1 | from pwn import * |
4.pwnvm
虚拟机+栈溢出+沙盒orw
见2020_9月博客 ==> vmpwn
5.pwn8
利用uaf漏洞,分割unsortedbin,通过构造获得fastbin,没有输出函数,爆破stdout泄露libc基址,最后将fd改为malloc_hook-0x23,从而将malloc_hook改为onegadget getshell
详细见2020_11月博客==> 上海赛wp
6.babyheap
主要在于tcache块的构造,尝试了好几种思路都失败了,并没找到特别取巧的思路
本题特点在于用realloc分配,并且只有一个全局变量存储,所以add和delete总是要成对出现
漏洞在add时有个offbyone,所以可以构造堆重叠,然后通过一系列的overlap来修改fd指针并申请到stdout,爆破泄露出libc基址,因为我们关闭了stdout,所以我们要用输出重定向 1>&2,然后修改free_hook为system即可
1 | # encoding=utf-8 |
爆破两位1/256的概率,脸太黑了远程没爆出来,不过本地测试成功
reference:
7.pwnserver
一道模拟服务器的题目
本来是想把ptrace(PTRACE_TRACEME, 0, 0, 0)反调试关掉的,PIE也关掉,放出来玩玩的,结果不知道是dockerfile有问题还是环境有问题,自己远程都没打通,再加上7天大家应该都累了,就没放
0x02 CRYPTO
1.简单的RSA
1 | from Crypto.Util.number import * |
靠着Google的力量
总的来说就是如果flag足够小,以至于可以认为能够只用key2(p+q+r质因子)解密,这样就是可以很快解出答案
脚本如下
1 | from Crypto.Util.number import * |
2.乱写的密码
人要看傻了,@文桑
程序逻辑就是随机编排了字母表,实在没想出方法,试试统计分析
看到krveked{fwgzszymyeu_eql0wu1trtldrm}
盲猜开头是cumtctf,然后根据正文的双字母及三字母的组合猜测对应关系
得到flag: cumtctf{probability_the0ry1suseful}
////吐血
方法太蠢了,坐等官方wp…
破案了,果然有工具:词频分析
3.出来签到啦
两关,第一关是AES_CBC,刚好最近考完密码学
1 | key = "19e6855d293a1b76ff44f18948b19bad" |
现在已知cipherText、cipherText1和key,只要求出IV即可得到flag
这里可以回顾一下刚刚过去的密码学课本,写几个式子直观一点,
$$
C1 = E(P1 ⊕ IV)\\P1 = D(C1) ⊕ IV
$$
这里我们要做的是伪造一个fakeIV,可得
$$
fakeP = D(C1) ⊕ fakeIV\\D(C1) = fakeP ⊕ fakeIV\\∵ D(C1) = P1 ⊕ IV\\∴ IV = fakeP ⊕ fakeIV ⊕ P1
$$
所以脚本如下:
1 | from Crypto.Cipher import AES |
输入口令OhOh_You_Find_It,打开challenge2
是一个线性反馈移位寄存器,
1 | flag = "bxsyyds{xxxxxxx} |
简单来说就是每一时刻的序列与mask按位与之后的结果逐位异或,放在序列末尾,首字母输出,就这样每8位输出一个字节写入result,共100个字节
很明显寄存器长度为28(7个字节),并且很容易得到反馈函数,(1&any = any ; 0^any = any )
所以lastbit实质上就是当前序列对应mask位的值进行异或,而我们已知输出序列的前27位,即可求出flag的最后一位,以此类推,最后即可得到完整flag
1 | mask = '1001000000100000001000100101' |
4.fakeRSA
题目描述
1 | from Crypto.Util.number import * |
cipher是个二维数组,两两一组,很奇怪(其实就是对明文每个字母分别加密)
没管那么多,直接对所有密文爆破
在可见字符范围内,数量并不多
脚本如下:
1 | n = 12457981652507620082899382981019023150522130836049180768230343208048934250515055225919349467798787130145322363535840724320789633007392777695461819640437560640209852226105362332801576692429778616279117389786582662174560154469003170305006785551902344355335769435756760811102857819829329033582792826564656344565450265474296871162147060177241714540253604539780517460406770813062769099245157298730033826355717057929207255864286698539967655185399554949075313213370119868838885318633758295272070101734349060701723266336600294581259003531484934286818352843046724120072304901058035864828216652283864232249168467598307241102541 |
5.Pocketbook
nc 过去
1 | [$] Welcome to CUMTCTF'winter |
sha256只爆破最后一位即可,通过验证后进入是一个交易系统
1 | 1. Transfer //交易 |
出生10块钱,赚够10000就可以买flag
这里要做是就是伪造单号,48位的单号16位一组可以分为3组,分别是sender/receiver/amount
可以先自己给ljzjsc转10块钱,然后获得自己的单号,然后在看其他单号分别截取下来,就可以拼凑出新的伪造好的单号,然后反复提交,直到赚够10000即可
思路很简单,脚本如下
1 | import hashlib |
坐等余额变为10000就行
6.ezRSA
1 | from Crypto.Util.number import * |
这里可以得到这些等价关系
$$
n=pq=4xyβ^2+2xβ+2yβ+1\\tip = 2xyβ + x + y\\∴tip = n-1/2β\\tip ≡ x+y(mod β) = x+y+kβ
$$
所以,可以爆破k得出x,y的值,进而得到φ(n)
1 | from Crypto.Util.number import * |
0x03 MISC
1.大鸟转转转
gif图片,用工具一帧一帧翻就行
话说,少了个F谁发现了!
2.没别的意思给你签个到顺便给你看看我老婆
不用看都知道是谁出的题啊喂@ljzjsc
3.程序软件工程师
base64隐写,都是脚本的锅,害得我动调了半天
4.夜之城之王
V哥亲自录视频,一开始就是lmhash爆破,但是36的14次方太大了,我的小电脑吃不消,于是放弃了
好在出题人给了6位的提示,再加上本人0.5倍速的细心观察,很容易找到一些线索,于是3个字的爆破可以说秒出结果😂
1 | import passlib.hash; |
5.helloBlockchain
智能合约的交互,开始我的钱包没钱一直无法部署,多亏ljzjsc推荐了一个新的水龙头
我的钱包才终于有了一块钱
然后题目是这样的,给了源码,
1 | pragma solidity ^0.5.13; |
getflag方法传入参数(这里是邮箱),就可以触发SendFlag事件,就可以得到flag了
首先要部署自己的合约,然后在At Address处填入目标合约
可以去查看目标合约的交易记录,
成功后到自己邮箱查看即可
6.easyblock
同样的环境,也提供了合约源码
1 | pragma solidity ^0.4.23; |
这里的危险函数是delegatecall
delegatecall:
调用后内置变量msg
的值不会修改为调用者,但执行环境为调用者的运行环境。(相当于复制被调用者的代码到调用者合约)
出题人是这样解释的:
所以说当调用nameContract合约的changename时,改变的并不是本合约内的hellocumtname变量,而是将owner地址改变(我以为要想办法执行构造函数所以一直在尝试合约实例化),
那既然这样就可以通过changename方法输入自己的地址,这样owner变量就被我们修改了
然后执行getflag方法即可。
ps:当时油不够怎么也发不出去,然后提了limit之后就可以了
7.backdoor
wireshark抓包就行,flag是个ip地址
0x04 REVERSE
1.re1
关键函数,很明显的base58的特征,直接工具解
2.re2
手动脱壳,用x32dbg出了一点问题,脱完没法运行
加密逻辑很复杂,直接进入最后的比较函数
很明显加密后数据和内存数据进行比较,那么只要知道加密逻辑就可以了
这里选择动调,由于当时脱壳没脱好,于是乎我直接用带壳的程序调试
内存数据:
输入32个’a’之后的加密数据:
脚本如下:
1 |
|
3.re5–game
挺有意思的题目
长时间不做逆向,连花指令都忘了,就长这样,看到jnz一个地址+2,应该很容易想到的我丢
于是去了花之后,IDA恢复正常
逆向题全是去了符号表的比较难受,逻辑不是很复杂,大体是这样的:
你的输入两两一组,相当于一个坐标,程序会根据你的坐标去一个10*10的map里找,取出相应的字母拼接,每5个字母为1组,然后加上‘@’进行分割,将这些字符放入一个指定内存,在主函数中会重新将这些字符放入栈中进行操作,最后的check函数算有两个,第一个check会比较每5个一组的字符串与第几个偏移的内存数据相同,会记下下标,并在另一个字典中按这个下标取出字母,直到所有比较结束,将根据你输入得到的字符串与字符串johnnysilverhand
进行比较,若相同则输出flag
这题有多种解法,我的方法是横坐标恒取0,即可绕过判断
脚本如下:
1 | qu='abcdefghijklmnopqrstuvwxyz!.?*' |
对了记录一个奇怪的坑点
一开始我并不知道这题要nc,所以我就在本地测试,当我输入答案之后字符串没问题
这是在栈中的自己生成的字符串:
这是在内存中的check字符串:
但是最后调用strcmp时,rdi却只取了前8个字节的字符串
所以最后比较就没有通过,挺离谱的,存疑
0x05 小结
太菜了…抽空复现一下其他题