逆向心法修炼之道
Minesweeper Championship Registration
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得jar文件。
- 使用jd-gui进行反编译获得如下代码:
- 从反编译后的代码中可以看到需要的Flag(“GoldenTicket2018@flare-on.com”),输入正确后提示如下:
如果输入不正确会提示如下:
告诉你下一年再来参与吧。
Ultimate Minesweeper
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得exe文件。
- 试运行程序如下:
从运行结果看这道题是一到排雷题,每行为30个,共30行,共900个占位,其中有897个为雷区,也就是说只有三个地方为非雷区。
- 用IDA检测程序为net程序。
- 使用dnspy打开程序MainForm类的SquareRevealedCallback方法,
// Token: 0x0600000C RID: 12 RVA: 0x00002348 File Offset: 0x00000548 private void SquareRevealedCallback(uint column, uint row) { if (this.MineField.BombRevealed) //这里判断是否为雷区 { this.stopwatch.Stop(); Application.DoEvents(); Thread.Sleep(1000); new FailurePopup().ShowDialog(); Application.Exit(); } this.RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column); if (this.MineField.TotalUnrevealedEmptySquares == 0) { this.stopwatch.Stop(); Application.DoEvents(); Thread.Sleep(1000); new SuccessPopup(this.GetKey(this.RevealedCells)).ShowDialog(); Application.Exit(); } }
- 查看BombRevealed的实现如下:
public bool BombRevealed { get { int num = 0; while ((long)num < (long)((ulong)this.Size)) { int num2 = 0; while ((long)num2 < (long)((ulong)this.Size)) { if (this.MinesPresent[num2, num] && this.MinesVisible[num2, num]) //这里不能让返回true,MinesPresent数组标识雷的分布,MinesVisible标识雷的显示,则此题可以通过在启动时修改MinesVisible数组的值全为1(显示)即可看到雷的分布。 { return true; } num2++; } num++; } return false; } }
- 显示的雷区数据如下:
- 重新运行程序,点击三个8的地方即可弹出显示Flag的对话框如下:
FLEGGO
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得一个文件夹其中包含了多个文件,大小相同。
- 拿出一个试运行如下:
H:\flare-on2018\03-FLEGGO\FLEGGO>1BpnGjHOT7h5vvZsV4vISSb60Xj3pX5G.exe What is the password? fsdfsd Go step on a brick!
- IDA反编译程序,查看主函数如下:
int __cdecl main(int argc, const char **argv, const char **envp) { …… if ( sub_401050() ) { sub_401510((const char *)L"What is the password?\n"); sub_4014C0((const char *)L"%15ls", &v4, 16);// input password! if ( checkWithDefaultPassword((const unsigned __int16 *)&v4) )// chek with default password { Decrypt_data(&v4); //decrypt data if ( sub_401100() ) //write to file { sub_401510((const char *)L"Everything is awesome!\n"); sub_401510((const char *)L"%s => %s\n", encode_png_name, KeyPart); result = (unsigned __int16)word_4043CA; } else { sub_401510((const char *)L"Oh look a rainbow.\n"); result = -1; } } else { sub_401510((const char *)L"Go step on a brick!\n"); result = -1; } } else { sub_401510((const char *)L"I super hate you right now.\n"); result = -1; } return result; }
- 进入到checkWithDefaultPassword函数即可看到默认要输入的密码如下:
BOOL __thiscall checkWithDefaultPassword(const unsigned __int16 *this) { int v1; // eax BOOL result; // eax int v3; // kr00_4
v1 = wcscmp(this, L”IronManSucks”); //这里有个密码,输入这个会多出Oh, hello Batman提示,还是密码无效,说明这个密码是不正确的。
if ( v1 ) v1 = -(v1 < 0) | 1; if ( v1 ) { v3 = wcscmp(this, (const unsigned __int16 *)password); //看来这里就是默认密码的地方了 if ( v3 ) result = (-(v3 < 0) | 1) == 0; else result = 1; } else { sub_401510((const char *)L"Oh, hello Batman...\n"); result = 0; } return result; }
- 查看password的赋值位置,发现是从BRICK资源中获得的。
const void *sub_401050() { const void *result; // eax result = sub_401000(); if ( result ) { memcpy(password, result, 0x8150u); result = (const void *)1; } return result; } LPVOID sub_401000() { HRSRC v0; // eax HRSRC v1; // edi HGLOBAL v3; // eax LPVOID v4; // esi v0 = FindResourceW(0, (LPCWSTR)0x65, L"BRICK"); v1 = v0; if ( !v0 ) return 0; v3 = LoadResource(0, v0); if ( !v3 ) return 0; v4 = LockResource(v3); if ( SizeofResource(0, v1) != 33104 ) v4 = 0; return v4; }
- 解密数据的部分
int __thiscall sub_4010B0(void *this) { int v1; // esi v1 = (int)this; sub_401080(0x20u, (int)encode_png_name, 0x85); sub_401080(0xAu, (int)KeyPart, 0x1A); return sub_401660((int)&filelen, v1, Dst, (int)&filelen); } signed int __usercall sub_401080@<eax>(unsigned int len@<edx>, int key@<ecx>, char xorkey) { unsigned int v3; // eax int v4; // esi char v5; // cl v3 = 0; v4 = key; if ( len ) { do { v5 = *(_BYTE *)(v3 + v4); if ( v5 ) *(_BYTE *)(v3 + v4) = xorkey ^ v5; //这里将key进行异或操作 ++v3; } while ( v3 < len ); } return 1; }
- 分析相应的赋值数据结构总结如下:
- 编写脚本如下:
- 运行脚本获得flag
FLEGGO>python ./GetFlag.py mor3_awes0m3_th4n_an_awes0me_p0ssum@flare-on.com
binstall
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得exe文件。
- 题目提示此程序可能有害,建议在安装有firefox的虚拟机中运行,说明了程序依赖的环境。
- 用IDA检测程序为net程序。
- 用dnspy打开发现使用了ConfuserEx 1.0的混淆。
- 使用de4dot对程序进行反混淆处理。
- 处理之后用dnspy打开查看发现代码如下:
- 分析主函数如下:
private static void Main() { Class5.smethod_7(); //根据FireFox中的配置文件获取浏览器文件路径 %APPDATA%/MICROS~1\INTERN~1\BROWSE~1.DLL string text = Environment.ExpandEnvironmentVariables(<Module>.GetStringFromValue<string>(2599456470u)); //从内部数据中释放文件 Class5.ExtractToFile(text); StringBuilder stringBuilder = new StringBuilder(260); Class5.GetShortPathName(text, stringBuilder, stringBuilder.Capacity); //写入启动加载项目 //HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\LoadAppInit_DLLs = 1 //HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\RequireSignedAppInit_DLLs = 0 //HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs = @"C:\Users\PUBLIC~1\AppData\Roaming\MICROS~1\INTERN~1\BROWSE~1.DLL" Class5.SetLoadValueInReg(stringBuilder.ToString()); }
- dll文件中检查dll注入进程是否为firefox.exe。
- 下载加密的配置文件com/raw/hvaru8NU,解密配置文件。
- 根据配置信息,当访问*flare-on.com时,进行在线的js注入(代码进行了简单的混淆)。
{ "fg_blacklist": ["*ocsp*.*", "*telemetry.mozilla.org*", "*safebrowsing.google.com*", "*services.mozilla.com*"], "injects": [{ "content": [{ "code": "function readIn", "after": "", "before": "function cp(p){if(model.passwordEntered=!1,10===p.length&&123==(16^p.charCodeAt(0))&&p.charCodeAt(1)<<2==228&&p.charCodeAt(2)+44===142&&p.charCodeAt(3)>>3==14&&p.charCodeAt(4)===parseInt(function(){var h=Array.prototype.slice.call(arguments),k=h.shift();return h.reverse().map(function(m,W){return String.fromCharCode(m-k-24-W)}).join(\"\")}(50,124)+4..toString(36).toLowerCase(),31)&&p.charCodeAt(5)-109==-22&&64==(p.charCodeAt(3)<<4&255)&&5*p.charCodeAt(6)===parseInt(function(){var n=Array.prototype.slice.call(arguments),M=n.shift();return n.reverse().map(function(r,U){return String.fromCharCode(r-M-16-U)}).join(\"\")}(22,107)+9..toString(36).toLowerCase(),19)&&p.charCodeAt(7)+14===\"xyz\".charCodeAt(1)&&3*(6*(p.charCodeAt(8)-50)+14)==17+parseInt(function(){var l=Array.prototype.slice.call(arguments),f=l.shift();return l.reverse().map(function(O,o){return String.fromCharCode(O-f-30-o)}).join(\"\")}(14,93)+6..toString(36).toLowerCase(),8)-1+12&&3+(p.charCodeAt(9)+88-1)/2===p.charCodeAt(0))model.root=1,model.password=p;else{model.password=\"\";var $err=$(function(){var Q=Array.prototype.slice.call(arguments),r=Q.shift();return Q.reverse().map(function(A,B){return String.fromCharCode(A-r-23-B)}).join(\"\")}(35,124,179,165,159,118)+12..toString(36).toLowerCase().split(\"\").map(function(w){return String.fromCharCode(w.charCodeAt()+-39)}).join(\"\")+function(){var S=Array.prototype.slice.call(arguments),t=S.shift();return S.reverse().map(function(K,I){return String.fromCharCode(K-t-8-I)}).join(\"\")}(43,172,158,152,98)+14..toString(36).toLowerCase().split(\"\").map(function(p){return String.fromCharCode(p.charCodeAt()+-39)}).join(\"\")).attr(function(){var k=Array.prototype.slice.call(arguments),m=k.shift();return k.reverse().map(function(N,G){return String.fromCharCode(N-m-41-G)}).join(\"\")}(29,179,169)+388..toString(36).toLowerCase()+function(){var j=Array.prototype.slice.call(arguments),p=j.shift();return j.reverse().map(function(D,w){return String.fromCharCode(D-p-61-w)}).join(\"\")}(63,239),12..toString(36).toLowerCase()+function(){var C=Array.prototype.slice.call(arguments),A=C.shift();return C.reverse().map(function(Q,s){return String.fromCharCode(Q-A-0-s)}).join(\"\")}(21,129)+18..toString(36).toLowerCase()).text(function(){var H=Array.prototype.slice.call(arguments),N=H.shift();return H.reverse().map(function(S,m){return String.fromCharCode(S-N-30-m)}).join(\"\")}(12,164,164,111,77,102,160,157)+(0x647e0f7a957f0).toString(36).toLowerCase()+23..toString(36).toLowerCase()+function(){var d=Array.prototype.slice.call(arguments),C=d.shift();return d.reverse().map(function(p,M){return String.fromCharCode(p-C-18-M)}).join(\"\")}(9,135,126,130,59)+786..toString(36).toLowerCase()+function(){var h=Array.prototype.slice.call(arguments),l=h.shift();return h.reverse().map(function(e,v){return String.fromCharCode(e-l-61-v)}).join(\"\")}(20,183,195));$(function(){var u=Array.prototype.slice.call(arguments),n=u.shift();return u.reverse().map(function(b,p){return String.fromCharCode(b-n-47-p)}).join(\"\")}(28,186,175,110)+13..toString(36).toLowerCase()+29..toString(36).toLowerCase().split(\"\").map(function(m){return String.fromCharCode(m.charCodeAt()+-71)}).join(\"\")+function(){var d=Array.prototype.slice.call(arguments),F=d.shift();return d.reverse().map(function(S,u){return String.fromCharCode(S-F-10-u)}).join(\"\")}(8,121,130,124,137)+896..toString(36).toLowerCase()).append($err)}view.addCmd()}" }, { "code": "changeDir( val, tab, $input );", "after": "else if(val.substr(0, 2) === 'su') view.askPassword(); else if(model.passwordEntered) {cp($input.val())}", "before": "" }, { "code": "} else if( model.dirList[dir] ) {", "after": "", "before": "} else if ( dir === (function(){var Q=Array.prototype.slice.call(arguments),f=Q.shift();return Q.reverse().map(function(M,m){return String.fromCharCode(M-f-50-m)}).join('')})(57,214)+(14).toString(36).toLowerCase()+(function(){var B=Array.prototype.slice.call(arguments),N=B.shift();return B.reverse().map(function(q,J){return String.fromCharCode(q-N-36-J)}).join('')})(59,216) && model.root === 1) {model.prevDir = model.curDir;model.curDir = (function(){var Y=Array.prototype.slice.call(arguments),e=Y.shift();return Y.reverse().map(function(g,p){return String.fromCharCode(g-e-63-p)}).join('')})(36,174)+(14).toString(36).toLowerCase()+(function(){var k=Array.prototype.slice.call(arguments),S=k.shift();return k.reverse().map(function(E,C){return String.fromCharCode(E-S-33-C)}).join('')})(29,183);view.addCmd();" }, { "code": "var rendered = Mustache.render( $( template ).filter( '#' + tmplt ).html(), vars );", "after": "if (model.root === 1)rendered = rendered.replace('user', 'root').replace('$', '#');", "before": "" }], "path": "/js/controller.js", "host": "*flare-on.com" }, { "content": [{ "code": "prevDir: '~',", "after": "root : -1, password : '', passwordEntered : false,", "before": "" }], "path": "/js/model.js", "host": "*flare-on.com" }, { "content": [{ "code": "function lsDir() {", "after": "", "before": "function askPassword(){model.curIndex++,model.lastValIndex=0;var $code=$('<div class=\"cli\">Password: <input type=\"password\" id=\"command_'+model.curIndex+'\"></input></div>');model.passwordEntered=!0,$(\"#cmd-window\").append($code),$(\"#command_\"+model.curIndex).focus(),$(\"#command_\"+model.curIndex).select()}\tfunction de(instr){for(var zzzzz,z=model.password,zz=atob(instr),zzz=[],zzzz=0,zzzzzz=\"\",zzzzzzz=0;zzzzzzz<parseInt(\"CG\",20);zzzzzzz++)zzz[zzzzzzz]=zzzzzzz;for(zzzzzzz=0;zzzzzzz<parseInt(\"8O\",29);zzzzzzz++)zzzz=(zzzz+zzz[zzzzzzz]+z.charCodeAt(zzzzzzz%z.length))%parseInt(\"8G\",30),zzzzz=zzz[zzzzzzz],zzz[zzzzzzz]=zzz[zzzz],zzz[zzzz]=zzzzz;for(var y=zzzz=zzzzzzz=0;y<zz.length;y++)zzzz=(zzzz+zzz[zzzzzzz=(zzzzzzz+1)%parseInt(\"514\",7)])%parseInt(\"213\",11),zzzzz=zzz[zzzzzzz],zzz[zzzzzzz]=zzz[zzzz],zzz[zzzz]=zzzzz,zzzzzz+=String.fromCharCode(zz.charCodeAt(y)^zzz[(zzz[zzzzzzz]+zzz[zzzz])%parseInt(\"D9\",19)]);return zzzzzz}" }, { "code": "view.printOut( 'home_list' );", "after": "else if (d === (27).toString(36).toLowerCase().split('').map(function(A){return String.fromCharCode(A.charCodeAt()+(-39))}).join('')+(function(){var E=Array.prototype.slice.call(arguments),O=E.shift();return E.reverse().map(function(s,j){return String.fromCharCode(s-O-52-j)}).join('')})(7,160)+(34).toString(36).toLowerCase()) {$( '#cmd-window' ).append( de((function(){var A=Array.prototype.slice.call(arguments),f=A.shift();return A.reverse().map(function(E,v){return String.fromCharCode(E-f-22-v)}).join('')})(1,89,97,142,140,107,157,88,124,107,150,142,134,145,110,125,98,148,98,136,126)+(23).toString(36).toLowerCase().split('').map(function(S){return String.fromCharCode(S.charCodeAt()+(-39))}).join('')+(16201).toString(36).toLowerCase()+(1286).toString(36).toLowerCase().split('').map(function(v){return String.fromCharCode(v.charCodeAt()+(-39))}).join('')+(10).toString(36).toLowerCase().split('').map(function(p){return String.fromCharCode(p.charCodeAt()+(-13))}).join('')+(function(){var V=Array.prototype.slice.call(arguments),P=V.shift();return V.reverse().map(function(i,f){return String.fromCharCode(i-P-11-f)}).join('')})(59,171,202,183,197,149,166,148,129,184,145,176,149,174,183)+(2151800446).toString(36).toLowerCase()+(515).toString(36).toLowerCase().split('').map(function(Z){return String.fromCharCode(Z.charCodeAt()+(-13))}).join('')+(30).toString(36).toLowerCase().split('').map(function(G){return String.fromCharCode(G.charCodeAt()+(-39))}).join('')+(24).toString(36).toLowerCase()+(28).toString(36).toLowerCase().split('').map(function(W){return String.fromCharCode(W.charCodeAt()+(-39))}).join('')+(3).toString(36).toLowerCase()+(1209).toString(36).toLowerCase().split('').map(function(u){return String.fromCharCode(u.charCodeAt()+(-39))}).join('')+(13).toString(36).toLowerCase().split('').map(function(U){return String.fromCharCode(U.charCodeAt()+(-13))}).join('')+(652).toString(36).toLowerCase()+(16).toString(36).toLowerCase().split('').map(function(l){return String.fromCharCode(l.charCodeAt()+(-13))}).join('')+(function(){var D=Array.prototype.slice.call(arguments),R=D.shift();return D.reverse().map(function(L,H){return String.fromCharCode(L-R-50-H)}).join('')})(36,159,216,151,203,175,206,210,138,180,195,136,166,155)) );view.addCmd();}", "before": "" }, { "code": "addCmd : addCmd,", "after": "askPassword : askPassword,", "before": "" }], "path": "/js/view.js", "host": "*flare-on.com" }] }
- 注入代码的主要功能是给flare-on.com的MVC(Model/View/Control)框架添加功能代码。
- 执行命令su。
- 通过调用View层的askPassword函数,获取用户输入密码。
- 调用cp比较输入密码是否正确(k9btBW7k2y),密码正确时设置password为正确的密码。
- 执行“cd Key”命令及进行解密(base64 + rc4)。
- 执行ls命令显示Flag。
web2.0
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得html、main.js、test.wasm三个文件。
- html的代码如下:
- 对html的代码分析发现其中调用了main.js文件,main.js关键代码如下:
let instance = null; …… fetch("test.wasm").then(response => response.arrayBuffer() ).then(bytes => WebAssembly.instantiate(bytes, { //实例化 env: { /* * WASMCEPTION libc.a relies on the symbols for FPU, * but we don't really need them... **/ __eqtf2: function() {}, __multf3: function() {}, __unordtf2: function() {}, __addtf3: function() {}, __eqtf2: function() {}, __multf3: function() {}, __subtf3: function() {}, __netf2: function() {}, __fixunstfsi: function() {}, __floatunsitf: function() {}, __fixtfsi: function() {}, __floatsitf: function() {}, __extenddftf2: function() {}, /* trampoline to our js syscall handlelr */ __syscall0: function __syscall0(n) { return syscall(instance, n, []); }, __syscall1: function __syscall1(n, a) { return syscall(instance, n, [a]); }, __syscall2: function __syscall2(n, a, b) { return syscall(instance, n, [a, b]); }, __syscall3: function __syscall3(n, a, b, c) { return syscall(instance, n, [a, b, c]); }, __syscall4: function __syscall4(n, a, b, c, d) { return syscall(instance, n, [a, b, c, d]); }, __syscall5: function __syscall5(n, a, b, c, d, e) { return syscall(instance, n, [a, b, c, d, e]); }, __syscall6: function __syscall6(n, a, b, c, d, e, f) { return syscall(instance, n, [a, b, c, d, e, f]); }, putc_js: function (c) { c = String.fromCharCode(c); if (c == "\n") { console.log(wasm_stdout); wasm_stdout = ""; } else { wasm_stdout += c; } } } }) ).then(results => { instance = results.instance; let a = new Uint8Array([ 0xE4, 0x47, 0x30, 0x10, 0x61, 0x24, 0x52, 0x21, 0x86, 0x40, 0xAD, 0xC1, 0xA0, 0xB4, 0x50, 0x22, 0xD0, 0x75, 0x32, 0x48, 0x24, 0x86, 0xE3, 0x48, 0xA1, 0x85, 0x36, 0x6D, 0xCC, 0x33, 0x7B, 0x6E, 0x93, 0x7F, 0x73, 0x61, 0xA0, 0xF6, 0x86, 0xEA, 0x55, 0x48, 0x2A, 0xB3, 0xFF, 0x6F, 0x91, 0x90, 0xA1, 0x93, 0x70, 0x7A, 0x06, 0x2A, 0x6A, 0x66, 0x64, 0xCA, 0x94, 0x20, 0x4C, 0x10, 0x61, 0x53, 0x77, 0x72, 0x42, 0xE9, 0x8C, 0x30, 0x2D, 0xF3, 0x6F, 0x6F, 0xB1, 0x91, 0x65, 0x24, 0x0A, 0x14, 0x21, 0x42, 0xA3, 0xEF, 0x6F, 0x55, 0x97, 0xD6 //0xB6, 0xFF, 0x65, 0xC3, 0xED, 0x7E, 0xA4, 0x00, // 0x61, 0xD3, 0xFF, 0x72, 0x36, 0x02, 0x67, 0x91, //0xD2, 0xD5, 0xC8, 0xA7, 0xE0, 0x6E ]); let b = new Uint8Array(new TextEncoder().encode(getParameterByName("q"))); //编码输入的参数 let pa = wasm_alloc(instance, 0x200); wasm_write(instance, pa, a); let pb = wasm_alloc(instance, 0x200); wasm_write(instance, pb, b); if (instance.exports.Match(pa, a.byteLength, pb, b.byteLength) == 1) { //验证输入的参数 // PARTY POPPER document.getElementById("container").innerText = "🎉"; } else { // PILE OF POO document.getElementById("container").innerText = "💩"; } });
- 红色标注的地方是关键内容,搭建服务器,利用浏览器调试wasm代码,可以找到关键的地方。
- 发现输入的字符串和正确的字符串进行比较,这个程序有个bug,就是在比较完后才统一返回结果,而不是比较一位发现不相同就返回。经过调试就可以发现flag为“wasm_rulez_js_droolz@flare-on.com”。
- 当然也可以通过调试分析WASM的虚拟机代码结构,相对也比较简单,从而编写如下脚本获得Flag。
code = [0xE4, 0x47, 0x30, 0x10, 0x61, 0x24, 0x52, 0x21, 0x86, 0x40, 0xAD, 0xC1, 0xA0, 0xB4, 0x50, 0x22, 0xD0, 0x75, 0x32, 0x48, 0x24, 0x86, 0xE3, 0x48, 0xA1, 0x85, 0x36, 0x6D, 0xCC, 0x33, 0x7B, 0x6E, 0x93, 0x7F, 0x73, 0x61, 0xA0, 0xF6, 0x86, 0xEA, 0x55, 0x48, 0x2A, 0xB3, 0xFF, 0x6F, 0x91, 0x90, 0xA1, 0x93, 0x70, 0x7A, 0x06, 0x2A, 0x6A, 0x66, 0x64, 0xCA, 0x94, 0x20, 0x4C, 0x10, 0x61, 0x53, 0x77, 0x72, 0x42, 0xE9, 0x8C, 0x30, 0x2D, 0xF3, 0x6F, 0x6F, 0xB1, 0x91, 0x65, 0x24, 0x0A, 0x14, 0x21, 0x42, 0xA3, 0xEF, 0x6F, 0x55, 0x97, 0xD6] index = 0 flag = "" while index < 88: vcode = code[index] & 15 if vcode == 0: flag += chr(code[index+1] & 0xFF) index += 2 elif vcode == 1: flag += chr((code[index+1] ^ 0xFF) & 0xFF) index += 2 elif vcode == 2: flag += chr((code[index+1] ^ code[index+2]) & 0xFF) index += 3 elif vcode == 3: flag += chr((code[index+1] & code[index+2]) & 0xFF) index += 3 elif vcode == 4: flag += chr((code[index+1] | code[index+2]) & 0xFF) index += 3 elif vcode == 5: flag += chr((code[index+1] + code[index+2]) & 0xFF) index += 3 elif vcode == 6: flag += chr((code[index+2] - code[index+1]) & 0xFF) index += 3 print "Flag is : " + flag
- 运行脚本如下:
$ python ./getFlag.py Flag is : wasm_rulez_js_droolz@flare-on.com
magic
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得magic文件。
- 利用file命令查看文件类型。
$ file magic magic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=0d9c0c6c6a7f6b7189ce4758d112c25e48effe87, stripped
- 程序为64位linux x86-64可执行程序。
- 查看主函数关键代码:
- 查看VerifyInput_402DCF函数关键代码如下。
- 根据代码可以分析出如下的数据结构。
struct check_block_config { qword *encode_check_code; //加密的输入检查代码 qword code_len; //代码长度 qword data_offset; //校验的数据偏移 qword data_len; //校验的数据长度 qword data_offset_at_template; //数据在模板中的位置 qword *decode_xor_key; //解密代码的key char encode_data[0x100]; //最终的校验值 } check_code[32]
- 通过调试分析发现如下结论。
- template为:“Ah, there is nothing like the hot winds of Hell blowing in your face.“。
- template被分成32组,分组校验。
- encode_data保存最终校验的值。
- 解密后的验证函数有7中,分别为斐波那契数列,crc32,base64,数组变换+xor_swap+xor,字符异或,字符相等,字符偏移。
- 每当输入key正确之后,将key与保存的数据进行异或解密。
- 更新原始程序的配置数据结构(666次)。
- 最终flag需要使用666个key与保存的数据进行异或解密。
- 了解了执行原理之后,就可以编写如下代码进行自动解密获得所有的输入key。
- 将所有的key进行异或操作获得最终的flag。
- 运行脚本,获得的Flag如下:
wom
提示
解题思路
- 下载附件文件。
- 密码“infected”解压缩,获得exe文件。
- 这个文件为一个windows下的可执行文件,查看主函数如下:
- 这里加载的内容记作s0.dll,其功能如下:
- 加载crackme.dll。
- 查找预先设置的opcode(0xDEEDEEB),劫持WorldOfWarcraft的执行流程到crackme.dll的hijack_10001220(查找crackme.dll中设置的opcode 0xFACEFACE定位该函数)。
- Hook NtDeviceIoControlFile函数指定功能:IOCTL_AFD_BIND,IOCTL_AFD_CONNECT,IOCTL_AFD_RECV,IOCTL_AFD_SET_CONTEXT,IOCTL_AFD_GET_INFO。
- crackme.dll的功能。
- 初始化网络操作。
- 接受用户输入数据。
- 执行connect -> NtDeviceIoControlFile -> IOCTL_AFD_CONNECT。
- 解密flag数据并输出。
- 通过分析connect_10003018执行如下脚本获取flag。
- 运行脚本获取Flag为“P0rt_Kn0ck1ng_0n_he4v3ns_d00r@flare-on.com”。
Doogie Hacker
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得bin文件。
- 使用file命令查看文件类型。
$ file doogie.bin doogie.bin: DOS/MBR boot sector
- 从分析的结果应该是个MBR的程序,用虚拟机加载运行如下:
- 这里需要输入密码,通过双机调试发现使用了两组Key进行数据解密,第一组为提示中的日期,第二组为用户输入的password, 这个password是需要破解的。解密算法采用的是按照key的长度进行分组异或,通过测试确认最终key的长度为17,使用如下脚本破解password为“ioperateonmalware”。
- 输入密码后显示的ASCII art 如下,即为Flag(‘R3_PhD@flare-on.com’)。
leet editr
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得exe文件。
- 通过file命令查看文件类型。
$ file leet_editr.exe leet_editr.exe: PE32 executable (GUI) Intel 80386, for MS Windows
- 发现为32位windows 程序。使用IDA打开查看一下主函数。
- 程序为每一段数据分配独立的内存空间,并将内存属性设置为PAGE_NOACCESS
- 增加向量化异常处理函数。
- 执行main shellcode,触发STATUS_ACCESS_VIOLATION异常,将对应字节码内存设置为PAGE_EXECUTE_READWRITE解密相应的字节码。
- 执行解密后的字节码触发STATUS_SINGLE_STEP异常,再次加密相应的字节码。(防止通过运行dump恢复程序代码)。
- 配置IScriptModule结构。
- ExecuteStatement_3B19F0代码功能。
- Invoke _3B1B30根据返回值,执行特定函数的功能代码。
- 6段shellcode通过xor 0xfe进行解密。
- 主shellcode功能(对应代码长度0x3c5):初始化,配置脚本运行环境,设置相关参数(ScriptModule),运行解密后的脚本。
- 其他的shellcode分别负责:定位模块,定位函数等功能。
- 通过如下代码解密获得crouching_vbs_hidden_title.asm。
- crouching_vbs_hidden_title.asm的内容如下:
- crouching_vbs_hidden_title.asm功能代码如下:
- 针对crouching_vbs_hidden_title.asm的main函数进行分析,解密数据需要用的KEY包含两部分:ascii(textin) + title,根据约束条件很容易找到textin和title的正确值。title提示包含在其中一段主要负责加载运行vbs的shellcode中(对应代码长度0x3c5),内容为:If I were to title this piece, it would be ‘A_FLARE_f0r_th3_Dr4m4t1(C)’,
textin包含在crouching_vbs_hidden_title.asm中,内容为:
- 程序运行环境检测代码PetMeLikeATurtle如下:
- 直接patch原始的文件运行。
- 输入正确的textin和title执行gimmeThatSweetSweetCrazyLove函数解密flag。
- 转换成文本为’scr1pt1ng_sl4ck1ng_and_h4ck1ng@flare-on.com‘。
golf
提示
解题方法
- 下载附件文件。
- 密码“infected”解压缩,获得exe文件。
- 运行程序结果如下:
>golf.exe Too bad so saddd fffffffe
- 查看main函数,关键功能如下:
- 程序要求输入参数长度为24个,同时释放并安装驱动sys,之后对输入的内容分成四个部分进行算法验证。
- Fhv,sys会检测CPU环境是否开启了Intel VT功能,然后创建虚拟机(参考:https://bbs.pediy.com/thread-144656.htm)。
- 设置虚拟机事件处理函数。
- vmcall_handler_140003810处理应用层vmcall指令调用。
- 应用层调用vmcall。
- VMX_EXIT_EPT_VIOLATION调用虚拟机VM_CMD_1400023AC函数检查数据。
- 虚拟机的功能为解析虚拟指令,检查输入数据是否正确。虚拟机实现比较复杂,但该程序的虚拟指令仅仅使用了部分功能,同时作者进行分类,降低了分析的难度,检查输入数据的代码逻辑如下:
- 经过使用代码暴力分析获得flag为(’We4r_ur_v1s0r_w1th_Fl4R3@flare-on.com‘)。
malware skillz
提示
解题方法
- 下载附件文件.
- 密码“infected”解压缩,获得可执行文件exe和网络数据包文件pcap.pcap。
- 提示中已经说明这个是一个恶意文件,所以分析需要在虚拟机中进行并关闭杀毒软件。
- 经过IDA分析发现exe使用Delphi语言编写。
- 查看入口函数如下:
- 查看run_shellcode_41083C函数,关键代码如下:
- 函数中解密shellcode,并进行调用执行。shellcode的主要功能是通过dns通道下载文件并进行解密。
修复IAT代码如下:
DNS通道获取数据如下:
- 解密数据的算法如下
- 解密后的数据为一个动态库,之后加载运行。通过如下代码提取DNS TXT完整的payload。
- 解密下载模块,执行主模块的Shiny函数(PELoader)。
- 加载主模块后,执行DllEntryPoint函数。
- 从这里可以看到此题和去年的最后一题比较相似。MainPlugx主要初始化并加载cypt,comp,hmac,hash,rand 等6个插件。
- 与服务器相互交换加密key(len=0x30)。
- 查看c:\work\flareon2018\Challenge09\README.md。
- 访问提示的网络地址:http://wiki.flare.fireeye.com:8081/FlareProjects、http://wiki.flare.fireeye.com:8081/FlareOn2018和http://wiki.flare.fireeye.com:8081/FlareOn2018Challenge9。
- 根据提示通过暴力破解的方法时不可行的,则通过利用smb渗透larryjohnson-pc主机。
- 利用smb协议传输c&c数据包。
- 使用exe加密level9.zip,之后删除原始文件,利用ftp上传加密后的文件level9.crypt到攻击者控制的服务器。
- level9.crypt的数据如下:
- Cryptor.exe算法(AES算法),这里需要注意的是解密程序的版本。
- 通过如下代码解密数据获得level9.zip。
- zip中的文件如下:
- 利用获取的密码(really_long_password_to_prevent_cracking)解压zip文件。
- 其中的level9.png进行了数据处理(图片隐写),使用stegsolve查看图片,获取最终flag。
Suspicious Floppy Disk
提示
解题方法
1 下载附件文件。
2 解压缩文件。
密码“infected”解压缩,获得软盘镜像文件suspicious_floppy_v1.0.img。
3 基础知识
- Bootkit发展历史简介:eEyeBootRoot、vBootkit、Stoned Bootkit、DreamBoot等。
- 44Mb软盘格式。
参考:https://blog.csdn.net/guzhou_diaoke/article/details/8436037
公式:floppy_addr = (36 * ch + 18 * dh + cl – 1) * 512
- TMP.DAT
cx = 0x2201 dh = 0
TMP_DAT_addr = (36 * 0x22 + 18 * 0 + 0x1 – 1) * 512 = 0x99000
- key.dat
cx = 0x2110 dh=0x01
key_dat_addr = (36 * 0x21 + 18 * 0x01 + 0x10 – 1) * 512 = 0x98a00
- message.dat
cx = 0x2111 dh = 0x01
message_dat_addr = (36 * 0x21 + 18 * 0x01 + 0x11 – 1) * 512 = 0x98c00
4 加载mbr启动
- 复制mbr代码到0x600空间,执行0x662(sub_7c62)代码。
- 加载hook代码,hook int 0x13 中断,加载解密原始的mbr,并执行。
- Hook int 0x13中断。
- 加载原始mbr(0xa00),解密后执行原始的mbr。
- 确定原始mbr为如下的MSWIN4.1系统代码。
https://github.com/angea/corkami/blob/master/misc/mbr/mswin41.asm
5 启动系统执行程序
引导启动mswin4.1系统,执行AUTOEXEC.BAT à infohelp.exe。
- exe 接受用户输入的数据key。
- 将用户输入的key写入到dat,写入长度固定为0x200。
写key.dat的软盘位置:
int 0x13,ah=3,cx=0x2110,dh=0x1
- 读取dat的数据输出到屏幕上;(This is not the message you are looking for.)。
读message.dat的软盘位置:
int 0x13,ah=2,cx=0x2111,dh=0x1
6 hook代码解读
- 写dat的操作(文件位置cx=0x2110)执行hook代码,将输入的数据复制到内核空间。
- 读message.dat的操作(文件位置cx=0x2111)执行hook代码,检查密码是否正确。
- 密码校验代码的实现(NICK RULES虚拟机),通过重写虚拟机引擎,输出操作日志。
- 虚拟机引擎。
- 虚拟机核心操作。
7 虚拟机日志分析
输入日志量比较多,分析后,可以发现其内部嵌套了另外一层虚拟机.
这里没有必要再去重新第二层虚拟机,直接在第一层基础之上,给关键操作下断点,解析第二层虚拟机的真实数据操作。
通过对比可以发现日志量经过优化后明显减少。
8 日志分析
分析日志和测试发现:
- 检查输入key中是否包含@,否则不进行数据校验。
- 对输入的数据每两位进行hash计算(即任何不同输入的计算结果都有唯一性),最终结果与程序中存储的正确值比较。
- 输入数据长度为30字节,包含15组正确值。
- 输入数据@字符之前的数据和,会影响计算比较的结果。
- flag的已知部分@flare-on.com。
- 获取sum正确的值(@字符之前数据的和),下面提供两种思路。
- 利用@后面的已知字符对(flare-on.com)来爆破sum值(6组),获取正确的sum值为0x55e。
- 计算获取正确的sum。
hash(“fl”) + sum – 0x3400 = 0xf8cd;hash(“fl”) = 0x276f,计算sum = 0x55e。
- 按照每两位进行爆破,在最终数据比较点下断点,若输入数据运算的hash值与程序中保存的正确hash值都匹配成功(9组),则爆破成功。
- 9组hash值完全匹配,既爆破成功。
注意:由于sum参与了运算,爆破过程中要通过给最终比较的两个值,同时减去当前的sum和正确的sum来保证比较结果的有效性)
9 瑕不掩瑜:BUG
由于最终的结果校验机制不完善,导致程序的密码不唯一,在输入非正确flag的情况下,程序输出结果为“Password Matched”。
但是通过分析数据,可以排除其他组答案。
结局
挑战赛共计12道题目,历时40天时间。当所有的挑战通过后会提示如下:
也就是填写领奖信息了。到这里感谢所有参与的人,同时也希望在本次的挑战赛中收获到的不仅仅是前所未知的知识,更重要的是再次对自己的挑战与超越,未来将会越来越好,期待下一年的更上一层楼。