RE lab 03 - Static Analysis

Lab files and setup

Download the lab files from here Password is infected

ELF structure

As described in the course, Linux executables follow the ELF structure. If we want to reverse engineer Linux executables, we must learn some basics about this structure.

Firstly, ELF files can have dual use: as an object file by compilers or as runnable files by the OS interpreter. This is the reason why ELF programs have both segments, also known as program headers and sections.

Segments

To investigate program headers on Linux we can use the readelf utility in the binutils package. Most of the time, the target binary is compiled by gcc or clang. In these cases, the structure (internal organization) of output binaries is basically the same. Let us now look at the structure of a classic “Hello, World” program.

$ readelf --program-headers  test

Elf file type is DYN (Shared object file)
Entry point 0x580
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000001f8 0x00000000000001f8  R E    0x8
  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000000089c 0x000000000000089c  R E    0x200000
  LOAD           0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8
                 0x0000000000000258 0x0000000000000260  RW     0x200000
  DYNAMIC        0x0000000000000df0 0x0000000000200df0 0x0000000000200df0
                 0x00000000000001e0 0x00000000000001e0  RW     0x8
  NOTE           0x0000000000000254 0x0000000000000254 0x0000000000000254
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x0000000000000754 0x0000000000000754 0x0000000000000754
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8
                 0x0000000000000228 0x0000000000000228  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got 

Sections

$ readelf --section-headers  test
There are 31 section headers, starting at offset 0x1a00:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002b8  000002b8
       00000000000000c0  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000000378  00000378
       0000000000000096  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000000040e  0000040e
       0000000000000010  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000000420  00000420
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000000440  00000440
       00000000000000d8  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000000518  00000518
       0000000000000018  0000000000000018  AI       5    24     8
  [11] .init             PROGBITS         0000000000000530  00000530
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000000550  00000550
       0000000000000020  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000000570  00000570
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         0000000000000580  00000580
       00000000000001b1  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         0000000000000734  00000734
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         0000000000000740  00000740
       0000000000000012  0000000000000000   A       0     0     4
  [17] .eh_frame_hdr     PROGBITS         0000000000000754  00000754
       000000000000003c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000000790  00000790
       000000000000010c  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000200dd8  00000dd8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000200de0  00000de0
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000200de8  00000de8
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .dynamic          DYNAMIC          0000000000200df0  00000df0
       00000000000001e0  0000000000000010  WA       6     0     8
  [23] .got              PROGBITS         0000000000200fd0  00000fd0
       0000000000000030  0000000000000008  WA       0     0     8
  [24] .got.plt          PROGBITS         0000000000201000  00001000
       0000000000000020  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000201020  00001020
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000201030  00001030
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00001030
       000000000000002d  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00001060
       0000000000000660  0000000000000018          29    48     8
  [29] .strtab           STRTAB           0000000000000000  000016c0
       000000000000022f  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  000018ef
       000000000000010c  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

For a reverse engineer the following sections are (usually) of interest:

For an exploit developer the following additional sections are of interest for information leakage/control flow hijacking purposes:

IDA cheat sheet:

Renaming/Redeclaring

Reorganizing the stack variables

When in doubt

Task 1: Reverse engineering with spoilers

Usually, when reverse engineering, all we have is a binary. Starting from it, we need to reconstruct (mainly through guessing/inferences) what the function names could be, what the variables are used for, what the program does as a whole.

For this task, you have a binary, task1, and also its corresponding source code, task1.c. Using the stripped binary, you will simulate normal reverse engineering by using the source code (instead of guessing).

Your task is to create a near-original replica of the original source in the IDA interface.

Task 2: Statically linked crackme - graybox analysis (dynamic + static)

In this task, you will learn to navigate through functions in a statically linked and stripped crackme.

Since the binary has a whopping 783 functions detected, you do not have the time or motivation to go through all of them. As such, you need to approach the problem in a clever and elegant way:

Task 3: Data Structures

In this task, you will learn to use the Structures functionality of IDA.

Only the simplest programs are written without any sort of data structures in mind. Even basic OOP features are implemented using structures; classes themselves are also compiled as structures. However, after compilation, structure and type information is lost (if we don’t have debugging symbols) but we can still observe repeated access patterns and infer what various structures might have looked like.

Look at the code in main and the password checking function, analyze the access patterns and verify that it matches the linked list structure below.

00000000 struc_1         struc ; (sizeof=0x10, mappedto_8)
00000000 field_0_idx     dd ?
00000004 field_4         db ?
00000005                 db ? ; undefined
00000006                 db ? ; undefined
00000007                 db ? ; undefined
00000008 field_8_next    dq ?                    ; offset
00000010 struc_1         ends