[2022ASISCTF]Rev-Deserve

附件

deserve_62ae0401215f98652482b329625d0b88c587e7ca.txz

解题过程

调试环境搭建

  1. 这是一个aarch64(arm64)架构的程序,可以在QEMU和Bochs等模拟器中运行这个程序,以QEMU为例,可以使用qemu-aarch64 -g 1234 /path/to/your/program 监听端口,用GDB连接端口即可动态调试,也可以使用IDA PRO的“Remot GDB Debugger选项进行调试”

启动程序需要先安装qemu,并且程序是动态链接的(如上图),因此需要安装相应版本的libc库以确保程序能够正常运行。

//安装lib库
sudo apt-get install gcc-aarch64-linux-gnu
//-L 指定动态链接库的地址,默认./lib/ld-xxx.so
//-g 开启gdb连接协议
qemu-aarch64 -L /usr/aarch64-linux-gnu/ -g 1234 ./deserve

如果要调试非本机架构的程序,需要使用gdb-multiarch。在ARM64指令集下调试的方式可能与其他指令集不同,例如NI(Non-Instruction)步过可能会进入函数,因此直接使用断点比较好。

gdb-multiarch
> set architecture aarch64
> target remote localhost 1234
//如果用gef,需要用gef-remote --qemu-user --qemu-binary ./deserve localhost 1234

输入

2. 运行程序时没有回显,并且在加上参数后会出现"usage: ./deserve"错误提示。通过IDA分析可知,程序的主函数在__libc_start_main中的sub_A80函数中,进一步分析发现在sub_FA0函数中存在feof(stdin)判断,需要从终端输入数据才能继续执行程序。通过fread(&v5[v3], 1uLL, 0x400uLL, stdin)可以一次性输入1024字节的数值保存。需要注意的是,在C语言中,要检测feof(stdin)最后需要使用非空格字符作为终端,因此可以使用Ctrl-D(DOS/Windows中的Ctrl-Z)作为结束标志。

3. 想用IDA调试,但是断点有点问题,还是改用gdb进行调试,先在加密函数入口处下断点0x4000000be0(linux有基地址0x4000000000)

第一轮变换

4. 第一处,判断输入数据是否为字符或数字。为此,程序使用了__ctype_b_loc()函数来存储对应字符的类型信息。例如,对于字母'a',由于满足isalnum,对应flag的二进制标志位为1,即0x0008。在判断时,程序会进行 & 操作,如果结果大于0,则说明输入数据为字符或数字,直接将其放在加密数据的后面。

关键代码:
v8 = __ctype_b_loc(); // 获取字符类型数组的指针
if ( ((*v8)[(unsigned __int8)input[i]] & 8) != 0 ) 

LDRB            W24, [X27,X20]          ; Load from Memory
MOV             X28, X21                ; Rd = Op2
LDR             X1, [X26]               ; Load from Memory
UBFIZ           X3, X24, #1, #8         ; Signed Bitfield Insert in Zero
LDRH            W3, [X1,X3]             ; Load from Memory
TBNZ            W3, #3, loc_C48         ; Test and Branch Non-Zero
  

a. 在程序中,需要判断输入数据是否为空格。如果输入数据为空格,程序会根据之前的加密数据进行进一步判断。如果上一个加密数据为小写字母,上上一个加密数据为"+",则将上一个加密数据变为大写字母。如果不满足这个条件,程序会将'/'放入加密数据中。

b. 除此之外的特殊字符

如果是"@$_!\"#%&'()*+,-./:;<=>?\n"v33存放的是指向iFlag的指针,下面(*BYTE)v33 - (unsigned __int8)"@$*!"#%&'()*+,-./:;<=>?\n"即偏移量,将偏移量+97存入加密数据,同时将+存入数据

 v33 = strchr("@$_!\"#%&'()*+,-./:;<=>?\n", iFlag);/
 firstEnc[v34] = (*BYTE)v33 - (unsigned __int8)"@$*!"#%&'()*+,-./:;<=>?\n" + 97;
 

如果是"[\\]^{|}~`\t",存入两个+和偏移量+97

综上,实际上是一个字符映射的过程,将特殊符号映射成了'+'和对应的字母。对应解密过程如下:

def convert_special_char(s):
    result = ""
    i = 0
    while i < len(s):
        if s[i:i+2] == "++":
            char = chr(ord(s[i+2]) - 97)
            result += "[\\]^{|}~`\t"[ord(char)]
            i += 3
        elif s[i] == "+" and s[i+1].islower() :
            char = chr(ord(s[i+1]) - 97)
            result += "@$_!\"#%&'()*+,-./:;<=>?\n"[ord(char)]
            i += 2
        elif  s[i] == "+" and s[i].isupper():
            result += "{} ".format(s[i].lower())
            i += 1
        elif s[i] == "/":
            result += " "
            i += 1
        else:
            result += s[i]
            i += 1
    return result

5. 补长度,如果第一轮加密的数据长度不能被3整除,则继续填充'/',直到长度能够被3整除,保证编码后的数据长度为4的倍数。

    if ( (v9 & 3) != 0 )
    {
      v11 = v9 + 1;
      do
      {
        v5[v11 - 1] = '/';
        v12 = (int)v11;
      }
      while ( v11++ & 3 );
      *a2 = v12;
    }

第二轮变换

6. 以上描述中的“加密数据”其实并没有被打印出来,也没有被保存。

在程序的后续部分,实际通过对base64编码进行解码,得到flag.enc。

简单分析如下:

将input的数据每次提取4个字符,转换为对应的base64编码,重新生成加密字符串。其中,a,b,c,d是输入的字符在"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"对应的偏移。

已知a,b,c的值都小于64,即每个数最高使用6个bit

(4 *(a & 0x3f )) | (b >> 4) = X 取出a的值放在X的高6位,将b的高2位存放在X的低2位

(16 * (b & 0xf)) | (c4 >> 2)= Y 将b的低4位放在Y的高4位,将c的高4位存放在Y的低4位

((c2 & 3) << 6) | d = Z 将C的低2位存放在Z的高2位,将d的值存放在Z的低6位

7. 得到的数据有很多的+号,需要再通过第一次的加密进行逆向得到flag

convert_special_char("+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+m+xCompressing/short/messages/is/essential+Nso/ASIS/has/developed/new/one+xThe/flag/for/this/task/is+RASIS++ec0mprEs51nG+csHOr7+ct3xT+cmE5s49es+cASIS+d++g///")

'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\nCompressing short messages is essential+Nso ASIS has developed new one\nThe flag for this task is+RASIS{c0mprEs51nG_sHOr7_t3xT_mE5s49es_ASIS!}   '

反思

这道题目有趣的地方在于它要求我们进行编码,而平常我们更多地是使用base64编码去解码。同时,它也需要我们对base64的编码原理有一定的了解,以便进行正确的编码过程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值