[CISCN2024]gdb_debug

Fxe0w0 / 2024-06-02 / 原文

考点

1.时间戳的高4位bit基本上可以认为是固定值, 因为百年内都不会发生变化
2.srand()固定种子后, rand()生成的随机数总是相同序列的伪随机数
3.IDA连接远程linux动态调试
4.IDA-python

解题过程

1.查看可执行文件信息
64位elf动态链接文件

➜  ciscn-re  file gdb_debug
gdb_debug: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=43ffbd35fe0a39e59a92c909a453d8deaeb3091b, stripped

2.IDA64反编译捋主函数逻辑
关键点有以下几点:
1.40行使用srand()设置了随机数种子, 而设置随机数种子使用的是time(0LL) & 0xF0000000, 也就是取时间戳的高4bit, 这使得随机数种子固定为了0x60000000
2.main函数内总共调用rand()生成了三次伪随机数:
第一次调用: 57行调用rand()用于异或操作
第二次调用: 73行调用rand()用于接下来的ptr换表
第三次调用: 99行调用rand()用于异或操作
3.main函数的整体逻辑是先在42行读取用户输入(正确的flag), 然后43行判断输入长度是否等于38以及输入字符串是否符合flag{}的格式, 接下来进行了四次处理后判断是否等于"congratulationstoyoucongratulationstoy"
这四次处理分别是:
1.45-59行: 异或加密
2.60-92行: Fisher-Yates 洗牌算法 进行 ptr 换表
3.93-110行: 异或加密
4.110-116行: 异或加密
其中1-3的处理均用到了rand()生成的随机数, 而步骤4用到的异或加密的字串固定在了byte_10A0中.
因为我们需要知道前三次所用的伪随机数都是什么

3.得到三次伪随机数值
分别在57行、91行、99行下断后记录每次循环中的数值

对于第一次操作即xor操作, 由于rand()生成后会将结果保存至v20中, 所以每次中断记录v20的值即可.
xor1 = [0xd9, 0xf, 0x18, 0xbd, 0xc7, 0x16, 0x81, 0xbe, 0xf8, 0x4a, 0x65, 0xf2, 0x5d, 0xab, 0x2b, 0x33, 0xd4, 0xa5, 0x67, 0x98, 0x9f, 0x7e, 0x2b, 0x5d, 0xc2, 0xaf, 0x8e, 0x3a, 0x4c, 0xa5, 0x75, 0x25, 0xb4, 0x8d, 0xe3, 0x7b, 0xa3, 0x64]

对于第二次即ptr换表操作,由于最后的ptr换表的表存放在了ptr中, 而ptr中的值随程序而变化, 最终直接在91行下断, 从内存中找到ptr1的值

ptr1 = [0x12, 0x0E, 0x1B, 0x1E, 0x11, 0x05, 0x07, 0x01, 0x10, 0x22, 0x06, 0x17, 0x16, 0x08, 0x19, 0x13, 0x04, 0x0F, 0x02, 0x0D, 0x25, 0x0C, 0x03, 0x15, 0x1C, 0x14, 0x0B, 0x1A, 0x18, 0x09, 0x1D, 0x23, 0x1F, 0x20, 0x24, 0x0A, 0x00, 0x21]

对于第三次操作即第二次xor操作, 因为rand的值最终保存在eax中, 而本次程序处理时并没有把rand()的结果保存出来, 为了获得xor2的值, 选择使用IDA-python得到xor2

最终输出的xor2:

xor2 = [0xde, 0xaa, 0x42, 0xfc, 0x9, 0xe8, 0xb2, 0x6, 0xd, 0x93, 0x61, 0xf4, 0x24, 0x49, 0x15, 0x1, 0xd7, 0xab, 0x4, 0x18, 0xcf, 0xe9, 0xd5, 0x96, 0x33, 0xca, 0xf9, 0x2a, 0x5e, 0xea, 0x2d, 0x3c, 0x94, 0x6f, 0x38, 0x9d, 0x58, 0xea]

4.编写脚本得到flag
三个用到rand()的地方的值都已经被得到, 最后一次异或用到的byte_55CEB7C010A0也可以在程序中得到, 条件齐全, 开始写脚本逆flag

def list_xor(a:list, b:list) -> list:
    result_list = [0] * len(a)
    if(len(a) == len(b)):
        for i in range(0, len(a)):
            result_list[i] = a[i] ^ b[i]
    else:
        raise ValueError('Something wrong')
    return result_list

xor1 = [0xd9, 0xf, 0x18, 0xbd, 0xc7, 0x16, 0x81, 0xbe, 0xf8, 0x4a, 0x65, 0xf2, 0x5d, 0xab, 0x2b, 0x33, 0xd4, 0xa5, 0x67, 0x98, 0x9f, 0x7e, 0x2b, 0x5d, 0xc2, 0xaf, 0x8e, 0x3a, 0x4c, 0xa5, 0x75, 0x25, 0xb4, 0x8d, 0xe3, 0x7b, 0xa3, 0x64]
ptr1 = [0x12, 0x0E, 0x1B, 0x1E, 0x11, 0x05, 0x07, 0x01, 0x10, 0x22, 0x06, 0x17, 0x16, 0x08, 0x19, 0x13, 0x04, 0x0F, 0x02, 0x0D, 0x25, 0x0C, 0x03, 0x15, 0x1C, 0x14, 0x0B, 0x1A, 0x18, 0x09, 0x1D, 0x23, 0x1F, 0x20, 0x24, 0x0A, 0x00, 0x21]
xor2 = [0xde, 0xaa, 0x42, 0xfc, 0x9, 0xe8, 0xb2, 0x6, 0xd, 0x93, 0x61, 0xf4, 0x24, 0x49, 0x15, 0x1, 0xd7, 0xab, 0x4, 0x18, 0xcf, 0xe9, 0xd5, 0x96, 0x33, 0xca, 0xf9, 0x2a, 0x5e, 0xea, 0x2d, 0x3c, 0x94, 0x6f, 0x38, 0x9d, 0x58, 0xea]
xor3 = [0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73, 0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88, 0x04, 0xD7, 0x12, 0xFE,0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2, 0x9D, 0x4D, 0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78]

encode = "congratulationstoyoucongratulationstoy"
encode_list = []
for ch in encode:
    encode_list.append(ord(ch))

# re: xor3
re_xor3_result = list_xor(xor3, encode_list)

# re: xor2
re_xor2_result = list_xor(xor2, re_xor3_result)

# re: plt1
re_plt1_result = [0] * len(ptr1)
for i in range(0, len(ptr1)):
    re_plt1_result[ptr1[i]] = re_xor2_result[i]

print(re_plt1_result)

# re: xor1
re_xor1_result = list_xor(xor1, re_plt1_result)

# get-flag
flag = ""
for item in re_xor1_result:
    flag += chr(item)
print(flag)

# flag{78bace5989660ee38f1fd980a4b4fbcd}

flag{78bace5989660ee38f1fd980a4b4fbcd}