查看原文
其他

看雪CTF.TSRC 2018 团队赛 第十题『侠义双雄』 解题思路

小雪 看雪学院 2019-05-25


第十题《侠义双雄》在今天(12月21日)中午12:00 结束攻击!共32支团队攻击成功,其中, AceHub 以 1848s 攻速夺得本题第一名!


本题结束后,防守团队排行榜如下:




最新赛况战况一览


第十题之后,攻击方最新排名情况如下:


中午放题搬砖狗哭哭 继续稳坐第一,前六名保持不变;7-10名大变动,其中  n0body 遗憾出局Top 10,111new111 进入Top 10 排行榜!



第十题 点评


crownless:

“侠义双雄”此题原本设计了javascript解密,反汇编,字符串变形,指令变形等关卡,但由于作者忽略了HTML代码在内存中很容易搜到的问题,导致题目很快被破解了。



第十题 出题团队简介


出题团队 战神伽罗


看到解的这么完美,我也是非常惊喜!很期待下次继续参与,只要有机会,我会一直参与!找到组织了。




第十题 设计思路


由看雪论坛  simpower  原创


设计说明:


1.首先将密码封装在javascript中,通过javascript将自身进行加密。


2.通过简单的汇编代码变形算法 (加减固定数值) ,将javascript代码编译到可执行区域,通过指令跳转和对硬编码的变形对javascript代码进行恢复。


3.将恢复的代码注入到IE内核当中,并显示出来,没有壳。



破解思路


1.静态文本编辑器可以看到一部分javascript代码。首先用od加载,搜索字符,可以看到一部分javascript代码,把代码复制出来之后发现不完整,启动起来之后,会将主要代码擦除。






2.然后在这附近下断点,往下滚动,可以看到函数内部有一些奇怪的指令变形,这就是存放剩余javascript的地方了:



3.在变形指令跳回处下断,单步跟踪之后,找到javascript的剩余部分:


发现jmp下面有好多很大的数字,怀疑是字符串做了变形,反复对比结尾(上一张图片),结尾有个 7F 00,估计就是字符串,于是将这段数据每个字节-7F,果然得到了ASCII码,就是剩下的javascript代码。


跟前半部分组合起来就是:

eval(function(s,p,a,c,k,e,d){for(i=0;i<k.length;i++)k[i]=k[i].replace(s, '');e=function(c){eval(document.write(String.fromCharCode(13)));return(eval(c<a)?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('$$$','8 4() {    1 = 6.3.e.f;    9 (1 == "b") {        2("5!")    } 7 {        2("g!<" + 1 + "> a d c 0 ;-)")    }}',62,17,'GUID$$$@a$$$@alert$$$@all$$$@ckpswd$$$@congratulations$$$@document$$$@else$$$@function$$$@if$$$@is$$$@kanxueCTF2018bySimpower91$$$@my$$$@not$$$@pswd$$$@value$$$@wrong$$$'.split('@'),0,{}))


4.javascript解密:


用记事本编写解密代码:

<textarea id="enc"></textarea><br>



<input type=submit onclick="jsdecode()">



<script>



function jsdecode() {



   var code = enc.value;



   code = code.replace(/^eval/, '');



   decode = eval(code);



   document.write(decode);



}



</script>


保存为html文件,然后用浏览器打开,即可解密。


得到真正的javascript代码:

function ckpswd() {

   a = document.all.pswd.value;

   if (a == "kanxueCTF2018bySimpower91") {

       alert("congratulations!")

   } else {

       alert("wrong!<" + a + "> is not my GUID ;-)")

   }

}


密码就在里面了。


题目运行效果:


原文链接:

https://bbs.pediy.com/thread-247387.htm




第十题  谍战 解题思路



本题解析由看雪论坛 ODPan 原创。


初探


可执行文件是Delphi编写。


尝试寻找checkMyFlag按键处理函数,作者是有意隐藏了。在错误提示界面有显示“VBScript”转而查看脚本信息。



VBScript


aScriptLanguage:                        ; DATA XREF: CODE:00469349↑o

CODE:00469598 text "UTF-16LE", '<script language="vbscript">',0Dh,0Ah

CODE:00469598 text "UTF-16LE", 'function alert(msg_str)',0Dh,0Ah

CODE:00469598 text "UTF-16LE", 'MsgBox msg_str,vbOKOnly + vbExclamation + vbApplica'

CODE:00469598 text "UTF-16LE", 'tionModal,""',0Dh,0Ah

CODE:00469598 text "UTF-16LE", 'End Function',0Dh,0Ah

CODE:00469598 text "UTF-16LE", '</script>',0

CODE:004696B8 dword_4696B8 dd 88000101h, 74697277h, 6E6C65h, 0FFFFFFFFh, 8Eh

CODE:004696B8                                         ; DATA XREF: CODE:00469357↑o

CODE:004696CC aCenterBrBrBrIn db '<center><br><br><br><input value="" id="pswd" size=39></input><br'

CODE:004696CC                                         ; DATA XREF: CODE:0046939C↑o

CODE:004696CC db '><br><br><input type=button value="checkMyFlag" onclick="ckpswd()'

CODE:004696CC db ';"></center>',0

CODE:0046975B align 4

这里我们可以看到,checkMyFlag相关处理和ckpswd() 和 pswd敏感信息。


在引用aScriptLanguage处下断点。

CODE:00469343 add     esp, 10h

CODE:00469346 lea     eax, [ebp-0Ch]

CODE:00469349 mov     edx, offset aScriptLanguage     ; "<script language=\"vbscript\">\r\nfunct"...


无意中在eax中指向的地址发现了如下信息:

05432D0  24 00 40 00 69 00 66 00  24 00 24 00 24 00 40 00  $.@.i.f.$.$.$.@.

05432E0  69 00 73 00 24 00 24 00  24 00 40 00 6B 00 61 00  i.s.$.$.$.@.k.a.

05432F0  6E 00 78 00 75 00 65 00  43 00 54 00 46 00 32 00  n.x.u.e.C.T.F.2.

0543300  30 00 31 00 38 00 62 00  79 00 53 00 69 00 6D 00  0.1.8.b.y.S.i.m.

0543310  70 00 6F 00 77 00 65 00  72 00 39 00 31 00 24 00  p.o.w.e.r.9.1.$.

0543320  24 00 24 00 40 00 6D 00  79 00 24 00 24 00 24 00  $.$.@.m.y.$.$.$.

0543330  40 00 6E 00 6F 00 74 00  24 00 24 00 24 00 40 00  @.n.o.t.$.$.$.@.

0543340  70 00 73 00 77 00 64 00  24 00 24 00 24 00 40 00  p.s.w.d.$.$.$.@.

0543350  76 00 61 00 6C 00 75 00  65 00 24 00 24 00 24 00  v.a.l.u.e.$.$.$.

0543360  40 00 77 00 72 00 6F 00  6E 00 67 00 24 00 24 00  @.w.r.o.n.g.$.$.

0543370  24 00 27 00 2E 00 73 00  70 00 6C 00 69 00 74 00  $.'...s.p.l.i.t.


逻辑看上去是个判断pw脚本,使用“kanxueCTF2018bySimpower91”测试了一下,居然通过了。肯定是个非预期了。



继续探索



还原代码


这个脚本从何而来呢?在引用脚本的上面存在这样一段代码,其中内部还有一部分加密的数据。

DE:004691A2 loc_4691A2:                             ; CODE XREF: CODE:00469468↓j

CODE:004691A2 nop

CODE:004691A3 nop

CODE:004691A4 pushf

CODE:004691A5 call    sub_467ACC

CODE:004691AA mov     word ptr [esi+72928992h], ss

CODE:004691B0 mov     dl, 73h

CODE:004691B2 jz      short loc_46915E

CODE:004691B4 add     ebx, [esi+37h]

CODE:004691B7 xor     edi, [ecx+19D217FFh]

CODE:004691B7 ; ---------------------------------------------------------------------------

CODE:004691BD db 0, 0, 74h

CODE:004691C0 dd 0BA7273AAh, 4591170Bh, 0BA720006h

CODE:004691CC db 0Bh, 0AFh, 97h

CODE:004691CF dd 0FFB96AC7h

CODE:004691D3 db 97h

CODE:004691D4 dd 0FFB96AE7h

CODE:004691D8 dd 936A72h, 0BA740000h, 77F7407h, 17FFFFFDh, 979Fh, 937A72h, 72AF0000h

CODE:004691D8 dd 837Ah, 0CA17AF00h, 7C000597h, 7A72F33Bh, 83h, 17FF95AFh, 597DBh, 14EF3B7Ch

CODE:004691D8 dd 92968CF6h, 919A9289h

CODE:0046921C db 9Bh, 0FFh, 68h

CODE:0046921F dd offset aScript_0                     ; "</script>"

CODE:00469223 ; ---------------------------------------------------------------------------

CODE:00469223 push    offset dword_46950C

CODE:00469228 push    offset dword_469518

CODE:0046922D lea     edx, [ebp-0B4h]

CODE:00469233 mov     eax, [ebp-8]

CODE:00469236 mov     eax, [eax+2F8h]

CODE:0046923C call    @Olectrls@TOleControl@GetOleObject$qqrv ; Olectrls::TOleControl::GetOleObject(void)


分析发现,函数467ACC() 将代码中的加密代码数据进行了解密,反汇编,重定位,动态解析执行,一条一条语句的执行。还原的程序解密了上面看到的脚本。


467ACC()-> 467878() -> 46750C()-->467938()

int __stdcall sub_46750C(int a1, int a2, int a3, _DWORD *a4, int *a5, _DWORD *a6)

{

 _DWORD *v6; // ebx

 int v7; // eax

 int v8; // edi

 int *v10; // [esp-Ch] [ebp-37Ch]

 void *v11; // [esp-8h] [ebp-378h]

 int *v12; // [esp-4h] [ebp-374h]

 void *v13; // [esp+0h] [ebp-370h]

 int v14; // [esp+Ch] [ebp-364h]

 int *v15; // [esp+10h] [ebp-360h]

 char v16; // [esp+14h] [ebp-35Ch]

 char v17; // [esp+24h] [ebp-34Ch]

 char v18; // [esp+28h] [ebp-348h]

 char v19; // [esp+128h] [ebp-248h]

 char *v20; // [esp+35Ch] [ebp-14h]

 int v21; // [esp+360h] [ebp-10h]

 int v22; // [esp+364h] [ebp-Ch]

 int v23; // [esp+368h] [ebp-8h]

 int v24; // [esp+36Ch] [ebp-4h]

 int savedregs; // [esp+370h] [ebp+0h]



 v15 = 0;

 v14 = 0;

 v21 = 0;

 v12 = &savedregs;

 v11 = &loc_4677B2;

 v10 = (int *)__readfsdword(0);

 __writefsdword(0, (unsigned int)&v10);

 v6 = sub_466DAC();

 sub_467938((int)v6, (const void *)a2, &v16, (int)v6, &v22);

 v22 = ((int (__stdcall *)(char *, int, signed int, char *, signed int, int *))v6[1036])(

         &v16,

         v22,

         0x400000,                             // 反汇编,LEA ECX,[SS:EBP-74]

         &v17,

         4,

         v10);

 *a4 = v22;

 v20 = (char *)(v6 + 1038);

 sub_464518(v6 + 1038, &v16, v22);

 *((_BYTE *)v6 + v22 + 4152) = 104;

 unknown_libname_54((int)&v21, &v19);

 if ( (unsigned __int8)sub_466EF8(v21, v6[10]) )

 {

   unknown_libname_54((int)&v21, &v18);

   v24 = sub_464598(&str___15[1], v21);

   v10 = &v21;

   unknown_libname_58(v21);

   System::__linkproc__ LStrCopy(v10);

   v24 = sub_4645FC(v21);

   if ( v22 - 1 >= 0 )

   {

     v7 = v22;

     v23 = 0;

     do

     {

       *((_BYTE *)v6 + v23++ + 4152) = 0x90u;

       --v7;

     }

     while ( v7 );

   }

   v24 += v22;

 }

 else if ( sub_464598(&str_CALL[1], v21) == 1 )

 {

   unknown_libname_54((int)&v21, &v18);

   if ( sub_464598(&str_FF[1], v21) != 1 )

   {

     v24 = sub_464598(&str___15[1], v21);

     v23 = v24 - 1;

     v8 = (v24 - 1) / 2;

     v20 = (char *)v6 + v8 + 4152;

     v10 = &v21;

     unknown_libname_58(v21);

     System::__linkproc__ LStrCopy(v10);

     v24 = sub_4645FC(v21);

     v23 = v24 + a2 - (_DWORD)(v6 + 1038);

     sub_464518(v20, &v23, v22 - v8);

   }

   v24 = v22;

 }

 else

 {

   v24 = v22;

 }

 *a6 = v24;

 v6[1] = v24;

 v24 = a3;

 v20 = (char *)v6 + v22 + 4153;

 sub_464518(v20, &v24, 4u);

 *((_BYTE *)v6 + v22 + 4157) = -61;

 *a5 = (int)(v6 + 1038);

 v6[9] = a2 + *a6;

 unknown_libname_54((int)&v15, &v19);

 v10 = v15;

 unknown_libname_54((int)&v14, &v18);

 sub_4678BC((int)v6, *a5, a2, (int)v10, v14, v22);

 __writefsdword(0, (unsigned int)v1


1);

 v13 = &loc_4677B9;

 System::__linkproc__ LStrArrayClr((int)&v14, 2);

 return System::__linkproc__ LStrClr(&v21);

}


int __fastcall sub_467938(int a1, const void *a2, void *a3, _DWORD *a4)

{

int result; // eax



if ( *(_WORD *)(a1 + 0x1062) )

  return (*(int (__fastcall **)(_DWORD, const void *))(a1 + 0x1060))(*(_DWORD *)(a1 + 0x1064), a2);// 对应 467974()

sub_464518(a3, a2, 0x10u);

result = (int)a4;

*a4 = 16;

return result;

}


int __fastcall sub_467974(int a1, int a2, int a3, _DWORD *a4)

{

 int v4; // ecx

 char v5; // zf

 _DWORD *v6; // eax

 unsigned int v8; // [esp+8h] [ebp-20h]

 void *v9; // [esp+Ch] [ebp-1Ch]

 int *v10; // [esp+10h] [ebp-18h]

 int v11; // [esp+20h] [ebp-8h]

 int v12; // [esp+24h] [ebp-4h]

 int savedregs; // [esp+28h] [ebp+0h]



 v11 = 0;

 v10 = &savedregs;

 v9 = &loc_467A18;

 v8 = __readfsdword(0);

 __writefsdword(0, (unsigned int)&v8);

 *a4 = 16;

 v12 = a3;

 v4 = 0;

 do

 {

   *(_BYTE *)(v12 + v4) = ~*(_BYTE *)(a2 + v4);

   ++v4;

 }

 while ( v4 != 16 );

 getInsStr(v12 + 2, (int)&v11);                // 获取指令opcode字符串

 System::__linkproc__ LStrCmp(v11, &str_simvmend[1]);

 if ( v5 )

 {

   v6 = sub_466DAC();

   v6[3] = v6[9];

 }

 __writefsdword(0, v8);

 v10 = (int *)&loc_467A1F;

 return System::__linkproc__ LStrClr(&v11);

}


467974()函数将0x4691AF所在数据(加密的代码)与0xFF异或解密,每次解密16个字节。如上所述经过了解密,反汇编,重定位的过程,组织好了一条汇编指令。

 

指令准备好了,而指令又在那里执行的呢?

int __usercall sub_467878@<eax>(int a1@<ebx>, int a2@<edi>, int a3@<esi>, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14)

{

 unsigned int v14; // et0

 int (__fastcall *v15)(unsigned int *, unsigned int); // ST10_4

 unsigned int v17; // [esp+14h] [ebp-2Ch]

 int v18; // [esp+30h] [ebp-10h]

 void **v19; // [esp+34h] [ebp-Ch]

 int *v20; // [esp+38h] [ebp-8h]

 int v21; // [esp+3Ch] [ebp-4h]

 unsigned int vars0; // [esp+40h] [ebp+0h]

 void *retaddr; // [esp+44h] [ebp+4h]



 vars0 = a3;

 v21 = a2;

 v20 = (int *)&vars0;

 v19 = &retaddr;

 v18 = a1;

 v14 = __readeflags();

 *(_DWORD *)(a13 + 40) = retaddr;

 sub_46750C(a13, a14, a12, &v20, (int *)&v19, &v18);

 vars0 = v17;

 __writeeflags(v17);

 __writeeflags(v17);

 return v15(&vars0, v14);   //jmp     [esp-4+var_3C]

                           执行解密后的单条指令

}


我们可以看到EIP跳转到0xA4038执行了一条指令,然后返回到0x467B10:

000A4038 8D 4D 8C                      lea     ecx, [ebp-74h]

000A403B 68 10 7B 46 00                push    offset loc_467B10

:000A4040 C3                            retn


继续下一条。


所有加密代码还原后如下:

004691B2 8B 55 FC                      mov     edx, [ebp-4]

CODE:004691B0                               ; ---------------------------------------------------------------------------

CODE:004691B1 8C                            db  8Ch

CODE:004691B2                               ; ---------------------------------------------------------------------------

CODE:004691B2 8B 55 FC                      mov     edx, [ebp-4]

CODE:004691B5 A1 C8 CC 46 00                mov     eax, ds:dword_46CCC8

CODE:004691BA E8 2D E6 FF FF                call    sub_4677EC

CODE:004691BF 8B 55 8C                      mov     edx, [ebp-74h]

CODE:004691C2 8D 45 F4                      lea     eax, [ebp-0Ch]

CODE:004691C5 E8 6E BA F9 FF                call    @System@@WStrFromLStr$qqrr17System@WideStringx17System@AnsiString ; System::__linkproc__ WStrFromLStr(System::WideString &,System::AnsiString)

CODE:004691CA 8D 45 F4                      lea     eax, [ebp-0Ch]

CODE:004691CD 50                            push    eax

CODE:004691CE 68 38 95 46 00                push    offset dword_469538

CODE:004691D3 68 18 95 46 00                push    offset dword_469518

CODE:004691D8 8D 95 6C FF FF FF             lea     edx, [ebp-94h]

CODE:004691DE 8B 45 F8                      mov     eax, [ebp-8]

CODE:004691E1 8B 80 F8 02 00 00             mov     eax, [eax+2F8h]

CODE:004691E7 E8 60 68 FF FF                call    @Olectrls@TOleControl@GetOleObject$qqrv ; Olectrls::TOleControl::GetOleObject(void)

CODE:004691EC 8D 85 6C FF FF FF             lea     eax, [ebp-94h]

CODE:004691F2 50                            push    eax

CODE:004691F3 8D 85 7C FF FF FF             lea     eax, [ebp-84h]

CODE:004691F9 50                            push    eax

CODE:004691FA E8 35 68 FA FF                call    @Variants@@DispInvoke$qp8TVarDatarx8TVarDatap16System@TCallDescpv ; Variants::__linkproc__ DispInvoke(TVarData *,TVarData &,System::TCallDesc *,void *)

CODE:004691FF 83 C4 0C                      add     esp, 0Ch

CODE:00469202 8D 85 7C FF FF FF             lea     eax, [ebp-84h]

CODE:00469208 50                            push    eax

CODE:00469209 6A 00                         push    0

CODE:0046920B E8 24 68 FA FF                call    @Variants@@DispInvoke$qp8TVarDatarx8TVarDatap16System@TCallDescpv ; Variants::__linkproc__ DispInvoke(TVarData *,TVarData &,System::TCallDesc *,void *)

CODE:00469210 83 C4 10                      add     esp, 10h

CODE:00469213 EB 09                         jmp     short loc_46921E    short loc_46921E



还原脚本


还原完代码,继续还原脚本,解密后的代码中有个函数sub_4677EC(),该函数还原脚本文件。

nt __usercall sub_4677EC@<eax>(_BYTE *a1@<edx>, int a2@<ecx>, int a3@<ebx>, int a4@<edi>, int a5@<esi>)

{

 int v5; // esi

 int v6; // edx

 unsigned int v8; // [esp-18h] [ebp-24h]

 void *v9; // [esp-14h] [ebp-20h]

 int *v10; // [esp-10h] [ebp-1Ch]

 int v11; // [esp-Ch] [ebp-18h]

 int v12; // [esp-8h] [ebp-14h]

 int v13; // [esp-4h] [ebp-10h]

 int v14; // [esp+0h] [ebp-Ch]

 int v15; // [esp+4h] [ebp-8h]

 _BYTE *i; // [esp+8h] [ebp-4h]

 int savedregs; // [esp+Ch] [ebp+0h]



 v15 = 0;

 v14 = 0;

 v13 = a3;

 v12 = a5;

 v11 = a4;

 v5 = a2;

 i = a1;

 v10 = &savedregs;

 v9 = &loc_467867;

 v8 = __readfsdword(0);

 __writefsdword(0, (unsigned int)&v8);

 LOBYTE(a3) = *a1;

 for ( i = a1 + 1; (unsigned __int8)a3 > 0x7Fu; ++i )

 {

   v6 = a3;

   LOBYTE(v6) = a3 - 127;

   unknown_libname_53(&v14, v6);

   System::__linkproc__ LStrCat(&v15, v14);

   LOBYTE(a3) = *i;

 }

 System::__linkproc__ LStrAsg(v5, v15);

 __writefsdword(0, v8);

 v10 = (int *)&loc_46786E;

 return System::__linkproc__ LStrArrayClr(&v14, 2);

}



//加密的脚本数据

CODE:004693F8 dword_4693F8    dd 0BFA3A3A3h, 0A3A3E5E8h, 0F2E8BFA3h, 0BFA3A3A3h, 0F7EDE0EAh

CODE:004693F8                                         ; DATA XREF: CODE:loc_469460↓o

CODE:004693F8                 dd 0D3C2E4F4h, 0B0AFB1C5h, 0D2F8E1B7h, 0EEEFECE8h, 0B8F1E4F6h

CODE:004693F8                 dd 0A3A3A3B0h, 0A3F8ECBFh, 0EDBFA3A3h, 0A3A3F3EEh, 0F2EFBFA3h

CODE:004693F8                 dd 0A3A3E3F6h, 0E0F5BFA3h, 0A3E4F4EBh, 0F6BFA3A3h, 0E6EDEEF1h

CODE:004693F8                 dd 0A6A3A3A3h, 0EBEFF2ADh, 0A6A7F3E8h, 0ABA8A6BFh, 0FCFAABAFh

CODE:004693F8                 dd 7FA8A8h


解密后的数据,就是我们开头看到的文本内容了。

f...$$$@if$$$@is

$$$@kanxueCTF201

8bySimpower91$$$

@my$$$@not$$$@ps

wd$$$@value$$$@w

rong$$$'.split('


Flag:kanxueCTF2018bySimpower91



原文链接:

https://bbs.pediy.com/thread-248550.htm







第十一题【 伊甸园】正在火热进行中


第11题/共15题


《伊甸园》将于 12月23 日中午 12:00 结束


赶紧参与进来吧~!


热门比赛进行中





合作伙伴 



腾讯安全应急响应中心 

TSRC,腾讯安全的先头兵,肩负腾讯公司安全漏洞、黑客入侵的发现和处理工作。这是个没有硝烟的战场,我们与两万多名安全专家并肩而行,捍卫全球亿万用户的信息、财产安全。一直以来,我们怀揣感恩之心,努力构建开放的TSRC交流平台,回馈安全社区。未来,我们将继续携手安全行业精英,探索互联网安全新方向,建设互联网生态安全,共铸“互联网+”新时代。







- End -


公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com


Modified on

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存