2019 RCTF
比赛时间:5月18日-5月19日
XCTF联赛内的RCTF,由福州大学RIOS战队主办。
De1ta最终排第7名。re再次ak
babyre
输入长度16,0-9a-z,然后hex转字节
之后用xxtea解密。根据提示,解密后要为Bingo!。xxtea最后一段逻辑,根据解密的最后一个字节的大小x,把明文倒数第x位用\0截断。
得到的明文先CRC16/CCITT,结果要为0x69E2。
明文每一位再异或0x17,然后输出。
即解密完应该是Bingo!异或0x17。
由于输出Bingo!,所以最后一位应该是,这样截断完再异或0x17就是Bingo!了。
这样我们得到了明文的前六位和最后一位,还差1位,这时可以用题目给出的hint爆出来。
也可以不爆破。因为根据用最后一位截断,可以推断出加密时的padding是用剩余字节数padding。即明文最后两位都是。
网上抄的xxtea代码:
1 |
|
rctf{05e8a376e4e0446e}
babyre2
输入account,password,data。
还是xxtea,先用account作为key,加密一组常量。
password的每一位减去十位和个位,减去的结果作为下标从data取数据,得到data2.
用data2异或0xCC作为key密之前的密文。
于是直接构造account:'A'*16,data'8D'*256,password随便16位。
1 | from pwn import * |
DontEatMe
开始有个反调试,直接跳过。
做的时候不清楚是啥加密,搜了一些常量也搜不到,于是自己逆。由于是Feistel密码结构,所以直接反向就可以了。
得到密文后,根据常量生成了一个迷宫,密文应是wasd来控制方向:
1 | 1111111111111111 |
密文:
ddddwwwaaawwwddd
解密:
1 |
|
RCTF{db824ef8605c5235b4bbacfa2ff8e087}
const和delta常量应该是开头某个的函数生成的,直接在调试过程中dump下来。
后面学习了一下密码算法,这是标准的blowfish算法,似乎当年是aes的备选算法之一。
asm
RISC-V架构。工具下载:https://github.com/riscv/riscv-gnu-toolchain
很大,慢慢下载。编译完总共3.6g...
指令手册没找到特别齐全的,官方的:http://crva.io/documents/RISC-V-Reader-Chinese-v2p1.pdf
讲的也不是特别细,没intel白皮书讲的那么详细,总之一条一条指令搜+连蒙带猜
用工具的objdump搞出汇编。一开始看到1w多行有点吓人,不过应该是静态编译的,所以很多都不用看。
先找主逻辑。strings一下发现有东西,能看到 flag plz 和%80s字符串。在010 editor中定位到字符串的位置在二进制文件的CF00处。结合反汇编的地址猜测偏移是0x10000,因此在寻找0x1cf00。刚好在0x101b6附近找到了0x1cf00的引用,以及0x1cf10的引用也找到了。
1 | 1017c: 000007b7 lui a5,0x0 |
主逻辑并不长,慢慢看不难发现是两个循环,第一个加密,第二个对比。
另外提一句,最终对比常量的地址0x1eba0(objdump生成的)有问题,附近找一下应该是0x1dba0,即二进制文件中0xdba0的地方。
脚本:
1 | cipher = [0x11, 0x76, 0xD0, 0x1E, 0x99, 0xB6, 0x2C, 0x91, 0x12, 0x45, 0xFB, 0x2A, 0x97, 0xC6, 0x63, 0xB8, 0x14, 0x7C, 0xE1, 0x1E, 0x83, 0xE6, 0x45, 0xA0, 0x19, 0x63, 0xDD, 0x32, 0xA4, 0xDF, 0x71] |
flag很秀:
RCTF{f5_is_not_real_reversing_}
所以真正的逆向应该是对着机器码逆向。
crack
通过字符串查找引用很容易找到主逻辑在sub_4025E0。
算法本质是这个问题:
https://projecteuler.net/problem=18
https://projecteuler.net/problem=67
总共有0x200*0x200行数据,每行的前n个元素是金字塔中对应元素,其余的元素根据输入的过程拼接起来变成一个函数,在00402762会被调用。
相加总和要为0x100758E540F。其实如果他没说这时最大值还真不好算。知道是要算最大值就好办了,可以用简单的动态规划来求出结果与过程。
1 | from copy import deepcopy |
得到512位的结果:
1 | 00000000010101000000000111100111111110100111100101001000101010010011101100111101011111111111111111001110111011011000000101110111001111100100011000000000000110001111110100000000001101110111010101011111000101110000011000111001110000000000000000000000011001000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100011111110000100111000000000000000000000000000000010000000000000001000001100000000000000101000000000100000010000000000000000010000000000000000000000 |
到这里还没完,进入第二部分。把之前生成的函数dump下来,分析一遍发现是个vm。bytecode就是搜字符串时的一大堆的一大堆01。实际上他把bytecode转成了六位二进制数。
写了个脚本解析了opcode:
1 | code = '000000010101100100000000000000110000010000010110100000000011000000000000000000001100110000000000110000000000000000000000101000000000000000000000000000000000110000000000010000000000000000000000101000000000111000000000000000000000110000011000110000111000010010110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000000000011000000000000000000000110000011000111000110100110000000000011000000000000000000000101000100000111000000000000000000000011000100000100000000000000000000000110100110000000000111000000000000000000000101000000000000000000000000000000000100110100000011000000000000000000000011000100000111000000000000000000000101100100000111111000100001011110000011010100000011011000011000000000000010110100000011000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000110100000000000000000000011000111000100010110000000000110100000000000000000000101000100000001100000000000000000000011000111000100010110000000000001100000000000000000000101000100000101100000000000000000000011000111000100010110000000000101100000000000000000000101000000000111000000000000000000000100000000000000000000000000000101000100000101100000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000111000000000000000000000011000111000101010100000011011111001000000000000010110100000011000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000111000000000000000000000011000111000110100110000000000000010000000000000000000101000100000111000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000011100000000000000000000011000111000100100110000000000100010000000000000000000101000100000000010000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000100010000000000000000000011000111000100010110000000000001000000000000000000000101000100000111000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000011100000000000000000000011000111000010100100000111000000000000000000000011000100000100000000000000000000000110100110000000000111000000000000000000000101000000000000001110010000000000000100110100000110100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000000000000000000000000000010100100000001100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000100000000000000000000000010100100000101100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000010000000000000000000000010100100000011100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000110000000000000000000000010100' |
没有按照汇编标准写,因为里面有些if不知道干嘛的,可能是混淆。个别opcode也没搞懂是做什么的。
伪代码(未完成):
1 | mov r0, 0x26a |
关键在前面有个乘法和对比,猜一下应该是输入后面再接一个二进制数,此数*7后等于0xf423f,即0x22e09 = 0b100010111000001001。测试一下是按照大端序的,即是100100000111010001,接在之前的512位后面就是最后的输入了。
flag画在一张图事实上。
13yR01sw3iy1l1n9
sourceguardian
1 |
|