首页
社区
课程
招聘
[原创]PEDIY2017P.CTF01Writeup 60s内的动静之争
2017-10-24 17:04 4062

[原创]PEDIY2017P.CTF01Writeup 60s内的动静之争

2017-10-24 17:04
4062
Target:样品为VC 6.0 MFC CHello 基础编程样例
0x01 60s内攻破的碰撞成分
0x02 动态分析与静态分析之争
0x03 MFC的启动过程 


0x01 60s内攻破的碰撞成分
    贞:从拿到样品开始算,如何在60s内得到验证的passKey?
(1)样品的图标折射出MFC的特征;
(2)拖进已经打开的IDA开始自动分析,同时双击启动样品实例;
(3)打开IDA->View->Open subviews->Strings查阅字符信息,如下;
    Address Length Type String
    .rdata:0040333E 00000006 C (16 bits) @7
    .rdata:00403558 00000008 C (&A)...
    .rdata:00403560 00000006 C pass!
    .rdata:00403580 00000017 C WelcomeToKanXueCtf2017
    .rdata:0040359E 00000006 C pass!
    .rdata:0040392C 0000000A C MFC42.DLL
    .rdata:00403976 0000000B C MSVCRT.dll
    .rdata:00403A8A 0000000D C KERNEL32.dll
    .rdata:00403B2C 0000000B C USER32.dll
(4)若不怀疑CTF存在简单性的可能,直接复制WelcomeToKanXueCtf2017到提交框就能恰好碰撞成功。
(5)若有所怀疑,且觉得WelcomeToKanXueCtf2017更可能是欢迎标题,敏感点落在pass!字符串身上。
    搁置验证尝试,IDA交叉引用pass!(快捷键X)到函数 Hi_msg_pass_success_401770 的如下代码段
.text:0040177B push    offset Caption  ;
.text:0040177B         ; 恭喜!
.text:00401780 push    offset Text     ; "pass!"
.text:00401785 push    0               ; hWnd
.text:00401787 call    ds:MessageBoxA
(6)继续追溯Hi_msg_pass_success_401770的交叉引用到Hi_CDialogPassCheck__Check_sub_4017F0函数体内,如下;
   在调用提示通过的函数Hi_msg_pass_success_401770上下文发现了strcmp 和 "WelcomeToKanXueCtf2017",
   这时可以足够的信任,"WelcomeToKanXueCtf2017"至少作为passKey最终比对明文的一部分,
   再多看一眼局部变量loc_passKeyPtr在上文的来源,其直接就是CEdit的内容,
   中间没有任何变换处理,与"WelcomeToKanXueCtf2017"比对后也没有更多的比对操作,
   基本可以肯定"WelcomeToKanXueCtf2017"就是该CTF的签到暗语,直接复制提交。
.text:004017F0 Hi_CDialogPassCheck__Check_sub_4017F0 proc near
.text:004017F0 loc_passKeyPtr             = dword ptr -8
.text:004017F0 loc_thisptrCDialogPassCheck= dword ptr -4
.text:004017F0 push    ebp
.text:004017F1 mov     ebp, esp
.text:004017F3 sub     esp, 48h
.text:004017F6 push    ebx
.text:004017F7 push    esi
.text:004017F8 push    edi
.text:004017F9 mov     [ebp+loc_thisptrCDialogPassCheck], ecx
.text:004017FC mov     eax, [ebp+loc_thisptrCDialogPassCheck]
.text:004017FF add     eax, 64h
.text:00401802 push    eax             ; struct CString *
.text:00401803 push    3EAh            ; int //CEdit 控件ID
.text:00401808 mov     ecx, [ebp+loc_thisptrCDialogPassCheck] ; this
.text:0040180B call    CWnd::GetDlgItem(int) //获取CEdit空间对象
.text:00401810 mov     ecx, eax        ; this
.text:00401812 call    CWnd::GetWindowTextA(CString &) 
//将CEdit的字符值赋予验证对话框对象位于偏移0x64处的passKey成员
//loc_thisptrCDialogPassCheck.m_64h_CString_passKey = input_pass_key
.text:00401817 mov     ecx, [ebp+loc_thisptrCDialogPassCheck]
.text:0040181A add     ecx, 64h
.text:0040181D call    Hi_CString_GetLength_4018D0 //passKey 长度
.text:00401822 push    eax             ; int
.text:00401823 mov     ecx, [ebp+loc_thisptrCDialogPassCheck]
.text:00401826 add     ecx, 64h        ; this
.text:00401829 call    CString::GetBuffer(int)     //passKey Ptr
.text:0040182E mov     [ebp+loc_passKeyPtr], eax
.text:00401831 mov     ecx, [ebp+loc_passKeyPtr]
.text:00401834 push    ecx             ; loc_passKeyPtr
.text:00401835 call    strlen
.text:0040183A add     esp, 4
.text:0040183D test    eax, eax
.text:0040183F jnz     short loc_401854
.text:00401841 push    0               ; unsigned int
.text:00401843 push    0               ; char *
.text:00401845 push    offset byte_403598 ;
.text:00401845         ; 请输入pass!
.text:0040184A mov     ecx, [ebp+loc_thisptrCDialogPassCheck] ; this
.text:0040184D call    CWnd::MessageBoxA(char const *,char const *,uint)
.text:00401852 jmp     short loc_401875
.text:00401854 ; ---------------------------------------------------------------------------
.text:00401854 loc_401854:             
.text:00401854 push    offset Str2     ; "WelcomeToKanXueCtf2017"
.text:00401859 mov     edx, [ebp+loc_passKeyPtr]
.text:0040185C push    edx             ; Str1
.text:0040185D call    strcmp
.text:00401862 add     esp, 8
.text:00401865 test    eax, eax
.text:00401867 jnz     short loc_401870
.text:00401869 call    Hi_msg_pass_success_401770 ;
.text:00401869         ; MessageBoxA(0,"pass!","恭喜!")
.text:0040186E jmp     short loc_401875
.text:00401870 ; ---------------------------------------------------------------------------
.text:00401870 loc_401870:             
.text:00401870 call    Hi_msg_ComeOn_fail_4017B0 ;
.text:00401870         ; MessageBoxA(0,"加油!","错了!")
.text:00401875
.text:00401875 loc_401875:          
.text:00401875        
.text:00401875 pop     edi
.text:00401876 pop     esi
.text:00401877 pop     ebx
.text:00401878 mov     esp, ebp
.text:0040187A pop     ebp
.text:0040187B retn
.text:0040187B Hi_CDialogPassCheck__Check_sub_4017F0 endp


0x02 动态分析与静态分析之争
    贞:在从外表看出来是MFC,体积较小的印象之初,是选择动态调试还是静态分析先?
(1)因为这是小体积的独立样例,核心的逻辑或数据一般都会在里面,
   且若无法避免需要动态调试分析时,动态调试分析的针对性需要静态分析辅助,
   即先上静态,一窥全貌,再做应对安排。
(2)实际上在应对确定MFC消息响应函数这块,静态分析可能比动态来得更直接快速。
   包括直接从MFC类对象的虚表函数中找到其定义的消息映射表,
   或如上述通过敏感字符提示信息的交叉引用逆向追溯。
   
    如在403310处为CDialogPassCheck验证对话框的消息响应映射表
    [nMsg:00000112,nCode:00000000,nId:00000000,nLID:00000000,nSig:00000012,pfn: sub_4015D0]//WM_SYSCOMMAND
    [nMsg:0000000F,nCode:00000000,nId:00000000,nLID:00000000,nSig:0000000C,pfn: sub_401650]//WM_PAINT
    [nMsg:00000037,nCode:00000000,nId:00000000,nLID:00000000,nSig:00000023,pfn: sub_401750]//WM_QUERYDRAGICON
    [nMsg:00000202,nCode:00000000,nId:00000000,nLID:00000000,nSig:00000031,pfn: sub_401880]//WM_LBUTTONUP
    [nMsg:0000001F,nCode:00000000,nId:00000000,nLID:00000000,nSig:0000000C,pfn: sub_4018B0]//WM_CANCELMODE
(3)应当注意到上述WM_LBUTTONUP对应的响应函数是sub_401880,此响应是针对在对话框非控件的区域鼠标左键单击(弹起)响应,
而非点击”验证“按钮的响应,验证按钮的点击响应函数是上述的 Hi_CDialogPassCheck__Check_sub_4017F0
其实际原型是 CDialog::OnOK(),为何能猜定其是OnOK的重载函数,这是由VC 6.0 MFC CDiag的虚函数顺序决定的,如下
类似地,我们还可以知道 OnInitDialog 的对应重载函数为 Hi_CDialogPassCheck_OnInitDialog_sub_401500等等。
    .rdata:0040353C dd offset CDialog::DoModal(void)
    .rdata:00403540 dd offset Hi_CDialogPassCheck_OnInitDialog_sub_401500
    .rdata:00403544 dd offset CDialog::OnSetFont(CFont *)
    .rdata:00403548 dd offset Hi_CDialogPassCheck__Check_sub_4017F0
    .rdata:0040354C dd offset CDialog::OnCancel(void)
    .rdata:00403550 dd offset CDialog::PreInitDialog(void)
  在了解MFC机制和类的及其虚表内存布局基础上,可对响应函数的直接静态定位

  
0x03 MFC的启动过程 
    贞:如何从程序及MFC机制静态得到关键结构与信息项。
(1)从 00401E9C start 起,做一些基本的初始化工作,包括app_type,fmode,commode,fdiv,cmdln等
(2)其中也包括下述两处的C\C++以及一些全局变量的初始化函数向量数组
    .text:00401F24 push    offset Hi_x1z_dword_404014
    .text:00401F29 push    offset Hi_x1a_dword_40401
    .text:00401F2E call    _initterm
    ------- -------- -------- -------- -------- -------- --------
    .text:00401F57 push    offset Hi_x2z_dword_40400C
    .text:00401F5C push    offset Hi_x2z_dword_404000
    .text:00401F61 call    _initterm
    ------- -------- -------- -------- -------- -------- --------
    .data:00404000 Hi_x2z_dword_404000 dd 0         
    .data:00404004 dd offset Hi_InitModuleState_bDll_linkerVersion_w_sub_40206F
    .data:00404008 dd offset Hi_g_HelloCWinApp_ctor__registeExitDtor_sub_4010D0
    .data:0040400C Hi_x2z_dword_40400C dd 0             
    .data:00404010 Hi_x1a_dword_40401 dd 0                
    .data:00404014 Hi_x1z_dword_404014 dd 0               
    
(3)上述Hi_InitModuleState_bDll_linkerVersion_w_sub_40206F 初始化线程模块对象的连接器版本0x600 即 (VC 6.0),非Dll模块(exe)
.text:00402074 Hi_InitModuleState_bDll_linkerVersion_sub_402074 proc near
.text:00402074         ; CODE XREF: Hi_InitModuleState_bDll_linkerVersion_w_sub_40206F↑j
.text:00402074 push    600h            ; P2_linkerVersion
.text:00402079 push    0               ; P1_bDll
.text:0040207B call    AfxInitialize(int,ulong)
.text:00402080 mov     Hi_bInitModuleState_404118, al
.text:00402085 retn
.text:00402085 Hi_InitModuleState_bDll_linkerVersion_sub_402074 endp
(4)Hi_g_HelloCWinApp_ctor__registeExitDtor_sub_4010D0 完成我们MFC开始中的全局CWinApp实例的初始化,并注册退出时需执行的析构函数

.text:004010D9 call    Hi_g_HelloCWinApp_ctor_sub_4010F0
.text:004010DE call    Hi_registeExitFunc_Hi_g_HelloCWinApp_dtor_sub_401110

(5)在 Hi_g_HelloCWinApp_ctor_sub_4010F0 中,我们可以看到Hi_g_HelloCWinApp_404028静态变量即为我们的CWinApp MFC实例

.text:004010F9 mov     ecx, offset Hi_g_HelloCWinApp_404028
.text:004010FE call    Hi_HelloCWinApp_ctor_sub_401040

(6)在 Hi_HelloCWinApp_ctor_sub_401040中,
我们可以得到CWinApp MFC实例Hi_g_HelloCWinApp_404028的虚表Hi_HelloCWinApp_vft_403240
进而在虚表中得到初始化函数 :00403298 dd offset Hi_g_HelloCWinApp::Init_401150(void)
.text:0040104E mov     ecx, [ebp+loc_thisptr_g_HelloCWinApp] ; this
.text:00401051 call    CWinApp::CWinApp(char const *)
.text:00401056 mov     eax, [ebp+loc_thisptr_g_HelloCWinApp]
.text:00401059 mov     dword ptr [eax], offset Hi_HelloCWinApp_vft_403240
.text:0040105F mov     eax, [ebp+loc_thisptr_g_HelloCWinApp]
(7)在CWinApp MFC实例Hi_g_HelloCWinApp_404028的初始化函数 Init_401150 中,
我们可以看到验证对话框是一局部变量以DoModal方式呈现的,而在DoModal将验证对话框呈现给用户之前,
验证对话框CDialogPassCheck的构造函数为 Hi_CDialogPassCheck_ctor_sub_401390
.text:00401188 lea     ecx, [ebp+loc_CDialogPassCheck]
.text:0040118B call    Hi_CDialogPassCheck_ctor_sub_401390
.text:00401190 ;   try {
.text:00401190 mov     [ebp+var_4], 0
.text:00401197 mov     eax, [ebp+loc_thisptrCHelloCWinApp]
.text:0040119A lea     ecx, [ebp+loc_CDialogPassCheck]
.text:0040119D mov     [eax+20h], ecx
.text:004011A0 lea     ecx, [ebp+loc_CDialogPassCheck] ; this
.text:004011A3 call    CDialog::DoModal(v
(8)在 Hi_CDialogPassCheck_ctor_sub_401390 中,
我们可以看到对话框使用的对话框资源ID 66h,拓展分析可以用资源查阅工具得到控件布局与对于的ID
这也能印证我们在0x01中提的".text:00401803 push    3EAh ; int //CEdit 控件ID”
实际上我们还可以得到验证对话框的数据成员变量,如其中的两个字符串成员变量
CDialogPassCheck{
  .60hww CString //此成员变量未使用,为何?
    //这我们可以猜测很可能是在MFC设计中作者重复添加控件变量所致,
    //或预留做增强功能使用,若用于存用户名,干啥用,只有天知道。
  .64hww CString //在后续的交互中,我们可以知道其就是 passKey的存储位置
}

text:004013B5 push    66h             ; unsigned int
.text:004013B7 mov     ecx, [ebp+loc_thisptr_CDialogPassCheck] ; this
.text:004013BA call    CDialog::CDialog(uint,CWnd *)
.text:004013BF ;   try {
.text:004013BF mov     [ebp+var_4], 0
.text:004013C6 mov     ecx, [ebp+loc_thisptr_CDialogPassCheck]
.text:004013C9 add     ecx, 60h        ; this
.text:004013CC call    CString::CString(void)
.text:004013CC ;   } // starts at 4013BF
.text:004013D1 ;   try {
.text:004013D1 mov     byte ptr [ebp+var_4], 1
.text:004013D5 mov     ecx, [ebp+loc_thisptr_CDialogPassCheck]
.text:004013D8 add     ecx, 64h        ; this
.text:004013DB call    CString::CString(void)
.text:004013DB ;   } // starts at 4013D1
.text:004013E0 ;   try {
.text:004013E0 mov     byte ptr [ebp+var_4], 2
.text:004013E4 mov     ecx, [ebp+loc_thisptr_CDialogPassCheck]
.text:004013E7 mov     dword ptr [ecx], offset Hi_CDialogPassCheck_vft_40347C
.text:004013ED push    offset dword_403478
.text:004013F2 mov     ecx, [ebp+loc_thisptr_CDialogPassCheck]
.text:004013F5 add     ecx, 64h
.text:004013F8 call    CString::operator=(char con*)//passKey 初始化为 ""

by tritium 2017-10-24

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 575
活跃值: (112)
能力值: ( LV5,RANK:140 )
在线值:
发帖
回帖
粉丝
五行猫 1 2017-11-2 00:37
2
0
牛逼,真的牛逼
游客
登录 | 注册 方可回帖
返回