jarvis->guess
前言
周末摸鱼,看做出来的人数以为好做,结果GG,感觉这个题有点像以前看过的一道crypto,总结一下好了
程序逻辑
下载下来的文件是用来本地测试的,开了socket,监听9999端口,其实就是模拟服务器跑的程序,所以本地通信建一个socket就好。重点是is_flag_correct()函数,是一个验证用户输入的函数,所以这个看起来更像一个逆向题。
调用这个函数前有一个fgets(inbuf,0x1000,stdin),如果长度这么长,那么在flag_hexa = flag_hex的时候构造一个100*’A’+’\x00’即可绕过长度检查,造成溢出,不过这个测试之后发现好像没什么用,fuzz没什么异常,继续看函数,里面check flag是通过跟一个预存在栈上的flag数组对比确定的,但是是通过bin_by_hex这个数组做了一次映射确定,而这个数组初始化又是由0x401100LL地址上的数据完成的,有趣的是这个数组的0x30处正是0,其ascii码也是0x30,同理A-F的ascii码对应其数组的索引。因此这个比较只需要用户按顺序输入flag即可,这个比较当然不如直接让用户输入和flag对比来的快,所以这里面一定有什么设计。
程序漏洞
漏洞就出现在数组索引的检查,数组其实就是一个字符串指针,array[i]等同于*(array+i),所以当i为负数时,其得到的数据是字符串指针低地址处的数据,这里的flag位置是ebp-0x150,bin_hex_index的位置在ebp-0x110,因此bin_hex_index[-0x40]即flag[0],想要验证成功,只需value1 = 0,value2 = flag[i]。flag[i] = bin_hex_index[-0x40+i],即用户输入chr(-0x40+i),但是注意chr()范围是0-256,因此-0x40+i需要+0x100(模100类似格式化字符串以前的知识),最终构造出一个完美验证通过的payload。
仅仅通过验证不是目的,我们还得知道真正的flag是什么,这里因为我们已经通过payload通过了验证,那么现在只需要修改Payload的第一、二字节,使其为ascii(20-127),重复验证,一旦得到通过即可确定第一个字节,后面的也同理,两个字节两个字节的确定,直到所有payload都被替换成真正的flag
exp.py
1 | #coding=utf-8 |
中间recv会报timeout,可以修改for循环start,断点验证,之后hex_encode即可
编外
看别人的wp有说patch改掉alarm然后方便调试的,这里也记录一下吧,虽然自己没调试
找到call _alarm位置,option->general,number of opcode bytes改为8
选中mov edi,78h call _alarm,打开Edit->Patch Program->change bytes
最后Edit->Patch Program->Apply patches to input file