首页
社区
课程
招聘
Flare-ON 9th 之第八题BackDoor
发表于: 2022-11-16 15:08 15603

Flare-ON 9th 之第八题BackDoor

2022-11-16 15:08
15603

今年由火眼举办的flare-on 9th CTF刚刚结束,接下来准备介绍令我印象比较深刻的第八题BackDoor的解题方法。
图片描述

使用DnSpy打开exe,确定无疑的是该C#编写的程序肯定经过了混淆,不是已知的任何一种混淆壳,无法使用De4dot直接反混淆:
图片描述

让我们简单先分析下混淆的原理,根据原理来完成去混淆,通过分析,有大部分函数是通过触发异常来完成解密方法体的调用的,如下所示:
图片描述
flare_71方法使用DynamicMethod根据传入的字节码数组来进行动态调用:
图片描述
这类受保护的方法还有一个很明显的特征就是开头是两个NOP,而且调用了flare_71,知道这些后就可以编写代码还原第一层混淆:
图片描述
代码如下所示,自动寻找符合特征的函数,调用目标exe里面的相关方法并修复exe。

在解密出第一层混淆,很快又发现了第二层混淆, 通过方法体的token算出hash,然后通过hash索引PE文件中区段数据,经过RC4解密解密出方法体。
图片描述
图片描述
第二层混淆被保护的方法体的名称都是flared打头的:
图片描述
接下来编写程序自动化找到这些函数,并调用RC4解密进行修复,代码如下所示:

至此,程序反混淆完成,开始分析。

在运行程序后,使用wireshark观察其网络活动,一开始我就注意到dns活动,猜测该后门和C&C交互使用DNS隧道进行通信:
图片描述
编写一个DNS server模拟C&C的响应包并对exe进行调试:

程序中处理数据包的地方如下所示,根据不同的功能码执行不同的cmd:
图片描述
通过动态调试,分析清楚了协议响应包的格式,
[agent_id]+[payload size]+[opcode]+[pad]

前面分析了那么多,那么flag究竟在哪呢?Rc4解密函数被调用了2次,一次用于解密方法体,另一次用于解密flag:
图片描述
RC4密钥是在处理数据包的同时,不断对append的数据,最后得到哈希值作为key:
图片描述
只有这些操作全部调用完成,才会触发解密逻辑:
图片描述
加密的数据和方法体一样,也是在区段数据里面,知道这些,就可以直接编写脚本解密出flag了,脚本如下所示:

得到flag:

图片描述

private static void flareon_wrap_decrypt(IList<TypeDef> typeDefs)
 {
 
     foreach (var typeDef in typeDefs)
         foreach (var methodDef in typeDef.Methods)
             if (methodDef.Module.Name == Assembly.ManifestModule.ScopeName && methodDef.HasBody &&
                 methodDef.Body.Instructions.Count > 2 && methodDef.Body.Instructions[0].OpCode == OpCodes.Nop &&
                 methodDef.Body.Instructions[1].OpCode == OpCodes.Nop)
             {
                 var is_wrap = false;
                 var find_true_call = false;
                 MethodDef true_call_MethodDef = null;
                 var is_get_all_args = false;
                 var args_token = new int[2];
                 var Instructions = methodDef.Body.Instructions;
 
 
                 for (var i = 0; i < Instructions.Count; i++)
                 {
                     if (!find_true_call && Instructions[i].OpCode == OpCodes.Call)
                     {
 
                         find_true_call = true;
                         true_call_MethodDef = (MethodDef)Instructions[i].Operand;
                     }
                     if (Instructions[i].OpCode == OpCodes.Ldsfld && Instructions[i + 1].OpCode == OpCodes.Ldsfld)
                     {
 
                         args_token[0] = ((FieldDef)Instructions[i].Operand).MDToken.ToInt32();
                         args_token[1] = ((FieldDef)Instructions[i + 1].Operand).MDToken.ToInt32();
                         Console.WriteLine("---------------------");
                         Console.WriteLine(Instructions[i].Operand.ToString());
 
                         Console.WriteLine(Instructions[i + 1].Operand.ToString());
                         Console.WriteLine("---------------------");
                         is_get_all_args = true;
                     }
                     if (Instructions[i].OpCode == OpCodes.Call && Instructions[i].Operand.ToString().Contains("flare_71") && is_get_all_args)
                     {
 
 
                         is_wrap = true;
                     }
 
                 }
                 if (is_wrap && find_true_call)
                 {
                     CurrentMethod = methodDef;
                     var fieldInfo0 = Assembly.Modules.FirstOrDefault().ResolveField(args_token[0]);
                     var fieldInfo1 = Assembly.Modules.FirstOrDefault().ResolveField(args_token[1]);
                     var arg0 = (Dictionary<uint, int>)fieldInfo0.GetValue(null);
                     var arg1 = (byte[])fieldInfo1.GetValue(null);
 
                     Console.WriteLine(methodDef.FullName);
                     var dm = flare.flare_71(Assembly.Modules.FirstOrDefault(), true_call_MethodDef.MDToken.ToInt32(), arg0, arg1);
 
                     var methodBody = MethodBodyReader.CreateCilBody(AssemblyWriter.moduleDef, arg1, null, true_call_MethodDef.Parameters, 1, true_call_MethodDef.Body.MaxStack, (uint)(arg1.Length), true_call_MethodDef.Body.LocalVarSigTok, GenericParamContext.Create(true_call_MethodDef));
 
                     true_call_MethodDef.FreeMethodBody();
                     true_call_MethodDef.Body = methodBody;
                     Console.WriteLine(true_call_MethodDef.Name);
 
 
 
                 }
 
             }
 }
private static void flareon_wrap_decrypt(IList<TypeDef> typeDefs)
 {
 
     foreach (var typeDef in typeDefs)
         foreach (var methodDef in typeDef.Methods)
             if (methodDef.Module.Name == Assembly.ManifestModule.ScopeName && methodDef.HasBody &&
                 methodDef.Body.Instructions.Count > 2 && methodDef.Body.Instructions[0].OpCode == OpCodes.Nop &&
                 methodDef.Body.Instructions[1].OpCode == OpCodes.Nop)
             {
                 var is_wrap = false;
                 var find_true_call = false;
                 MethodDef true_call_MethodDef = null;
                 var is_get_all_args = false;
                 var args_token = new int[2];
                 var Instructions = methodDef.Body.Instructions;
 
 
                 for (var i = 0; i < Instructions.Count; i++)
                 {
                     if (!find_true_call && Instructions[i].OpCode == OpCodes.Call)
                     {
 
                         find_true_call = true;
                         true_call_MethodDef = (MethodDef)Instructions[i].Operand;
                     }
                     if (Instructions[i].OpCode == OpCodes.Ldsfld && Instructions[i + 1].OpCode == OpCodes.Ldsfld)
                     {
 
                         args_token[0] = ((FieldDef)Instructions[i].Operand).MDToken.ToInt32();
                         args_token[1] = ((FieldDef)Instructions[i + 1].Operand).MDToken.ToInt32();
                         Console.WriteLine("---------------------");
                         Console.WriteLine(Instructions[i].Operand.ToString());
 
                         Console.WriteLine(Instructions[i + 1].Operand.ToString());
                         Console.WriteLine("---------------------");
                         is_get_all_args = true;
                     }
                     if (Instructions[i].OpCode == OpCodes.Call && Instructions[i].Operand.ToString().Contains("flare_71") && is_get_all_args)
                     {
 
 
                         is_wrap = true;
                     }
 
                 }
                 if (is_wrap && find_true_call)
                 {
                     CurrentMethod = methodDef;
                     var fieldInfo0 = Assembly.Modules.FirstOrDefault().ResolveField(args_token[0]);
                     var fieldInfo1 = Assembly.Modules.FirstOrDefault().ResolveField(args_token[1]);
                     var arg0 = (Dictionary<uint, int>)fieldInfo0.GetValue(null);
                     var arg1 = (byte[])fieldInfo1.GetValue(null);
 
                     Console.WriteLine(methodDef.FullName);
                     var dm = flare.flare_71(Assembly.Modules.FirstOrDefault(), true_call_MethodDef.MDToken.ToInt32(), arg0, arg1);
 
                     var methodBody = MethodBodyReader.CreateCilBody(AssemblyWriter.moduleDef, arg1, null, true_call_MethodDef.Parameters, 1, true_call_MethodDef.Body.MaxStack, (uint)(arg1.Length), true_call_MethodDef.Body.LocalVarSigTok, GenericParamContext.Create(true_call_MethodDef));
 
                     true_call_MethodDef.FreeMethodBody();
                     true_call_MethodDef.Body = methodBody;
                     Console.WriteLine(true_call_MethodDef.Name);
 
 
 
                 }
 
             }
 }
private static void flareon_decrypt(IList<TypeDef> typeDefs)
{
 
    foreach (var typeDef in typeDefs)
        foreach (var methodDef in typeDef.Methods)
            if (methodDef.Module.Name == Assembly.ManifestModule.ScopeName  &&
                  methodDef.ToString().Contains("flared"))
            {
                Console.WriteLine(methodDef.Name);
                var token = methodDef.MDToken.ToInt32();
                var method = Assembly.Modules.FirstOrDefault()?.ResolveMethod(token);
                var ILcode = method.GetMethodBody().GetILAsByteArray();
                var hash_text = flare.flared_66(Assembly.Modules.FirstOrDefault(), token);
                byte[] sec_data = GetSectionData(hash_text);
                byte[] decrypted_IL_code = flare.rc4(new byte[] { 18, 120, 171, 223 }, sec_data);
                var dm = flare.flared_67(Assembly.Modules.FirstOrDefault(), decrypted_IL_code, token);
                Console.WriteLine(sec_data.Length);
 
                var methodBody = MethodBodyReader.CreateCilBody(AssemblyWriter.moduleDef, decrypted_IL_code, null, methodDef.Parameters, 1, methodDef.Body.MaxStack, (uint)(decrypted_IL_code.Length), methodDef.Body.LocalVarSigTok, GenericParamContext.Create(methodDef));
 
                if(methodDef.Body.HasExceptionHandlers)
                {
                    Console.WriteLine(methodDef.Name+": " +methodDef.Body.ExceptionHandlers.Count);
                }
                methodDef.FreeMethodBody();
                methodDef.Body = methodBody;
            }
 
}
private static void flareon_decrypt(IList<TypeDef> typeDefs)
{
 
    foreach (var typeDef in typeDefs)
        foreach (var methodDef in typeDef.Methods)
            if (methodDef.Module.Name == Assembly.ManifestModule.ScopeName  &&
                  methodDef.ToString().Contains("flared"))
            {
                Console.WriteLine(methodDef.Name);
                var token = methodDef.MDToken.ToInt32();
                var method = Assembly.Modules.FirstOrDefault()?.ResolveMethod(token);
                var ILcode = method.GetMethodBody().GetILAsByteArray();
                var hash_text = flare.flared_66(Assembly.Modules.FirstOrDefault(), token);
                byte[] sec_data = GetSectionData(hash_text);
                byte[] decrypted_IL_code = flare.rc4(new byte[] { 18, 120, 171, 223 }, sec_data);
                var dm = flare.flared_67(Assembly.Modules.FirstOrDefault(), decrypted_IL_code, token);
                Console.WriteLine(sec_data.Length);
 
                var methodBody = MethodBodyReader.CreateCilBody(AssemblyWriter.moduleDef, decrypted_IL_code, null, methodDef.Parameters, 1, methodDef.Body.MaxStack, (uint)(decrypted_IL_code.Length), methodDef.Body.LocalVarSigTok, GenericParamContext.Create(methodDef));
 
                if(methodDef.Body.HasExceptionHandlers)
                {
                    Console.WriteLine(methodDef.Name+": " +methodDef.Body.ExceptionHandlers.Count);
                }
                methodDef.FreeMethodBody();
                methodDef.Body = methodBody;
            }
 
}
from dnslib import *
from dnslib.server import *
import sys
import time
 
class TestResolver:
    def __init__(self):
        self.data=[]
        op=[2, 10, 8, 19, 11, 1, 15, 13, 22, 16, 5, 12, 21, 3, 18, 17, 20, 14, 9, 7, 4]
        for i in op:
            op_str=str(i)
            payload_len=len(op_str)
            s=['43']
            for k in range(payload_len):
                s.append(str(ord(op_str[k])))
            s=s+(4-len(s))*["0"]
            pl='.'.join(s)
            self.data+=(['192.0.0.%d'%(payload_len+1)]+[pl])
 
        self.data=100*self.data
        print(self.data)
        self.pos=0
 
    def resolve(self,request,handler):
 
        reply = request.reply()
        qname = request.q.qname
        qtype = request.q.qtype
 
        if "flare-on.com" in str(qname) and QTYPE[qtype]=='A':
            answer = RR(rname=qname,ttl=60, rdata=A(self.data[self.pos]))
            self.pos+=1
            reply.add_answer(answer)
            return reply
        reply.header.rcode = getattr(RCODE,'NXDOMAIN')
        return reply
 
 
def main():
    resolver = TestResolver()
    logger = DNSLogger(prefix=False)
    dns_server = DNSServer(resolver,port=53, address='0.0.0.0', logger=logger)
    dns_server.start_thread()
    try:
        while True:
            time.sleep(600)
            sys.stderr.flush()
            sys.stdout.flush()
    except KeyboardInterrupt:
        sys.exit(0)
if __name__ == '__main__':
    main()
from dnslib import *
from dnslib.server import *
import sys
import time
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-11-16 20:26 被wmsuper编辑 ,原因:
上传的附件:
收藏
免费 6
支持
分享
最新回复 (5)
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
文章很精彩,感谢分享!
2022-11-16 20:27
0
雪    币: 3172
活跃值: (5156)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
3
晴天师傅tql
2022-11-16 20:40
0
雪    币: 2040
活跃值: (4950)
能力值: ( LV13,RANK:278 )
在线值:
发帖
回帖
粉丝
4
晴天师傅tql
2022-11-16 20:48
0
雪    币: 4120
活跃值: (5822)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
5
2022-11-17 14:47
0
游客
登录 | 注册 方可回帖
返回
//