hctf babycrack writeup – 第一次做js的逆向 菜鸡写的超详细writeup emmm….. 原来也没接触过js逆向 这也是第一次做 比较菜 大佬勿喷… ps:写的比较乱 参考的时候先搜索原本函数 然后再进行变形 我这里是通过chrome的debug去打断点分析和terminal对变量做一些操作去推测并验证 把分析出来的已知函数用起个名然后全局替换
一块块拆出来吧 以下顺序为函数调用顺序
1 2 3 4 5 6 7 8 (function (_0xd4b7d6, _0xad25ab ) { var _0x5e3956 = function (_0x1661d3 ) { while (--_0x1661d3) { _0xd4b7d6['push' ](_0xd4b7d6['shift' ]()); } }; _0x5e3956(++_0xad25ab); }(_0x180a, 0x1a2 ));
优化一下
1 2 3 4 5 6 7 8 (function (function_stack, param2 ) { var push_shift = function (param ) { while (--param) { function_stack['push' ](function_stack['shift' ]()); } }; push_shift(++param2); }(function_stack, 0x1a2 ));
由此可以看出,通过array_name['function_name']()
可以调用数组的自带方法,等价于array_name.function_name()
这里function_stack.push(function_stack.shift())
0x1a2次 刚开始感觉这里没啥用 其实是把array的第一个元素给放到最后一个 一共做了0x1a2次 最后这个数组长这个样
1 2 3 4 window ['onload' ] = function ( ) { setInterval(_0xa180('0x38' ), 0x32 ); test(); };
给_0xa180起个名叫exec_function,去上面看看这个函数干啥了
1 2 3 4 5 var _0xa180 = function (_0x5c351c, _0x2046d8 ) { _0x5c351c = _0x5c351c - 0x0 ; var _0x26f3b3 = function_stack[_0x5c351c]; return _0x26f3b3; };
很明显了,是把函数名从function_stack里拿出来
1 2 3 4 5 var exec_function = function (position, _0x2046d8 ) { position = position - 0x0 ; var function_name = function_stack[position]; return function_name; };
这块也很明显了,每50ms调用变形后的function_stack[0x38]就是window.console.clear();window.console.log('Welcome to HCTF :>')'
然后再来看调用的test()函数
1 2 3 4 5 6 7 8 9 10 11 12 13 function test ( ) { var _0x5bf136 = document [exec_function('0x32' )](exec_function('0x33' ))['value' ]; if (_0x5bf136 == '' ) { console [exec_function('0x34' )](exec_function('0x35' )); return ![]; } var _0x4d0e29 = check(_0x5bf136); if (_0x4d0e29) { alert(exec_function('0x36' )); } else { alert(exec_function('0x37' )); } }
还原回去
1 2 3 4 5 6 7 8 9 10 11 12 13 function test ( ) { var value = document .getElementById(message)['value' ]; if (value == '' ) { console .log('Welcom to HCTF :>' ); return ![]; } var status = check(value); if (status) { alert("Congratulations! you got it!" ); } else { alert("Sorry, you are wrong..." ); } }
应该到了核心的部分 check函数 这个函数比较复杂 一点点拆开看吧
1 2 3 var _0x2e2f8d = ['code' , exec_function('0x0' ), exec_function('0x1' ), exec_function('0x2' ), 'invalidMonetizationCode' , exec_function('0x3' ), exec_function('0x4' ), exec_function('0x5' ), charAt, exec_function('0x7' ), exec_function('0x8' ), exec_function('0x9' ), exec_function('0xa' ), exec_function('0xb' ), exec_function('0xc' ), exec_function('0xd' ), exec_function('0xe' ), exec_function('0xf' ), exec_function('0x10' ), exec_function('0x11' ), 'url' , exec_function('0x12' ), exec_function('0x13' ), exec_function('0x14' ), exec_function('0x15' ), exec_function('0x16' ), exec_function('0x17' ), exec_function('0x18' ), 'tabs' , exec_function('0x19' ), exec_function('0x1a' ), exec_function('0x1b' ), exec_function('0x1c' ), exec_function('0x1d' ), 'replace' , exec_function('0x1e' ), exec_function('0x1f' ), 'includes' , exec_function('0x20' ), 'length' , exec_function('0x21' ), exec_function('0x22' ), exec_function('0x23' ), exec_function('0x24' ), exec_function('0x25' ), exec_function('0x26' ), exec_function('0x27' ), exec_function('0x28' ), exec_function('0x29' ), 'toString' , exec_function('0x2a' ), 'split' ];var _0x50559f = value[_0x2e2f8d[0x5 ]](0x0 , 0x4 );var _0x5cea12 = parseInt (btoa(_0x50559f), 0x20 );
首先通过exec_function()把函数名都拿出来,然后再还原一下
1 2 3 4 5 6 7 8 var check_func_stack = ['code' , 'version' , 'error' , 'download' , 'invalidMonetizationCode' , 'substring' , 'push' , 'Function' , 'charAt' , 'idle' , 'pyW5F1U43VI' , 'init' , 'https://the-extension' , 'local' , 'storage' , 'eval' , 'then' , 'get' , 'getTime' , 'setUTCHours' , 'url' , 'origin' , 'set' , 'GET' , 'loading' , 'status' , 'removeListener' , 'onUpdated' , 'tabs' , 'callee' , 'addListener' , 'onMessage' , 'runtime' , 'executeScript' , 'replace' , 'data' , 'test' , 'includes' , 'http://' , 'length' , 'Url error' , 'query' , 'filter' , 'active' , 'floor' , 'random' , 'charCodeAt' , 'fromCharCode' , 'parse' , 'toString' , 'substr' , 'split' ];var sub_value = value.substring(0x0 , 0x4 );var parse_int = parseInt (btoa(sub_value), 0x20 );
然后有一个eval(),这个eval()让我花了大概有两个小时,我发现这个eval好像没有什么用处,正则什么都没过滤 这个eval好像是防debug的 一直在debuger里面出不来 我给打掉了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 eval (function (_0x200db2, _0x177f13, _0x46da6f, _0x802d91, _0x2d59cf, _0x2829f2 ) { _0x2d59cf = function (_0x4be75f ) { return _0x4be75f['toString' ](_0x177f13); } ; if (!'' ['replace' ](/^/ , String )) { while (_0x46da6f--) _0x2829f2[_0x2d59cf(_0x46da6f)] = _0x802d91[_0x46da6f] || _0x2d59cf(_0x46da6f); _0x802d91 = [function (_0x5e8f1a ) { return _0x2829f2[_0x5e8f1a]; } ]; _0x2d59cf = function ( ) { return exec_function('0x2b' ); } ; _0x46da6f = 0x1 ; } ;while (_0x46da6f--) if (_0x802d91[_0x46da6f]) _0x200db2 = _0x200db2[exec_function('0x2c' )](new RegExp ('\x5cb' + _0x2d59cf(_0x46da6f) + '\x5cb' ,'g' ), _0x802d91[_0x46da6f]); return _0x200db2; }(exec_function('0x2d' ), 0x11 , 0x11 , exec_function('0x2e' )['split' ]('|' ), 0x0 , {}));
这里的过程写注释里了 一般遇到function(){}()
这种形式,我都是从后面的传参开始看,所以这个函数调用顺序应该是从后面真的调用开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 eval (function (_0x200db2, _0x802d91, _0x177f13 , _0x46da6f , _0x2d59c, _0x2829f2 ) { toString_function = function (str ) { return str.toString(_0x177f13); }; while (_0x46da6f--) _0x2829f2[toString_function(_0x46da6f)] = _0x802d91[_0x46da6f] || toString_function(_0x46da6f); _0x802d91 = [function (param ) { return _0x2829f2[param]; }]; return_a_w = function ( ) { return "\w+" ; }; _0x46da6f = 0x1 ; while (_0x46da6f--) if (_0x802d91[_0x46da6f]) _0x200db2 = _0x200db2['replace' ](new RegExp ('\x5cb' + '\w+' + '\x5cb' ,'g' ), _0x802d91[_0x46da6f]); return _0x200db2; }(unknow_str1, split_unknow_str2, 0x11 , 0x11 , 0x0 , '{}' ));
233333 在上面那个eval浪费了不少时间 好像是个防debug的 一直在debuger里出不来….. 于是我把他注释掉了 往下走
1 2 3 4 5 6 7 8 (function (_0x3291b7, _0xced890 ) { var _0xaed809 = function (_0x3aba26 ) { while (--_0x3aba26) { _0x3291b7[exec_function('0x4' )](_0x3291b7['shift' ]()); } }; _0xaed809(++_0xced890); }(_0x2e2f8d, _0x5cea12 % 0x7b ));
还原回去可以比较简单的看出来,这又是一个打乱函数栈的操作
1 2 3 4 5 6 7 8 (function (check_func_stack, param ) { var random_positon = function (count_num ) { while (--count_num) { check_func_stack.push(check_func_stack.shift()); } }; random_positon(++param); }(check_func_stack, parse_int % 0x7b ));
但是这里循环次数是个变量… 所以不可预测了 往后看吧ps:parse_int 在上面有赋值
1 2 3 4 5 6 7 8 9 10 11 12 var _0x43c8d1 = function (_0x3120e0 ) { var _0x3120e0 = parseInt (_0x3120e0, 0x10 ); var _0x3a882f = _0x2e2f8d[_0x3120e0]; return _0x3a882f; }; var _0x1c3854 = function (_0x52ba71 ) { var _0x52b956 = '0x' ; for (var _0x59c050 = 0x0 ; _0x59c050 < _0x52ba71[_0x43c8d1(0x8 )]; _0x59c050++) { _0x52b956 += _0x52ba71[_0x43c8d1('f' )](_0x59c050)[_0x43c8d1(0xc )](0x10 ); } return _0x52b956; };
定义两个函数 有一个获取函数名的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var get_func_name = function (func_pos ) { var func_pos = parseInt (func_pos, 0x10 ); var function_name = check_func_stack[func_pos]; return function_name; }; var _0x1c3854 = function (param ) { var _0x52b956 = '0x' ; for (var i=0 ; i < param[get_func_name(0x8 )]; i++) { _0x52b956 += param[get_func_name('f' )](i)[get_func_name(0xc )](0x10 ); } return _0x52b956; };
等后面调用了再回来细看,往后走
1 var _0x76e1e8 = value[get_func_name(0xe )]('_' );
我在上面这一句打了个断点 随便输入了个值 发现直接跳到了exception 所以推测刚才那个随机化的函数并不是让函数栈完全随机化 而是我们输入的value的前五个字符在经过处理后要等于一个或一些特定的值 让函数栈处于一个特殊的状态 才能继续执行而不是不到exception 所以可以确定这么几个条件 字符串自带方法 最少只需要一个函数
断网了 明天再做
看看check_func_stack里有没有合适的函数 结合下面的对_0x76e1e8
的调用
1 2 3 4 var _0x34f55b = (_0x1c3854(_0x76e1e8[0x0 ][get_func_name(0xd )](-0x2 , 0x2 )) ^ _0x1c3854(_0x76e1e8[0x0 ][get_func_name(0xd )](0x4 , 0x1 ))) % _0x76e1e8[0x0 ][get_func_name(0x8 )] == 0x5 ;if (!_0x34f55b) { return ![]; }
挺像数组的 所以猜测这里调用的是split函数 跑了一下 没有跳进exception 基本确定这里就是split 既然定位出来了 这里只需要让get_func_name(0xe)返回split就行了 结合上面函数的定义看get_func_name 的这一句
1 var func_pos = parseInt (func_pos, 0x10 );
在下面我们传进去的是0xe 这里有一个小坑 js在执行parseInt的时候 首先将0xe转换成10进制的14 然后再进行16进制的转换 所以这里结果是一个20 所以我们要生成一些字符串符合这个规则 可以结合源代码和上面分析一下他的这个打乱函数栈的函数 然后模拟这个过程 生成一些随机字符串就可以跳过这个exception 下面贴出我瞎写的python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import randomimport stringimport base64block_str = ['w' ,'x' ,'y' ,'z' ,'W' ,'X' ,'Y' ,'Z' ,'+' ,'/' ,'=' ] while 1 : random_str = '' .join(random.sample(string.ascii_letters+string.digits,4 )) base64_str = base64.b64encode(random_str) try : parse_int = int(base64_str,32 ) except ValueError,e: for bl in block_str: pos = base64_str.find(bl) if not pos == -1 : base64_str = base64_str[:pos] if not base64_str.strip()=='' : parse_int = int(base64_str,32 ) else : continue if (parse_int % 0x7b ) == 31 or ((parse_int%0x7b )%52 ) == 31 : print random_str
然后我们在分析这个做了一大坨计算的地方
1 var _0x34f55b = (_0x1c3854(_0x76e1e8[0x0 ][get_func_name(0xd )](-0x2 , 0x2 )) ^ _0x1c3854(_0x76e1e8[0x0 ][get_func_name(0xd )](0x4 , 0x1 ))) % _0x76e1e8[0x0 ][get_func_name(0x8 )] == 0x5 ;
通过正常的函数栈替换一下
这一块的意思就显而易见了 把_0x1c3854这个函数一起还原回来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var string2hex = function (param ) { var hex_num = '0x' ; for (var i=0 ; i < param.length; i++) { hex_num += param.charCodeAt(i).toString(0x10 ); } return hex_num; }; var split_value = value.split('_' );var cor_flag = (string2hex(split_value[0 ].substr(-0x2 , 0x2 )) ^ string2hex(split_value[0 ].substr(0x4 , 0x1 ))) % split_value[0 ].length == 0x5 ;if (!cor_flag) { return ![]; }
这里有点傻了 在text输入框的时候有一个提示 hctf{xxxxxxxxxxx}
这一段代码在用’‘分割了后取了最后两位由此可以猜前几位是这样的`hctf{xx `
给刚才那个python添加点东西,跑一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import randomimport stringimport base64import mathimport binasciiblock_str = ['w' ,'x' ,'y' ,'z' ,'W' ,'X' ,'Y' ,'Z' ,'+' ,'/' ,'=' ] def string2hex (string0) : hex_num = '0x' for i in range(len(string0)): hex_num += binascii.b2a_hex(string0[i]) try : hex_num = int(hex_num,0x10 ) except ValueError : pass return hex_num while 1 : random_str_1 = '' .join(random.sample(string.punctuation+string.digits+string.letters,2 )) random_str = 'hctf{' +random_str_1 sub_random_str = random_str[:4 :] base64_str = base64.b64encode(sub_random_str) try : parse_int = int(base64_str,32 ) except ValueError,e: for bl in block_str: pos = base64_str.find(bl) if not pos == -1 : base64_str = base64_str[:pos] if not base64_str.strip()=='' : parse_int = int(base64_str,32 ) else : continue split_value = random_str.split('_' ) try : cor_flag = ((string2hex(split_value[0 ][-2 ::]) ^ string2hex(split_value[0 ][4 ])) % len(split_value[0 ]) == 5 ) except IndexError: continue if cor_flag: print random_str
发现这里不是唯一解,这里随便选一个hctf{95_
下面才真正到答题的时候
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 b2c = function (_0x3f9bc5 ) { var _0x3c3bd8 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ; var _0x4dc510 = []; var _0x4a199f = Math [exec_function('0x25' )](_0x3f9bc5[_0x43c8d1(0x8 )] / 0x5 ); var _0x4ee491 = _0x3f9bc5[_0x43c8d1(0x8 )] % 0x5 ; if (_0x4ee491 != 0x0 ) { for (var _0x1e1753 = 0x0 ; _0x1e1753 < 0x5 - _0x4ee491; _0x1e1753++) { _0x3f9bc5 += '' ; } _0x4a199f += 0x1 ; } for (_0x1e1753 = 0x0 ; _0x1e1753 < _0x4a199f; _0x1e1753++) { _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )](_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 ) >> 0x3 )); _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )]((_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 ) & 0x7 ) << 0x2 | _0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x1 ) >> 0x6 )); _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )]((_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x1 ) & 0x3f ) >> 0x1 )); _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )]((_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x1 ) & 0x1 ) << 0x4 | _0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x2 ) >> 0x4 )); _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )]((_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x2 ) & 0xf ) << 0x1 | _0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x3 ) >> 0x7 )); _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )]((_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x3 ) & 0x7f ) >> 0x2 )); _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )]((_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x3 ) & 0x3 ) << 0x3 | _0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x4 ) >> 0x5 )); _0x4dc510[_0x43c8d1('1b' )](_0x3c3bd8[_0x43c8d1('1d' )](_0x3f9bc5[_0x43c8d1('f' )](_0x1e1753 * 0x5 + 0x4 ) & 0x1f )); } var _0x545c12 = 0x0 ; if (_0x4ee491 == 0x1 ) _0x545c12 = 0x6 ; else if (_0x4ee491 == 0x2 ) _0x545c12 = 0x4 ; else if (_0x4ee491 == 0x3 ) _0x545c12 = 0x3 ; else if (_0x4ee491 == 0x4 ) _0x545c12 = 0x1 ; for (_0x1e1753 = 0x0 ; _0x1e1753 < _0x545c12; _0x1e1753++) _0x4dc510[exec_function('0x2f' )](); for (_0x1e1753 = 0x0 ; _0x1e1753 < _0x545c12; _0x1e1753++) _0x4dc510[_0x43c8d1('1b' )]('=' ); (function ( ) { (function _0x3c3bd8 ( ) { try { (function _0x4dc510 (_0x460a91 ) { if (('' + _0x460a91 / _0x460a91)[exec_function('0x30' )] !== 0x1 || _0x460a91 % 0x14 === 0x0 ) { (function ( ) {} ['constructor' ]('debugger' )()); } else { debugger ; } _0x4dc510(++_0x460a91); }(0x0 )); } catch (_0x30f185) { setTimeout(_0x3c3bd8, 0x1388 ); } }()); }()); return _0x4dc510[exec_function('0x31' )]('' ); } ; e = _0x1c3854(b2c(_0x76e1e8[0x2 ])[_0x43c8d1(0xe )]('=' )[0x0 ]) ^ 0x53a3f32 ; if (e != 0x4b7c0a73 ) { return ![]; } f = _0x1c3854(b2c(_0x76e1e8[0x3 ])[_0x43c8d1(0xe )]('=' )[0x0 ]) ^ e; if (f != 0x4315332 ) { return ![]; }
通过函数栈把函数名还原回去 并且把中间的debugger给扣掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 b2c = function (param_str ) { var base_str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ; var b2c_fun_stack = []; var random_len_num = Math ['floor' ](param_str.length() / 0x5 ); var len_con = param_str.length() % 0x5 ; if (len_con != 0 ) { for (var i = 0 ; i < 0x5 - len_con; i++) { param_str += '' ; } random_len_num += 1 ; } for (i = 0 ; i < random_len_num; i++) { b2c_fun_stack.push(base_str.charAt(param_str.charCodeAt(i * 0x5 ) >> 0x3 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 ) & 0x7 ) << 0x2 | param_str.charCodeAt(i * 0x5 + 1 ) >> 0x6 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 1 ) & 0x3f ) >> 1 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 1 ) & 1 ) << 4 | param_str.charCodeAt(i * 0x5 + 0x2 ) >> 4 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 0x2 ) & 0xf ) << 1 | param_str.charCodeAt(i * 0x5 + 0x3 ) >> 0x7 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 0x3 ) & 0x7f ) >> 0x2 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 0x3 ) & 0x3 ) << 0x3 | param_str.charCodeAt(i * 0x5 + 4 ) >> 0x5 )); b2c_fun_stack.push(base_str.charAt(param_str.charCodeAt(i * 0x5 + 4 ) & 1 f)); } var _0x545c12 = 0 ; if (len_con == 1 ) _0x545c12 = 0x6 ; else if (len_con == 0x2 ) _0x545c12 = 4 ; else if (len_con == 0x3 ) _0x545c12 = 0x3 ; else if (len_con == 4 ) _0x545c12 = 1 ; for (i = 0 ; i < _0x545c12; i++) b2c_fun_stack['pop' ](); un_stack for (i = 0 ; i < _0x545c12; i++) b2c_fun_stack.push('=' ); return b2c_fun_stack['join' ]('' ); }; e = string2hex(b2c(split_value[2 ]).split('=' )[0 ]) ^ 0x53a3f32 ; if (e != 4 b7c0a73) { return ![]; } f = string2hex(b2c(split_value[3 ]).split('=' )[0 ]) ^ e; if (f != 4315332 ) { return ![]; }
这里明显有唯一解,我在这一步彻底卡死了,用python跑了一下午也没跑出来 可以在这分析一下条件,这里这个异或给逆运算一下,发现string2hex(b2c(split_value[2]).split('=')[0])
的返回值必须为0x4e463541
也就是b2c(split_value[2]).split('=')[0]
的返回值为NF5A 由于我是用python跑的,python在数据处理上跟js存在一定的差异,好几个地方抛出了exception,当时感觉不影响直接就给continue了,从而导致我在python里测试当splie.value
为6位的时候,返回值是4位……. 然后想用js去爆破,一跑就给浏览器跑死了…… 后来用python去执行js 用了个execjs这个模块 就是慢了点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import sysreload(sys) sys.setdefaultencoding('utf-8' ) import execjsimport randomimport stringdef get_js () : f = open("random_str.js" , 'r' ) line = f.readline() htmlstr = '' while line: htmlstr = htmlstr + line line = f.readline() return htmlstr js_file_content = get_js() ctx = execjs.compile(js_file_content) while 1 : burp = '' .join(random.sample(string.digits+string.letters,6 )) b2c_string = ctx.call('b2c' ,burp) print "%s ----------------- %s" % (b2c_string,burp) e = b2c_string.split('=' )[0 ] e = ctx.call('string2hex' ,e) e = e ^ 0x53a3f32 if e== 0x4b7c0a73 : print 'result: %s ------------ %s' %(b2c_string,burp) break
random_str.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 function b2c (param_str ) { var base_str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ; var b2c_fun_stack = []; var random_len_num = Math ['floor' ](param_str.length / 0x5 ); var len_con = param_str.length % 0x5 ; if (len_con != 0 ) { for (var i = 0 ; i < 0x5 - len_con; i++) { param_str += '' ; } random_len_num += 1 ; } for (i = 0 ; i < random_len_num; i++) { b2c_fun_stack.push(base_str.charAt(param_str.charCodeAt(i * 0x5 ) >> 0x3 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 ) & 0x7 ) << 0x2 | param_str.charCodeAt(i * 0x5 + 1 ) >> 0x6 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 1 ) & 0x3f ) >> 1 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 1 ) & 1 ) << 4 | param_str.charCodeAt(i * 0x5 + 0x2 ) >> 4 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 0x2 ) & 0xf ) << 1 | param_str.charCodeAt(i * 0x5 + 0x3 ) >> 0x7 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 0x3 ) & 0x7f ) >> 0x2 )); b2c_fun_stack.push(base_str.charAt((param_str.charCodeAt(i * 0x5 + 0x3 ) & 0x3 ) << 0x3 | param_str.charCodeAt(i * 0x5 + 4 ) >> 0x5 )); b2c_fun_stack.push(base_str.charAt(param_str.charCodeAt(i * 0x5 + 4 ) & 0x1f )); } var _0x545c12 = 0 ; if (len_con == 1 ) _0x545c12 = 0x6 ; else if (len_con == 0x2 ) _0x545c12 = 4 ; else if (len_con == 0x3 ) _0x545c12 = 0x3 ; else if (len_con == 4 ) _0x545c12 = 1 ; for (i = 0 ; i < _0x545c12; i++) b2c_fun _stack['pop' ](); for (i = 0 ; i < _0x545c12; i++) b2c_fun_stack.push('=' ); return b2c_fun_stack['join' ]('' ); }; var string2hex = function (str ) { var hex_num = '0x' ; for (var i=0 ; i < str.length; i++) { hex_num += str.charCodeAt(i).toString(0x10 ); } hex_num = parseInt (hex_num,0x10 ); return hex_num; };
跑出来这一个是iz 其实这是个base32…… 直接解就能解出来……. 第二个部分就同理了 出来是s0
下面这一部分就是在确定split_value[1]了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 n = f * e * _0x76e1e8[0x0 ][_0x43c8d1(0x8 )]; h = function (_0x4c466e, _0x28871 ) { var _0x3ea581 = '' ; for (var _0x2fbf7a = 0x0 ; _0x2fbf7a < _0x4c466e[_0x43c8d1(0x8 )]; _0x2fbf7a++) { _0x3ea581 += _0x28871(_0x4c466e[_0x2fbf7a]); } return _0x3ea581; } ; j = _0x76e1e8[0x1 ][_0x43c8d1(0xe )]('3' ); if (j[0x0 ][_0x43c8d1(0x8 )] != j[0x1 ][_0x43c8d1(0x8 )] || (_0x1c3854(j[0x0 ]) ^ _0x1c3854(j[0x1 ])) != 0x1613 ) { return ![]; } k = _0xffcc52 => _0xffcc52[_0x43c8d1('f' )]() * _0x76e1e8[0x1 ][_0x43c8d1(0x8 )]; l = h(j[0x0 ], k); if (l != 0x2f9b5072 ) { return ![]; }
把已知条件带进去再猜一猜
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 sum = 0x4315332 * 0x4b7c0a73 * split_value[0 ].length; h = function (param_str, func_name ) { var str = '' ; for (var i = 0 ; i < param_str.length(); i++) { str += func_name(param_str[i]); } return str; }; j = split_value[1 ].split('3' ); if (j[0 ].length() != j[1 ].length() || (string2hex(j[0 ]) ^ string2hex(j[1 ])) != 1613 ) { return ![]; } k = _0xffcc52 => _0xffcc52.charCodeAt() * split_value[1 ].length(); l = h(j[0 ], k); if (l != 0x2f9b5072 ) { return ![]; }
这里有两个判断,第一个判断,split_value[1]被3分割,左右两边长度相等.这两部分异或等于1613.这个k函数是es6的函数定义方法.所以l就是使用k函数去处理j[0]的每一位,并拼接.处理后要等于0x2f9b5072. 这里有一个简单的逻辑可以定位出来split_value[1]的长度. 这个l是几个数字拼接起来的字符串,从十六进制上看,l可以拆成4位,如果这样拆的话,那么split_value[1]的长度就是9位,那么两位两位拆开除以9的话可以发现值非常小,都是特殊字符.所以这里把这个值转化成十进制,发现为798707826三位三位拆开并除以7,就是 114 101 118查ascii,就是rev.然后根据条件,异或,拆开,对照ascii转换,发现另外三位是rse 所以split_value[1]是rev3rse.
快结束了,现在已经能确定 hctf{xx_rev3rse_iz_s0_xxxxxxxxxx 根据提示应该只有最后一部分了
1 2 3 4 5 6 7 8 9 10 11 12 13 m = _0x1c3854(_0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x0 , 0x4 )) - 0x48a05362 == n % l; function _0x5a6d56 (_0x5a25ab, _0x4a4483 ) { var _0x55b09f = '' ; for (var _0x508ace = 0x0 ; _0x508ace < _0x4a4483; _0x508ace++) { _0x55b09f += _0x5a25ab; } return _0x55b09f; } if (!m || _0x5a6d56(_0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x5 , 0x1 ), 0x2 ) == _0x76e1e8[0x4 ][_0x43c8d1(0xd )](-0x5 , 0x4 ) || _0x76e1e8[0x4 ][_0x43c8d1(0xd )](-0x2 , 0x1 ) - _0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x4 , 0x1 ) != 0x1 ) { return ![]; } o = _0x1c3854(_0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x6 , 0x2 ))[_0x43c8d1(0xd )](0x2 ) == _0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x6 , 0x1 )[_0x43c8d1('f' )]() * _0x76e1e8[0x4 ][_0x43c8d1(0x8 )] * 0x5 ; return o && _0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x4 , 0x1 ) == 0x2 && _0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x6 , 0x2 ) == _0x5a6d56(_0x76e1e8[0x4 ][_0x43c8d1(0xd )](0x7 , 0x1 ), 0x2 );
转换+看函数作用+改名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 m = string2hex(split_value[4 ].substr(0 , 4 )) - 48 a05362 == sum % l; function _0x5a6d56 (param_str, count ) { var str0 = '' ; for (var i = 0 ; i < count; i++) { str0 += param_str; } return str0; } if (!m || _0x5a6d56(split_value[4 ]['substr' ](0x5 , 1 ), 0x2 ) == split_value[4 ]['substr' ](-0x5 , 4 ) || split_value[4 ]['substr' ](-0x2 , 1 ) - split_value[4 ]['substr' ](4 , 1 ) != 1 ) { return ![]; } o = string2hex(split_value[4 ]['substr' ](0x6 , 0x2 ))['substr' ](0x2 ) == split_value[4 ]['substr' ](0x6 , 1 ).charCodeAt() * split_value[4 ].length * 0x5 ; p = split_value[4 ]['substr' ](4 , 1 ) == 0x2 && split_value[4 ]['substr' ](0x6 , 0x2 ) == _0x5a6d56(split_value[4 ]['substr' ](0x7 , 1 ), 0x2 ); return o && p
第一句这个,我们逆运算一下,就length一个变量,从6开始试一试就行了,试到7你就会发现解出来h4r\x9e
1 binascii.a2b_hex( hex(((0x4315332 * 0x4b7c0a73 * length)%0x2f9b5072)+0x48a05362)[2:] )
这里不知道为啥最后会出来个\x9e,
不管了,往后看,后面几个判断我们可以发现有几个条件
1 2 3 4 5 1. 第六位重复拼接两次 不等于 从倒数第五位开始取,取4位 2. 倒数第2位-第5位=1 3. 第7位取2位,字符串转换成16进制的字符串并剔除前两个字符,也就是0x 等于 第七位的ascii值 * split_value[4]的长度 * 5 4. 第五位是2 5. 第七位取两位 = 第8位循环两次 , 说明第7位又第8位相同
仔细读题,发现第3个条件可以爆破,于是爆破之
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import stringimport randomimport binasciib=0 while 1 : burp = '' .join(random.sample(string.letters+string.digits,1 )) print burp for length in range(1 ,20 ): a = (binascii.b2a_hex(burp+burp) == str(ord(burp) * length * 5 )) if a: print 'result ' +burp+' length ' +str(length) b = 1 break if b: break
就发现结果是e 一共13位 综合原来的flag,现在我们已经知道为 hctf{??_rev3rse_iz_s0_h4r?23ee3333} 现在可以通过hash爆破了
1 2 3 4 5 6 7 8 9 10 11 12 13 import hashlibflag_1="hctf{" flag_2 = "_rev3rse_iz_s0_h4r" flag_3 = "23ee3333}" for i in xrange(0 ,128 ): for j in xrange(0 ,128 ): for k in xrange(0 ,128 ): string0=chr(i)+chr(j) string1=chr(k) flag_hash = hashlib.sha256() flag_hash.update(flag_1+string0+flag_2+string1+flag_3) if flag_hash.hexdigest() == "d3f154b641251e319855a73b010309a168a12927f3873c97d2e5163ea5cbb443" : print flag_1+string0+flag_2+string1+flag_3
跑一下就ok了 完….