xp0int Posted on May 5 2019 题目描述:Do you love yy ? 下载后查看文件类型,为64位程序 ```bash $ file yy yy: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=a48620628ac444471e5ed0844b04d501eb5fd6ed, not stripped ``` 拖进IDA分析: `main`: ```pseudocode int __cdecl main(int argc, const char **argv, const char **envp) { size_t flag_len; // rax printf("give me flag:", argv, envp, argv); __isoc99_scanf("%255s", flag); // lower、number、{、}、_、C、T、F flag_len = strlen(flag); yyin = fmemopen(flag, flag_len, "r"); yyparse(); return 0; } ``` 主要逻辑是读取flag,求出flag长度,调用`yyparse`函数判断flag是否正确。 函数名一栏中,发现大量与`yy`有关的变量、函数。 ![function_name](https://leanote.com/api/file/getImage?fileId=5cc923a3ab64414dc9001a5a) 通过搜索,发现与lex(词法分析器)、yacc(语法分析器)有关,属于编译原理的知识,由于没学过,直接搜索相关例子分析一下: 如下: `test.lex` ```lex %{ int wordCount = 0; int spaceCount = 0; int numberCount = 0; %} chars [A-Za-z\_\'\.\"] numbers ([0-9])+ delim [" "\n\t] whitespace {delim}+ words {chars}+ %% {words} {wordCount++;} {whitespace} {spaceCount++;} {numbers} {numberCount++;} %% void main() { yylex(); printf(" Number of words: %d\n", wordCount); printf(" Number of spaces: %d\n", spaceCount); printf(" Number of nubmers: %d\n", numberCount); } int yywrap() { return 1; } ``` 程序用于统计字母、空格、数字的数量。 先用flex生成.c文件,然后编译生成可执行文件,用IDA分析,找到主体部分如下: ![test_yylex](https://leanote.com/api/file/getImage?fileId=5cc923a3ab64414dc9001a5b) 该部分利用switch统计字母、空格、数字的数量。由此可知yy程序中的yylex也有类似作用: ![yy_yylex](https://leanote.com/api/file/getImage?fileId=5cc923a3ab64414fbd001a3a) 观察分析可知字符有43种可能,若不属于这43种情况则程序会输出提示`input illegal!`。 由于是对flag进行分析,测试可知包含`*`、`{`、`}`、`a`~`z`、`0`~`9`、`C`、`T`、`F`、`_`,其余情况均不可行。 同时根据flag格式,可以测试得到flag需类似*CTF{abcd},否则提示错误`syntax error`。 通过上述分析,可知: ①lex生成yylex函数,将输入的符号转化成相应的标识进行后续的处理; ②yacc生成yyparse函数,根据yylex得到的标识进行相应的操作。 接下来分析yyparse对得到的标识进行相关操作的部分: ![yy_yyparse](https://leanote.com/api/file/getImage?fileId=5cc923a3ab64414fbd001a39) 可以判断是根据标识进行相应操作,经过gdb调试: 当字符为`{`时进行如下操作: ```ida case 3: pc = 0; *buffer = *&append; break; ``` 字符为`a`~`z`、`0`~`9`使用box[]相应的替换,idx为字符对应的索引: ```ida v = pc++; *(buffer + v) = box[idx]; break; ``` 字符为`_`进行加密操作: ```ida case 0xb: encrypt(buffer); break; ``` 然后继续进行替换: ```ida case 7: pc = 0; *buffer = *&append; break; ``` 字符为`}`进行加密: ```ida case 0x8: encrypt(buffer); break; ``` 然后进行最后的判断: ```ida case 2: if ( !memcmp(&result, cmp, 160uLL) ) puts("Congratulations!"); else puts("try again!"); break; ``` 字符为`a`~`z`、`0`~`9`时对应的box替换如下: alp = 'abcdefghijklmnopqrstuvwxyz0123456789' box[] = [ 0x82, 0x05, 0x86, 0x8A, 0x0B, 0x11, 0x96, 0x1D, 0x27, 0xA9, 0x2B, 0xB1, 0xF3, 0x5E, 0x37, 0x38, 0xC2, 0x47, 0x4E, 0x4F, 0xD6, 0x58, 0xDE, 0xE2, 0xE5, 0xE6, 0x67, 0x6B, 0xEC, 0xED, 0x6F, 0xF2, 0x73, 0xF5, 0x77, 0x7F] 如字符为`a`时,buffer对应字节替换成`0x82` 对buffer进行替换后经过AES-CBC加密,初始IV为0,仅给出每一轮的密钥如下: ![round_key](https://leanote.com/api/file/getImage?fileId=5cc923a3ab64414fbd001a3b) 第一次加密完后将结果保存在result+0x10处。 ![AES-CBC_1st](https://leanote.com/api/file/getImage?fileId=5cc923a3ab64414dc9001a5c) 后续调用AESencrypt,将上次加密结果作为IV进行加密,结果保存在下一个16字节中,直到完成9次加密。 然后进行`memcmp(&result, cmp, 160uLL)`判断加密结果与密文是否相符。 因此仅需将密文解密,进行逆操作即可得到flag。 ①先将cmp上各密文解密: AES_decrypt = [ 0xe5e5258631ab6eafb114fe76783d1eff, 0xbf904a7e652ba80e80c70072f43b0ced, 0x48b4e2a0d9cab76c1828bb0662faeec2, 0xc2d1fb9c9ac55903a59f62e28f607c87, 0x7e48129b839abdf56d2e9ef0e2f7655c, 0xebb2d434e854bca66563294bb86b07fd, 0xe876afe6d909194c28b8cc36e9943479, 0xbccd62baf2f72f345911f9a6ac0d4461, 0xcabfc34ebce35beb8e667dea5a4ae0b7, ] ②再异或上一次的结果得到明文: trans_m = [ 0xe5e5258631ab6eafb114fe76783d1eff, 0x11d65e864f6b675eb114fe76783d1eff, 0x6b4e258631ab6eafb114fe76783d1eff, 0x1d6f478a31ab6eafb114fe76783d1eff, 0x825e8a8631ab6eafb114fe76783d1eff, 0x5e67258631ab6eafb114fe76783d1eff, 0x5eeded8a31ab6eafb114fe76783d1eff, 0x4f37258631ab6eafb114fe76783d1eff, 0x47ed58ed474eedafb114fe76783d1eff] ③再与替换前的buffer(即append)比较找出替换的地方然后将其提取即可。 ### solve.py ```python #!/usr/bin/env python2 import string alp = string.lowercase + string.digits box = [ 0x82, 0x05, 0x86, 0x8A, 0x0B, 0x11, 0x96, 0x1D, 0x27, 0xA9, 0x2B, 0xB1, 0xF3, 0x5E, 0x37, 0x38, 0xC2, 0x47, 0x4E, 0x4F, 0xD6, 0x58, 0xDE, 0xE2, 0xE5, 0xE6, 0x67, 0x6B, 0xEC, 0xED, 0x6F, 0xF2, 0x73, 0xF5, 0x77, 0x7F, ] result = [ 0x0, 0xAE4614F82A40CF5031D3FE048C061212, 0x23FAC726E861D9C3A93C45701AC7F03D, 0xDFBEBC16AB6E37AC148B9C94F75D6278, 0xFC16981DB231D35ADC3A60869ACA7BA3, 0xB5D5F1B2D9FFD209D477D73DC0561902, 0xB69B426CE8A277E399AC324091A92A86, 0xF3FA473CC35C419BE80507D0D4305A9E, 0x8D529BA3FBADB6443F72839C2277FE48, 0xFE868412004EEDFFAC441923841F12CA, ] AES_decrypt = [ 0xe5e5258631ab6eafb114fe76783d1eff, 0xbf904a7e652ba80e80c70072f43b0ced, 0x48b4e2a0d9cab76c1828bb0662faeec2, 0xc2d1fb9c9ac55903a59f62e28f607c87, 0x7e48129b839abdf56d2e9ef0e2f7655c, 0xebb2d434e854bca66563294bb86b07fd, 0xe876afe6d909194c28b8cc36e9943479, 0xbccd62baf2f72f345911f9a6ac0d4461, 0xcabfc34ebce35beb8e667dea5a4ae0b7, ] trans_m = [] for i in range(len(AES_decrypt)): trans_m.append(hex(AES_decrypt[i] ^ result[i])[2:-1]); #print(trans_m) flag = '' append = '6124258631ab6eafb114fe76783d1eff' flag += '*CTF{' for i in range(len(trans_m)): for j in range(len(append)): if append[j*2:j*2+2] != trans_m[i][j*2:j*2+2]: idx = box.index(int(trans_m[i][j*2:j*2+2], 16)) flag += alp[idx] flag += '_' # print(flag) flag = flag[:-1] + '}' print(flag) # *CTF{yy_funt10n_1s_h4rd_and_n0_n33d_to_r3v3rs3} --> # *CTF{yy_funct10n_1s_h4rd_and_n0_n33d_to_r3v3rs3} ``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Misc] babyflash - wwmm [Misc] Neuron Break - LanceaKing
没有帐号? 立即注册