2023阿里云CTF 字节码跳动 将给到的附件解压可以得到
可以看到flagchecker
两个文件,用010 editor可以看到.jsc中有right,wrong和main等字样,所以可以知道flag的主要检查逻辑就在这里了,.txt里面给的是js的v8字节码。
run.sh 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 # !/bin/bash # please figure out the real flag. if [ $# != 1 ]; then echo "Use: bash $0 aliyunctf{00000000000000000000000000000000}" exit 0 fi FLAG=$1 if [ -f "flagchecker.js" ]; then # build this challenge # make sure $FLAG is the real flag cmd1=`./node flagchecker.js $FLAG` if [ $cmd1 != "Right!" ];then echo "Exit" exit 0 fi # generate bytecode of flagchecker.js ./node --print-bytecode flagchecker.js $FLAG > flagchecker_bytecode.txt # compile flagchecker.js to flagchecker.jsc, for you to verify your flag. ./node ./runner.js $FLAG tar czvf jsbytecodechall1.tar.gz ./node ./runner.js ./flagchecker.jsc ./run.sh flagchecker_bytecode.txt else # check your flag ./node ./runner.js $FLAG fi
run.sh告诉我们,flagchecker.jsc是flagchecker.js编译产⽣的,⽤来验证flag,并且将flagchecker.js的字节码输出到了 flagchecker_bytecode.txt中。如果不存在flagchecker.js⽂件,则直接使⽤./node ./runner.js $FLAG 检验flag
由于在.jsc文件中找到了main函数,所以我直接在.txt文件中搜索这个函数名了,最后找到了一个非常可疑的地方
这里不仅有main,还有ccc和aaa两个函数,而后面的两个函数刚好也在.jsc的文件中出现过,所以猜测主要逻辑就在这里。
继续在.txt中寻找着三个函数,找到了对应的字节码,然后直接手动恢复
参考
Ignition Bytecode for V8 Interpreter - 翻车鱼 (shi1011.cn)
function ccc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function ccc (input, output, check ) { let key = 170 ; for (let i = 0 ; i < 19 ; i++) { output[i] = (input[i] + 51 + key) & 255 ; key = output[i]; } let key1 = 85 ; for (let i = 19 ; i < 43 ; i++) { output[i] = (input[i] + key1) & 255 ; key1 = (output[i] ^ key1) & 255 ; } if (key1 != 159 ){ return false ; } }
function aaa 1 2 3 4 5 6 7 8 9 10 function aaa (argv0 ) { const buf = Buffer .from (argv0); if (buf.length == 43 ) { return false ; } const v0 = Buffer .alloc (43 ); const v1 = Buffer .from ("3edd7925cd6e04ab44f25bef57bc53bd20b74b8c11f893090fdcdfddad0709100100fe6a9230333234fbae" , "hex" ); return ccc (buf, v0, v1); }
function main 1 2 3 4 5 6 7 8 9 10 function main ( ) { if (!process.argv [2 ]) { console .log ("Wrong!" ); } if (aaa (process.argv [2 ])){ console .log ("Right!" ); } else { console .log ("Wrong!" ); } }
最后的解密脚本
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function decrypt_ccc ( ) { const v0 = Buffer .from ("3edd7925cd6e04ab44f25bef57bc53bd20b74b8c11f893090fdcdfddad0709100100fe6a9230333234fbae" , "hex" ); const v1 = Buffer .alloc (43 ); let i = 0 ; let key = 170 ; for (i = 0 ; i < 19 ; i++) { v1[i] = (v0[i] - 51 - key) & 255 ; key = v0[i]; } let key2 = 85 ; for (i = 19 ; i < 43 ; i++) { v1[i] = (v0[i] - key2) & 255 ; key2 = (v0[i] ^ key2) & 255 ; } console .log (v1.toString ("hex" )); }decrypt_ccc ();
1 2 3 a = "616c6979756e6374667b36613532643730646137383063666537663732313838393735333561346636317d" print (bytes .fromhex(a).decode())
比赛时没做出来主要还是没有找到合适的教程,v8字节码是一点也没接触过,做这个过度依赖chatgpt了,实际上它的翻译远不如自己手动翻译,