squ1rrelCTF/camelcamelcamel ocmal逆向,不会
动调找到比较,验证为逐字节比较,可以尝试测信道(flag长度为40,没找到cmp的时候和40进行比较的地方,不然可以通过更改40的值来逐位爆破,这种方法最快)
pintools爆破脚本如下,爆破要好几十分钟
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 import sysimport subprocessimport argparseimport refrom tqdm import tqdmfrom string import printableprint (printable)printable="?}_0123456789sabcdefghijklmnopqrtuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" PIN = "/home/mjw/pin-external-3.31-98869-gfa6f126a8-gcc-linux/pin" INSCOUNT32 = "/home/mjw/pin-external-3.31-98869-gfa6f126a8-gcc-linux/source/tools/ManualExamples/obj-ia32/inscount0.so" INSCOUNT64 = "/home/mjw/pin-external-3.31-98869-gfa6f126a8-gcc-linux/source/tools/ManualExamples/obj-intel64/inscount0.so" def pin (file,passwd ): try : command = "echo " + passwd + " | " + PIN + " -t " + INSCOUNT + " -- " + file + " ; cat inscount.out" output = subprocess.check_output(command, shell=True , stderr=subprocess.PIPE) except : print ("Unexpected error:" , sys.exc_info()[0 ]) raise output = re.findall(r"Count ([\w.-]+)" , output.decode()) return int ('' .join(output)) INSCOUNT=INSCOUNT64 file="/home/mjw/Desktop/camelcamelcamel" base="" for i in range (0x28 ): counts=[0 ]*len (printable) for j in tqdm(range (len (printable))): try_data = (base+printable[j]).ljust(0x28 , "0" ) count=pin(file,try_data) print (try_data,count) counts[j]=count maxcount=max (counts) idx=counts.index(maxcount) base+=printable[idx]
所以尝试用frida在0x3F6E0插桩,统计cmp的次数,来确定正确的字节实现爆破
实现代码如下
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 import fridaimport subprocessimport timefrom tqdm import tqdmprintable = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ}!#$%&'()*+,-./:;<=>?@[\\]^`|" def get_count (try_data ): process_name = "/home/mjw/Desktop/camelcamelcamel" process = subprocess.Popen( process_name, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) time.sleep(0.1 ) session = frida.attach(process.pid) hook_script = """ var count = 0; // 初始化调用次数 rpc.exports = { hooknative: function () { let base = Module.findBaseAddress("camelcamelcamel"); // 查找模块基址 let addr = base.add(0x3f6e0); // 偏移地址 = 函数地址 Interceptor.attach(addr, { onEnter: function (args) { count++; // 每次调用时 +1 send(count); // 发送 count 给 Python } }); } }; """ script = session.create_script(hook_script) def on_message (message, data ): if message['type' ] == 'send' : global result result = message['payload' ] script.on("message" , on_message) script.load() script.exports_sync.hooknative() process.stdin.write(try_data.encode('utf-8' )) process.stdin.flush() output, error = process.communicate() s = output.decode('gbk' ) process.terminate() return result, s base = "" for i in tqdm(range (len (base), 40 )): found = False for j in printable: try_data = (base + j).ljust(0x28 , "0" ) count = -1 while count == -1 : try : count, output = get_count(try_data) except : pass print (try_data, count, i, output) if "In" not in output: print ("flag:" , try_data) exit() if count >= i + 2 : found = True base += j print (base) break if not found: print ("not found!" ) exit()
正常跑完代码只需要3min左右,但frida.attach可能会卡死或者出错找不到,可以手动添加已经爆破掉的字节到base,再重新运行python代码
或者将计数代码封装到incount.py
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 76 77 78 79 80 81 import fridaimport subprocessimport timeimport argparseparser = argparse.ArgumentParser(description="Frida hook runner" ) parser.add_argument("process_name" , help ="Path to the target binary" ) parser.add_argument("offset" , help ="Hex offset (e.g. 0x3f6e0)" ) parser.add_argument("try_data" , help ="Input data for stdin" ) args = parser.parse_args() process_name = args.process_name offset = int (args.offset, 16 ) try_data = args.try_data process = subprocess.Popen( process_name, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) time.sleep(0.1 ) session = frida.attach(process.pid) hook_script = """ var count = 0; var attached = false; rpc.exports = { hooknative: function (modname, offsetHex) { if (attached) return; attached = true; var base = Module.findBaseAddress(modname); if (base === null) { send("error: module not found"); return; } var addr = base.add(ptr(offsetHex)); Interceptor.attach(addr, { onEnter: function (args) { count++; send(count); } }); } }; """ script = session.create_script(hook_script) def on_message (message, data ): if message['type' ] == 'send' : global result result = message['payload' ] elif message['type' ] == 'error' : print ("JS error:" , message['stack' ]) script.on("message" , on_message) script.load() script.exports_sync.hooknative(process_name, hex (offset)) process.stdin.write(try_data.encode('utf-8' )) process.stdin.flush() output, error = process.communicate() s = output.decode('gbk' , errors='ignore' ) process.terminate() print (result, s)
然后exp.py不断启动该py代码,不会卡死,但还会出错,概率比前面低,并且多耗了一倍的时间,大约要6min
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 import subprocessfrom tqdm import tqdmprintable="_{}0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+,-./:;<=>?@[\]^`|" base="" for i in tqdm(range (len (base),40 )): for j in printable: count=-2 while count==-2 : try : try_data=(base+j).ljust(40 ,"0" ) process = subprocess.Popen(['python3' , 'incount.py' ,"/home/mjw/Desktop/camelcamelcamel" ,'0x3f6e0' ,try_data], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) stdout, stderr = process.communicate() temp=stdout.split(" " ) count=int (temp[0 ]) output=temp[1 ] except :pass if "In" not in output: print ("flag:" ,try_data) exit() if count>=i+2 : base+=j print (base) break
不清楚为啥会count有时候会统计出错,导致找不到,汗流浃背了