羊城杯_2020_babyre 题解

本文详细解析了2020羊城杯逆向题,涉及DES和AES加密算法,以及动态调试技巧。题目首先通过DES加密16字节输入并与数组对比,接着使用AES加密32字节输入,通过异或操作得到最终flag。解题关键在于理解SMC自修改代码和动态调试过程中固定密钥的获取。

题目信息

  本题目来自于2020年羊城杯的逆向题,可以在buuoj上找到题目复现。

考查知识

  本题目考查的知识点有:DES/AES算法,SMC自修改代码,以及合理利用动态调试来快速解题。
  关于动态调试,这里很多人可能会遇到环境问题,主要是因为题目调用了openssl的动态加密算法库,所以本地也得有一个。同时还需要注意openssl加密算法库的版本问题,该题目必须使用libcrypto.so.1.0.0。这里我给出该算法库文件 – libcrypto.so.1.0.0,可以不用完整安装openssl,将该文件放到/usr/lib目录下即可。

题目分析

  如下截图所示,展示的是程序main函数的逻辑,一开始的sub_402563函数即是SMC代码,会修改最后面if判断中的sub_40272D,这里由于我已经动态调试并重新定义了一个函数,所以显示的是这样,没有修改过的话显示的只是一个地址。因此绕过该SMC也比较简单,直接动态调试运行到代码12行后即可,当然如果想静态分析的话,可能会有点繁琐,该函数点进去后的执行逻辑还是比较多。
  简单说下这里的主逻辑,将输入的16字节长的字符串用DES加密后和byte_6040C0数组进行比较,相同则进入到sub_40272D函数中。这里提示一点,DES_string_to_key函数并不是把this is my key直接作为key,相反是会按照算法生成一个8字节密钥存储在v6变量中,因此可以通过动态调试得出,每次的值是固定的。

babyre-1
  下面看看sub_40272D函数的主逻辑,如下截图所示,这里一开始又会让我们输入一串字符,中间三个未命名函数是AES加密算法,可以通过插件Findcrypt快速帮我们判断,而AES所需要的key就是一开始输入的16字节长字符串。

babyre-2
  至此,题目的逻辑就很清楚了,将一开始输入的字符串作为这里AES加密的密钥,然后这里需要我们输入32字节长的字符串也就是flag。将flag作AES加密,然后是异或,最后又是一次异或和交换,最终和byte_604100数组中的值进行比较。

WP脚本

  这里给出求解脚本,相关细节会在注释中进行说明。

from Crypto.Cipher import DES, AES


## 求解AES密钥
# DES_string_to_key --> key 需要动态调试得出
key = b"\xAD\x52\xF2\x4C\xE3\x2C\x20\xD6"
# byte_6040C0
text = b"\x0A\xF4\xEE\xC8\x42\x8A\x9B\xDB\xA2\x26\x6F\xEE\xEE\xE0\xD8\xA2"

iv = b"\x00"*8  # cbc模式,需要设置iv,即初始的8字节异或值,一般为0
aes_key = DES.new(key, DES.MODE_CBC, iv).decrypt(text)
print(aes_key)
# aes_key = b'th1s1sth3n1c3k3y'


## get flag
# byte_604100
res = "BDADB4841063B3E1C6842D6FBA8874C49032EA2EC6286570C97578A00B9FA6"
res = bytearray.fromhex(res)

flag = [0] * 32
flag[31] = 0xc4  # 程序中给了最后一字节的值
flags = []

# 异或过程求解存在多解
def deep_search(flag, deep):
    if deep == 0:
        flags.append(flag.copy())
    else:
        for i in range(0xff):
            if ((2 * (i ^ 0x13) + 7) ^ (i % 9 + flag[deep] + 2)) & 0xff == res[deep-1]:
                flag[deep-1] = i
                deep_search(flag, deep-1)

deep_search(flag, 31)
print(flags, "\nfind solver number is: ", len(flags))

# get the rigtht one
for flag in flags:
    for i in range(31, -1, -1):
        for j in range(int(i/4)):
            flag[i] ^= flag[j]
    res = AES.new(aes_key, AES.MODE_ECB).decrypt(bytes(flag))
    print(res)

  如下截图所示,存在24个结果,但只有含有GWHT的字符串是flag,另外buuoj上提交的flag格式是这样的flag{md5(th1s_gam3_1s_s0_c00l_and_d)},md5的话找个在线网站算一下就行。

babyre-3

总结

不忘初心,砥砺前行!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值