看雪CTF.TSRC 2018 团队赛 第十题『侠义双雄』 解题思路
第十题《侠义双雄》在今天(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